diff --git a/commafeed-client/biome.json b/commafeed-client/biome.json index bb845edb..72c5957b 100644 --- a/commafeed-client/biome.json +++ b/commafeed-client/biome.json @@ -4,7 +4,7 @@ "indentStyle": "space", "indentWidth": 4, "lineEnding": "lf", - "lineWidth": 140 + "lineWidth": 120 }, "javascript": { "formatter": { diff --git a/commafeed-client/src/App.tsx b/commafeed-client/src/App.tsx index fd93c839..ac01c6bd 100644 --- a/commafeed-client/src/App.tsx +++ b/commafeed-client/src/App.tsx @@ -38,7 +38,8 @@ function Providers( children: React.ReactNode }> ) { - const primaryColor = useAppSelector(state => state.user.settings?.primaryColor) || Constants.theme.defaultPrimaryColor + const primaryColor = + useAppSelector(state => state.user.settings?.primaryColor) || Constants.theme.defaultPrimaryColor return ( } /> } /> } /> - } sidebar={} sidebarVisible={sidebarVisible} />}> + } sidebar={} sidebarVisible={sidebarVisible} />} + > } /> } /> diff --git a/commafeed-client/src/app/client.ts b/commafeed-client/src/app/client.ts index 2e68b68b..8e547516 100644 --- a/commafeed-client/src/app/client.ts +++ b/commafeed-client/src/app/client.ts @@ -61,7 +61,8 @@ export const client = { getRoot: async () => await axiosInstance.get("category/get"), modify: async (req: CategoryModificationRequest) => await axiosInstance.post("category/modify", req), collapse: async (req: CollapseRequest) => await axiosInstance.post("category/collapse", req), - getEntries: async (req: GetEntriesPaginatedRequest) => await axiosInstance.get("category/entries", { params: req }), + getEntries: async (req: GetEntriesPaginatedRequest) => + await axiosInstance.get("category/entries", { params: req }), markEntries: async (req: MarkRequest) => await axiosInstance.post("category/mark", req), add: async (req: AddCategoryRequest) => await axiosInstance.post("category/add", req), delete: async (req: IDRequest) => await axiosInstance.post("category/delete", req), @@ -76,7 +77,8 @@ export const client = { feed: { get: async (id: string) => await axiosInstance.get(`feed/get/${id}`), modify: async (req: FeedModificationRequest) => await axiosInstance.post("feed/modify", req), - getEntries: async (req: GetEntriesPaginatedRequest) => await axiosInstance.get("feed/entries", { params: req }), + getEntries: async (req: GetEntriesPaginatedRequest) => + await axiosInstance.get("feed/entries", { params: req }), markEntries: async (req: MarkRequest) => await axiosInstance.post("feed/mark", req), fetchFeed: async (req: FeedInfoRequest) => await axiosInstance.post("feed/fetch", req), refreshAll: async () => await axiosInstance.get("feed/refreshAll"), @@ -107,7 +109,8 @@ export const client = { register: async (req: RegistrationRequest) => await axiosInstance.post("user/register", req), initialSetup: async (req: InitialSetupRequest) => await axiosInstance.post("user/initialSetup", req), passwordReset: async (req: PasswordResetRequest) => await axiosInstance.post("user/passwordReset", req), - passwordResetCallback: async (req: PasswordResetConfirmationRequest) => await axiosInstance.post("user/passwordResetCallback", req), + passwordResetCallback: async (req: PasswordResetConfirmationRequest) => + await axiosInstance.post("user/passwordResetCallback", req), getSettings: async () => await axiosInstance.get("user/settings"), saveSettings: async (settings: Settings) => await axiosInstance.post("user/settings", settings), sendTestPushNotification: async (settings: PushNotificationSettings) => @@ -156,9 +159,13 @@ function isCommaFeedApplicationError(err: AxiosError): err is AxiosError { - return !!err.response && !!err.response.data && typeof err.response.data === "object" && "message" in err.response.data + return ( + !!err.response && !!err.response.data && typeof err.response.data === "object" && "message" in err.response.data + ) } function isMessageArrayError(err: AxiosError): err is AxiosError<{ errors: string[] }> { - return !!err.response && !!err.response.data && typeof err.response.data === "object" && "errors" in err.response.data + return ( + !!err.response && !!err.response.data && typeof err.response.data === "object" && "errors" in err.response.data + ) } diff --git a/commafeed-client/src/app/entries/slice.ts b/commafeed-client/src/app/entries/slice.ts index efb8dbf6..49818d66 100644 --- a/commafeed-client/src/app/entries/slice.ts +++ b/commafeed-client/src/app/entries/slice.ts @@ -1,6 +1,14 @@ import { createSlice, type PayloadAction } from "@reduxjs/toolkit" import { Constants } from "@/app/constants" -import { loadEntries, loadMoreEntries, markAllEntries, markEntry, markMultipleEntries, starEntry, tagEntry } from "@/app/entries/thunks" +import { + loadEntries, + loadMoreEntries, + markAllEntries, + markEntry, + markMultipleEntries, + starEntry, + tagEntry, +} from "@/app/entries/thunks" import type { Entry } from "@/app/types" export type EntrySourceType = "category" | "feed" | "tag" @@ -85,12 +93,16 @@ export const entriesSlice = createSlice({ } }) builder.addCase(markAllEntries.pending, (state, action) => { - for (const e of state.entries.filter(e => (action.meta.arg.req.olderThan ? e.date < action.meta.arg.req.olderThan : true))) { + for (const e of state.entries.filter(e => + action.meta.arg.req.olderThan ? e.date < action.meta.arg.req.olderThan : true + )) { e.read = true } }) builder.addCase(starEntry.pending, (state, action) => { - for (const e of state.entries.filter(e => action.meta.arg.entry.id === e.id && action.meta.arg.entry.feedId === e.feedId)) { + for (const e of state.entries.filter( + e => action.meta.arg.entry.id === e.id && action.meta.arg.entry.feedId === e.feedId + )) { e.starred = action.meta.arg.starred } }) diff --git a/commafeed-client/src/app/entries/thunks.ts b/commafeed-client/src/app/entries/thunks.ts index 14b57830..c94d6a2e 100644 --- a/commafeed-client/src/app/entries/thunks.ts +++ b/commafeed-client/src/app/entries/thunks.ts @@ -40,7 +40,9 @@ export const loadMoreEntries = createAppAsyncThunk("entries/loadMore", async (_, const state = thunkApi.getState() const { source } = state.entries const offset = - state.user.settings?.readingMode === "all" || state.entries.search || (source.type === "category" && source.id === "starred") + state.user.settings?.readingMode === "all" || + state.entries.search || + (source.type === "category" && source.id === "starred") ? state.entries.entries.length : state.entries.entries.filter(e => !e.read).length const endpoint = getEndpoint(state.entries.source.type) @@ -220,7 +222,9 @@ export const selectEntry = createAppAsyncThunk( const entryIndex = state.entries.entries.indexOf(entry) const entriesToKeepOnTopWhenScrolling = - viewMode === "expanded" ? 0 : Math.min(state.user.settings?.entriesToKeepOnTopWhenScrolling ?? 0, entryIndex) + viewMode === "expanded" + ? 0 + : Math.min(state.user.settings?.entriesToKeepOnTopWhenScrolling ?? 0, entryIndex) const entryToScrollTo = state.entries.entries[entryIndex - entriesToKeepOnTopWhenScrolling] const entryElement = document.getElementById(Constants.dom.entryId(entry)) @@ -228,7 +232,8 @@ export const selectEntry = createAppAsyncThunk( if (entryElement && entryElementToScrollTo) { const scrollMode = state.user.settings?.scrollMode const entryEntirelyVisible = - Constants.layout.isTopVisible(entryElementToScrollTo) && Constants.layout.isBottomVisible(entryElement) + Constants.layout.isTopVisible(entryElementToScrollTo) && + Constants.layout.isBottomVisible(entryElement) if (scrollMode === "always" || (scrollMode === "if_needed" && !entryEntirelyVisible)) { const scrollSpeed = state.user.settings?.scrollSpeed const margin = viewMode === "detailed" ? 8 : 3 @@ -242,7 +247,12 @@ export const selectEntry = createAppAsyncThunk( } ) -const scrollToEntry = (entryElement: HTMLElement, margin: number, scrollSpeed: number | undefined, onScrollEnded: () => void) => { +const scrollToEntry = ( + entryElement: HTMLElement, + margin: number, + scrollSpeed: number | undefined, + onScrollEnded: () => void +) => { const header = document.getElementsByTagName("header").item(0)?.getBoundingClientRect() const offset = (header?.bottom ?? 0) + margin scrollToWithCallback({ diff --git a/commafeed-client/src/app/redirect/thunks.ts b/commafeed-client/src/app/redirect/thunks.ts index 25b5144f..ad9a6f30 100644 --- a/commafeed-client/src/app/redirect/thunks.ts +++ b/commafeed-client/src/app/redirect/thunks.ts @@ -2,11 +2,17 @@ import { createAppAsyncThunk } from "@/app/async-thunk" import { Constants } from "@/app/constants" import { redirectTo } from "@/app/redirect/slice" -export const redirectToLogin = createAppAsyncThunk("redirect/login", (_, thunkApi) => thunkApi.dispatch(redirectTo("/login"))) +export const redirectToLogin = createAppAsyncThunk("redirect/login", (_, thunkApi) => + thunkApi.dispatch(redirectTo("/login")) +) -export const redirectToRegistration = createAppAsyncThunk("redirect/register", (_, thunkApi) => thunkApi.dispatch(redirectTo("/register"))) +export const redirectToRegistration = createAppAsyncThunk("redirect/register", (_, thunkApi) => + thunkApi.dispatch(redirectTo("/register")) +) -export const redirectToInitialSetup = createAppAsyncThunk("redirect/initialSetup", (_, thunkApi) => thunkApi.dispatch(redirectTo("/setup"))) +export const redirectToInitialSetup = createAppAsyncThunk("redirect/initialSetup", (_, thunkApi) => + thunkApi.dispatch(redirectTo("/setup")) +) export const redirectToApiDocumentation = createAppAsyncThunk("redirect/api", () => { window.location.href = "api-documentation/" @@ -38,15 +44,21 @@ export const redirectToFeedDetails = createAppAsyncThunk("redirect/feed/details" thunkApi.dispatch(redirectTo(`/app/feed/${id}/details`)) ) -export const redirectToTag = createAppAsyncThunk("redirect/tag", (id: string, thunkApi) => thunkApi.dispatch(redirectTo(`/app/tag/${id}`))) +export const redirectToTag = createAppAsyncThunk("redirect/tag", (id: string, thunkApi) => + thunkApi.dispatch(redirectTo(`/app/tag/${id}`)) +) export const redirectToTagDetails = createAppAsyncThunk("redirect/tag/details", (id: string, thunkApi) => thunkApi.dispatch(redirectTo(`/app/tag/${id}/details`)) ) -export const redirectToAdd = createAppAsyncThunk("redirect/add", (_, thunkApi) => thunkApi.dispatch(redirectTo("/app/add"))) +export const redirectToAdd = createAppAsyncThunk("redirect/add", (_, thunkApi) => + thunkApi.dispatch(redirectTo("/app/add")) +) -export const redirectToSettings = createAppAsyncThunk("redirect/settings", (_, thunkApi) => thunkApi.dispatch(redirectTo("/app/settings"))) +export const redirectToSettings = createAppAsyncThunk("redirect/settings", (_, thunkApi) => + thunkApi.dispatch(redirectTo("/app/settings")) +) export const redirectToAdminUsers = createAppAsyncThunk("redirect/admin/users", (_, thunkApi) => thunkApi.dispatch(redirectTo("/app/admin/users")) @@ -56,6 +68,10 @@ export const redirectToMetrics = createAppAsyncThunk("redirect/admin/metrics", ( thunkApi.dispatch(redirectTo("/app/admin/metrics")) ) -export const redirectToDonate = createAppAsyncThunk("redirect/donate", (_, thunkApi) => thunkApi.dispatch(redirectTo("/app/donate"))) +export const redirectToDonate = createAppAsyncThunk("redirect/donate", (_, thunkApi) => + thunkApi.dispatch(redirectTo("/app/donate")) +) -export const redirectToAbout = createAppAsyncThunk("redirect/about", (_, thunkApi) => thunkApi.dispatch(redirectTo("/app/about"))) +export const redirectToAbout = createAppAsyncThunk("redirect/about", (_, thunkApi) => + thunkApi.dispatch(redirectTo("/app/about")) +) diff --git a/commafeed-client/src/app/server/thunks.ts b/commafeed-client/src/app/server/thunks.ts index a071a7af..0ebcb486 100644 --- a/commafeed-client/src/app/server/thunks.ts +++ b/commafeed-client/src/app/server/thunks.ts @@ -1,4 +1,7 @@ import { createAppAsyncThunk } from "@/app/async-thunk" import { client } from "@/app/client" -export const reloadServerInfos = createAppAsyncThunk("server/infos", async () => await client.server.getServerInfos().then(r => r.data)) +export const reloadServerInfos = createAppAsyncThunk( + "server/infos", + async () => await client.server.getServerInfos().then(r => r.data) +) diff --git a/commafeed-client/src/app/store.ts b/commafeed-client/src/app/store.ts index 7dac25d9..92e2edfe 100644 --- a/commafeed-client/src/app/store.ts +++ b/commafeed-client/src/app/store.ts @@ -41,4 +41,5 @@ export type AppDispatch = typeof store.dispatch export const useAppDispatch: () => AppDispatch = useDispatch export const useAppSelector: TypedUseSelectorHook = useSelector -export const useShallowEqualAppSelector: TypedUseSelectorHook = selector => useSelector(selector, shallowEqual) +export const useShallowEqualAppSelector: TypedUseSelectorHook = selector => + useSelector(selector, shallowEqual) diff --git a/commafeed-client/src/app/tree/thunks.ts b/commafeed-client/src/app/tree/thunks.ts index 1b1ec2ba..1d1246c4 100644 --- a/commafeed-client/src/app/tree/thunks.ts +++ b/commafeed-client/src/app/tree/thunks.ts @@ -6,7 +6,10 @@ import { incrementUnreadCount } from "@/app/tree/slice" import type { CollapseRequest, Subscription } from "@/app/types" import { flattenCategoryTree, visitCategoryTree } from "@/app/utils" -export const reloadTree = createAppAsyncThunk("tree/reload", async () => await client.category.getRoot().then(r => r.data)) +export const reloadTree = createAppAsyncThunk( + "tree/reload", + async () => await client.category.getRoot().then(r => r.data) +) export const collapseTreeCategory = createAppAsyncThunk( "tree/category/collapse", diff --git a/commafeed-client/src/app/user/thunks.ts b/commafeed-client/src/app/user/thunks.ts index 04a5990d..861567fc 100644 --- a/commafeed-client/src/app/user/thunks.ts +++ b/commafeed-client/src/app/user/thunks.ts @@ -2,14 +2,26 @@ import { createAppAsyncThunk } from "@/app/async-thunk" import { client } from "@/app/client" import type { Settings } from "@/app/types" -export const reloadSettings = createAppAsyncThunk("settings/reload", async () => await client.user.getSettings().then(r => r.data)) +export const reloadSettings = createAppAsyncThunk( + "settings/reload", + async () => await client.user.getSettings().then(r => r.data) +) -export const reloadProfile = createAppAsyncThunk("profile/reload", async () => await client.user.getProfile().then(r => r.data)) +export const reloadProfile = createAppAsyncThunk( + "profile/reload", + async () => await client.user.getProfile().then(r => r.data) +) -export const reloadTags = createAppAsyncThunk("entries/tags", async () => await client.entry.getTags().then(r => r.data)) +export const reloadTags = createAppAsyncThunk( + "entries/tags", + async () => await client.entry.getTags().then(r => r.data) +) -export const changeSettings = createAppAsyncThunk("settings/change", async (newSettings: Partial, thunkApi) => { - const { settings } = thunkApi.getState().user - if (!settings) return - await client.user.saveSettings({ ...settings, ...newSettings }) -}) +export const changeSettings = createAppAsyncThunk( + "settings/change", + async (newSettings: Partial, thunkApi) => { + const { settings } = thunkApi.getState().user + if (!settings) return + await client.user.saveSettings({ ...settings, ...newSettings }) + } +) diff --git a/commafeed-client/src/app/utils.ts b/commafeed-client/src/app/utils.ts index 8e5cd3a3..046d5dc1 100644 --- a/commafeed-client/src/app/utils.ts +++ b/commafeed-client/src/app/utils.ts @@ -43,13 +43,27 @@ export function categoryHasNewEntries(category?: TreeCategory): boolean { .some(f => f.hasNewEntries) } -export const calculatePlaceholderSize = ({ width, height, maxWidth }: { width?: number; height?: number; maxWidth: number }) => { +export const calculatePlaceholderSize = ({ + width, + height, + maxWidth, +}: { + width?: number + height?: number + maxWidth: number +}) => { const placeholderWidth = width && Math.min(width, maxWidth) const placeholderHeight = height && width && width > maxWidth ? height * (maxWidth / width) : height return { width: placeholderWidth, height: placeholderHeight } } -export const scrollToWithCallback = ({ options, onScrollEnded }: { options: ScrollToOptions; onScrollEnded: () => void }) => { +export const scrollToWithCallback = ({ + options, + onScrollEnded, +}: { + options: ScrollToOptions + onScrollEnded: () => void +}) => { const offset = (options.top ?? 0).toFixed() const onScroll = throttle(100, () => { diff --git a/commafeed-client/src/components/ActionButton.tsx b/commafeed-client/src/components/ActionButton.tsx index 2a2b5eef..d518442e 100644 --- a/commafeed-client/src/components/ActionButton.tsx +++ b/commafeed-client/src/components/ActionButton.tsx @@ -1,6 +1,14 @@ import type { MessageDescriptor } from "@lingui/core" import { useLingui } from "@lingui/react" -import { ActionIcon, type ActionIconVariant, Box, Button, type ButtonVariant, Tooltip, useMantineTheme } from "@mantine/core" +import { + ActionIcon, + type ActionIconVariant, + Box, + Button, + type ButtonVariant, + Tooltip, + useMantineTheme, +} from "@mantine/core" import { forwardRef, type MouseEventHandler, type ReactNode } from "react" import { Constants } from "@/app/constants" import { useActionButton } from "@/hooks/useActionButton" diff --git a/commafeed-client/src/components/MarkAllAsReadConfirmationDialog.tsx b/commafeed-client/src/components/MarkAllAsReadConfirmationDialog.tsx index bed3d46b..3fc85090 100644 --- a/commafeed-client/src/components/MarkAllAsReadConfirmationDialog.tsx +++ b/commafeed-client/src/components/MarkAllAsReadConfirmationDialog.tsx @@ -14,7 +14,9 @@ export function MarkAllAsReadConfirmationDialog() { const sourceLabel = useAppSelector(state => state.entries.sourceLabel) const entriesTimestamp = useAppSelector(state => state.entries.timestamp) ?? Date.now() const search = useAppSelector(state => state.entries.search) - const markAllAsReadNavigateToNextUnread = useAppSelector(state => state.user.settings?.markAllAsReadNavigateToNextUnread) + const markAllAsReadNavigateToNextUnread = useAppSelector( + state => state.user.settings?.markAllAsReadNavigateToNextUnread + ) const dispatch = useAppDispatch() const onConfirm = async () => { @@ -33,7 +35,8 @@ export function MarkAllAsReadConfirmationDialog() { ) const isAllCategorySelected = source.type === "category" && source.id === Constants.categories.all.id - if (markAllAsReadNavigateToNextUnread && !isAllCategorySelected) await dispatch(selectNextUnreadTreeItem({ direction: "forward" })) + if (markAllAsReadNavigateToNextUnread && !isAllCategorySelected) + await dispatch(selectNextUnreadTreeItem({ direction: "forward" })) } return ( @@ -51,7 +54,8 @@ export function MarkAllAsReadConfirmationDialog() { )} {threshold > 0 && ( - Are you sure you want to mark entries older than {threshold} days of {sourceLabel} as read? + Are you sure you want to mark entries older than {threshold} days of{" "} + {sourceLabel} as read? )} diff --git a/commafeed-client/src/components/ReceivePushNotificationsChechbox.tsx b/commafeed-client/src/components/ReceivePushNotificationsChechbox.tsx index 62908d83..84cf650d 100644 --- a/commafeed-client/src/components/ReceivePushNotificationsChechbox.tsx +++ b/commafeed-client/src/components/ReceivePushNotificationsChechbox.tsx @@ -15,5 +15,12 @@ export const ReceivePushNotificationsChechbox = (props: CheckboxProps) => { description = Push notifications are not configured in your user settings. } - return Receive push notifications} disabled={disabled} description={description} {...props} /> + return ( + Receive push notifications} + disabled={disabled} + description={description} + {...props} + /> + ) } diff --git a/commafeed-client/src/components/admin/UserEdit.tsx b/commafeed-client/src/components/admin/UserEdit.tsx index 0e5cc366..fd17c9b6 100644 --- a/commafeed-client/src/components/admin/UserEdit.tsx +++ b/commafeed-client/src/components/admin/UserEdit.tsx @@ -34,7 +34,11 @@ export function UserEdit(props: Readonly) {
Name} {...form.getInputProps("name")} required /> - Password} {...form.getInputProps("password")} required={!props.user} /> + Password} + {...form.getInputProps("password")} + required={!props.user} + /> E-mail} {...form.getInputProps("email")} /> Admin} {...form.getInputProps("admin", { type: "checkbox" })} /> Enabled} {...form.getInputProps("enabled", { type: "checkbox" })} /> diff --git a/commafeed-client/src/components/content/Content.test.tsx b/commafeed-client/src/components/content/Content.test.tsx index a4bcef58..07785c07 100644 --- a/commafeed-client/src/components/content/Content.test.tsx +++ b/commafeed-client/src/components/content/Content.test.tsx @@ -15,7 +15,9 @@ describe("Content component", () => { }) it("renders iframe tag when included in content", () => { - const { container } = render(, { wrapper: MantineProvider }) + const { container } = render(, { + wrapper: MantineProvider, + }) expect(container.querySelector("iframe")).toHaveAttribute("src", "https://example.com") }) diff --git a/commafeed-client/src/components/content/Content.tsx b/commafeed-client/src/components/content/Content.tsx index 7465f355..b43ceb71 100644 --- a/commafeed-client/src/components/content/Content.tsx +++ b/commafeed-client/src/components/content/Content.tsx @@ -1,6 +1,14 @@ import { Box, Mark } from "@mantine/core" import escapeStringRegexp from "escape-string-regexp" -import { ALLOWED_TAG_LIST, type ChildrenNode, Interweave, Matcher, type MatchResponse, type Node, type TransformCallback } from "interweave" +import { + ALLOWED_TAG_LIST, + type ChildrenNode, + Interweave, + Matcher, + type MatchResponse, + type Node, + type TransformCallback, +} from "interweave" import React from "react" import styleToObject from "style-to-object" import { Constants } from "@/app/constants" diff --git a/commafeed-client/src/components/content/FeedEntries.tsx b/commafeed-client/src/components/content/FeedEntries.tsx index 749d4276..ff557e46 100644 --- a/commafeed-client/src/components/content/FeedEntries.tsx +++ b/commafeed-client/src/components/content/FeedEntries.tsx @@ -300,7 +300,9 @@ export function FeedEntries() { entry={entry} expanded={!!entry.expanded || viewMode === "expanded"} selected={entry.id === selectedEntryId} - showSelectionIndicator={entry.id === selectedEntryId && (!entry.expanded || viewMode === "expanded")} + showSelectionIndicator={ + entry.id === selectedEntryId && (!entry.expanded || viewMode === "expanded") + } maxWidth={sidebarVisible ? Constants.layout.entryMaxWidth : undefined} onHeaderClick={event => headerClicked(entry, event)} onHeaderRightClick={event => headerRightClicked(entry, event)} diff --git a/commafeed-client/src/components/content/FeedEntry.tsx b/commafeed-client/src/components/content/FeedEntry.tsx index 8fd784ce..5f935609 100644 --- a/commafeed-client/src/components/content/FeedEntry.tsx +++ b/commafeed-client/src/components/content/FeedEntry.tsx @@ -34,67 +34,80 @@ const useStyles = tss maxWidth?: number fontSizePercentage: number }>() - .create(({ theme, colorScheme, read, expanded, viewMode, rtl, showSelectionIndicator, maxWidth, fontSizePercentage }) => { - let backgroundColor: string - if (colorScheme === "dark") { - backgroundColor = read ? "inherit" : theme.colors.dark[5] - } else { - backgroundColor = read && !expanded ? theme.colors.gray[0] : "inherit" - } + .create( + ({ + theme, + colorScheme, + read, + expanded, + viewMode, + rtl, + showSelectionIndicator, + maxWidth, + fontSizePercentage, + }) => { + let backgroundColor: string + if (colorScheme === "dark") { + backgroundColor = read ? "inherit" : theme.colors.dark[5] + } else { + backgroundColor = read && !expanded ? theme.colors.gray[0] : "inherit" + } - let marginY = 10 - if (viewMode === "title") { - marginY = 2 - } else if (viewMode === "cozy") { - marginY = 6 - } + let marginY = 10 + if (viewMode === "title") { + marginY = 2 + } else if (viewMode === "cozy") { + marginY = 6 + } - let mobileMarginY = 6 - if (viewMode === "title") { - mobileMarginY = 2 - } else if (viewMode === "cozy") { - mobileMarginY = 4 - } + let mobileMarginY = 6 + if (viewMode === "title") { + mobileMarginY = 2 + } else if (viewMode === "cozy") { + mobileMarginY = 4 + } - let backgroundHoverColor = backgroundColor - if (!expanded && !read) { - backgroundHoverColor = colorScheme === "dark" ? theme.colors.dark[6] : theme.colors.gray[1] - } + let backgroundHoverColor = backgroundColor + if (!expanded && !read) { + backgroundHoverColor = colorScheme === "dark" ? theme.colors.dark[6] : theme.colors.gray[1] + } - let paperBorderLeftColor = "" - if (showSelectionIndicator) { - const borderLeftColor = colorScheme === "dark" ? theme.colors[theme.primaryColor][4] : theme.colors[theme.primaryColor][6] - paperBorderLeftColor = `${borderLeftColor} !important` - } + let paperBorderLeftColor = "" + if (showSelectionIndicator) { + const borderLeftColor = + colorScheme === "dark" ? theme.colors[theme.primaryColor][4] : theme.colors[theme.primaryColor][6] + paperBorderLeftColor = `${borderLeftColor} !important` + } - return { - paper: { - backgroundColor, - borderLeftColor: paperBorderLeftColor, - marginTop: marginY, - marginBottom: marginY, - [`@media (max-width: ${Constants.layout.mobileBreakpoint}px)`]: { - marginTop: mobileMarginY, - marginBottom: mobileMarginY, - }, - "@media (hover: hover)": { - "&:hover": { - backgroundColor: backgroundHoverColor, + return { + paper: { + backgroundColor, + borderLeftColor: paperBorderLeftColor, + marginTop: marginY, + marginBottom: marginY, + [`@media (max-width: ${Constants.layout.mobileBreakpoint}px)`]: { + marginTop: mobileMarginY, + marginBottom: mobileMarginY, + }, + "@media (hover: hover)": { + "&:hover": { + backgroundColor: backgroundHoverColor, + }, }, }, - }, - headerLink: { - fontSize: `${fontSizePercentage}%`, - color: "inherit", - textDecoration: "none", - }, - body: { - fontSize: `${fontSizePercentage}%`, - direction: rtl ? "rtl" : "ltr", - maxWidth: maxWidth ?? "100%", - }, + headerLink: { + fontSize: `${fontSizePercentage}%`, + color: "inherit", + textDecoration: "none", + }, + body: { + fontSize: `${fontSizePercentage}%`, + direction: rtl ? "rtl" : "ltr", + maxWidth: maxWidth ?? "100%", + }, + } } - }) + ) export function FeedEntry(props: Readonly) { const viewMode = useAppSelector(state => state.user.localSettings.viewMode) @@ -116,7 +129,9 @@ export function FeedEntry(props: Readonly) { const showExternalLinkIcon = externalLinkDisplayMode && ["always", mobile ? "on_mobile" : "on_desktop"].includes(externalLinkDisplayMode) const showStarIcon = - props.entry.markable && starIconDisplayMode && ["always", mobile ? "on_mobile" : "on_desktop"].includes(starIconDisplayMode) + props.entry.markable && + starIconDisplayMode && + ["always", mobile ? "on_mobile" : "on_desktop"].includes(starIconDisplayMode) const swipeHandlers = useSwipeable({ onSwipedLeft: props.onSwipedLeft, diff --git a/commafeed-client/src/components/content/FeedEntryContextMenu.tsx b/commafeed-client/src/components/content/FeedEntryContextMenu.tsx index b90fae3d..8dac88ed 100644 --- a/commafeed-client/src/components/content/FeedEntryContextMenu.tsx +++ b/commafeed-client/src/components/content/FeedEntryContextMenu.tsx @@ -35,7 +35,12 @@ export function FeedEntryContextMenu(props: Readonly) const { openLinkInBackgroundTab } = useBrowserExtension() return ( - + { window.open(props.entry.url, "_blank", "noreferrer") @@ -63,13 +68,19 @@ export function FeedEntryContextMenu(props: Readonly) {props.entry.markable && ( <> - await dispatch(starEntry({ entry: props.entry, starred: !props.entry.starred }))}> + + await dispatch(starEntry({ entry: props.entry, starred: !props.entry.starred })) + } + > {props.entry.starred ? : } {props.entry.starred ? Unstar : Star} - await dispatch(markEntry({ entry: props.entry, read: !props.entry.read }))}> + await dispatch(markEntry({ entry: props.entry, read: !props.entry.read }))} + > {props.entry.read ? : } {props.entry.read ? Keep unread : Mark as read} diff --git a/commafeed-client/src/components/content/FeedEntryFooter.tsx b/commafeed-client/src/components/content/FeedEntryFooter.tsx index d046ac4f..2832d0d9 100644 --- a/commafeed-client/src/components/content/FeedEntryFooter.tsx +++ b/commafeed-client/src/components/content/FeedEntryFooter.tsx @@ -1,7 +1,16 @@ import { msg } from "@lingui/core/macro" import { useLingui } from "@lingui/react" import { Group, Indicator, Popover, TagsInput } from "@mantine/core" -import { TbArrowBarToDown, TbExternalLink, TbMail, TbMailOpened, TbShare, TbStar, TbStarOff, TbTag } from "react-icons/tb" +import { + TbArrowBarToDown, + TbExternalLink, + TbMail, + TbMailOpened, + TbShare, + TbStar, + TbStarOff, + TbTag, +} from "react-icons/tb" import { markEntriesUpToEntry, markEntry, starEntry, tagEntry } from "@/app/entries/thunks" import { useAppDispatch, useAppSelector } from "@/app/store" import type { Entry } from "@/app/types" @@ -71,7 +80,12 @@ export function FeedEntryFooter(props: Readonly) { {tags && ( - + } label={msg`Tags`} /> diff --git a/commafeed-client/src/components/content/ShareButtons.tsx b/commafeed-client/src/components/content/ShareButtons.tsx index 24b849d7..a00914bd 100644 --- a/commafeed-client/src/components/content/ShareButtons.tsx +++ b/commafeed-client/src/components/content/ShareButtons.tsx @@ -104,7 +104,9 @@ export function ShareButtons( }> ) { const sharingSettings = useAppSelector(state => state.user.settings?.sharingSettings) - const enabledSharingSites = (Object.keys(Constants.sharing) as Array).filter(site => sharingSettings?.[site]) + const enabledSharingSites = (Object.keys(Constants.sharing) as Array).filter( + site => sharingSettings?.[site] + ) const url = encodeURIComponent(props.url) const desc = encodeURIComponent(props.description) const clipboardAvailable = typeof navigator.clipboard !== "undefined" @@ -119,7 +121,9 @@ export function ShareButtons( {showNativeSection && ( {clipboardAvailable && } - {nativeSharingAvailable && } + {nativeSharingAvailable && ( + + )} )} diff --git a/commafeed-client/src/components/content/add/AddCategory.tsx b/commafeed-client/src/components/content/add/AddCategory.tsx index 3099019c..d0ff6d2c 100644 --- a/commafeed-client/src/components/content/add/AddCategory.tsx +++ b/commafeed-client/src/components/content/add/AddCategory.tsx @@ -36,7 +36,12 @@ export function AddCategory() { - Category} placeholder={_(msg`Category`)} {...form.getInputProps("name")} required /> + Category} + placeholder={_(msg`Category`)} + {...form.getInputProps("name")} + required + /> Parent} {...form.getInputProps("parentId")} clearable /> )} {activeStep === 1 && ( - )} diff --git a/commafeed-client/src/components/content/edit/FilteringExpressionEditor.tsx b/commafeed-client/src/components/content/edit/FilteringExpressionEditor.tsx index cf2a89a0..4f570dd2 100644 --- a/commafeed-client/src/components/content/edit/FilteringExpressionEditor.tsx +++ b/commafeed-client/src/components/content/edit/FilteringExpressionEditor.tsx @@ -26,7 +26,16 @@ const fields: Field[] = [ { name: "categoriesLower", label: "Categories (lower case)" }, ] -const textOperators = new Set(["=", "!=", "contains", "beginsWith", "endsWith", "doesNotContain", "doesNotBeginWith", "doesNotEndWith"]) +const textOperators = new Set([ + "=", + "!=", + "contains", + "beginsWith", + "endsWith", + "doesNotContain", + "doesNotBeginWith", + "doesNotEndWith", +]) function toCelString(query: RuleGroupType): string { if (query.rules.length === 0) { diff --git a/commafeed-client/src/components/content/header/Star.tsx b/commafeed-client/src/components/content/header/Star.tsx index fe0e087d..e81e6f15 100644 --- a/commafeed-client/src/components/content/header/Star.tsx +++ b/commafeed-client/src/components/content/header/Star.tsx @@ -24,7 +24,10 @@ export function Star( } return ( - Unstar : Star} openDelay={Constants.tooltip.delay}> + Unstar : Star} + openDelay={Constants.tooltip.delay} + > {props.entry.starred ? : } diff --git a/commafeed-client/src/components/header/Header.tsx b/commafeed-client/src/components/header/Header.tsx index d648d990..5860760c 100644 --- a/commafeed-client/src/components/header/Header.tsx +++ b/commafeed-client/src/components/header/Header.tsx @@ -17,7 +17,13 @@ import { TbSortDescending, TbUser, } from "react-icons/tb" -import { markAllAsReadWithConfirmationIfRequired, reloadEntries, search, selectNextEntry, selectPreviousEntry } from "@/app/entries/thunks" +import { + markAllAsReadWithConfirmationIfRequired, + reloadEntries, + search, + selectNextEntry, + selectPreviousEntry, +} from "@/app/entries/thunks" import { useAppDispatch, useAppSelector } from "@/app/store" import { changeSettings } from "@/app/user/thunks" import { ActionButton } from "@/components/ActionButton" @@ -143,7 +149,13 @@ export function Header() { onClick={toggleReadingMode} /> : } + icon={ + settings.readingOrder === "asc" ? ( + + ) : ( + + ) + } label={settings.readingOrder === "asc" ? msg`Asc` : msg`Desc`} onClick={toggleReadingOrder} /> @@ -160,7 +172,11 @@ export function Header() { placeholder={_(msg`Search`)} {...searchForm.getInputProps("search")} leftSection={} - rightSection={ await (searchFromStore && dispatch(search("")))} />} + rightSection={ + await (searchFromStore && dispatch(search("")))} + /> + } autoFocus /> diff --git a/commafeed-client/src/components/header/ProfileMenu.tsx b/commafeed-client/src/components/header/ProfileMenu.tsx index 4758eae4..2a6b6978 100644 --- a/commafeed-client/src/components/header/ProfileMenu.tsx +++ b/commafeed-client/src/components/header/ProfileMenu.tsx @@ -31,7 +31,13 @@ import { } from "react-icons/tb" import { throttle } from "throttle-debounce" import { client } from "@/app/client" -import { redirectToAbout, redirectToAdminUsers, redirectToDonate, redirectToMetrics, redirectToSettings } from "@/app/redirect/thunks" +import { + redirectToAbout, + redirectToAdminUsers, + redirectToDonate, + redirectToMetrics, + redirectToSettings, +} from "@/app/redirect/thunks" import { useAppDispatch, useAppSelector } from "@/app/store" import type { ViewMode } from "@/app/types" import { setFontSizePercentage, setViewMode } from "@/app/user/slice" @@ -124,7 +130,13 @@ export function ProfileMenu(props: Readonly) { } return ( - + {props.control} {profile && {profile.name}} @@ -164,7 +176,9 @@ export function ProfileMenu(props: Readonly) { }} > Fetch all my feeds now - {!forceRefreshEnabled && ({dayjs.duration(nextAvailableForceRefresh - now.getTime()).format("HH:mm:ss")})} + {!forceRefreshEnabled && ( + ({dayjs.duration(nextAvailableForceRefresh - now.getTime()).format("HH:mm:ss")}) + )} diff --git a/commafeed-client/src/components/settings/CustomCodeSettings.tsx b/commafeed-client/src/components/settings/CustomCodeSettings.tsx index fe0c7c8a..bfd78507 100644 --- a/commafeed-client/src/components/settings/CustomCodeSettings.tsx +++ b/commafeed-client/src/components/settings/CustomCodeSettings.tsx @@ -83,7 +83,11 @@ export function CustomCodeSettings() { - diff --git a/commafeed-client/src/components/settings/DisplaySettings.tsx b/commafeed-client/src/components/settings/DisplaySettings.tsx index 441de9ae..9fc363fb 100644 --- a/commafeed-client/src/components/settings/DisplaySettings.tsx +++ b/commafeed-client/src/components/settings/DisplaySettings.tsx @@ -111,7 +111,11 @@ export function DisplaySettings() { /> Navigate to the next category/feed with unread entries when marking all entries as read} + label={ + + Navigate to the next category/feed with unread entries when marking all entries as read + + } checked={settings.markAllAsReadNavigateToNextUnread} onChange={e => dispatch(changeSettings({ markAllAsReadNavigateToNextUnread: e.currentTarget.checked }))} /> diff --git a/commafeed-client/src/components/settings/ProfileSettings.tsx b/commafeed-client/src/components/settings/ProfileSettings.tsx index d03f180c..fd3d08a1 100644 --- a/commafeed-client/src/components/settings/ProfileSettings.tsx +++ b/commafeed-client/src/components/settings/ProfileSettings.tsx @@ -1,6 +1,18 @@ import { useLingui } from "@lingui/react" import { Trans } from "@lingui/react/macro" -import { Anchor, Box, Button, Checkbox, Divider, Group, Input, PasswordInput, Stack, Text, TextInput } from "@mantine/core" +import { + Anchor, + Box, + Button, + Checkbox, + Divider, + Group, + Input, + PasswordInput, + Stack, + Text, + TextInput, +} from "@mantine/core" import { useForm } from "@mantine/form" import { openConfirmModal } from "@mantine/modals" import { useEffect } from "react" @@ -91,8 +103,9 @@ export function ProfileSettings() { label={API key} description={ - This is your API key. It can be used for some read-only API operations and grants access to the Fever API - and the Google Reader API. Use the form at the bottom of the page to generate a new API key + This is your API key. It can be used for some read-only API operations and grants access + to the Fever API and the Google Reader API. Use the form at the bottom of the page to + generate a new API key } readOnly @@ -103,7 +116,8 @@ export function ProfileSettings() { label={OPML export} description={ - Export your subscriptions and categories as an OPML file that can be imported in other feed reading services + Export your subscriptions and categories as an OPML file that can be imported in other + feed reading services } > @@ -118,8 +132,8 @@ export function ProfileSettings() { label={Fever API} description={ - CommaFeed is compatible with the Fever API. Use the following URL in your Fever-compatible mobile client. - Login with your username and your API key. + CommaFeed is compatible with the Fever API. Use the following URL in your + Fever-compatible mobile client. Login with your username and your API key. } > @@ -134,8 +148,8 @@ export function ProfileSettings() { label={Google Reader API} description={ - CommaFeed is compatible with the Google Reader API. Use the following URL in your Google Reader-compatible - mobile client. Login with your username and your API key. + CommaFeed is compatible with the Google Reader API. Use the following URL in your Google + Reader-compatible mobile client. Login with your username and your API key. } > @@ -165,8 +179,14 @@ export function ProfileSettings() { description={Changing password will generate a new API key} {...form.getInputProps("newPassword")} /> - Confirm password} {...form.getInputProps("newPasswordConfirmation")} /> - Generate new API key} {...form.getInputProps("newApiKey", { type: "checkbox" })} /> + Confirm password} + {...form.getInputProps("newPasswordConfirmation")} + /> + Generate new API key} + {...form.getInputProps("newApiKey", { type: "checkbox" })} + />