forked from Archives/Athou_commafeed
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 {
|
||||
className?: string
|
||||
icon?: ReactNode
|
||||
label?: string
|
||||
label?: ReactNode
|
||||
onClick?: MouseEventHandler
|
||||
variant?: ActionIconVariant & ButtonVariant
|
||||
showLabelOnMobile?: boolean
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { t } from "@lingui/macro"
|
||||
import { Alert as MantineAlert, Box } from "@mantine/core"
|
||||
import { Trans } from "@lingui/macro"
|
||||
import { Box, Alert as MantineAlert } from "@mantine/core"
|
||||
import { Fragment } from "react"
|
||||
import { TbAlertCircle, TbAlertTriangle, TbCircleCheck } from "react-icons/tb"
|
||||
|
||||
@@ -10,24 +10,24 @@ export interface ErrorsAlertProps {
|
||||
}
|
||||
|
||||
export function Alert(props: ErrorsAlertProps) {
|
||||
let title: string
|
||||
let title: React.ReactNode
|
||||
let color: string
|
||||
let icon: React.ReactNode
|
||||
|
||||
const level = props.level ?? "error"
|
||||
switch (level) {
|
||||
case "error":
|
||||
title = t`Error`
|
||||
title = <Trans>Error</Trans>
|
||||
color = "red"
|
||||
icon = <TbAlertCircle />
|
||||
break
|
||||
case "warning":
|
||||
title = t`Warning`
|
||||
title = <Trans>Warning</Trans>
|
||||
color = "orange"
|
||||
icon = <TbAlertTriangle />
|
||||
break
|
||||
case "success":
|
||||
title = t`Success`
|
||||
title = <Trans>Success</Trans>
|
||||
color = "green"
|
||||
icon = <TbCircleCheck />
|
||||
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 { useForm } from "@mantine/form"
|
||||
import { client, errorToStrings } from "app/client"
|
||||
@@ -29,11 +29,11 @@ export function UserEdit(props: UserEditProps) {
|
||||
|
||||
<form onSubmit={form.onSubmit(saveUser.execute)}>
|
||||
<Stack>
|
||||
<TextInput label={t`Name`} {...form.getInputProps("name")} required />
|
||||
<PasswordInput label={t`Password`} {...form.getInputProps("password")} required={!props.user} />
|
||||
<TextInput type="email" label={t`E-mail`} {...form.getInputProps("email")} />
|
||||
<Checkbox label={t`Admin`} {...form.getInputProps("admin", { type: "checkbox" })} />
|
||||
<Checkbox label={t`Enabled`} {...form.getInputProps("enabled", { type: "checkbox" })} />
|
||||
<TextInput label={<Trans>Name</Trans>} {...form.getInputProps("name")} required />
|
||||
<PasswordInput label={<Trans>Password</Trans>} {...form.getInputProps("password")} required={!props.user} />
|
||||
<TextInput type="email" label={<Trans>E-mail</Trans>} {...form.getInputProps("email")} />
|
||||
<Checkbox label={<Trans>Admin</Trans>} {...form.getInputProps("admin", { type: "checkbox" })} />
|
||||
<Checkbox label={<Trans>Enabled</Trans>} {...form.getInputProps("enabled", { type: "checkbox" })} />
|
||||
|
||||
<Group>
|
||||
<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 { Constants } from "app/constants"
|
||||
import {
|
||||
@@ -234,7 +234,13 @@ export function FeedEntries() {
|
||||
)
|
||||
})
|
||||
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 />
|
||||
return (
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { t, Trans } from "@lingui/macro"
|
||||
import { Trans } from "@lingui/macro"
|
||||
import { createStyles, Group } from "@mantine/core"
|
||||
import { Constants } from "app/constants"
|
||||
import { markEntriesUpToEntry, markEntry, starEntry } from "app/slices/entries"
|
||||
@@ -29,6 +29,7 @@ const useStyles = createStyles(theme => ({
|
||||
}))
|
||||
|
||||
const menuId = (entry: Entry) => entry.id
|
||||
|
||||
export function FeedEntryContextMenu(props: FeedEntryContextMenuProps) {
|
||||
const { classes, theme } = useStyles()
|
||||
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 }))}>
|
||||
<Group>
|
||||
{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>
|
||||
</Item>
|
||||
<Item onClick={() => dispatch(markEntry({ entry: props.entry, read: !props.entry.read }))}>
|
||||
<Group>
|
||||
{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>
|
||||
</Item>
|
||||
<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 { useMediaQuery } from "@mantine/hooks"
|
||||
import { Constants } from "app/constants"
|
||||
@@ -50,20 +50,20 @@ export function FeedEntryFooter(props: FeedEntryFooterProps) {
|
||||
{props.entry.markable && (
|
||||
<ActionButton
|
||||
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}
|
||||
/>
|
||||
)}
|
||||
<ActionButton
|
||||
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 }))}
|
||||
/>
|
||||
|
||||
{showSharingButtons && (
|
||||
<Popover withArrow withinPortal shadow="md" positionDependencies={[scrollPosition]} closeOnClickOutside={!mobile}>
|
||||
<Popover.Target>
|
||||
<ActionButton icon={<TbShare size={18} />} label={t`Share`} />
|
||||
<ActionButton icon={<TbShare size={18} />} label={<Trans>Share</Trans>} />
|
||||
</Popover.Target>
|
||||
<Popover.Dropdown>
|
||||
<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.Target>
|
||||
<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>
|
||||
</Popover.Target>
|
||||
<Popover.Dropdown>
|
||||
@@ -94,13 +94,13 @@ export function FeedEntryFooter(props: FeedEntryFooterProps) {
|
||||
)}
|
||||
|
||||
<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>
|
||||
</ButtonToolbar>
|
||||
|
||||
<ActionButton
|
||||
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))}
|
||||
/>
|
||||
</Group>
|
||||
|
||||
@@ -33,8 +33,8 @@ export function AddCategory() {
|
||||
|
||||
<form onSubmit={form.onSubmit(addCategory.execute)}>
|
||||
<Stack>
|
||||
<TextInput label={t`Category`} placeholder={t`Category`} {...form.getInputProps("name")} required />
|
||||
<CategorySelect label={t`Parent`} {...form.getInputProps("parentId")} clearable />
|
||||
<TextInput label={<Trans>Category</Trans>} placeholder={t`Category`} {...form.getInputProps("name")} required />
|
||||
<CategorySelect label={<Trans>Parent</Trans>} {...form.getInputProps("parentId")} clearable />
|
||||
<Group position="center">
|
||||
<Button variant="default" onClick={() => dispatch(redirectToSelectedSource())}>
|
||||
<Trans>Cancel</Trans>
|
||||
|
||||
@@ -36,9 +36,14 @@ export function ImportOpml() {
|
||||
<form onSubmit={form.onSubmit(v => importOpml.execute(v.file))}>
|
||||
<Stack>
|
||||
<FileInput
|
||||
label={t`OPML file`}
|
||||
label={<Trans>OPML file</Trans>}
|
||||
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")}
|
||||
required
|
||||
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 { useForm } from "@mantine/form"
|
||||
import { client, errorToStrings } from "app/client"
|
||||
@@ -74,24 +74,33 @@ export function Subscribe() {
|
||||
<form onSubmit={nextStep}>
|
||||
<Stepper active={activeStep} onStepClick={setActiveStep}>
|
||||
<Stepper.Step
|
||||
label={t`Analyze feed`}
|
||||
description={t`Check that the feed is working`}
|
||||
label={<Trans>Analyze feed</Trans>}
|
||||
description={<Trans>Check that the feed is working</Trans>}
|
||||
allowStepSelect={activeStep === 1}
|
||||
>
|
||||
<TextInput
|
||||
label={t`Feed URL`}
|
||||
label={<Trans>Feed URL</Trans>}
|
||||
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
|
||||
autoFocus
|
||||
{...step0Form.getInputProps("url")}
|
||||
/>
|
||||
</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>
|
||||
<TextInput label={t`Feed URL`} {...step1Form.getInputProps("url")} disabled />
|
||||
<TextInput label={t`Feed name`} {...step1Form.getInputProps("title")} required autoFocus />
|
||||
<CategorySelect label={t`Category`} {...step1Form.getInputProps("categoryId")} clearable />
|
||||
<TextInput label={<Trans>Feed URL</Trans>} {...step1Form.getInputProps("url")} disabled />
|
||||
<TextInput label={<Trans>Feed name</Trans>} {...step1Form.getInputProps("title")} required autoFocus />
|
||||
<CategorySelect label={<Trans>Category</Trans>} {...step1Form.getInputProps("categoryId")} clearable />
|
||||
</Stack>
|
||||
</Stepper.Step>
|
||||
</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 { useForm } from "@mantine/form"
|
||||
import { reloadEntries, search } from "app/slices/entries"
|
||||
@@ -17,6 +17,7 @@ function HeaderDivider() {
|
||||
}
|
||||
|
||||
const iconSize = 18
|
||||
|
||||
export function Header() {
|
||||
const settings = useAppSelector(state => state.user.settings)
|
||||
const profile = useAppSelector(state => state.user.profile)
|
||||
@@ -40,26 +41,30 @@ export function Header() {
|
||||
return (
|
||||
<Center>
|
||||
<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} />
|
||||
|
||||
<HeaderDivider />
|
||||
|
||||
<ActionButton
|
||||
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"))}
|
||||
/>
|
||||
<ActionButton
|
||||
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"))}
|
||||
/>
|
||||
|
||||
<Popover>
|
||||
<Popover.Target>
|
||||
<Indicator disabled={!searchFromStore}>
|
||||
<ActionButton icon={<TbSearch size={iconSize} />} label={t`Search`} />
|
||||
<ActionButton icon={<TbSearch size={iconSize} />} label={<Trans>Search</Trans>} />
|
||||
</Indicator>
|
||||
</Popover.Target>
|
||||
<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 { markAllEntries } from "app/slices/entries"
|
||||
@@ -17,7 +17,7 @@ export function MarkAllAsReadButton(props: { iconSize: number }) {
|
||||
|
||||
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>
|
||||
<Text size="sm">
|
||||
{threshold === 0 && (
|
||||
@@ -72,7 +72,7 @@ export function MarkAllAsReadButton(props: { iconSize: number }) {
|
||||
</Modal>
|
||||
<ActionButton
|
||||
icon={<TbChecks size={props.iconSize} />}
|
||||
label={t`Mark all as read`}
|
||||
label={<Trans>Mark all as read</Trans>}
|
||||
onClick={() => {
|
||||
setThreshold(0)
|
||||
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 { showNotification } from "@mantine/notifications"
|
||||
import { client } from "app/client"
|
||||
@@ -111,7 +111,7 @@ export function ProfileMenu(props: ProfileMenuProps) {
|
||||
onClick={() =>
|
||||
client.feed.refreshAll().then(() => {
|
||||
showNotification({
|
||||
message: t`Your feeds have been queued for refresh.`,
|
||||
message: <Trans>Your feeds have been queued for refresh.</Trans>,
|
||||
color: "green",
|
||||
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 { Constants } from "app/constants"
|
||||
import { changeLanguage, changeScrollMarks, changeScrollSpeed, changeSharingSetting, changeShowRead } from "app/slices/user"
|
||||
@@ -17,7 +17,7 @@ export function DisplaySettings() {
|
||||
return (
|
||||
<Stack>
|
||||
<Select
|
||||
description={t`Language`}
|
||||
description={<Trans>Language</Trans>}
|
||||
value={language}
|
||||
data={locales.map(l => ({
|
||||
value: l.key,
|
||||
@@ -27,24 +27,24 @@ export function DisplaySettings() {
|
||||
/>
|
||||
|
||||
<Switch
|
||||
label={t`Scroll smoothly when navigating between entries`}
|
||||
label={<Trans>Scroll smoothly when navigating between entries</Trans>}
|
||||
checked={scrollSpeed ? scrollSpeed > 0 : false}
|
||||
onChange={e => dispatch(changeScrollSpeed(e.currentTarget.checked))}
|
||||
/>
|
||||
|
||||
<Switch
|
||||
label={t`Show feeds and categories with no unread entries`}
|
||||
label={<Trans>Show feeds and categories with no unread entries</Trans>}
|
||||
checked={showRead}
|
||||
onChange={e => dispatch(changeShowRead(e.currentTarget.checked))}
|
||||
/>
|
||||
|
||||
<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}
|
||||
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}>
|
||||
{(Object.keys(Constants.sharing) as Array<keyof SharingSettings>).map(site => (
|
||||
|
||||
@@ -41,13 +41,13 @@ export function ProfileSettings() {
|
||||
|
||||
const openDeleteProfileModal = () =>
|
||||
openConfirmModal({
|
||||
title: t`Delete account`,
|
||||
title: <Trans>Delete account</Trans>,
|
||||
children: (
|
||||
<Text size="sm">
|
||||
<Trans>Are you sure you want to delete your account? There's no turning back!</Trans>
|
||||
</Text>
|
||||
),
|
||||
labels: { confirm: t`Confirm`, cancel: t`Cancel` },
|
||||
labels: { confirm: <Trans>Confirm</Trans>, cancel: <Trans>Cancel</Trans> },
|
||||
confirmProps: { color: "red" },
|
||||
onConfirm: () => deleteProfile.execute(),
|
||||
})
|
||||
@@ -77,12 +77,16 @@ export function ProfileSettings() {
|
||||
|
||||
<form onSubmit={form.onSubmit(saveProfile.execute)}>
|
||||
<Stack>
|
||||
<Input.Wrapper label={t`User name`}>
|
||||
<Input.Wrapper label={<Trans>User name</Trans>}>
|
||||
<Box>{profile?.name}</Box>
|
||||
</Input.Wrapper>
|
||||
<Input.Wrapper
|
||||
label={t`OPML export`}
|
||||
description={t`Export your subscriptions and categories as an OPML file that can be imported in other feed reading services`}
|
||||
label={<Trans>OPML export</Trans>}
|
||||
description={
|
||||
<Trans>
|
||||
Export your subscriptions and categories as an OPML file that can be imported in other feed reading services
|
||||
</Trans>
|
||||
}
|
||||
>
|
||||
<Box>
|
||||
<Anchor href="rest/feed/export" download="commafeed_opml.xml">
|
||||
@@ -91,20 +95,20 @@ export function ProfileSettings() {
|
||||
</Box>
|
||||
</Input.Wrapper>
|
||||
<PasswordInput
|
||||
label={t`Current password`}
|
||||
description={t`Enter your current password to change profile settings`}
|
||||
label={<Trans>Current password</Trans>}
|
||||
description={<Trans>Enter your current password to change profile settings</Trans>}
|
||||
required
|
||||
{...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
|
||||
label={t`New password`}
|
||||
description={t`Changing password will generate a new API key`}
|
||||
label={<Trans>New password</Trans>}
|
||||
description={<Trans>Changing password will generate a new API key</Trans>}
|
||||
{...form.getInputProps("newPassword")}
|
||||
/>
|
||||
<PasswordInput label={t`Confirm password`} {...form.getInputProps("newPasswordConfirmation")} />
|
||||
<TextInput label={t`API key`} readOnly value={profile?.apiKey} />
|
||||
<Checkbox label={t`Generate new API key`} {...form.getInputProps("newApiKey", { type: "checkbox" })} />
|
||||
<PasswordInput label={<Trans>Confirm password</Trans>} {...form.getInputProps("newPasswordConfirmation")} />
|
||||
<TextInput label={<Trans>API key</Trans>} readOnly value={profile?.apiKey} />
|
||||
<Checkbox label={<Trans>Generate new API key</Trans>} {...form.getInputProps("newApiKey", { type: "checkbox" })} />
|
||||
|
||||
<Group>
|
||||
<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 { Constants } from "app/constants"
|
||||
import {
|
||||
@@ -27,6 +27,7 @@ const expandedIcon = <TbChevronDown size={16} />
|
||||
const collapsedIcon = <TbChevronRight size={16} />
|
||||
|
||||
const errorThreshold = 9
|
||||
|
||||
export function Tree() {
|
||||
const root = useAppSelector(state => state.tree.rootCategory)
|
||||
const source = useAppSelector(state => state.entries.source)
|
||||
@@ -63,7 +64,7 @@ export function Tree() {
|
||||
const allCategoryNode = () => (
|
||||
<TreeNode
|
||||
id={Constants.categories.all.id}
|
||||
name={t`All`}
|
||||
name={<Trans>All</Trans>}
|
||||
icon={allIcon}
|
||||
unread={categoryUnreadCount(root)}
|
||||
selected={source.type === "category" && source.id === Constants.categories.all.id}
|
||||
@@ -76,7 +77,7 @@ export function Tree() {
|
||||
const starredCategoryNode = () => (
|
||||
<TreeNode
|
||||
id={Constants.categories.starred.id}
|
||||
name={t`Starred`}
|
||||
name={<Trans>Starred</Trans>}
|
||||
icon={starredIcon}
|
||||
unread={0}
|
||||
selected={source.type === "category" && source.id === Constants.categories.starred.id}
|
||||
|
||||
@@ -5,8 +5,8 @@ import { UnreadCount } from "./UnreadCount"
|
||||
|
||||
interface TreeNodeProps {
|
||||
id: string
|
||||
name: string
|
||||
icon: ReactNode | string
|
||||
name: ReactNode
|
||||
icon: ReactNode
|
||||
unread: number
|
||||
selected: 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 { openSpotlight, SpotlightAction, SpotlightProvider } from "@mantine/spotlight"
|
||||
import { redirectToFeed } from "app/slices/redirect"
|
||||
@@ -11,6 +11,7 @@ import { TbSearch } from "react-icons/tb"
|
||||
export interface TreeSearchProps {
|
||||
feeds: Subscription[]
|
||||
}
|
||||
|
||||
export function TreeSearch(props: TreeSearchProps) {
|
||||
const dispatch = useAppDispatch()
|
||||
|
||||
@@ -40,7 +41,7 @@ export function TreeSearch(props: TreeSearchProps) {
|
||||
searchIcon={searchIcon}
|
||||
searchPlaceholder={t`Search`}
|
||||
shortcut="ctrl+k"
|
||||
nothingFoundMessage={t`Nothing found`}
|
||||
nothingFoundMessage={<Trans>Nothing found</Trans>}
|
||||
>
|
||||
<TextInput
|
||||
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 { useMediaQuery } from "@mantine/hooks"
|
||||
import { client } from "app/client"
|
||||
@@ -76,7 +76,7 @@ function Buttons() {
|
||||
<ButtonToolbar>
|
||||
{serverInfos?.demoAccountEnabled && (
|
||||
<ActionButton
|
||||
label={t`Try the demo!`}
|
||||
label={<Trans>Try the demo!</Trans>}
|
||||
icon={<TbClock size={iconSize} />}
|
||||
variant="outline"
|
||||
onClick={() => login.execute({ name: "demo", password: "demo" })}
|
||||
@@ -84,7 +84,7 @@ function Buttons() {
|
||||
/>
|
||||
)}
|
||||
<ActionButton
|
||||
label={t`Log in`}
|
||||
label={<Trans>Log in</Trans>}
|
||||
icon={<TbKey size={iconSize} />}
|
||||
variant="outline"
|
||||
onClick={() => dispatch(redirectToLogin())}
|
||||
@@ -92,7 +92,7 @@ function Buttons() {
|
||||
/>
|
||||
{serverInfos?.allowRegistrations && (
|
||||
<ActionButton
|
||||
label={t`Sign up`}
|
||||
label={<Trans>Sign up</Trans>}
|
||||
icon={<TbUserPlus size={iconSize} />}
|
||||
variant="filled"
|
||||
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 { closeAllModals, openConfirmModal, openModal } from "@mantine/modals"
|
||||
import { client, errorToStrings } from "app/client"
|
||||
@@ -7,6 +7,7 @@ import { UserEdit } from "components/admin/UserEdit"
|
||||
import { Alert } from "components/Alert"
|
||||
import { Loader } from "components/Loader"
|
||||
import { RelativeDate } from "components/RelativeDate"
|
||||
import { ReactNode } from "react"
|
||||
import { useAsync, useAsyncCallback } from "react-async-hook"
|
||||
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({
|
||||
title,
|
||||
children: (
|
||||
@@ -45,7 +46,7 @@ export function AdminUsersPage() {
|
||||
const openUserDeleteModal = (user: UserModel) => {
|
||||
const userName = user.name
|
||||
openConfirmModal({
|
||||
title: t`Delete user`,
|
||||
title: <Trans>Delete user</Trans>,
|
||||
children: (
|
||||
<Text size="sm">
|
||||
<Trans>
|
||||
@@ -53,7 +54,7 @@ export function AdminUsersPage() {
|
||||
</Trans>
|
||||
</Text>
|
||||
),
|
||||
labels: { confirm: t`Confirm`, cancel: t`Cancel` },
|
||||
labels: { confirm: <Trans>Confirm</Trans>, cancel: <Trans>Cancel</Trans> },
|
||||
confirmProps: { color: "red" },
|
||||
onConfirm: () => deleteUser.execute({ id: user.id }),
|
||||
})
|
||||
@@ -65,7 +66,7 @@ export function AdminUsersPage() {
|
||||
<Title order={3} mb="md">
|
||||
<Group>
|
||||
<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} />
|
||||
</ActionIcon>
|
||||
</Group>
|
||||
@@ -126,7 +127,7 @@ export function AdminUsersPage() {
|
||||
</td>
|
||||
<td>
|
||||
<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} />
|
||||
</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()
|
||||
return (
|
||||
<Box my="xl">
|
||||
@@ -38,7 +38,7 @@ function NextUnreadBookmarklet() {
|
||||
|
||||
return (
|
||||
<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
|
||||
data={[
|
||||
{ value: "desc", label: t`Newest first` },
|
||||
@@ -46,7 +46,7 @@ function NextUnreadBookmarklet() {
|
||||
]}
|
||||
value={order}
|
||||
onChange={e => setOrder(e.target.value)}
|
||||
description={t`Order`}
|
||||
description={<Trans>Order</Trans>}
|
||||
/>
|
||||
<Trans>Drag link to bookmark bar</Trans>
|
||||
<span> </span>
|
||||
@@ -58,6 +58,7 @@ function NextUnreadBookmarklet() {
|
||||
}
|
||||
|
||||
const bitcoinAddress = <Code>{Constants.bitcoinWalletAddress}</Code>
|
||||
|
||||
export function AboutPage() {
|
||||
const version = useAppSelector(state => state.server.serverInfos?.version)
|
||||
const revision = useAppSelector(state => state.server.serverInfos?.gitCommit)
|
||||
@@ -65,7 +66,7 @@ export function AboutPage() {
|
||||
return (
|
||||
<Container size="xl">
|
||||
<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>
|
||||
<Trans>
|
||||
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>
|
||||
</Box>
|
||||
</Section>
|
||||
<Section title={t`Goodies`} icon={<TbPuzzle size={24} />}>
|
||||
<Section title={<Trans>Goodies</Trans>} icon={<TbPuzzle size={24} />}>
|
||||
<List>
|
||||
<List.Item>
|
||||
<Trans>Browser extentions</Trans>
|
||||
@@ -161,10 +162,10 @@ export function AboutPage() {
|
||||
</List.Item>
|
||||
</List>
|
||||
</Section>
|
||||
<Section title={t`Keyboard shortcuts`} icon={<TbKeyboard size={24} />}>
|
||||
<Section title={<Trans>Keyboard shortcuts</Trans>} icon={<TbKeyboard size={24} />}>
|
||||
<KeyboardShortcutsHelp />
|
||||
</Section>
|
||||
<Section title={t`REST API`} icon={<TbRocket size={24} />}>
|
||||
<Section title={<Trans>REST API</Trans>} icon={<TbRocket size={24} />}>
|
||||
<Anchor onClick={() => dispatch(redirectToApiDocumentation())}>
|
||||
<Trans>Go to the API documentation.</Trans>
|
||||
</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 { useForm } from "@mantine/form"
|
||||
import { openConfirmModal } from "@mantine/modals"
|
||||
@@ -48,7 +48,7 @@ export function CategoryDetailsPage() {
|
||||
const openDeleteCategoryModal = () => {
|
||||
const categoryName = category?.name
|
||||
return openConfirmModal({
|
||||
title: t`Delete Category`,
|
||||
title: <Trans>Delete Category</Trans>,
|
||||
children: (
|
||||
<Text size="sm">
|
||||
<Trans>
|
||||
@@ -56,7 +56,7 @@ export function CategoryDetailsPage() {
|
||||
</Trans>
|
||||
</Text>
|
||||
),
|
||||
labels: { confirm: t`Confirm`, cancel: t`Cancel` },
|
||||
labels: { confirm: <Trans>Confirm</Trans>, cancel: <Trans>Cancel</Trans> },
|
||||
confirmProps: { color: "red" },
|
||||
onConfirm: () => deleteCategory.execute({ id: +id }),
|
||||
})
|
||||
@@ -91,7 +91,7 @@ export function CategoryDetailsPage() {
|
||||
<form onSubmit={form.onSubmit(modifyCategory.execute)}>
|
||||
<Stack>
|
||||
<Title order={3}>{category.name}</Title>
|
||||
<Input.Wrapper label={t`Generated feed url`}>
|
||||
<Input.Wrapper label={<Trans>Generated feed url</Trans>}>
|
||||
<Box>
|
||||
{apiKey && (
|
||||
<Anchor
|
||||
@@ -108,14 +108,14 @@ export function CategoryDetailsPage() {
|
||||
|
||||
{editable && (
|
||||
<>
|
||||
<TextInput label={t`Name`} {...form.getInputProps("name")} required />
|
||||
<TextInput label={<Trans>Name</Trans>} {...form.getInputProps("name")} required />
|
||||
<CategorySelect
|
||||
label={t`Parent Category`}
|
||||
label={<Trans>Parent Category</Trans>}
|
||||
{...form.getInputProps("parentId")}
|
||||
clearable
|
||||
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 { useForm } from "@mantine/form"
|
||||
import { openConfirmModal } from "@mantine/modals"
|
||||
@@ -47,6 +47,7 @@ function FilteringExpressionDescription() {
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export function FeedDetailsPage() {
|
||||
const { id } = useParams()
|
||||
if (!id) throw Error("id required")
|
||||
@@ -75,7 +76,7 @@ export function FeedDetailsPage() {
|
||||
const openUnsubscribeModal = () => {
|
||||
const feedName = feed?.name
|
||||
return openConfirmModal({
|
||||
title: t`Unsubscribe`,
|
||||
title: <Trans>Unsubscribe</Trans>,
|
||||
children: (
|
||||
<Text size="sm">
|
||||
<Trans>
|
||||
@@ -83,7 +84,7 @@ export function FeedDetailsPage() {
|
||||
</Trans>
|
||||
</Text>
|
||||
),
|
||||
labels: { confirm: t`Confirm`, cancel: t`Cancel` },
|
||||
labels: { confirm: <Trans>Confirm</Trans>, cancel: <Trans>Cancel</Trans> },
|
||||
confirmProps: { color: "red" },
|
||||
onConfirm: () => unsubscribe.execute({ id: +id }),
|
||||
})
|
||||
@@ -112,34 +113,34 @@ export function FeedDetailsPage() {
|
||||
<form onSubmit={form.onSubmit(modifyFeed.execute)}>
|
||||
<Stack>
|
||||
<Title order={3}>{feed.name}</Title>
|
||||
<Input.Wrapper label={t`Feed URL`}>
|
||||
<Input.Wrapper label={<Trans>Feed URL</Trans>}>
|
||||
<Box>
|
||||
<Anchor href={feed.feedUrl} target="_blank" rel="noreferrer">
|
||||
{feed.feedUrl}
|
||||
</Anchor>
|
||||
</Box>
|
||||
</Input.Wrapper>
|
||||
<Input.Wrapper label={t`Website`}>
|
||||
<Input.Wrapper label={<Trans>Website</Trans>}>
|
||||
<Box>
|
||||
<Anchor href={feed.feedLink} target="_blank" rel="noreferrer">
|
||||
{feed.feedLink}
|
||||
</Anchor>
|
||||
</Box>
|
||||
</Input.Wrapper>
|
||||
<Input.Wrapper label={t`Last refresh`}>
|
||||
<Input.Wrapper label={<Trans>Last refresh</Trans>}>
|
||||
<Box>
|
||||
<RelativeDate date={feed.lastRefresh} />
|
||||
</Box>
|
||||
</Input.Wrapper>
|
||||
<Input.Wrapper label={t`Last refresh message`}>
|
||||
<Box>{feed.message ?? t`N/A`}</Box>
|
||||
<Input.Wrapper label={<Trans>Last refresh message</Trans>}>
|
||||
<Box>{feed.message ?? <Trans>N/A</Trans>}</Box>
|
||||
</Input.Wrapper>
|
||||
<Input.Wrapper label={t`Next refresh`}>
|
||||
<Input.Wrapper label={<Trans>Next refresh</Trans>}>
|
||||
<Box>
|
||||
<RelativeDate date={feed.nextRefresh} />
|
||||
</Box>
|
||||
</Input.Wrapper>
|
||||
<Input.Wrapper label={t`Generated feed url`}>
|
||||
<Input.Wrapper label={<Trans>Generated feed url</Trans>}>
|
||||
<Box>
|
||||
{apiKey && (
|
||||
<Anchor href={`rest/feed/entriesAsFeed?id=${feed.id}&apiKey=${apiKey}`} target="_blank" rel="noreferrer">
|
||||
@@ -150,11 +151,11 @@ export function FeedDetailsPage() {
|
||||
</Box>
|
||||
</Input.Wrapper>
|
||||
|
||||
<TextInput label={t`Name`} {...form.getInputProps("name")} required />
|
||||
<CategorySelect label={t`Category`} {...form.getInputProps("categoryId")} clearable />
|
||||
<NumberInput label={t`Position`} {...form.getInputProps("position")} required min={0} />
|
||||
<TextInput label={<Trans>Name</Trans>} {...form.getInputProps("name")} required />
|
||||
<CategorySelect label={<Trans>Category</Trans>} {...form.getInputProps("categoryId")} clearable />
|
||||
<NumberInput label={<Trans>Position</Trans>} {...form.getInputProps("position")} required min={0} />
|
||||
<TextInput
|
||||
label={t`Filtering expression`}
|
||||
label={<Trans>Filtering expression</Trans>}
|
||||
description={<FilteringExpressionDescription />}
|
||||
{...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 { useViewportSize } from "@mantine/hooks"
|
||||
import { Constants } from "app/constants"
|
||||
@@ -77,7 +77,7 @@ export function FeedEntriesPage(props: FeedEntriesPageProps) {
|
||||
|
||||
<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>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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 { Constants } from "app/constants"
|
||||
@@ -16,7 +16,7 @@ export function TagDetailsPage() {
|
||||
<Container>
|
||||
<Stack>
|
||||
<Title order={3}>{id}</Title>
|
||||
<Input.Wrapper label={t`Generated feed url`}>
|
||||
<Input.Wrapper label={<Trans>Generated feed url</Trans>}>
|
||||
<Box>
|
||||
{apiKey && (
|
||||
<Anchor
|
||||
|
||||
@@ -42,15 +42,17 @@ export function LoginPage() {
|
||||
<form onSubmit={form.onSubmit(login.execute)}>
|
||||
<Stack>
|
||||
<TextInput
|
||||
label={t`User Name or E-mail`}
|
||||
label={<Trans>User Name or E-mail</Trans>}
|
||||
placeholder={t`User Name or E-mail`}
|
||||
{...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"
|
||||
required
|
||||
/>
|
||||
<PasswordInput
|
||||
label={t`Password`}
|
||||
label={<Trans>Password</Trans>}
|
||||
placeholder={t`Password`}
|
||||
{...form.getInputProps("password")}
|
||||
size="md"
|
||||
|
||||
@@ -53,7 +53,7 @@ export function PasswordRecoveryPage() {
|
||||
<Stack>
|
||||
<TextInput
|
||||
type="email"
|
||||
label={t`E-mail`}
|
||||
label={<Trans>E-mail</Trans>}
|
||||
placeholder={t`E-mail`}
|
||||
{...form.getInputProps("email")}
|
||||
size="md"
|
||||
|
||||
@@ -53,14 +53,14 @@ export function RegistrationPage() {
|
||||
<TextInput label="User Name" placeholder="User Name" {...form.getInputProps("name")} size="md" required />
|
||||
<TextInput
|
||||
type="email"
|
||||
label={t`E-mail address`}
|
||||
label={<Trans>E-mail address</Trans>}
|
||||
placeholder={t`E-mail address`}
|
||||
{...form.getInputProps("email")}
|
||||
size="md"
|
||||
required
|
||||
/>
|
||||
<PasswordInput
|
||||
label={t`Password`}
|
||||
label={<Trans>Password</Trans>}
|
||||
placeholder={t`Password`}
|
||||
{...form.getInputProps("password")}
|
||||
size="md"
|
||||
|
||||
Reference in New Issue
Block a user