• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

cofacts / rumors-site / 4076429062

pending completion
4076429062

push

github

GitHub
Bump degenerator and pm2

387 of 604 branches covered (64.07%)

Branch coverage included in aggregate %.

925 of 1113 relevant lines covered (83.11%)

12.91 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

78.0
/components/Hyperlinks.js
1
import gql from 'graphql-tag';
2
import { t } from 'ttag';
3
import { Box, CircularProgress } from '@material-ui/core';
4
import { makeStyles } from '@material-ui/core/styles';
5
import { useQuery } from '@apollo/react-hooks';
6

7
import { HyperlinkIcon } from 'components/icons';
8

9
const useStyles = makeStyles(theme => ({
1✔
10
  root: {
11
    padding: '12px 16px',
12
    margin: '0 8px 8px 0',
13
    background: theme.palette.secondary[50],
14
    borderRadius: 8,
15
    maxWidth: '100%',
16
    '& h1': {
17
      overflow: 'hidden',
18
      textOverflow: 'ellipsis',
19
      whiteSpace: 'nowrap',
20
      margin: 0,
21
      marginBottom: 10,
22
      maxWidth: 'inherit',
23
      fontSize: 14,
24
    },
25
  },
26
  url: {
27
    color: theme.palette.secondary[300],
28
    display: 'flex',
29
    alignItems: 'center',
30
    marginTop: 16,
31
    '& a': {
32
      color: theme.palette.secondary[300],
33
      display: 'block',
34
      whiteSpace: 'nowrap',
35
      overflow: 'hidden',
36
      textOverflow: 'ellipsis',
37
      textDecoration: 'none',
38
      marginLeft: 8,
39
    },
40
  },
41
  preview: {
42
    display: 'flex',
43
    borderLeft: `4px solid ${theme.palette.secondary[200]}`,
44
    paddingLeft: 10,
45
    paddingRight: 15,
46
    minHeight: 40,
47
    justifyContent: 'space-between',
48
    '& .summary': {
49
      color: theme.palette.secondary[500],
50
      overflow: 'hidden',
51
      margin: 0,
52
      display: '-webkit-box',
53
      boxOrient: 'vertical',
54
      textOverflow: 'ellipsis',
55
      lineClamp: 2,
56
    },
57
    '& .image': {
58
      marginLeft: 10,
59
      margin: 0,
60
      maxHeight: 60,
61
      minWidth: 60,
62
      background: `#ccc center center no-repeat`,
63
      backgroundSize: 'cover',
64
      backgroundImage: ({ topImageUrl }) => `url(${topImageUrl})`,
3✔
65
      borderRadius: 3,
66
    },
67
  },
68
  error: {
69
    color: 'firebrick',
70
    fontStyle: 'italic',
71
  },
72
}));
73

74
const HyperlinkData = gql`
1✔
75
  fragment HyperlinkData on Hyperlink {
76
    title
77
    url
78
    summary
79
    topImageUrl
80
    error
81
  }
82
`;
83

84
export const POLLING_QUERY = {
1✔
85
  articles: gql`
86
    query PollingArticleHyperlink($id: String!) {
87
      GetArticle(id: $id) {
88
        id
89
        hyperlinks {
90
          ...HyperlinkData
91
        }
92
      }
93
    }
94
    ${HyperlinkData}
95
  `,
96
  replies: gql`
97
    query PollingReplyHyperlink($id: String!) {
98
      GetReply(id: $id) {
99
        id
100
        hyperlinks {
101
          ...HyperlinkData
102
        }
103
      }
104
    }
105
    ${HyperlinkData}
106
  `,
107
};
108

109
/**
110
 * @param {string} error - One of ResolveError https://github.com/cofacts/url-resolver/blob/master/src/typeDefs/ResolveError.graphql
111
 */
112
function getErrorText(error) {
113
  switch (error) {
1!
114
    case 'NAME_NOT_RESOLVED':
115
      return t`Domain name cannot be resolved`;
×
116
    case 'UNSUPPORTED':
117
    case 'INVALID_URL':
118
      return t`URL is malformed or not supported`;
×
119
    case 'NOT_REACHABLE':
120
      return t`Cannot get data from URL`;
1✔
121
    case 'HTTPS_ERROR':
122
      return t`Target site contains HTTPS error`;
×
123
    default:
124
      return t`Unknown error`;
×
125
  }
126
}
127

128
/**
129
 * @param {object} props.hyperlink
130
 * @param {string} props.rel - rel prop for the hyperlink (other than noopener and noreferrer)
131
 */
132
function Hyperlink({ hyperlink, rel = '' }) {
3✔
133
  const { title, topImageUrl, error, url } = hyperlink;
3✔
134
  const summary = (hyperlink.summary || '').slice(0, 200);
3✔
135

136
  const classes = useStyles({ topImageUrl });
3✔
137

138
  return (
3✔
139
    <article className={classes.root}>
140
      <h1 title={title}>{title}</h1>
141
      <div className={classes.preview}>
142
        <p className="summary" title={summary}>
143
          {summary}
144
        </p>
145
        {topImageUrl && <figure className="image" />}
4✔
146
        {error && <p className="error">{getErrorText(error)}</p>}
4✔
147
      </div>
148
      <span className={classes.url}>
149
        <HyperlinkIcon />
150
        <a href={url} target="_blank" rel={`noopener noreferrer ${rel}`}>
151
          {url}
152
        </a>
153
      </span>
154
    </article>
155
  );
156
}
157

158
function PollingHyperlink({ pollingType, pollingId }) {
159
  // The loaded data will populate apollo-client's normalized Article/Reply cache via apollo-client
160
  // automatic cache updates.
161
  // Ref: https://www.apollographql.com/docs/react/caching/cache-configuration/#automatic-cache-updates
162
  //
163
  // Therefore, we don't need to read query results here.
164
  //
165
  useQuery(POLLING_QUERY[pollingType], {
1✔
166
    variables: { id: pollingId },
167
    pollInterval: 2000,
168
  });
169

170
  return <CircularProgress />;
1✔
171
}
172

173
/**
174
 * @param {object[] | null} props.hyperlinks
175
 * @param {'articles'|'replies'?} props.pollingType - poll article or reply for hyperlinks when it's not loaded (null)
176
 * @param {string?} props.pollingId - polling article or reply id for hyperlinks when it's not loaded (null)
177
 * @param {string?} props.rel - rel prop for the hyperlink (other than noopener and noreferrer)
178
 */
179
function Hyperlinks({ hyperlinks, pollingType, pollingId, rel }) {
180
  if (!((pollingId && pollingType) || (!pollingId && !pollingType))) {
3!
181
    throw new Error('pollingType and pollingId must be specified together');
×
182
  }
183

184
  if (hyperlinks && hyperlinks.length === 0) return null;
3✔
185

186
  return (
2✔
187
    <Box
188
      component="section"
189
      display="flex"
190
      flexDirection="row"
191
      flexWrap="wrap"
192
      mt={2}
193
      mb={1}
194
    >
195
      {(hyperlinks || []).map((hyperlink, idx) => (
3✔
196
        <Hyperlink key={idx} hyperlink={hyperlink} rel={rel} />
3✔
197
      ))}
198
      {!hyperlinks && pollingId && (
4✔
199
        <PollingHyperlink pollingId={pollingId} pollingType={pollingType} />
200
      )}
201
    </Box>
202
  );
203
}
204

205
Hyperlinks.fragments = {
1✔
206
  HyperlinkData,
207
};
208

209
export default Hyperlinks;
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc