Feature complete

pull/149/head
Philipp Heckel 2022-03-11 11:46:19 -05:00
parent 7b186af765
commit 0544a6f00d
3 changed files with 57 additions and 10 deletions

View File

@ -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)

View File

@ -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

View File

@ -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,