mirror of
https://github.com/Athou/commafeed.git
synced 2026-03-21 21:37:29 +00:00
use Trans as much as possible to ease lingui upgrade to 4.0
This commit is contained in:
@@ -6,7 +6,7 @@ import { forwardRef, MouseEventHandler, ReactNode } from "react"
|
|||||||
interface ActionButtonProps {
|
interface ActionButtonProps {
|
||||||
className?: string
|
className?: string
|
||||||
icon?: ReactNode
|
icon?: ReactNode
|
||||||
label?: string
|
label?: ReactNode
|
||||||
onClick?: MouseEventHandler
|
onClick?: MouseEventHandler
|
||||||
variant?: ActionIconVariant & ButtonVariant
|
variant?: ActionIconVariant & ButtonVariant
|
||||||
showLabelOnMobile?: boolean
|
showLabelOnMobile?: boolean
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { t } from "@lingui/macro"
|
import { Trans } from "@lingui/macro"
|
||||||
import { Alert as MantineAlert, Box } from "@mantine/core"
|
import { Box, Alert as MantineAlert } from "@mantine/core"
|
||||||
import { Fragment } from "react"
|
import { Fragment } from "react"
|
||||||
import { TbAlertCircle, TbAlertTriangle, TbCircleCheck } from "react-icons/tb"
|
import { TbAlertCircle, TbAlertTriangle, TbCircleCheck } from "react-icons/tb"
|
||||||
|
|
||||||
@@ -10,24 +10,24 @@ export interface ErrorsAlertProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function Alert(props: ErrorsAlertProps) {
|
export function Alert(props: ErrorsAlertProps) {
|
||||||
let title: string
|
let title: React.ReactNode
|
||||||
let color: string
|
let color: string
|
||||||
let icon: React.ReactNode
|
let icon: React.ReactNode
|
||||||
|
|
||||||
const level = props.level ?? "error"
|
const level = props.level ?? "error"
|
||||||
switch (level) {
|
switch (level) {
|
||||||
case "error":
|
case "error":
|
||||||
title = t`Error`
|
title = <Trans>Error</Trans>
|
||||||
color = "red"
|
color = "red"
|
||||||
icon = <TbAlertCircle />
|
icon = <TbAlertCircle />
|
||||||
break
|
break
|
||||||
case "warning":
|
case "warning":
|
||||||
title = t`Warning`
|
title = <Trans>Warning</Trans>
|
||||||
color = "orange"
|
color = "orange"
|
||||||
icon = <TbAlertTriangle />
|
icon = <TbAlertTriangle />
|
||||||
break
|
break
|
||||||
case "success":
|
case "success":
|
||||||
title = t`Success`
|
title = <Trans>Success</Trans>
|
||||||
color = "green"
|
color = "green"
|
||||||
icon = <TbCircleCheck />
|
icon = <TbCircleCheck />
|
||||||
break
|
break
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { t, Trans } from "@lingui/macro"
|
import { Trans } from "@lingui/macro"
|
||||||
import { Box, Button, Checkbox, Group, PasswordInput, Stack, TextInput } from "@mantine/core"
|
import { Box, Button, Checkbox, Group, PasswordInput, Stack, TextInput } from "@mantine/core"
|
||||||
import { useForm } from "@mantine/form"
|
import { useForm } from "@mantine/form"
|
||||||
import { client, errorToStrings } from "app/client"
|
import { client, errorToStrings } from "app/client"
|
||||||
@@ -29,11 +29,11 @@ export function UserEdit(props: UserEditProps) {
|
|||||||
|
|
||||||
<form onSubmit={form.onSubmit(saveUser.execute)}>
|
<form onSubmit={form.onSubmit(saveUser.execute)}>
|
||||||
<Stack>
|
<Stack>
|
||||||
<TextInput label={t`Name`} {...form.getInputProps("name")} required />
|
<TextInput label={<Trans>Name</Trans>} {...form.getInputProps("name")} required />
|
||||||
<PasswordInput label={t`Password`} {...form.getInputProps("password")} required={!props.user} />
|
<PasswordInput label={<Trans>Password</Trans>} {...form.getInputProps("password")} required={!props.user} />
|
||||||
<TextInput type="email" label={t`E-mail`} {...form.getInputProps("email")} />
|
<TextInput type="email" label={<Trans>E-mail</Trans>} {...form.getInputProps("email")} />
|
||||||
<Checkbox label={t`Admin`} {...form.getInputProps("admin", { type: "checkbox" })} />
|
<Checkbox label={<Trans>Admin</Trans>} {...form.getInputProps("admin", { type: "checkbox" })} />
|
||||||
<Checkbox label={t`Enabled`} {...form.getInputProps("enabled", { type: "checkbox" })} />
|
<Checkbox label={<Trans>Enabled</Trans>} {...form.getInputProps("enabled", { type: "checkbox" })} />
|
||||||
|
|
||||||
<Group>
|
<Group>
|
||||||
<Button variant="default" onClick={props.onCancel}>
|
<Button variant="default" onClick={props.onCancel}>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { t } from "@lingui/macro"
|
import { Trans } from "@lingui/macro"
|
||||||
import { openModal } from "@mantine/modals"
|
import { openModal } from "@mantine/modals"
|
||||||
import { Constants } from "app/constants"
|
import { Constants } from "app/constants"
|
||||||
import {
|
import {
|
||||||
@@ -234,7 +234,13 @@ export function FeedEntries() {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
useMousetrap("g a", () => dispatch(redirectToRootCategory()))
|
useMousetrap("g a", () => dispatch(redirectToRootCategory()))
|
||||||
useMousetrap("?", () => openModal({ title: t`Keyboard shortcuts`, size: "xl", children: <KeyboardShortcutsHelp /> }))
|
useMousetrap("?", () =>
|
||||||
|
openModal({
|
||||||
|
title: <Trans>Keyboard shortcuts</Trans>,
|
||||||
|
size: "xl",
|
||||||
|
children: <KeyboardShortcutsHelp />,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
if (!entries) return <Loader />
|
if (!entries) return <Loader />
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { t, Trans } from "@lingui/macro"
|
import { Trans } from "@lingui/macro"
|
||||||
import { createStyles, Group } from "@mantine/core"
|
import { createStyles, Group } from "@mantine/core"
|
||||||
import { Constants } from "app/constants"
|
import { Constants } from "app/constants"
|
||||||
import { markEntriesUpToEntry, markEntry, starEntry } from "app/slices/entries"
|
import { markEntriesUpToEntry, markEntry, starEntry } from "app/slices/entries"
|
||||||
@@ -29,6 +29,7 @@ const useStyles = createStyles(theme => ({
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
const menuId = (entry: Entry) => entry.id
|
const menuId = (entry: Entry) => entry.id
|
||||||
|
|
||||||
export function FeedEntryContextMenu(props: FeedEntryContextMenuProps) {
|
export function FeedEntryContextMenu(props: FeedEntryContextMenuProps) {
|
||||||
const { classes, theme } = useStyles()
|
const { classes, theme } = useStyles()
|
||||||
const sourceType = useAppSelector(state => state.entries.source.type)
|
const sourceType = useAppSelector(state => state.entries.source.type)
|
||||||
@@ -64,13 +65,13 @@ export function FeedEntryContextMenu(props: FeedEntryContextMenuProps) {
|
|||||||
<Item onClick={() => dispatch(starEntry({ entry: props.entry, starred: !props.entry.starred }))}>
|
<Item onClick={() => dispatch(starEntry({ entry: props.entry, starred: !props.entry.starred }))}>
|
||||||
<Group>
|
<Group>
|
||||||
{props.entry.starred ? <TbStarOff size={iconSize} /> : <TbStar size={iconSize} />}
|
{props.entry.starred ? <TbStarOff size={iconSize} /> : <TbStar size={iconSize} />}
|
||||||
{props.entry.starred ? t`Unstar` : t`Star`}
|
{props.entry.starred ? <Trans>Unstar</Trans> : <Trans>Star</Trans>}
|
||||||
</Group>
|
</Group>
|
||||||
</Item>
|
</Item>
|
||||||
<Item onClick={() => dispatch(markEntry({ entry: props.entry, read: !props.entry.read }))}>
|
<Item onClick={() => dispatch(markEntry({ entry: props.entry, read: !props.entry.read }))}>
|
||||||
<Group>
|
<Group>
|
||||||
{props.entry.read ? <TbEyeOff size={iconSize} /> : <TbEyeCheck size={iconSize} />}
|
{props.entry.read ? <TbEyeOff size={iconSize} /> : <TbEyeCheck size={iconSize} />}
|
||||||
{props.entry.read ? t`Keep unread` : t`Mark as read`}
|
{props.entry.read ? <Trans>Keep unread</Trans> : <Trans>Mark as read</Trans>}
|
||||||
</Group>
|
</Group>
|
||||||
</Item>
|
</Item>
|
||||||
<Item onClick={() => dispatch(markEntriesUpToEntry(props.entry))}>
|
<Item onClick={() => dispatch(markEntriesUpToEntry(props.entry))}>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { t } from "@lingui/macro"
|
import { t, Trans } from "@lingui/macro"
|
||||||
import { Group, Indicator, MultiSelect, Popover } from "@mantine/core"
|
import { Group, Indicator, MultiSelect, Popover } from "@mantine/core"
|
||||||
import { useMediaQuery } from "@mantine/hooks"
|
import { useMediaQuery } from "@mantine/hooks"
|
||||||
import { Constants } from "app/constants"
|
import { Constants } from "app/constants"
|
||||||
@@ -50,20 +50,20 @@ export function FeedEntryFooter(props: FeedEntryFooterProps) {
|
|||||||
{props.entry.markable && (
|
{props.entry.markable && (
|
||||||
<ActionButton
|
<ActionButton
|
||||||
icon={props.entry.read ? <TbEyeOff size={18} /> : <TbEyeCheck size={18} />}
|
icon={props.entry.read ? <TbEyeOff size={18} /> : <TbEyeCheck size={18} />}
|
||||||
label={props.entry.read ? t`Keep unread` : t`Mark as read`}
|
label={props.entry.read ? <Trans>Keep unread</Trans> : <Trans>Mark as read</Trans>}
|
||||||
onClick={readStatusButtonClicked}
|
onClick={readStatusButtonClicked}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<ActionButton
|
<ActionButton
|
||||||
icon={props.entry.starred ? <TbStarOff size={18} /> : <TbStar size={18} />}
|
icon={props.entry.starred ? <TbStarOff size={18} /> : <TbStar size={18} />}
|
||||||
label={props.entry.starred ? t`Unstar` : t`Star`}
|
label={props.entry.starred ? <Trans>Unstar</Trans> : <Trans>Star</Trans>}
|
||||||
onClick={() => dispatch(starEntry({ entry: props.entry, starred: !props.entry.starred }))}
|
onClick={() => dispatch(starEntry({ entry: props.entry, starred: !props.entry.starred }))}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{showSharingButtons && (
|
{showSharingButtons && (
|
||||||
<Popover withArrow withinPortal shadow="md" positionDependencies={[scrollPosition]} closeOnClickOutside={!mobile}>
|
<Popover withArrow withinPortal shadow="md" positionDependencies={[scrollPosition]} closeOnClickOutside={!mobile}>
|
||||||
<Popover.Target>
|
<Popover.Target>
|
||||||
<ActionButton icon={<TbShare size={18} />} label={t`Share`} />
|
<ActionButton icon={<TbShare size={18} />} label={<Trans>Share</Trans>} />
|
||||||
</Popover.Target>
|
</Popover.Target>
|
||||||
<Popover.Dropdown>
|
<Popover.Dropdown>
|
||||||
<ShareButtons url={props.entry.url} description={props.entry.title} />
|
<ShareButtons url={props.entry.url} description={props.entry.title} />
|
||||||
@@ -75,7 +75,7 @@ export function FeedEntryFooter(props: FeedEntryFooterProps) {
|
|||||||
<Popover withArrow withinPortal shadow="md" positionDependencies={[scrollPosition]} closeOnClickOutside={!mobile}>
|
<Popover withArrow withinPortal shadow="md" positionDependencies={[scrollPosition]} closeOnClickOutside={!mobile}>
|
||||||
<Popover.Target>
|
<Popover.Target>
|
||||||
<Indicator label={props.entry.tags.length} showZero={false} dot={false} inline size={16}>
|
<Indicator label={props.entry.tags.length} showZero={false} dot={false} inline size={16}>
|
||||||
<ActionButton icon={<TbTag size={18} />} label={t`Tags`} />
|
<ActionButton icon={<TbTag size={18} />} label={<Trans>Tags</Trans>} />
|
||||||
</Indicator>
|
</Indicator>
|
||||||
</Popover.Target>
|
</Popover.Target>
|
||||||
<Popover.Dropdown>
|
<Popover.Dropdown>
|
||||||
@@ -94,13 +94,13 @@ export function FeedEntryFooter(props: FeedEntryFooterProps) {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
<a href={props.entry.url} target="_blank" rel="noreferrer">
|
<a href={props.entry.url} target="_blank" rel="noreferrer">
|
||||||
<ActionButton icon={<TbExternalLink size={18} />} label={t`Open link`} />
|
<ActionButton icon={<TbExternalLink size={18} />} label={<Trans>Open link</Trans>} />
|
||||||
</a>
|
</a>
|
||||||
</ButtonToolbar>
|
</ButtonToolbar>
|
||||||
|
|
||||||
<ActionButton
|
<ActionButton
|
||||||
icon={<TbArrowBarToDown size={18} />}
|
icon={<TbArrowBarToDown size={18} />}
|
||||||
label={t`Mark as read up to here`}
|
label={<Trans>Mark as read up to here</Trans>}
|
||||||
onClick={() => dispatch(markEntriesUpToEntry(props.entry))}
|
onClick={() => dispatch(markEntriesUpToEntry(props.entry))}
|
||||||
/>
|
/>
|
||||||
</Group>
|
</Group>
|
||||||
|
|||||||
@@ -33,8 +33,8 @@ export function AddCategory() {
|
|||||||
|
|
||||||
<form onSubmit={form.onSubmit(addCategory.execute)}>
|
<form onSubmit={form.onSubmit(addCategory.execute)}>
|
||||||
<Stack>
|
<Stack>
|
||||||
<TextInput label={t`Category`} placeholder={t`Category`} {...form.getInputProps("name")} required />
|
<TextInput label={<Trans>Category</Trans>} placeholder={t`Category`} {...form.getInputProps("name")} required />
|
||||||
<CategorySelect label={t`Parent`} {...form.getInputProps("parentId")} clearable />
|
<CategorySelect label={<Trans>Parent</Trans>} {...form.getInputProps("parentId")} clearable />
|
||||||
<Group position="center">
|
<Group position="center">
|
||||||
<Button variant="default" onClick={() => dispatch(redirectToSelectedSource())}>
|
<Button variant="default" onClick={() => dispatch(redirectToSelectedSource())}>
|
||||||
<Trans>Cancel</Trans>
|
<Trans>Cancel</Trans>
|
||||||
|
|||||||
@@ -36,9 +36,14 @@ export function ImportOpml() {
|
|||||||
<form onSubmit={form.onSubmit(v => importOpml.execute(v.file))}>
|
<form onSubmit={form.onSubmit(v => importOpml.execute(v.file))}>
|
||||||
<Stack>
|
<Stack>
|
||||||
<FileInput
|
<FileInput
|
||||||
label={t`OPML file`}
|
label={<Trans>OPML file</Trans>}
|
||||||
placeholder={t`OPML file`}
|
placeholder={t`OPML file`}
|
||||||
description={t`An opml file is an XML file containing feed URLs and categories. You can get an OPML file by exporting your data from other feed reading services.`}
|
description={
|
||||||
|
<Trans>
|
||||||
|
An opml file is an XML file containing feed URLs and categories. You can get an OPML file by exporting your
|
||||||
|
data from other feed reading services.
|
||||||
|
</Trans>
|
||||||
|
}
|
||||||
{...form.getInputProps("file")}
|
{...form.getInputProps("file")}
|
||||||
required
|
required
|
||||||
accept="application/xml"
|
accept="application/xml"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { t, Trans } from "@lingui/macro"
|
import { Trans } from "@lingui/macro"
|
||||||
import { Box, Button, Group, Stack, Stepper, TextInput } from "@mantine/core"
|
import { Box, Button, Group, Stack, Stepper, TextInput } from "@mantine/core"
|
||||||
import { useForm } from "@mantine/form"
|
import { useForm } from "@mantine/form"
|
||||||
import { client, errorToStrings } from "app/client"
|
import { client, errorToStrings } from "app/client"
|
||||||
@@ -74,24 +74,33 @@ export function Subscribe() {
|
|||||||
<form onSubmit={nextStep}>
|
<form onSubmit={nextStep}>
|
||||||
<Stepper active={activeStep} onStepClick={setActiveStep}>
|
<Stepper active={activeStep} onStepClick={setActiveStep}>
|
||||||
<Stepper.Step
|
<Stepper.Step
|
||||||
label={t`Analyze feed`}
|
label={<Trans>Analyze feed</Trans>}
|
||||||
description={t`Check that the feed is working`}
|
description={<Trans>Check that the feed is working</Trans>}
|
||||||
allowStepSelect={activeStep === 1}
|
allowStepSelect={activeStep === 1}
|
||||||
>
|
>
|
||||||
<TextInput
|
<TextInput
|
||||||
label={t`Feed URL`}
|
label={<Trans>Feed URL</Trans>}
|
||||||
placeholder="http://www.mysite.com/rss"
|
placeholder="http://www.mysite.com/rss"
|
||||||
description={t`The URL for the feed you want to subscribe to. You can also use the website's url directly and CommaFeed will try to find the feed in the page.`}
|
description={
|
||||||
|
<Trans>
|
||||||
|
The URL for the feed you want to subscribe to. You can also use the website's url directly and CommaFeed
|
||||||
|
will try to find the feed in the page.
|
||||||
|
</Trans>
|
||||||
|
}
|
||||||
required
|
required
|
||||||
autoFocus
|
autoFocus
|
||||||
{...step0Form.getInputProps("url")}
|
{...step0Form.getInputProps("url")}
|
||||||
/>
|
/>
|
||||||
</Stepper.Step>
|
</Stepper.Step>
|
||||||
<Stepper.Step label={t`Subscribe`} description={t`Subscribe to the feed`} allowStepSelect={false}>
|
<Stepper.Step
|
||||||
|
label={<Trans>Subscribe</Trans>}
|
||||||
|
description={<Trans>Subscribe to the feed</Trans>}
|
||||||
|
allowStepSelect={false}
|
||||||
|
>
|
||||||
<Stack>
|
<Stack>
|
||||||
<TextInput label={t`Feed URL`} {...step1Form.getInputProps("url")} disabled />
|
<TextInput label={<Trans>Feed URL</Trans>} {...step1Form.getInputProps("url")} disabled />
|
||||||
<TextInput label={t`Feed name`} {...step1Form.getInputProps("title")} required autoFocus />
|
<TextInput label={<Trans>Feed name</Trans>} {...step1Form.getInputProps("title")} required autoFocus />
|
||||||
<CategorySelect label={t`Category`} {...step1Form.getInputProps("categoryId")} clearable />
|
<CategorySelect label={<Trans>Category</Trans>} {...step1Form.getInputProps("categoryId")} clearable />
|
||||||
</Stack>
|
</Stack>
|
||||||
</Stepper.Step>
|
</Stepper.Step>
|
||||||
</Stepper>
|
</Stepper>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { t } from "@lingui/macro"
|
import { t, Trans } from "@lingui/macro"
|
||||||
import { ActionIcon, Center, Divider, Indicator, Popover, TextInput } from "@mantine/core"
|
import { ActionIcon, Center, Divider, Indicator, Popover, TextInput } from "@mantine/core"
|
||||||
import { useForm } from "@mantine/form"
|
import { useForm } from "@mantine/form"
|
||||||
import { reloadEntries, search } from "app/slices/entries"
|
import { reloadEntries, search } from "app/slices/entries"
|
||||||
@@ -17,6 +17,7 @@ function HeaderDivider() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const iconSize = 18
|
const iconSize = 18
|
||||||
|
|
||||||
export function Header() {
|
export function Header() {
|
||||||
const settings = useAppSelector(state => state.user.settings)
|
const settings = useAppSelector(state => state.user.settings)
|
||||||
const profile = useAppSelector(state => state.user.profile)
|
const profile = useAppSelector(state => state.user.profile)
|
||||||
@@ -40,26 +41,30 @@ export function Header() {
|
|||||||
return (
|
return (
|
||||||
<Center>
|
<Center>
|
||||||
<ButtonToolbar>
|
<ButtonToolbar>
|
||||||
<ActionButton icon={<TbRefresh size={iconSize} />} label={t`Refresh`} onClick={() => dispatch(reloadEntries())} />
|
<ActionButton
|
||||||
|
icon={<TbRefresh size={iconSize} />}
|
||||||
|
label={<Trans>Refresh</Trans>}
|
||||||
|
onClick={() => dispatch(reloadEntries())}
|
||||||
|
/>
|
||||||
<MarkAllAsReadButton iconSize={iconSize} />
|
<MarkAllAsReadButton iconSize={iconSize} />
|
||||||
|
|
||||||
<HeaderDivider />
|
<HeaderDivider />
|
||||||
|
|
||||||
<ActionButton
|
<ActionButton
|
||||||
icon={settings.readingMode === "all" ? <TbEye size={iconSize} /> : <TbEyeOff size={iconSize} />}
|
icon={settings.readingMode === "all" ? <TbEye size={iconSize} /> : <TbEyeOff size={iconSize} />}
|
||||||
label={settings.readingMode === "all" ? t`All` : t`Unread`}
|
label={settings.readingMode === "all" ? <Trans>All</Trans> : <Trans>Unread</Trans>}
|
||||||
onClick={() => dispatch(changeReadingMode(settings.readingMode === "all" ? "unread" : "all"))}
|
onClick={() => dispatch(changeReadingMode(settings.readingMode === "all" ? "unread" : "all"))}
|
||||||
/>
|
/>
|
||||||
<ActionButton
|
<ActionButton
|
||||||
icon={settings.readingOrder === "asc" ? <TbArrowUp size={iconSize} /> : <TbArrowDown size={iconSize} />}
|
icon={settings.readingOrder === "asc" ? <TbArrowUp size={iconSize} /> : <TbArrowDown size={iconSize} />}
|
||||||
label={settings.readingOrder === "asc" ? t`Asc` : t`Desc`}
|
label={settings.readingOrder === "asc" ? <Trans>Asc</Trans> : <Trans>Desc</Trans>}
|
||||||
onClick={() => dispatch(changeReadingOrder(settings.readingOrder === "asc" ? "desc" : "asc"))}
|
onClick={() => dispatch(changeReadingOrder(settings.readingOrder === "asc" ? "desc" : "asc"))}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Popover>
|
<Popover>
|
||||||
<Popover.Target>
|
<Popover.Target>
|
||||||
<Indicator disabled={!searchFromStore}>
|
<Indicator disabled={!searchFromStore}>
|
||||||
<ActionButton icon={<TbSearch size={iconSize} />} label={t`Search`} />
|
<ActionButton icon={<TbSearch size={iconSize} />} label={<Trans>Search</Trans>} />
|
||||||
</Indicator>
|
</Indicator>
|
||||||
</Popover.Target>
|
</Popover.Target>
|
||||||
<Popover.Dropdown>
|
<Popover.Dropdown>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { t, Trans } from "@lingui/macro"
|
import { Trans } from "@lingui/macro"
|
||||||
|
|
||||||
import { Button, Code, Group, Modal, Slider, Stack, Text } from "@mantine/core"
|
import { Button, Code, Group, Modal, Slider, Stack, Text } from "@mantine/core"
|
||||||
import { markAllEntries } from "app/slices/entries"
|
import { markAllEntries } from "app/slices/entries"
|
||||||
@@ -17,7 +17,7 @@ export function MarkAllAsReadButton(props: { iconSize: number }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Modal opened={opened} onClose={() => setOpened(false)} title={t`Mark all entries as read`}>
|
<Modal opened={opened} onClose={() => setOpened(false)} title={<Trans>Mark all entries as read</Trans>}>
|
||||||
<Stack>
|
<Stack>
|
||||||
<Text size="sm">
|
<Text size="sm">
|
||||||
{threshold === 0 && (
|
{threshold === 0 && (
|
||||||
@@ -72,7 +72,7 @@ export function MarkAllAsReadButton(props: { iconSize: number }) {
|
|||||||
</Modal>
|
</Modal>
|
||||||
<ActionButton
|
<ActionButton
|
||||||
icon={<TbChecks size={props.iconSize} />}
|
icon={<TbChecks size={props.iconSize} />}
|
||||||
label={t`Mark all as read`}
|
label={<Trans>Mark all as read</Trans>}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setThreshold(0)
|
setThreshold(0)
|
||||||
setOpened(true)
|
setOpened(true)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { t, Trans } from "@lingui/macro"
|
import { Trans } from "@lingui/macro"
|
||||||
import { Box, Divider, Group, Menu, SegmentedControl, SegmentedControlItem, useMantineColorScheme } from "@mantine/core"
|
import { Box, Divider, Group, Menu, SegmentedControl, SegmentedControlItem, useMantineColorScheme } from "@mantine/core"
|
||||||
import { showNotification } from "@mantine/notifications"
|
import { showNotification } from "@mantine/notifications"
|
||||||
import { client } from "app/client"
|
import { client } from "app/client"
|
||||||
@@ -111,7 +111,7 @@ export function ProfileMenu(props: ProfileMenuProps) {
|
|||||||
onClick={() =>
|
onClick={() =>
|
||||||
client.feed.refreshAll().then(() => {
|
client.feed.refreshAll().then(() => {
|
||||||
showNotification({
|
showNotification({
|
||||||
message: t`Your feeds have been queued for refresh.`,
|
message: <Trans>Your feeds have been queued for refresh.</Trans>,
|
||||||
color: "green",
|
color: "green",
|
||||||
autoClose: 1000,
|
autoClose: 1000,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { t } from "@lingui/macro"
|
import { Trans } from "@lingui/macro"
|
||||||
import { Divider, Select, SimpleGrid, Stack, Switch } from "@mantine/core"
|
import { Divider, Select, SimpleGrid, Stack, Switch } from "@mantine/core"
|
||||||
import { Constants } from "app/constants"
|
import { Constants } from "app/constants"
|
||||||
import { changeLanguage, changeScrollMarks, changeScrollSpeed, changeSharingSetting, changeShowRead } from "app/slices/user"
|
import { changeLanguage, changeScrollMarks, changeScrollSpeed, changeSharingSetting, changeShowRead } from "app/slices/user"
|
||||||
@@ -17,7 +17,7 @@ export function DisplaySettings() {
|
|||||||
return (
|
return (
|
||||||
<Stack>
|
<Stack>
|
||||||
<Select
|
<Select
|
||||||
description={t`Language`}
|
description={<Trans>Language</Trans>}
|
||||||
value={language}
|
value={language}
|
||||||
data={locales.map(l => ({
|
data={locales.map(l => ({
|
||||||
value: l.key,
|
value: l.key,
|
||||||
@@ -27,24 +27,24 @@ export function DisplaySettings() {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<Switch
|
<Switch
|
||||||
label={t`Scroll smoothly when navigating between entries`}
|
label={<Trans>Scroll smoothly when navigating between entries</Trans>}
|
||||||
checked={scrollSpeed ? scrollSpeed > 0 : false}
|
checked={scrollSpeed ? scrollSpeed > 0 : false}
|
||||||
onChange={e => dispatch(changeScrollSpeed(e.currentTarget.checked))}
|
onChange={e => dispatch(changeScrollSpeed(e.currentTarget.checked))}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Switch
|
<Switch
|
||||||
label={t`Show feeds and categories with no unread entries`}
|
label={<Trans>Show feeds and categories with no unread entries</Trans>}
|
||||||
checked={showRead}
|
checked={showRead}
|
||||||
onChange={e => dispatch(changeShowRead(e.currentTarget.checked))}
|
onChange={e => dispatch(changeShowRead(e.currentTarget.checked))}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Switch
|
<Switch
|
||||||
label={t`In expanded view, scrolling through entries mark them as read`}
|
label={<Trans>In expanded view, scrolling through entries mark them as read</Trans>}
|
||||||
checked={scrollMarks}
|
checked={scrollMarks}
|
||||||
onChange={e => dispatch(changeScrollMarks(e.currentTarget.checked))}
|
onChange={e => dispatch(changeScrollMarks(e.currentTarget.checked))}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Divider label={t`Sharing sites`} labelPosition="center" />
|
<Divider label={<Trans>Sharing sites</Trans>} labelPosition="center" />
|
||||||
|
|
||||||
<SimpleGrid cols={2}>
|
<SimpleGrid cols={2}>
|
||||||
{(Object.keys(Constants.sharing) as Array<keyof SharingSettings>).map(site => (
|
{(Object.keys(Constants.sharing) as Array<keyof SharingSettings>).map(site => (
|
||||||
|
|||||||
@@ -41,13 +41,13 @@ export function ProfileSettings() {
|
|||||||
|
|
||||||
const openDeleteProfileModal = () =>
|
const openDeleteProfileModal = () =>
|
||||||
openConfirmModal({
|
openConfirmModal({
|
||||||
title: t`Delete account`,
|
title: <Trans>Delete account</Trans>,
|
||||||
children: (
|
children: (
|
||||||
<Text size="sm">
|
<Text size="sm">
|
||||||
<Trans>Are you sure you want to delete your account? There's no turning back!</Trans>
|
<Trans>Are you sure you want to delete your account? There's no turning back!</Trans>
|
||||||
</Text>
|
</Text>
|
||||||
),
|
),
|
||||||
labels: { confirm: t`Confirm`, cancel: t`Cancel` },
|
labels: { confirm: <Trans>Confirm</Trans>, cancel: <Trans>Cancel</Trans> },
|
||||||
confirmProps: { color: "red" },
|
confirmProps: { color: "red" },
|
||||||
onConfirm: () => deleteProfile.execute(),
|
onConfirm: () => deleteProfile.execute(),
|
||||||
})
|
})
|
||||||
@@ -77,12 +77,16 @@ export function ProfileSettings() {
|
|||||||
|
|
||||||
<form onSubmit={form.onSubmit(saveProfile.execute)}>
|
<form onSubmit={form.onSubmit(saveProfile.execute)}>
|
||||||
<Stack>
|
<Stack>
|
||||||
<Input.Wrapper label={t`User name`}>
|
<Input.Wrapper label={<Trans>User name</Trans>}>
|
||||||
<Box>{profile?.name}</Box>
|
<Box>{profile?.name}</Box>
|
||||||
</Input.Wrapper>
|
</Input.Wrapper>
|
||||||
<Input.Wrapper
|
<Input.Wrapper
|
||||||
label={t`OPML export`}
|
label={<Trans>OPML export</Trans>}
|
||||||
description={t`Export your subscriptions and categories as an OPML file that can be imported in other feed reading services`}
|
description={
|
||||||
|
<Trans>
|
||||||
|
Export your subscriptions and categories as an OPML file that can be imported in other feed reading services
|
||||||
|
</Trans>
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<Box>
|
<Box>
|
||||||
<Anchor href="rest/feed/export" download="commafeed_opml.xml">
|
<Anchor href="rest/feed/export" download="commafeed_opml.xml">
|
||||||
@@ -91,20 +95,20 @@ export function ProfileSettings() {
|
|||||||
</Box>
|
</Box>
|
||||||
</Input.Wrapper>
|
</Input.Wrapper>
|
||||||
<PasswordInput
|
<PasswordInput
|
||||||
label={t`Current password`}
|
label={<Trans>Current password</Trans>}
|
||||||
description={t`Enter your current password to change profile settings`}
|
description={<Trans>Enter your current password to change profile settings</Trans>}
|
||||||
required
|
required
|
||||||
{...form.getInputProps("currentPassword")}
|
{...form.getInputProps("currentPassword")}
|
||||||
/>
|
/>
|
||||||
<TextInput type="email" label={t`E-mail`} {...form.getInputProps("email")} required />
|
<TextInput type="email" label={<Trans>E-mail</Trans>} {...form.getInputProps("email")} required />
|
||||||
<PasswordInput
|
<PasswordInput
|
||||||
label={t`New password`}
|
label={<Trans>New password</Trans>}
|
||||||
description={t`Changing password will generate a new API key`}
|
description={<Trans>Changing password will generate a new API key</Trans>}
|
||||||
{...form.getInputProps("newPassword")}
|
{...form.getInputProps("newPassword")}
|
||||||
/>
|
/>
|
||||||
<PasswordInput label={t`Confirm password`} {...form.getInputProps("newPasswordConfirmation")} />
|
<PasswordInput label={<Trans>Confirm password</Trans>} {...form.getInputProps("newPasswordConfirmation")} />
|
||||||
<TextInput label={t`API key`} readOnly value={profile?.apiKey} />
|
<TextInput label={<Trans>API key</Trans>} readOnly value={profile?.apiKey} />
|
||||||
<Checkbox label={t`Generate new API key`} {...form.getInputProps("newApiKey", { type: "checkbox" })} />
|
<Checkbox label={<Trans>Generate new API key</Trans>} {...form.getInputProps("newApiKey", { type: "checkbox" })} />
|
||||||
|
|
||||||
<Group>
|
<Group>
|
||||||
<Button variant="default" onClick={() => dispatch(redirectToSelectedSource())}>
|
<Button variant="default" onClick={() => dispatch(redirectToSelectedSource())}>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { t } from "@lingui/macro"
|
import { Trans } from "@lingui/macro"
|
||||||
import { Box, Stack } from "@mantine/core"
|
import { Box, Stack } from "@mantine/core"
|
||||||
import { Constants } from "app/constants"
|
import { Constants } from "app/constants"
|
||||||
import {
|
import {
|
||||||
@@ -27,6 +27,7 @@ const expandedIcon = <TbChevronDown size={16} />
|
|||||||
const collapsedIcon = <TbChevronRight size={16} />
|
const collapsedIcon = <TbChevronRight size={16} />
|
||||||
|
|
||||||
const errorThreshold = 9
|
const errorThreshold = 9
|
||||||
|
|
||||||
export function Tree() {
|
export function Tree() {
|
||||||
const root = useAppSelector(state => state.tree.rootCategory)
|
const root = useAppSelector(state => state.tree.rootCategory)
|
||||||
const source = useAppSelector(state => state.entries.source)
|
const source = useAppSelector(state => state.entries.source)
|
||||||
@@ -63,7 +64,7 @@ export function Tree() {
|
|||||||
const allCategoryNode = () => (
|
const allCategoryNode = () => (
|
||||||
<TreeNode
|
<TreeNode
|
||||||
id={Constants.categories.all.id}
|
id={Constants.categories.all.id}
|
||||||
name={t`All`}
|
name={<Trans>All</Trans>}
|
||||||
icon={allIcon}
|
icon={allIcon}
|
||||||
unread={categoryUnreadCount(root)}
|
unread={categoryUnreadCount(root)}
|
||||||
selected={source.type === "category" && source.id === Constants.categories.all.id}
|
selected={source.type === "category" && source.id === Constants.categories.all.id}
|
||||||
@@ -76,7 +77,7 @@ export function Tree() {
|
|||||||
const starredCategoryNode = () => (
|
const starredCategoryNode = () => (
|
||||||
<TreeNode
|
<TreeNode
|
||||||
id={Constants.categories.starred.id}
|
id={Constants.categories.starred.id}
|
||||||
name={t`Starred`}
|
name={<Trans>Starred</Trans>}
|
||||||
icon={starredIcon}
|
icon={starredIcon}
|
||||||
unread={0}
|
unread={0}
|
||||||
selected={source.type === "category" && source.id === Constants.categories.starred.id}
|
selected={source.type === "category" && source.id === Constants.categories.starred.id}
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ import { UnreadCount } from "./UnreadCount"
|
|||||||
|
|
||||||
interface TreeNodeProps {
|
interface TreeNodeProps {
|
||||||
id: string
|
id: string
|
||||||
name: string
|
name: ReactNode
|
||||||
icon: ReactNode | string
|
icon: ReactNode
|
||||||
unread: number
|
unread: number
|
||||||
selected: boolean
|
selected: boolean
|
||||||
expanded?: boolean
|
expanded?: boolean
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { t } from "@lingui/macro"
|
import { t, Trans } from "@lingui/macro"
|
||||||
import { Box, Center, Kbd, TextInput } from "@mantine/core"
|
import { Box, Center, Kbd, TextInput } from "@mantine/core"
|
||||||
import { openSpotlight, SpotlightAction, SpotlightProvider } from "@mantine/spotlight"
|
import { openSpotlight, SpotlightAction, SpotlightProvider } from "@mantine/spotlight"
|
||||||
import { redirectToFeed } from "app/slices/redirect"
|
import { redirectToFeed } from "app/slices/redirect"
|
||||||
@@ -11,6 +11,7 @@ import { TbSearch } from "react-icons/tb"
|
|||||||
export interface TreeSearchProps {
|
export interface TreeSearchProps {
|
||||||
feeds: Subscription[]
|
feeds: Subscription[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export function TreeSearch(props: TreeSearchProps) {
|
export function TreeSearch(props: TreeSearchProps) {
|
||||||
const dispatch = useAppDispatch()
|
const dispatch = useAppDispatch()
|
||||||
|
|
||||||
@@ -40,7 +41,7 @@ export function TreeSearch(props: TreeSearchProps) {
|
|||||||
searchIcon={searchIcon}
|
searchIcon={searchIcon}
|
||||||
searchPlaceholder={t`Search`}
|
searchPlaceholder={t`Search`}
|
||||||
shortcut="ctrl+k"
|
shortcut="ctrl+k"
|
||||||
nothingFoundMessage={t`Nothing found`}
|
nothingFoundMessage={<Trans>Nothing found</Trans>}
|
||||||
>
|
>
|
||||||
<TextInput
|
<TextInput
|
||||||
placeholder={t`Search`}
|
placeholder={t`Search`}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { t } from "@lingui/macro"
|
import { Trans } from "@lingui/macro"
|
||||||
import { Anchor, Box, Center, Container, Divider, Group, Image, Title, useMantineColorScheme } from "@mantine/core"
|
import { Anchor, Box, Center, Container, Divider, Group, Image, Title, useMantineColorScheme } from "@mantine/core"
|
||||||
import { useMediaQuery } from "@mantine/hooks"
|
import { useMediaQuery } from "@mantine/hooks"
|
||||||
import { client } from "app/client"
|
import { client } from "app/client"
|
||||||
@@ -76,7 +76,7 @@ function Buttons() {
|
|||||||
<ButtonToolbar>
|
<ButtonToolbar>
|
||||||
{serverInfos?.demoAccountEnabled && (
|
{serverInfos?.demoAccountEnabled && (
|
||||||
<ActionButton
|
<ActionButton
|
||||||
label={t`Try the demo!`}
|
label={<Trans>Try the demo!</Trans>}
|
||||||
icon={<TbClock size={iconSize} />}
|
icon={<TbClock size={iconSize} />}
|
||||||
variant="outline"
|
variant="outline"
|
||||||
onClick={() => login.execute({ name: "demo", password: "demo" })}
|
onClick={() => login.execute({ name: "demo", password: "demo" })}
|
||||||
@@ -84,7 +84,7 @@ function Buttons() {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<ActionButton
|
<ActionButton
|
||||||
label={t`Log in`}
|
label={<Trans>Log in</Trans>}
|
||||||
icon={<TbKey size={iconSize} />}
|
icon={<TbKey size={iconSize} />}
|
||||||
variant="outline"
|
variant="outline"
|
||||||
onClick={() => dispatch(redirectToLogin())}
|
onClick={() => dispatch(redirectToLogin())}
|
||||||
@@ -92,7 +92,7 @@ function Buttons() {
|
|||||||
/>
|
/>
|
||||||
{serverInfos?.allowRegistrations && (
|
{serverInfos?.allowRegistrations && (
|
||||||
<ActionButton
|
<ActionButton
|
||||||
label={t`Sign up`}
|
label={<Trans>Sign up</Trans>}
|
||||||
icon={<TbUserPlus size={iconSize} />}
|
icon={<TbUserPlus size={iconSize} />}
|
||||||
variant="filled"
|
variant="filled"
|
||||||
onClick={() => dispatch(redirectToRegistration())}
|
onClick={() => dispatch(redirectToRegistration())}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { t, Trans } from "@lingui/macro"
|
import { Trans } from "@lingui/macro"
|
||||||
import { ActionIcon, Box, Code, Container, Group, Table, Text, Title, useMantineTheme } from "@mantine/core"
|
import { ActionIcon, Box, Code, Container, Group, Table, Text, Title, useMantineTheme } from "@mantine/core"
|
||||||
import { closeAllModals, openConfirmModal, openModal } from "@mantine/modals"
|
import { closeAllModals, openConfirmModal, openModal } from "@mantine/modals"
|
||||||
import { client, errorToStrings } from "app/client"
|
import { client, errorToStrings } from "app/client"
|
||||||
@@ -7,6 +7,7 @@ import { UserEdit } from "components/admin/UserEdit"
|
|||||||
import { Alert } from "components/Alert"
|
import { Alert } from "components/Alert"
|
||||||
import { Loader } from "components/Loader"
|
import { Loader } from "components/Loader"
|
||||||
import { RelativeDate } from "components/RelativeDate"
|
import { RelativeDate } from "components/RelativeDate"
|
||||||
|
import { ReactNode } from "react"
|
||||||
import { useAsync, useAsyncCallback } from "react-async-hook"
|
import { useAsync, useAsyncCallback } from "react-async-hook"
|
||||||
import { TbCheck, TbPencil, TbPlus, TbTrash, TbX } from "react-icons/tb"
|
import { TbCheck, TbPencil, TbPlus, TbTrash, TbX } from "react-icons/tb"
|
||||||
|
|
||||||
@@ -26,7 +27,7 @@ export function AdminUsersPage() {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const openUserEditModal = (title: string, user?: UserModel) => {
|
const openUserEditModal = (title: ReactNode, user?: UserModel) => {
|
||||||
openModal({
|
openModal({
|
||||||
title,
|
title,
|
||||||
children: (
|
children: (
|
||||||
@@ -45,7 +46,7 @@ export function AdminUsersPage() {
|
|||||||
const openUserDeleteModal = (user: UserModel) => {
|
const openUserDeleteModal = (user: UserModel) => {
|
||||||
const userName = user.name
|
const userName = user.name
|
||||||
openConfirmModal({
|
openConfirmModal({
|
||||||
title: t`Delete user`,
|
title: <Trans>Delete user</Trans>,
|
||||||
children: (
|
children: (
|
||||||
<Text size="sm">
|
<Text size="sm">
|
||||||
<Trans>
|
<Trans>
|
||||||
@@ -53,7 +54,7 @@ export function AdminUsersPage() {
|
|||||||
</Trans>
|
</Trans>
|
||||||
</Text>
|
</Text>
|
||||||
),
|
),
|
||||||
labels: { confirm: t`Confirm`, cancel: t`Cancel` },
|
labels: { confirm: <Trans>Confirm</Trans>, cancel: <Trans>Cancel</Trans> },
|
||||||
confirmProps: { color: "red" },
|
confirmProps: { color: "red" },
|
||||||
onConfirm: () => deleteUser.execute({ id: user.id }),
|
onConfirm: () => deleteUser.execute({ id: user.id }),
|
||||||
})
|
})
|
||||||
@@ -65,7 +66,7 @@ export function AdminUsersPage() {
|
|||||||
<Title order={3} mb="md">
|
<Title order={3} mb="md">
|
||||||
<Group>
|
<Group>
|
||||||
<Trans>Manage users</Trans>
|
<Trans>Manage users</Trans>
|
||||||
<ActionIcon color={theme.primaryColor} onClick={() => openUserEditModal(t`Add user`)}>
|
<ActionIcon color={theme.primaryColor} onClick={() => openUserEditModal(<Trans>Add user</Trans>)}>
|
||||||
<TbPlus size={20} />
|
<TbPlus size={20} />
|
||||||
</ActionIcon>
|
</ActionIcon>
|
||||||
</Group>
|
</Group>
|
||||||
@@ -126,7 +127,7 @@ export function AdminUsersPage() {
|
|||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<Group>
|
<Group>
|
||||||
<ActionIcon color={theme.primaryColor} onClick={() => openUserEditModal(t`Edit user`, u)}>
|
<ActionIcon color={theme.primaryColor} onClick={() => openUserEditModal(<Trans>Edit user</Trans>, u)}>
|
||||||
<TbPencil size={18} />
|
<TbPencil size={18} />
|
||||||
</ActionIcon>
|
</ActionIcon>
|
||||||
<ActionIcon
|
<ActionIcon
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ const useStyles = createStyles(() => ({
|
|||||||
},
|
},
|
||||||
}))
|
}))
|
||||||
|
|
||||||
function Section(props: { title: string; icon: React.ReactNode; children: React.ReactNode }) {
|
function Section(props: { title: React.ReactNode; icon: React.ReactNode; children: React.ReactNode }) {
|
||||||
const { classes } = useStyles()
|
const { classes } = useStyles()
|
||||||
return (
|
return (
|
||||||
<Box my="xl">
|
<Box my="xl">
|
||||||
@@ -38,7 +38,7 @@ function NextUnreadBookmarklet() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
<CategorySelect value={categoryId} onChange={c => c && setCategoryId(c)} withAll description={t`Category`} />
|
<CategorySelect value={categoryId} onChange={c => c && setCategoryId(c)} withAll description={<Trans>Category</Trans>} />
|
||||||
<NativeSelect
|
<NativeSelect
|
||||||
data={[
|
data={[
|
||||||
{ value: "desc", label: t`Newest first` },
|
{ value: "desc", label: t`Newest first` },
|
||||||
@@ -46,7 +46,7 @@ function NextUnreadBookmarklet() {
|
|||||||
]}
|
]}
|
||||||
value={order}
|
value={order}
|
||||||
onChange={e => setOrder(e.target.value)}
|
onChange={e => setOrder(e.target.value)}
|
||||||
description={t`Order`}
|
description={<Trans>Order</Trans>}
|
||||||
/>
|
/>
|
||||||
<Trans>Drag link to bookmark bar</Trans>
|
<Trans>Drag link to bookmark bar</Trans>
|
||||||
<span> </span>
|
<span> </span>
|
||||||
@@ -58,6 +58,7 @@ function NextUnreadBookmarklet() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const bitcoinAddress = <Code>{Constants.bitcoinWalletAddress}</Code>
|
const bitcoinAddress = <Code>{Constants.bitcoinWalletAddress}</Code>
|
||||||
|
|
||||||
export function AboutPage() {
|
export function AboutPage() {
|
||||||
const version = useAppSelector(state => state.server.serverInfos?.version)
|
const version = useAppSelector(state => state.server.serverInfos?.version)
|
||||||
const revision = useAppSelector(state => state.server.serverInfos?.gitCommit)
|
const revision = useAppSelector(state => state.server.serverInfos?.gitCommit)
|
||||||
@@ -65,7 +66,7 @@ export function AboutPage() {
|
|||||||
return (
|
return (
|
||||||
<Container size="xl">
|
<Container size="xl">
|
||||||
<SimpleGrid cols={2} breakpoints={[{ maxWidth: Constants.layout.mobileBreakpoint, cols: 1 }]}>
|
<SimpleGrid cols={2} breakpoints={[{ maxWidth: Constants.layout.mobileBreakpoint, cols: 1 }]}>
|
||||||
<Section title={t`About`} icon={<TbHelp size={24} />}>
|
<Section title={<Trans>About</Trans>} icon={<TbHelp size={24} />}>
|
||||||
<Box>
|
<Box>
|
||||||
<Trans>
|
<Trans>
|
||||||
CommaFeed version {version} ({revision})
|
CommaFeed version {version} ({revision})
|
||||||
@@ -119,7 +120,7 @@ export function AboutPage() {
|
|||||||
<Trans>For those of you who prefer bitcoin, here is the address: {bitcoinAddress}</Trans>
|
<Trans>For those of you who prefer bitcoin, here is the address: {bitcoinAddress}</Trans>
|
||||||
</Box>
|
</Box>
|
||||||
</Section>
|
</Section>
|
||||||
<Section title={t`Goodies`} icon={<TbPuzzle size={24} />}>
|
<Section title={<Trans>Goodies</Trans>} icon={<TbPuzzle size={24} />}>
|
||||||
<List>
|
<List>
|
||||||
<List.Item>
|
<List.Item>
|
||||||
<Trans>Browser extentions</Trans>
|
<Trans>Browser extentions</Trans>
|
||||||
@@ -161,10 +162,10 @@ export function AboutPage() {
|
|||||||
</List.Item>
|
</List.Item>
|
||||||
</List>
|
</List>
|
||||||
</Section>
|
</Section>
|
||||||
<Section title={t`Keyboard shortcuts`} icon={<TbKeyboard size={24} />}>
|
<Section title={<Trans>Keyboard shortcuts</Trans>} icon={<TbKeyboard size={24} />}>
|
||||||
<KeyboardShortcutsHelp />
|
<KeyboardShortcutsHelp />
|
||||||
</Section>
|
</Section>
|
||||||
<Section title={t`REST API`} icon={<TbRocket size={24} />}>
|
<Section title={<Trans>REST API</Trans>} icon={<TbRocket size={24} />}>
|
||||||
<Anchor onClick={() => dispatch(redirectToApiDocumentation())}>
|
<Anchor onClick={() => dispatch(redirectToApiDocumentation())}>
|
||||||
<Trans>Go to the API documentation.</Trans>
|
<Trans>Go to the API documentation.</Trans>
|
||||||
</Anchor>
|
</Anchor>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { t, Trans } from "@lingui/macro"
|
import { Trans } from "@lingui/macro"
|
||||||
import { Anchor, Box, Button, Code, Container, Divider, Group, Input, NumberInput, Stack, Text, TextInput, Title } from "@mantine/core"
|
import { Anchor, Box, Button, Code, Container, Divider, Group, Input, NumberInput, Stack, Text, TextInput, Title } from "@mantine/core"
|
||||||
import { useForm } from "@mantine/form"
|
import { useForm } from "@mantine/form"
|
||||||
import { openConfirmModal } from "@mantine/modals"
|
import { openConfirmModal } from "@mantine/modals"
|
||||||
@@ -48,7 +48,7 @@ export function CategoryDetailsPage() {
|
|||||||
const openDeleteCategoryModal = () => {
|
const openDeleteCategoryModal = () => {
|
||||||
const categoryName = category?.name
|
const categoryName = category?.name
|
||||||
return openConfirmModal({
|
return openConfirmModal({
|
||||||
title: t`Delete Category`,
|
title: <Trans>Delete Category</Trans>,
|
||||||
children: (
|
children: (
|
||||||
<Text size="sm">
|
<Text size="sm">
|
||||||
<Trans>
|
<Trans>
|
||||||
@@ -56,7 +56,7 @@ export function CategoryDetailsPage() {
|
|||||||
</Trans>
|
</Trans>
|
||||||
</Text>
|
</Text>
|
||||||
),
|
),
|
||||||
labels: { confirm: t`Confirm`, cancel: t`Cancel` },
|
labels: { confirm: <Trans>Confirm</Trans>, cancel: <Trans>Cancel</Trans> },
|
||||||
confirmProps: { color: "red" },
|
confirmProps: { color: "red" },
|
||||||
onConfirm: () => deleteCategory.execute({ id: +id }),
|
onConfirm: () => deleteCategory.execute({ id: +id }),
|
||||||
})
|
})
|
||||||
@@ -91,7 +91,7 @@ export function CategoryDetailsPage() {
|
|||||||
<form onSubmit={form.onSubmit(modifyCategory.execute)}>
|
<form onSubmit={form.onSubmit(modifyCategory.execute)}>
|
||||||
<Stack>
|
<Stack>
|
||||||
<Title order={3}>{category.name}</Title>
|
<Title order={3}>{category.name}</Title>
|
||||||
<Input.Wrapper label={t`Generated feed url`}>
|
<Input.Wrapper label={<Trans>Generated feed url</Trans>}>
|
||||||
<Box>
|
<Box>
|
||||||
{apiKey && (
|
{apiKey && (
|
||||||
<Anchor
|
<Anchor
|
||||||
@@ -108,14 +108,14 @@ export function CategoryDetailsPage() {
|
|||||||
|
|
||||||
{editable && (
|
{editable && (
|
||||||
<>
|
<>
|
||||||
<TextInput label={t`Name`} {...form.getInputProps("name")} required />
|
<TextInput label={<Trans>Name</Trans>} {...form.getInputProps("name")} required />
|
||||||
<CategorySelect
|
<CategorySelect
|
||||||
label={t`Parent Category`}
|
label={<Trans>Parent Category</Trans>}
|
||||||
{...form.getInputProps("parentId")}
|
{...form.getInputProps("parentId")}
|
||||||
clearable
|
clearable
|
||||||
withoutCategoryIds={[id]}
|
withoutCategoryIds={[id]}
|
||||||
/>
|
/>
|
||||||
<NumberInput label={t`Position`} {...form.getInputProps("position")} required min={0} />
|
<NumberInput label={<Trans>Position</Trans>} {...form.getInputProps("position")} required min={0} />
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { t, Trans } from "@lingui/macro"
|
import { Trans } from "@lingui/macro"
|
||||||
import { Anchor, Box, Button, Code, Container, Divider, Group, Input, NumberInput, Stack, Text, TextInput, Title } from "@mantine/core"
|
import { Anchor, Box, Button, Code, Container, Divider, Group, Input, NumberInput, Stack, Text, TextInput, Title } from "@mantine/core"
|
||||||
import { useForm } from "@mantine/form"
|
import { useForm } from "@mantine/form"
|
||||||
import { openConfirmModal } from "@mantine/modals"
|
import { openConfirmModal } from "@mantine/modals"
|
||||||
@@ -47,6 +47,7 @@ function FilteringExpressionDescription() {
|
|||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function FeedDetailsPage() {
|
export function FeedDetailsPage() {
|
||||||
const { id } = useParams()
|
const { id } = useParams()
|
||||||
if (!id) throw Error("id required")
|
if (!id) throw Error("id required")
|
||||||
@@ -75,7 +76,7 @@ export function FeedDetailsPage() {
|
|||||||
const openUnsubscribeModal = () => {
|
const openUnsubscribeModal = () => {
|
||||||
const feedName = feed?.name
|
const feedName = feed?.name
|
||||||
return openConfirmModal({
|
return openConfirmModal({
|
||||||
title: t`Unsubscribe`,
|
title: <Trans>Unsubscribe</Trans>,
|
||||||
children: (
|
children: (
|
||||||
<Text size="sm">
|
<Text size="sm">
|
||||||
<Trans>
|
<Trans>
|
||||||
@@ -83,7 +84,7 @@ export function FeedDetailsPage() {
|
|||||||
</Trans>
|
</Trans>
|
||||||
</Text>
|
</Text>
|
||||||
),
|
),
|
||||||
labels: { confirm: t`Confirm`, cancel: t`Cancel` },
|
labels: { confirm: <Trans>Confirm</Trans>, cancel: <Trans>Cancel</Trans> },
|
||||||
confirmProps: { color: "red" },
|
confirmProps: { color: "red" },
|
||||||
onConfirm: () => unsubscribe.execute({ id: +id }),
|
onConfirm: () => unsubscribe.execute({ id: +id }),
|
||||||
})
|
})
|
||||||
@@ -112,34 +113,34 @@ export function FeedDetailsPage() {
|
|||||||
<form onSubmit={form.onSubmit(modifyFeed.execute)}>
|
<form onSubmit={form.onSubmit(modifyFeed.execute)}>
|
||||||
<Stack>
|
<Stack>
|
||||||
<Title order={3}>{feed.name}</Title>
|
<Title order={3}>{feed.name}</Title>
|
||||||
<Input.Wrapper label={t`Feed URL`}>
|
<Input.Wrapper label={<Trans>Feed URL</Trans>}>
|
||||||
<Box>
|
<Box>
|
||||||
<Anchor href={feed.feedUrl} target="_blank" rel="noreferrer">
|
<Anchor href={feed.feedUrl} target="_blank" rel="noreferrer">
|
||||||
{feed.feedUrl}
|
{feed.feedUrl}
|
||||||
</Anchor>
|
</Anchor>
|
||||||
</Box>
|
</Box>
|
||||||
</Input.Wrapper>
|
</Input.Wrapper>
|
||||||
<Input.Wrapper label={t`Website`}>
|
<Input.Wrapper label={<Trans>Website</Trans>}>
|
||||||
<Box>
|
<Box>
|
||||||
<Anchor href={feed.feedLink} target="_blank" rel="noreferrer">
|
<Anchor href={feed.feedLink} target="_blank" rel="noreferrer">
|
||||||
{feed.feedLink}
|
{feed.feedLink}
|
||||||
</Anchor>
|
</Anchor>
|
||||||
</Box>
|
</Box>
|
||||||
</Input.Wrapper>
|
</Input.Wrapper>
|
||||||
<Input.Wrapper label={t`Last refresh`}>
|
<Input.Wrapper label={<Trans>Last refresh</Trans>}>
|
||||||
<Box>
|
<Box>
|
||||||
<RelativeDate date={feed.lastRefresh} />
|
<RelativeDate date={feed.lastRefresh} />
|
||||||
</Box>
|
</Box>
|
||||||
</Input.Wrapper>
|
</Input.Wrapper>
|
||||||
<Input.Wrapper label={t`Last refresh message`}>
|
<Input.Wrapper label={<Trans>Last refresh message</Trans>}>
|
||||||
<Box>{feed.message ?? t`N/A`}</Box>
|
<Box>{feed.message ?? <Trans>N/A</Trans>}</Box>
|
||||||
</Input.Wrapper>
|
</Input.Wrapper>
|
||||||
<Input.Wrapper label={t`Next refresh`}>
|
<Input.Wrapper label={<Trans>Next refresh</Trans>}>
|
||||||
<Box>
|
<Box>
|
||||||
<RelativeDate date={feed.nextRefresh} />
|
<RelativeDate date={feed.nextRefresh} />
|
||||||
</Box>
|
</Box>
|
||||||
</Input.Wrapper>
|
</Input.Wrapper>
|
||||||
<Input.Wrapper label={t`Generated feed url`}>
|
<Input.Wrapper label={<Trans>Generated feed url</Trans>}>
|
||||||
<Box>
|
<Box>
|
||||||
{apiKey && (
|
{apiKey && (
|
||||||
<Anchor href={`rest/feed/entriesAsFeed?id=${feed.id}&apiKey=${apiKey}`} target="_blank" rel="noreferrer">
|
<Anchor href={`rest/feed/entriesAsFeed?id=${feed.id}&apiKey=${apiKey}`} target="_blank" rel="noreferrer">
|
||||||
@@ -150,11 +151,11 @@ export function FeedDetailsPage() {
|
|||||||
</Box>
|
</Box>
|
||||||
</Input.Wrapper>
|
</Input.Wrapper>
|
||||||
|
|
||||||
<TextInput label={t`Name`} {...form.getInputProps("name")} required />
|
<TextInput label={<Trans>Name</Trans>} {...form.getInputProps("name")} required />
|
||||||
<CategorySelect label={t`Category`} {...form.getInputProps("categoryId")} clearable />
|
<CategorySelect label={<Trans>Category</Trans>} {...form.getInputProps("categoryId")} clearable />
|
||||||
<NumberInput label={t`Position`} {...form.getInputProps("position")} required min={0} />
|
<NumberInput label={<Trans>Position</Trans>} {...form.getInputProps("position")} required min={0} />
|
||||||
<TextInput
|
<TextInput
|
||||||
label={t`Filtering expression`}
|
label={<Trans>Filtering expression</Trans>}
|
||||||
description={<FilteringExpressionDescription />}
|
description={<FilteringExpressionDescription />}
|
||||||
{...form.getInputProps("filter")}
|
{...form.getInputProps("filter")}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { t, Trans } from "@lingui/macro"
|
import { Trans } from "@lingui/macro"
|
||||||
import { ActionIcon, Anchor, Box, Center, Divider, Group, Title, useMantineTheme } from "@mantine/core"
|
import { ActionIcon, Anchor, Box, Center, Divider, Group, Title, useMantineTheme } from "@mantine/core"
|
||||||
import { useViewportSize } from "@mantine/hooks"
|
import { useViewportSize } from "@mantine/hooks"
|
||||||
import { Constants } from "app/constants"
|
import { Constants } from "app/constants"
|
||||||
@@ -77,7 +77,7 @@ export function FeedEntriesPage(props: FeedEntriesPageProps) {
|
|||||||
|
|
||||||
<FeedEntries />
|
<FeedEntries />
|
||||||
|
|
||||||
{!hasMore && <Divider my="xl" label={t`No more entries`} labelPosition="center" />}
|
{!hasMore && <Divider my="xl" label={<Trans>No more entries</Trans>} labelPosition="center" />}
|
||||||
</Box>
|
</Box>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { t, Trans } from "@lingui/macro"
|
import { Trans } from "@lingui/macro"
|
||||||
|
|
||||||
import { Anchor, Box, Button, Container, Group, Input, Stack, Title } from "@mantine/core"
|
import { Anchor, Box, Button, Container, Group, Input, Stack, Title } from "@mantine/core"
|
||||||
import { Constants } from "app/constants"
|
import { Constants } from "app/constants"
|
||||||
@@ -16,7 +16,7 @@ export function TagDetailsPage() {
|
|||||||
<Container>
|
<Container>
|
||||||
<Stack>
|
<Stack>
|
||||||
<Title order={3}>{id}</Title>
|
<Title order={3}>{id}</Title>
|
||||||
<Input.Wrapper label={t`Generated feed url`}>
|
<Input.Wrapper label={<Trans>Generated feed url</Trans>}>
|
||||||
<Box>
|
<Box>
|
||||||
{apiKey && (
|
{apiKey && (
|
||||||
<Anchor
|
<Anchor
|
||||||
|
|||||||
@@ -42,15 +42,17 @@ export function LoginPage() {
|
|||||||
<form onSubmit={form.onSubmit(login.execute)}>
|
<form onSubmit={form.onSubmit(login.execute)}>
|
||||||
<Stack>
|
<Stack>
|
||||||
<TextInput
|
<TextInput
|
||||||
label={t`User Name or E-mail`}
|
label={<Trans>User Name or E-mail</Trans>}
|
||||||
placeholder={t`User Name or E-mail`}
|
placeholder={t`User Name or E-mail`}
|
||||||
{...form.getInputProps("name")}
|
{...form.getInputProps("name")}
|
||||||
description={serverInfos?.demoAccountEnabled ? t`Try out CommaFeed with the demo account: demo/demo` : ""}
|
description={
|
||||||
|
serverInfos?.demoAccountEnabled ? <Trans>Try out CommaFeed with the demo account: demo/demo</Trans> : ""
|
||||||
|
}
|
||||||
size="md"
|
size="md"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
<PasswordInput
|
<PasswordInput
|
||||||
label={t`Password`}
|
label={<Trans>Password</Trans>}
|
||||||
placeholder={t`Password`}
|
placeholder={t`Password`}
|
||||||
{...form.getInputProps("password")}
|
{...form.getInputProps("password")}
|
||||||
size="md"
|
size="md"
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ export function PasswordRecoveryPage() {
|
|||||||
<Stack>
|
<Stack>
|
||||||
<TextInput
|
<TextInput
|
||||||
type="email"
|
type="email"
|
||||||
label={t`E-mail`}
|
label={<Trans>E-mail</Trans>}
|
||||||
placeholder={t`E-mail`}
|
placeholder={t`E-mail`}
|
||||||
{...form.getInputProps("email")}
|
{...form.getInputProps("email")}
|
||||||
size="md"
|
size="md"
|
||||||
|
|||||||
@@ -53,14 +53,14 @@ export function RegistrationPage() {
|
|||||||
<TextInput label="User Name" placeholder="User Name" {...form.getInputProps("name")} size="md" required />
|
<TextInput label="User Name" placeholder="User Name" {...form.getInputProps("name")} size="md" required />
|
||||||
<TextInput
|
<TextInput
|
||||||
type="email"
|
type="email"
|
||||||
label={t`E-mail address`}
|
label={<Trans>E-mail address</Trans>}
|
||||||
placeholder={t`E-mail address`}
|
placeholder={t`E-mail address`}
|
||||||
{...form.getInputProps("email")}
|
{...form.getInputProps("email")}
|
||||||
size="md"
|
size="md"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
<PasswordInput
|
<PasswordInput
|
||||||
label={t`Password`}
|
label={<Trans>Password</Trans>}
|
||||||
placeholder={t`Password`}
|
placeholder={t`Password`}
|
||||||
{...form.getInputProps("password")}
|
{...form.getInputProps("password")}
|
||||||
size="md"
|
size="md"
|
||||||
|
|||||||
Reference in New Issue
Block a user