Web app: implement markdown support
This commit is contained in:
parent
56ed4f0515
commit
f989fd0743
7 changed files with 648 additions and 8 deletions
|
@ -55,6 +55,15 @@ class Prefs {
|
|||
async setTheme(mode) {
|
||||
await this.db.prefs.put({ key: "theme", value: mode });
|
||||
}
|
||||
|
||||
async markdownAlwaysEnabled() {
|
||||
const record = await this.db.prefs.get("markdownAlwaysEnabled");
|
||||
return record?.value ?? false;
|
||||
}
|
||||
|
||||
async setMarkdownAlwaysEnabled(enabled) {
|
||||
await this.db.prefs.put({ key: "markdownAlwaysEnabled", value: enabled });
|
||||
}
|
||||
}
|
||||
|
||||
const prefs = new Prefs(db());
|
||||
|
|
|
@ -89,15 +89,15 @@ export const maybeWithAuth = (headers, user) => {
|
|||
return headers;
|
||||
};
|
||||
|
||||
export const maybeAppendActionErrors = (message, notification) => {
|
||||
export const maybeActionErrors = (notification) => {
|
||||
const actionErrors = (notification.actions ?? [])
|
||||
.map((action) => action.error)
|
||||
.filter((action) => !!action)
|
||||
.join("\n");
|
||||
if (actionErrors.length === 0) {
|
||||
return message;
|
||||
return undefined;
|
||||
}
|
||||
return `${message}\n\n${actionErrors}`;
|
||||
return actionErrors;
|
||||
};
|
||||
|
||||
export const shuffle = (arr) => {
|
||||
|
|
|
@ -24,7 +24,9 @@ import { useLiveQuery } from "dexie-react-hooks";
|
|||
import InfiniteScroll from "react-infinite-scroll-component";
|
||||
import { Trans, useTranslation } from "react-i18next";
|
||||
import { useOutletContext } from "react-router-dom";
|
||||
import { formatBytes, formatShortDateTime, maybeAppendActionErrors, openUrl, shortUrl, topicShortUrl, unmatchedTags } from "../app/utils";
|
||||
import { useRemark } from "react-remark";
|
||||
import styled from "@emotion/styled";
|
||||
import { formatBytes, formatShortDateTime, maybeActionErrors, openUrl, shortUrl, topicShortUrl, unmatchedTags } from "../app/utils";
|
||||
import { formatMessage, formatTitle } from "../app/notificationUtils";
|
||||
import { LightboxBackdrop, Paragraph, VerticallyCenteredContainer } from "./styles";
|
||||
import subscriptionManager from "../app/SubscriptionManager";
|
||||
|
@ -35,6 +37,7 @@ import priority5 from "../img/priority-5.svg";
|
|||
import logoOutline from "../img/ntfy-outline.svg";
|
||||
import AttachmentIcon from "./AttachmentIcon";
|
||||
import { useAutoSubscribe } from "./hooks";
|
||||
import prefs from "../app/Prefs";
|
||||
|
||||
const priorityFiles = {
|
||||
1: priority1,
|
||||
|
@ -159,6 +162,63 @@ const autolink = (s) => {
|
|||
return <>{parts}</>;
|
||||
};
|
||||
|
||||
const MarkdownContainer = styled("div")`
|
||||
line-height: 1;
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6,
|
||||
p,
|
||||
pre,
|
||||
ul,
|
||||
ol {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
p {
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
margin: 0;
|
||||
padding-inline: 1rem;
|
||||
background: ${(theme) => (theme.mode === "light" ? "#f1f1f1" : "#aeaeae")};
|
||||
}
|
||||
|
||||
ul,
|
||||
ol {
|
||||
padding-inline: 1rem;
|
||||
}
|
||||
`;
|
||||
|
||||
const MarkdownContent = ({ content }) => {
|
||||
const [reactContent, setMarkdownSource] = useRemark();
|
||||
|
||||
useEffect(() => {
|
||||
setMarkdownSource(content);
|
||||
}, [content]);
|
||||
|
||||
return <MarkdownContainer>{reactContent}</MarkdownContainer>;
|
||||
};
|
||||
|
||||
const NotificationBody = ({ notification }) => {
|
||||
const markdownAlwaysEnabled = useLiveQuery(async () => prefs.markdownAlwaysEnabled());
|
||||
|
||||
// TODO: check notification content-type when implemented on the server
|
||||
const displayAsMarkdown = markdownAlwaysEnabled;
|
||||
|
||||
const formatted = formatMessage(notification);
|
||||
|
||||
if (displayAsMarkdown) {
|
||||
return <MarkdownContent content={formatted} />;
|
||||
}
|
||||
|
||||
return autolink(formatted);
|
||||
};
|
||||
|
||||
const NotificationItem = (props) => {
|
||||
const { t, i18n } = useTranslation();
|
||||
const { notification } = props;
|
||||
|
@ -183,6 +243,7 @@ const NotificationItem = (props) => {
|
|||
const hasClickAction = notification.click;
|
||||
const hasUserActions = notification.actions && notification.actions.length > 0;
|
||||
const showActions = hasAttachmentActions || hasClickAction || hasUserActions;
|
||||
|
||||
return (
|
||||
<Card sx={{ padding: 1 }} role="listitem" aria-label={t("notifications_list_item")}>
|
||||
<CardContent>
|
||||
|
@ -230,7 +291,8 @@ const NotificationItem = (props) => {
|
|||
</Typography>
|
||||
)}
|
||||
<Typography variant="body1" sx={{ whiteSpace: "pre-line" }}>
|
||||
{autolink(maybeAppendActionErrors(formatMessage(notification), notification))}
|
||||
<NotificationBody notification={notification} />
|
||||
{maybeActionErrors(notification)}
|
||||
</Typography>
|
||||
{attachment && <Attachment attachment={attachment} />}
|
||||
{tags && (
|
||||
|
|
|
@ -259,6 +259,26 @@ const Theme = () => {
|
|||
);
|
||||
};
|
||||
|
||||
const MarkdownAlwaysEnabled = () => {
|
||||
const { t } = useTranslation();
|
||||
const labelId = "prefMarkdown";
|
||||
const enabled = useLiveQuery(async () => prefs.markdownAlwaysEnabled());
|
||||
const handleChange = async (ev) => {
|
||||
await prefs.setMarkdownAlwaysEnabled(ev.target.value);
|
||||
};
|
||||
|
||||
return (
|
||||
<Pref labelId={labelId} title={t("prefs_appearance_markdown_always_enabled_title")}>
|
||||
<FormControl fullWidth variant="standard" sx={{ m: 1 }}>
|
||||
<Select value={enabled ?? false} onChange={handleChange} aria-labelledby={labelId}>
|
||||
<MenuItem value>{t("prefs_appearance_markdown_always_enabled_on")}</MenuItem>
|
||||
<MenuItem value={false}>{t("prefs_appearance_markdown_always_enabled_off")}</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
</Pref>
|
||||
);
|
||||
};
|
||||
|
||||
const WebPushEnabled = () => {
|
||||
const { t } = useTranslation();
|
||||
const labelId = "prefWebPushEnabled";
|
||||
|
@ -513,6 +533,7 @@ const Appearance = () => {
|
|||
<PrefGroup>
|
||||
<Theme />
|
||||
<Language />
|
||||
<MarkdownAlwaysEnabled />
|
||||
</PrefGroup>
|
||||
</Card>
|
||||
);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue