Feature complete
parent
7b186af765
commit
0544a6f00d
|
@ -62,5 +62,6 @@ Third party libraries and resources:
|
|||
* [github/gemoji](https://github.com/github/gemoji) (MIT) is used for emoji support (specifically the [emoji.json](https://raw.githubusercontent.com/github/gemoji/master/db/emoji.json) file)
|
||||
* [Lightbox with vanilla JS](https://yossiabramov.com/blog/vanilla-js-lightbox) as a lightbox on the landing page
|
||||
* [HTTP middleware for gzip compression](https://gist.github.com/CJEnright/bc2d8b8dc0c1389a9feeddb110f822d7) (MIT) is used for serving static files
|
||||
* [Regex for auto-linking](https://github.com/bryanwoods/autolink-js) (MIT) is used to highlight links (the library is not used)
|
||||
* [Statically linking go-sqlite3](https://www.arp242.net/static-go.html)
|
||||
* [Linked tabs in mkdocs](https://facelessuser.github.io/pymdown-extensions/extensions/tabbed/#linked-tabs)
|
||||
|
|
|
@ -20,9 +20,6 @@ import ErrorBoundary from "./ErrorBoundary";
|
|||
import routes from "./routes";
|
||||
import {useAutoSubscribe, useConnectionListeners} from "./hooks";
|
||||
|
||||
// TODO link lighlighting
|
||||
// TODO "copy url" toast
|
||||
// TODO "copy link url" button
|
||||
// TODO add drag and drop
|
||||
// TODO races when two tabs are open
|
||||
// TODO investigate service workers
|
||||
|
|
|
@ -1,5 +1,16 @@
|
|||
import Container from "@mui/material/Container";
|
||||
import {ButtonBase, CardActions, CardContent, CircularProgress, Fade, Link, Modal, Stack} from "@mui/material";
|
||||
import {
|
||||
ButtonBase,
|
||||
CardActions,
|
||||
CardContent,
|
||||
CircularProgress,
|
||||
Fade,
|
||||
Link,
|
||||
Modal,
|
||||
Snackbar,
|
||||
Stack,
|
||||
Tooltip
|
||||
} from "@mui/material";
|
||||
import Card from "@mui/material/Card";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import * as React from "react";
|
||||
|
@ -9,7 +20,7 @@ import {
|
|||
formatMessage,
|
||||
formatShortDateTime,
|
||||
formatTitle,
|
||||
openUrl,
|
||||
openUrl, shortUrl,
|
||||
topicShortUrl,
|
||||
unmatchedTags
|
||||
} from "../app/utils";
|
||||
|
@ -66,6 +77,7 @@ const SingleSubscription = (props) => {
|
|||
const NotificationList = (props) => {
|
||||
const pageSize = 20;
|
||||
const notifications = props.notifications;
|
||||
const [snackOpen, setSnackOpen] = useState(false);
|
||||
const [maxCount, setMaxCount] = useState(pageSize);
|
||||
const count = Math.min(notifications.length, maxCount);
|
||||
|
||||
|
@ -81,7 +93,7 @@ const NotificationList = (props) => {
|
|||
dataLength={count}
|
||||
next={() => setMaxCount(prev => prev + pageSize)}
|
||||
hasMore={count < notifications.length}
|
||||
loader={<h1>aa</h1>}
|
||||
loader={<>Loading ...</>}
|
||||
scrollThreshold={0.7}
|
||||
scrollableTarget="main"
|
||||
>
|
||||
|
@ -91,7 +103,14 @@ const NotificationList = (props) => {
|
|||
<NotificationItem
|
||||
key={notification.id}
|
||||
notification={notification}
|
||||
onShowSnack={() => setSnackOpen(true)}
|
||||
/>)}
|
||||
<Snackbar
|
||||
open={snackOpen}
|
||||
autoHideDuration={3000}
|
||||
onClose={() => setSnackOpen(false)}
|
||||
message="Copied to clipboard"
|
||||
/>
|
||||
</Stack>
|
||||
</Container>
|
||||
</InfiniteScroll>
|
||||
|
@ -109,6 +128,10 @@ const NotificationItem = (props) => {
|
|||
console.log(`[Notifications] Deleting notification ${notification.id} from ${subscriptionId}`);
|
||||
await subscriptionManager.deleteNotification(notification.id)
|
||||
}
|
||||
const handleCopy = (s) => {
|
||||
navigator.clipboard.writeText(s);
|
||||
props.onShowSnack();
|
||||
};
|
||||
const expired = attachment && attachment.expires && attachment.expires < Date.now()/1000;
|
||||
const showAttachmentActions = attachment && !expired;
|
||||
const showClickAction = notification.click;
|
||||
|
@ -133,22 +156,48 @@ const NotificationItem = (props) => {
|
|||
</svg>}
|
||||
</Typography>
|
||||
{notification.title && <Typography variant="h5" component="div">{formatTitle(notification)}</Typography>}
|
||||
<Typography variant="body1" sx={{ whiteSpace: 'pre-line' }}>{formatMessage(notification)}</Typography>
|
||||
<Typography variant="body1" sx={{ whiteSpace: 'pre-line' }}>{autolink(formatMessage(notification))}</Typography>
|
||||
{attachment && <Attachment attachment={attachment}/>}
|
||||
{tags && <Typography sx={{ fontSize: 14 }} color="text.secondary">Tags: {tags}</Typography>}
|
||||
</CardContent>
|
||||
{showActions &&
|
||||
<CardActions sx={{paddingTop: 0}}>
|
||||
{showAttachmentActions && <>
|
||||
<Button onClick={() => navigator.clipboard.writeText(attachment.url)}>Copy URL</Button>
|
||||
<Tooltip title="Copy attachment URL to clipboard">
|
||||
<Button onClick={() => handleCopy(attachment.url)}>Copy URL</Button>
|
||||
</Tooltip>
|
||||
<Tooltip title={`Go to ${attachment.url}`}>
|
||||
<Button onClick={() => openUrl(attachment.url)}>Open attachment</Button>
|
||||
</Tooltip>
|
||||
</>}
|
||||
{showClickAction && <>
|
||||
<Tooltip title="Copy link URL to clipboard">
|
||||
<Button onClick={() => handleCopy(notification.click)}>Copy link</Button>
|
||||
</Tooltip>
|
||||
<Tooltip title={`Go to ${notification.click}`}>
|
||||
<Button onClick={() => openUrl(notification.click)}>Open link</Button>
|
||||
</Tooltip>
|
||||
</>}
|
||||
{showClickAction && <Button onClick={() => openUrl(notification.click)}>Open link</Button>}
|
||||
</CardActions>}
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace links with <Link/> components; this is a combination of the genius function
|
||||
* in [1] and the regex in [2].
|
||||
*
|
||||
* [1] https://github.com/facebook/react/issues/3386#issuecomment-78605760
|
||||
* [2] https://github.com/bryanwoods/autolink-js/blob/master/autolink.js#L9
|
||||
*/
|
||||
const autolink = (s) => {
|
||||
const parts = s.split(/(\bhttps?:\/\/[\-A-Z0-9+\u0026\u2019@#\/%?=()~_|!:,.;]*[\-A-Z0-9+\u0026@#\/%=~()_|]\b)/gi);
|
||||
for (let i = 1; i < parts.length; i += 2) {
|
||||
parts[i] = <Link key={i} href={parts[i]} underline="hover" target="_blank" rel="noreferrer,noopener">{shortUrl(parts[i])}</Link>;
|
||||
}
|
||||
return <>{parts}</>;
|
||||
};
|
||||
|
||||
const priorityFiles = {
|
||||
1: priority1,
|
||||
2: priority2,
|
||||
|
|
Loading…
Reference in New Issue