File size: 3,956 Bytes
4279593
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2bab159
4279593
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c3d9a20
4279593
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
import * as React from 'react';
import { useState, useEffect, useCallback } from 'react';
import Box from '@mui/material/Box';
import Card from '@mui/material/Card';
import CardContent from '@mui/material/CardContent';
import Typography from '@mui/material/Typography';
import './Sources.css';

// Helper function to extract a friendly domain name from a URL.
const getDomainName = (url) => {
  try {
    const hostname = new URL(url).hostname;
    // Remove "www." if present.
    const domain = hostname.startsWith('www.') ? hostname.slice(4) : hostname;
    // Return the first part in title case.
    const parts = domain.split('.');
    return parts[0].charAt(0).toUpperCase() + parts[0].slice(1);
  } catch (err) {
    return url;
  }
};

export default function Sources({ sources, handleSourceClick }) {
  // "sources" prop is the payload passed from the parent.
  const [fetchedSources, setFetchedSources] = useState([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  const fetchSources = useCallback(async () => {
    setLoading(true);
    setError(null);
    const startTime = Date.now(); // record start time
    try {
      // Use sources.payload if it exists.
      const bodyData = sources && sources.payload ? sources.payload : sources;
      const res = await fetch("/action/sources", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(bodyData)
      });
      const data = await res.json();
      // Backend returns {"result": [...]}
      setFetchedSources(data.result);
    } catch (err) {
      console.error("Error fetching sources:", err);
      setError("Error fetching sources.");
    }
    const elapsed = Date.now() - startTime;
    // Ensure that the loading state lasts at least 1 second.
    if (elapsed < 500) {
      setTimeout(() => {
        setLoading(false);
      }, 500 - elapsed);
    } else {
      setLoading(false);
    }
  }, [sources]);

  useEffect(() => {
    if (sources) {
      fetchSources();
    }
  }, [sources, fetchSources]);

  if (loading) {
    return (
      <Box className="sources-container">
        <Typography className="loading-sources" variant="body2">Loading Sources...</Typography>
      </Box>
    );
  }

  if (error) {
    return (
      <Box className="sources-container">
        <Typography variant="body2" color="error">{error}</Typography>
      </Box>
    );
  }

  return (
    <Box className="sources-container">
      {fetchedSources.map((source, index) => {
        const domain = getDomainName(source.link);
        let hostname = '';
        try {
          hostname = new URL(source.link).hostname;
        } catch (err) {
          hostname = source.link;
        }
        return (
          <Card
            key={index}
            variant="outlined"
            className="source-card"
            onClick={() => handleSourceClick(source)}
          >
            <CardContent>
              {/* Header/Title */}
              <Typography variant="h6" component="div" className="source-title">
                {source.title}
              </Typography>
              {/* Link info: icon, domain, bullet, serial number */}
              <Typography variant="body2" className="source-link">
                <img 
                  src={`https://www.google.com/s2/favicons?domain=${hostname}`} 
                  alt={domain} 
                  className="source-icon" 
                />
                <span className="source-domain">{domain}</span>
                <span className="separator"></span>
                <span className="source-serial">{index + 1}</span>
              </Typography>
              {/* Description */}
              <Typography variant="body2" className="source-description">
                {source.description}
              </Typography>
            </CardContent>
          </Card>
        );
      })}
    </Box>
  );
}