add @/ prefix for absolute imports

This commit is contained in:
Athou
2025-06-27 16:29:31 +02:00
parent 77661930f0
commit a9527f59a9
91 changed files with 561 additions and 422 deletions

View File

@@ -3,38 +3,38 @@ import { I18nProvider } from "@lingui/react"
import { MantineProvider } from "@mantine/core" import { MantineProvider } from "@mantine/core"
import { ModalsProvider } from "@mantine/modals" import { ModalsProvider } from "@mantine/modals"
import { Notifications } from "@mantine/notifications" import { Notifications } from "@mantine/notifications"
import { Constants } from "app/constants"
import { redirectTo } from "app/redirect/slice"
import { reloadServerInfos } from "app/server/thunks"
import { useAppDispatch, useAppSelector } from "app/store"
import { categoryUnreadCount } from "app/utils"
import { DisablePullToRefresh } from "components/DisablePullToRefresh"
import { ErrorBoundary } from "components/ErrorBoundary"
import { Header } from "components/header/Header"
import { Tree } from "components/sidebar/Tree"
import { useAppLoading } from "hooks/useAppLoading"
import { useBrowserExtension } from "hooks/useBrowserExtension"
import { useI18n } from "i18n"
import { AdminUsersPage } from "pages/admin/AdminUsersPage"
import { MetricsPage } from "pages/admin/MetricsPage"
import { AboutPage } from "pages/app/AboutPage"
import { AddPage } from "pages/app/AddPage"
import { CategoryDetailsPage } from "pages/app/CategoryDetailsPage"
import { DonatePage } from "pages/app/DonatePage"
import { FeedDetailsPage } from "pages/app/FeedDetailsPage"
import { FeedEntriesPage } from "pages/app/FeedEntriesPage"
import Layout from "pages/app/Layout"
import { SettingsPage } from "pages/app/SettingsPage"
import { TagDetailsPage } from "pages/app/TagDetailsPage"
import { LoginPage } from "pages/auth/LoginPage"
import { PasswordRecoveryPage } from "pages/auth/PasswordRecoveryPage"
import { RegistrationPage } from "pages/auth/RegistrationPage"
import { WelcomePage } from "pages/WelcomePage"
import React, { useEffect, useState } from "react" import React, { useEffect, useState } from "react"
import { isSafari } from "react-device-detect" import { isSafari } from "react-device-detect"
import ReactGA from "react-ga4" import ReactGA from "react-ga4"
import { HashRouter, Navigate, Route, Routes, useLocation, useNavigate } from "react-router-dom" import { HashRouter, Navigate, Route, Routes, useLocation, useNavigate } from "react-router-dom"
import Tinycon from "tinycon" import Tinycon from "tinycon"
import { Constants } from "@/app/constants"
import { redirectTo } from "@/app/redirect/slice"
import { reloadServerInfos } from "@/app/server/thunks"
import { useAppDispatch, useAppSelector } from "@/app/store"
import { categoryUnreadCount } from "@/app/utils"
import { DisablePullToRefresh } from "@/components/DisablePullToRefresh"
import { ErrorBoundary } from "@/components/ErrorBoundary"
import { Header } from "@/components/header/Header"
import { Tree } from "@/components/sidebar/Tree"
import { useAppLoading } from "@/hooks/useAppLoading"
import { useBrowserExtension } from "@/hooks/useBrowserExtension"
import { useI18n } from "@/i18n"
import { AdminUsersPage } from "@/pages/admin/AdminUsersPage"
import { MetricsPage } from "@/pages/admin/MetricsPage"
import { AboutPage } from "@/pages/app/AboutPage"
import { AddPage } from "@/pages/app/AddPage"
import { CategoryDetailsPage } from "@/pages/app/CategoryDetailsPage"
import { DonatePage } from "@/pages/app/DonatePage"
import { FeedDetailsPage } from "@/pages/app/FeedDetailsPage"
import { FeedEntriesPage } from "@/pages/app/FeedEntriesPage"
import Layout from "@/pages/app/Layout"
import { SettingsPage } from "@/pages/app/SettingsPage"
import { TagDetailsPage } from "@/pages/app/TagDetailsPage"
import { LoginPage } from "@/pages/auth/LoginPage"
import { PasswordRecoveryPage } from "@/pages/auth/PasswordRecoveryPage"
import { RegistrationPage } from "@/pages/auth/RegistrationPage"
import { WelcomePage } from "@/pages/WelcomePage"
function Providers(props: { children: React.ReactNode }) { function Providers(props: { 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
@@ -73,7 +73,7 @@ function Providers(props: { children: React.ReactNode }) {
} }
// api documentation page is very large, load only on-demand // api documentation page is very large, load only on-demand
const ApiDocumentationPage = React.lazy(async () => await import("pages/app/ApiDocumentationPage")) const ApiDocumentationPage = React.lazy(async () => await import("@/pages/app/ApiDocumentationPage"))
function AppRoutes() { function AppRoutes() {
const sidebarVisible = useAppSelector(state => state.tree.sidebarVisible) const sidebarVisible = useAppSelector(state => state.tree.sidebarVisible)

View File

@@ -1,5 +1,5 @@
import { createAsyncThunk } from "@reduxjs/toolkit" import { createAsyncThunk } from "@reduxjs/toolkit"
import type { AppDispatch, RootState } from "app/store" import type { AppDispatch, RootState } from "@/app/store"
export const createAppAsyncThunk = createAsyncThunk.withTypes<{ export const createAppAsyncThunk = createAsyncThunk.withTypes<{
state: RootState state: RootState

View File

@@ -1,12 +1,12 @@
import { configureStore } from "@reduxjs/toolkit" import { configureStore } from "@reduxjs/toolkit"
import { client } from "app/client"
import { loadEntries, loadMoreEntries, markAllEntries, markEntry } from "app/entries/thunks"
import { type RootState, reducers } from "app/store"
import type { Entries, Entry } from "app/types"
import type { AxiosResponse } from "axios" import type { AxiosResponse } from "axios"
import { beforeEach, describe, expect, it, vi } from "vitest" import { beforeEach, describe, expect, it, vi } from "vitest"
import { client } from "@/app/client"
import { loadEntries, loadMoreEntries, markAllEntries, markEntry } from "@/app/entries/thunks"
import { type RootState, reducers } from "@/app/store"
import type { Entries, Entry } from "@/app/types"
vi.mock(import("app/client")) vi.mock(import("@/app/client"))
describe("entries", () => { describe("entries", () => {
beforeEach(() => { beforeEach(() => {
@@ -27,7 +27,12 @@ describe("entries", () => {
} as AxiosResponse<Entries>) } as AxiosResponse<Entries>)
const store = configureStore({ reducer: reducers }) const store = configureStore({ reducer: reducers })
const promise = store.dispatch(loadEntries({ source: { type: "feed", id: "feed-id" }, clearSearch: true })) const promise = store.dispatch(
loadEntries({
source: { type: "feed", id: "feed-id" },
clearSearch: true,
})
)
expect(store.getState().entries.source.type).toBe("feed") expect(store.getState().entries.source.type).toBe("feed")
expect(store.getState().entries.source.id).toBe("feed-id") expect(store.getState().entries.source.id).toBe("feed-id")
@@ -130,11 +135,19 @@ describe("entries", () => {
} as RootState, } as RootState,
}) })
store.dispatch(markAllEntries({ sourceType: "category", req: { id: "all", read: true } })) store.dispatch(
markAllEntries({
sourceType: "category",
req: { id: "all", read: true },
})
)
expect(store.getState().entries.entries).toStrictEqual([ expect(store.getState().entries.entries).toStrictEqual([
{ id: "3", read: true }, { id: "3", read: true },
{ id: "4", read: true }, { id: "4", read: true },
]) ])
expect(client.category.markEntries).toHaveBeenCalledWith({ id: "all", read: true }) expect(client.category.markEntries).toHaveBeenCalledWith({
id: "all",
read: true,
})
}) })
}) })

View File

@@ -1,7 +1,7 @@
import { createSlice, type PayloadAction } from "@reduxjs/toolkit" import { createSlice, type PayloadAction } from "@reduxjs/toolkit"
import { Constants } from "app/constants" 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" import type { Entry } from "@/app/types"
export type EntrySourceType = "category" | "feed" | "tag" export type EntrySourceType = "category" | "feed" | "tag"

View File

@@ -1,13 +1,19 @@
import { createAppAsyncThunk } from "app/async-thunk"
import { client } from "app/client"
import { Constants } from "app/constants"
import { type EntrySource, type EntrySourceType, entriesSlice, setMarkAllAsReadConfirmationDialogOpen, setSearch } from "app/entries/slice"
import type { RootState } from "app/store"
import { reloadTree, selectNextUnreadTreeItem } from "app/tree/thunks"
import type { Entry, MarkRequest, TagRequest } from "app/types"
import { reloadTags } from "app/user/thunks"
import { scrollToWithCallback } from "app/utils"
import { flushSync } from "react-dom" import { flushSync } from "react-dom"
import { createAppAsyncThunk } from "@/app/async-thunk"
import { client } from "@/app/client"
import { Constants } from "@/app/constants"
import {
type EntrySource,
type EntrySourceType,
entriesSlice,
setMarkAllAsReadConfirmationDialogOpen,
setSearch,
} from "@/app/entries/slice"
import type { RootState } from "@/app/store"
import { reloadTree, selectNextUnreadTreeItem } from "@/app/tree/thunks"
import type { Entry, MarkRequest, TagRequest } from "@/app/types"
import { reloadTags } from "@/app/user/thunks"
import { scrollToWithCallback } from "@/app/utils"
const getEndpoint = (sourceType: EntrySourceType) => const getEndpoint = (sourceType: EntrySourceType) =>
sourceType === "category" || sourceType === "tag" ? client.category.getEntries : client.feed.getEntries sourceType === "category" || sourceType === "tag" ? client.category.getEntries : client.feed.getEntries

View File

@@ -1,6 +1,6 @@
import { redirectToCategory } from "app/redirect/thunks"
import { store } from "app/store"
import { describe, expect, it } from "vitest" import { describe, expect, it } from "vitest"
import { redirectToCategory } from "@/app/redirect/thunks"
import { store } from "@/app/store"
describe("redirects", () => { describe("redirects", () => {
it("redirects to category", async () => { it("redirects to category", async () => {

View File

@@ -1,6 +1,6 @@
import { createAppAsyncThunk } from "app/async-thunk" import { createAppAsyncThunk } from "@/app/async-thunk"
import { Constants } from "app/constants" import { Constants } from "@/app/constants"
import { redirectTo } from "app/redirect/slice" 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")))

View File

@@ -1,6 +1,6 @@
import { createSlice, type PayloadAction } from "@reduxjs/toolkit" import { createSlice, type PayloadAction } from "@reduxjs/toolkit"
import { reloadServerInfos } from "app/server/thunks" import { reloadServerInfos } from "@/app/server/thunks"
import type { ServerInfo } from "app/types" import type { ServerInfo } from "@/app/types"
interface ServerState { interface ServerState {
serverInfos?: ServerInfo serverInfos?: ServerInfo

View File

@@ -1,4 +1,4 @@
import { createAppAsyncThunk } from "app/async-thunk" import { createAppAsyncThunk } from "@/app/async-thunk"
import { client } from "app/client" 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))

View File

@@ -1,11 +1,11 @@
import { configureStore } from "@reduxjs/toolkit" import { configureStore } from "@reduxjs/toolkit"
import { entriesSlice } from "app/entries/slice"
import { redirectSlice } from "app/redirect/slice"
import { serverSlice } from "app/server/slice"
import { treeSlice } from "app/tree/slice"
import type { LocalSettings } from "app/types"
import { initialLocalSettings, userSlice } from "app/user/slice"
import { type TypedUseSelectorHook, useDispatch, useSelector } from "react-redux" import { type TypedUseSelectorHook, useDispatch, useSelector } from "react-redux"
import { entriesSlice } from "@/app/entries/slice"
import { redirectSlice } from "@/app/redirect/slice"
import { serverSlice } from "@/app/server/slice"
import { treeSlice } from "@/app/tree/slice"
import type { LocalSettings } from "@/app/types"
import { initialLocalSettings, userSlice } from "@/app/user/slice"
export const reducers = { export const reducers = {
entries: entriesSlice.reducer, entries: entriesSlice.reducer,

View File

@@ -1,9 +1,9 @@
import { createSlice, type PayloadAction } from "@reduxjs/toolkit" import { createSlice, type PayloadAction } from "@reduxjs/toolkit"
import { loadEntries, markEntry } from "app/entries/thunks" import { loadEntries, markEntry } from "@/app/entries/thunks"
import { redirectTo } from "app/redirect/slice" import { redirectTo } from "@/app/redirect/slice"
import { collapseTreeCategory, reloadTree } from "app/tree/thunks" import { collapseTreeCategory, reloadTree } from "@/app/tree/thunks"
import type { Category, Subscription } from "app/types" import type { Category, Subscription } from "@/app/types"
import { flattenCategoryTree, visitCategoryTree } from "app/utils" import { flattenCategoryTree, visitCategoryTree } from "@/app/utils"
export interface TreeSubscription extends Subscription { export interface TreeSubscription extends Subscription {
// client-side only flag // client-side only flag

View File

@@ -1,10 +1,10 @@
import { createAppAsyncThunk } from "app/async-thunk" import { createAppAsyncThunk } from "@/app/async-thunk"
import { client } from "app/client" import { client } from "@/app/client"
import { Constants } from "app/constants" import { Constants } from "@/app/constants"
import { redirectToCategory, redirectToFeed } from "app/redirect/thunks" import { redirectToCategory, redirectToFeed } from "@/app/redirect/thunks"
import { incrementUnreadCount } from "app/tree/slice" import { incrementUnreadCount } from "@/app/tree/slice"
import type { CollapseRequest, Subscription } from "app/types" import type { CollapseRequest, Subscription } from "@/app/types"
import { flattenCategoryTree, visitCategoryTree } from "app/utils" 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))

View File

@@ -1,13 +1,13 @@
import { configureStore } from "@reduxjs/toolkit" import { configureStore } from "@reduxjs/toolkit"
import { client } from "app/client"
import { loadEntries } from "app/entries/thunks"
import { type RootState, reducers } from "app/store"
import { newFeedEntriesDiscovered, selectNextUnreadTreeItem } from "app/tree/thunks"
import type { Category, Entries, Entry, Subscription } from "app/types"
import type { AxiosResponse } from "axios" import type { AxiosResponse } from "axios"
import { beforeEach, describe, expect, it, vi } from "vitest" import { beforeEach, describe, expect, it, vi } from "vitest"
import { client } from "@/app/client"
import { loadEntries } from "@/app/entries/thunks"
import { type RootState, reducers } from "@/app/store"
import { newFeedEntriesDiscovered, selectNextUnreadTreeItem } from "@/app/tree/thunks"
import type { Category, Entries, Entry, Subscription } from "@/app/types"
vi.mock(import("app/client")) vi.mock(import("@/app/client"))
const createCategory = (id: string): Category => ({ const createCategory = (id: string): Category => ({
id, id,

View File

@@ -1,7 +1,7 @@
import { t } from "@lingui/core/macro" import { t } from "@lingui/core/macro"
import { showNotification } from "@mantine/notifications" import { showNotification } from "@mantine/notifications"
import { createSlice, isAnyOf, type PayloadAction } from "@reduxjs/toolkit" import { createSlice, isAnyOf, type PayloadAction } from "@reduxjs/toolkit"
import type { LocalSettings, Settings, UserModel, ViewMode } from "app/types" import type { LocalSettings, Settings, UserModel, ViewMode } from "@/app/types"
import { import {
changeCustomContextMenu, changeCustomContextMenu,
changeEntriesToKeepOnTopWhenScrolling, changeEntriesToKeepOnTopWhenScrolling,

View File

@@ -1,7 +1,7 @@
import { createAppAsyncThunk } from "app/async-thunk" import { createAppAsyncThunk } from "@/app/async-thunk"
import { client } from "app/client" import { client } from "@/app/client"
import { reloadEntries } from "app/entries/thunks" import { reloadEntries } from "@/app/entries/thunks"
import type { IconDisplayMode, ReadingMode, ReadingOrder, ScrollMode, SharingSettings } from "app/types" import type { IconDisplayMode, ReadingMode, ReadingOrder, ScrollMode, SharingSettings } 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))

View File

@@ -1,5 +1,5 @@
import type { TreeCategory } from "app/tree/slice"
import { throttle } from "throttle-debounce" import { throttle } from "throttle-debounce"
import type { TreeCategory } from "@/app/tree/slice"
import type { Category } from "./types" import type { Category } from "./types"
export function visitCategoryTree( export function visitCategoryTree(

View File

@@ -1,8 +1,8 @@
import type { I18nContext } from "@lingui/react" import type { I18nContext } from "@lingui/react"
import { MantineProvider } from "@mantine/core" import { MantineProvider } from "@mantine/core"
import { fireEvent, render, screen, waitFor } from "@testing-library/react" import { fireEvent, render, screen, waitFor } from "@testing-library/react"
import { useActionButton } from "hooks/useActionButton"
import { describe, expect, it, vi } from "vitest" import { describe, expect, it, vi } from "vitest"
import { useActionButton } from "@/hooks/useActionButton"
import { ActionButton } from "./ActionButton" import { ActionButton } from "./ActionButton"
vi.mock(import("@lingui/react"), () => ({ vi.mock(import("@lingui/react"), () => ({
@@ -10,7 +10,7 @@ vi.mock(import("@lingui/react"), () => ({
_: msg => msg, _: msg => msg,
} as I18nContext), } as I18nContext),
})) }))
vi.mock(import("hooks/useActionButton")) vi.mock(import("@/hooks/useActionButton"))
const label = "Test Label" const label = "Test Label"
const icon = "Test Icon" const icon = "Test Icon"
@@ -18,7 +18,9 @@ describe("ActionButton", () => {
it("renders Button with label on desktop", () => { it("renders Button with label on desktop", () => {
vi.mocked(useActionButton).mockReturnValue({ mobile: false, spacing: 0 }) vi.mocked(useActionButton).mockReturnValue({ mobile: false, spacing: 0 })
render(<ActionButton label={label} icon={icon} />, { wrapper: MantineProvider }) render(<ActionButton label={label} icon={icon} />, {
wrapper: MantineProvider,
})
expect(screen.getByText(label)).toBeInTheDocument() expect(screen.getByText(label)).toBeInTheDocument()
expect(screen.getByText(icon)).toBeInTheDocument() expect(screen.getByText(icon)).toBeInTheDocument()
}) })
@@ -26,7 +28,9 @@ describe("ActionButton", () => {
it("renders ActionIcon with tooltip on mobile", async () => { it("renders ActionIcon with tooltip on mobile", async () => {
vi.mocked(useActionButton).mockReturnValue({ mobile: true, spacing: 0 }) vi.mocked(useActionButton).mockReturnValue({ mobile: true, spacing: 0 })
render(<ActionButton label={label} icon={icon} />, { wrapper: MantineProvider }) render(<ActionButton label={label} icon={icon} />, {
wrapper: MantineProvider,
})
expect(screen.queryByText(label)).not.toBeInTheDocument() expect(screen.queryByText(label)).not.toBeInTheDocument()
expect(screen.getByText(icon)).toBeInTheDocument() expect(screen.getByText(icon)).toBeInTheDocument()
@@ -39,7 +43,9 @@ describe("ActionButton", () => {
vi.mocked(useActionButton).mockReturnValue({ mobile: false, spacing: 0 }) vi.mocked(useActionButton).mockReturnValue({ mobile: false, spacing: 0 })
const clickListener = vi.fn() const clickListener = vi.fn()
render(<ActionButton label={label} icon={icon} onClick={clickListener} />, { wrapper: MantineProvider }) render(<ActionButton label={label} icon={icon} onClick={clickListener} />, {
wrapper: MantineProvider,
})
fireEvent.click(screen.getByRole("button")) fireEvent.click(screen.getByRole("button"))
expect(clickListener).toHaveBeenCalled() expect(clickListener).toHaveBeenCalled()

View File

@@ -2,9 +2,9 @@ import type { MessageDescriptor } from "@lingui/core"
import { useLingui } from "@lingui/react" import { useLingui } from "@lingui/react"
import { ActionIcon, Box, Button, type ButtonVariant, Tooltip, useMantineTheme } from "@mantine/core" import { ActionIcon, Box, Button, type ButtonVariant, Tooltip, useMantineTheme } from "@mantine/core"
import type { ActionIconVariant } from "@mantine/core/lib/components/ActionIcon/ActionIcon" import type { ActionIconVariant } from "@mantine/core/lib/components/ActionIcon/ActionIcon"
import { Constants } from "app/constants"
import { useActionButton } from "hooks/useActionButton"
import { forwardRef, type MouseEventHandler, type ReactNode } from "react" import { forwardRef, type MouseEventHandler, type ReactNode } from "react"
import { Constants } from "@/app/constants"
import { useActionButton } from "@/hooks/useActionButton"
interface ActionButtonProps { interface ActionButtonProps {
icon: ReactNode icon: ReactNode

View File

@@ -1,9 +1,9 @@
import { Trans } from "@lingui/react/macro" import { Trans } from "@lingui/react/macro"
import { Box, Dialog, Text } from "@mantine/core" import { Box, Dialog, Text } from "@mantine/core"
import { useAppDispatch, useAppSelector } from "app/store"
import { setAnnouncementHash } from "app/user/slice"
import { Content } from "components/content/Content"
import { useAsync } from "react-async-hook" import { useAsync } from "react-async-hook"
import { useAppDispatch, useAppSelector } from "@/app/store"
import { setAnnouncementHash } from "@/app/user/slice"
import { Content } from "@/components/content/Content"
const sha256Hex = async (input: string | undefined) => { const sha256Hex = async (input: string | undefined) => {
const data = new TextEncoder().encode(input) const data = new TextEncoder().encode(input)

View File

@@ -1,4 +1,4 @@
export const DisablePullToRefresh = () => { export const DisablePullToRefresh = () => {
import("./DisablePullToRefresh.css") import("./DisablePullToRefresh.css")
return <></> return null
} }

View File

@@ -1,5 +1,5 @@
import { ErrorPage } from "pages/ErrorPage"
import React, { type ReactNode } from "react" import React, { type ReactNode } from "react"
import { ErrorPage } from "@/pages/ErrorPage"
interface ErrorBoundaryProps { interface ErrorBoundaryProps {
children?: ReactNode children?: ReactNode

View File

@@ -1,7 +1,7 @@
import { Box, Center } from "@mantine/core" import { Box, Center } from "@mantine/core"
import { useState } from "react" import { useState } from "react"
import { TbPhoto } from "react-icons/tb" import { TbPhoto } from "react-icons/tb"
import { tss } from "tss" import { tss } from "@/tss"
interface ImageWithPlaceholderWhileLoadingProps { interface ImageWithPlaceholderWhileLoadingProps {
src: string src: string

View File

@@ -1,7 +1,7 @@
import { Trans } from "@lingui/react/macro" import { Trans } from "@lingui/react/macro"
import { Anchor, Box, Kbd, Stack, Table } from "@mantine/core" import { Anchor, Box, Kbd, Stack, Table } from "@mantine/core"
import { useOs } from "@mantine/hooks" import { useOs } from "@mantine/hooks"
import { Constants } from "app/constants" import { Constants } from "@/app/constants"
export function KeyboardShortcutsHelp() { export function KeyboardShortcutsHelp() {
const isMacOS = useOs() === "macos" const isMacOS = useOs() === "macos"

View File

@@ -1,5 +1,5 @@
import { Image } from "@mantine/core" import { Image } from "@mantine/core"
import logo from "assets/logo.svg" import logo from "@/assets/logo.svg"
export interface LogoProps { export interface LogoProps {
size: number size: number

View File

@@ -1,11 +1,11 @@
import { Trans } from "@lingui/react/macro" import { Trans } from "@lingui/react/macro"
import { Button, Code, Group, Modal, Slider, Stack, Text } from "@mantine/core" import { Button, Code, Group, Modal, Slider, Stack, Text } from "@mantine/core"
import { Constants } from "app/constants"
import { setMarkAllAsReadConfirmationDialogOpen } from "app/entries/slice"
import { markAllEntries } from "app/entries/thunks"
import { useAppDispatch, useAppSelector } from "app/store"
import { selectNextUnreadTreeItem } from "app/tree/thunks"
import { useState } from "react" import { useState } from "react"
import { Constants } from "@/app/constants"
import { setMarkAllAsReadConfirmationDialogOpen } from "@/app/entries/slice"
import { markAllEntries } from "@/app/entries/thunks"
import { useAppDispatch, useAppSelector } from "@/app/store"
import { selectNextUnreadTreeItem } from "@/app/tree/thunks"
export function MarkAllAsReadConfirmationDialog() { export function MarkAllAsReadConfirmationDialog() {
const [threshold, setThreshold] = useState(0) const [threshold, setThreshold] = useState(0)

View File

@@ -1,8 +1,8 @@
import { Trans } from "@lingui/react/macro" import { Trans } from "@lingui/react/macro"
import { Tooltip } from "@mantine/core" import { Tooltip } from "@mantine/core"
import { Constants } from "app/constants"
import dayjs from "dayjs" import dayjs from "dayjs"
import { useNow } from "hooks/useNow" import { Constants } from "@/app/constants"
import { useNow } from "@/hooks/useNow"
export function RelativeDate(props: { date: Date | number | undefined }) { export function RelativeDate(props: { date: Date | number | undefined }) {
const now = useNow(60 * 1000) const now = useNow(60 * 1000)

View File

@@ -1,11 +1,11 @@
import { Trans } from "@lingui/react/macro" import { Trans } from "@lingui/react/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 type { AdminSaveUserRequest, UserModel } from "app/types"
import { Alert } from "components/Alert"
import { useAsyncCallback } from "react-async-hook" import { useAsyncCallback } from "react-async-hook"
import { TbDeviceFloppy } from "react-icons/tb" import { TbDeviceFloppy } from "react-icons/tb"
import { client, errorToStrings } from "@/app/client"
import type { AdminSaveUserRequest, UserModel } from "@/app/types"
import { Alert } from "@/components/Alert"
interface UserEditProps { interface UserEditProps {
user?: UserModel user?: UserModel

View File

@@ -1,7 +1,7 @@
import { Input, Textarea } from "@mantine/core" import { Input, Textarea } from "@mantine/core"
import RichCodeEditor from "components/code/RichCodeEditor"
import { useMobile } from "hooks/useMobile"
import type { ReactNode } from "react" import type { ReactNode } from "react"
import RichCodeEditor from "@/components/code/RichCodeEditor"
import { useMobile } from "@/hooks/useMobile"
interface CodeEditorProps { interface CodeEditorProps {
label?: ReactNode label?: ReactNode

View File

@@ -1,6 +1,6 @@
import { Loader } from "components/Loader"
import { useColorScheme } from "hooks/useColorScheme"
import { useAsync } from "react-async-hook" import { useAsync } from "react-async-hook"
import { Loader } from "@/components/Loader"
import { useColorScheme } from "@/hooks/useColorScheme"
const init = async () => { const init = async () => {
window.MonacoEnvironment = { window.MonacoEnvironment = {

View File

@@ -1,6 +1,6 @@
import { TypographyStylesProvider } from "@mantine/core" import { TypographyStylesProvider } from "@mantine/core"
import type { ReactNode } from "react" import type { ReactNode } from "react"
import { tss } from "tss" import { tss } from "@/tss"
/** /**
* This component is used to provide basic styles to html typography elements. * This component is used to provide basic styles to html typography elements.

View File

@@ -1,7 +1,7 @@
import { MantineProvider } from "@mantine/core" import { MantineProvider } from "@mantine/core"
import { render } from "@testing-library/react" import { render } from "@testing-library/react"
import { Content } from "components/content/Content"
import { describe, expect, it } from "vitest" import { describe, expect, it } from "vitest"
import { Content } from "@/components/content/Content"
describe("Content component", () => { describe("Content component", () => {
it("renders basic content", () => { it("renders basic content", () => {

View File

@@ -1,13 +1,13 @@
import { Box, Mark } from "@mantine/core" import { Box, Mark } from "@mantine/core"
import { Constants } from "app/constants"
import { calculatePlaceholderSize } from "app/utils"
import { BasicHtmlStyles } from "components/content/BasicHtmlStyles"
import { ImageWithPlaceholderWhileLoading } from "components/ImageWithPlaceholderWhileLoading"
import escapeStringRegexp from "escape-string-regexp" 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 React from "react"
import styleToObject from "style-to-object" import styleToObject from "style-to-object"
import { tss } from "tss" import { Constants } from "@/app/constants"
import { calculatePlaceholderSize } from "@/app/utils"
import { BasicHtmlStyles } from "@/components/content/BasicHtmlStyles"
import { ImageWithPlaceholderWhileLoading } from "@/components/ImageWithPlaceholderWhileLoading"
import { tss } from "@/tss"
export interface ContentProps { export interface ContentProps {
content: string content: string

View File

@@ -1,5 +1,5 @@
import { BasicHtmlStyles } from "components/content/BasicHtmlStyles" import { BasicHtmlStyles } from "@/components/content/BasicHtmlStyles"
import { ImageWithPlaceholderWhileLoading } from "components/ImageWithPlaceholderWhileLoading" import { ImageWithPlaceholderWhileLoading } from "@/components/ImageWithPlaceholderWhileLoading"
export function Enclosure(props: { enclosureType: string; enclosureUrl: string }) { export function Enclosure(props: { enclosureType: string; enclosureUrl: string }) {
const hasVideo = props.enclosureType.startsWith("video") const hasVideo = props.enclosureType.startsWith("video")

View File

@@ -1,8 +1,12 @@
import { Trans } from "@lingui/react/macro" import { Trans } from "@lingui/react/macro"
import { Box } from "@mantine/core" import { Box } from "@mantine/core"
import { openModal } from "@mantine/modals" import { openModal } from "@mantine/modals"
import { Constants } from "app/constants" import { useEffect } from "react"
import type { ExpendableEntry } from "app/entries/slice" import { useContextMenu } from "react-contexify"
import InfiniteScroll from "react-infinite-scroller"
import { throttle } from "throttle-debounce"
import { Constants } from "@/app/constants"
import type { ExpendableEntry } from "@/app/entries/slice"
import { import {
loadMoreEntries, loadMoreEntries,
markAllAsReadWithConfirmationIfRequired, markAllAsReadWithConfirmationIfRequired,
@@ -12,19 +16,15 @@ import {
selectNextEntry, selectNextEntry,
selectPreviousEntry, selectPreviousEntry,
starEntry, starEntry,
} from "app/entries/thunks" } from "@/app/entries/thunks"
import { redirectToRootCategory } from "app/redirect/thunks" import { redirectToRootCategory } from "@/app/redirect/thunks"
import { useAppDispatch, useAppSelector } from "app/store" import { useAppDispatch, useAppSelector } from "@/app/store"
import { toggleSidebar } from "app/tree/slice" import { toggleSidebar } from "@/app/tree/slice"
import { selectNextUnreadTreeItem } from "app/tree/thunks" import { selectNextUnreadTreeItem } from "@/app/tree/thunks"
import { KeyboardShortcutsHelp } from "components/KeyboardShortcutsHelp" import { KeyboardShortcutsHelp } from "@/components/KeyboardShortcutsHelp"
import { Loader } from "components/Loader" import { Loader } from "@/components/Loader"
import { useBrowserExtension } from "hooks/useBrowserExtension" import { useBrowserExtension } from "@/hooks/useBrowserExtension"
import { useMousetrap } from "hooks/useMousetrap" import { useMousetrap } from "@/hooks/useMousetrap"
import { useEffect } from "react"
import { useContextMenu } from "react-contexify"
import InfiniteScroll from "react-infinite-scroller"
import { throttle } from "throttle-debounce"
import { FeedEntry } from "./FeedEntry" import { FeedEntry } from "./FeedEntry"
export function FeedEntries() { export function FeedEntries() {

View File

@@ -1,13 +1,13 @@
import { Box, Divider, type MantineRadius, type MantineSpacing, Paper } from "@mantine/core" import { Box, Divider, type MantineRadius, type MantineSpacing, Paper } from "@mantine/core"
import { Constants } from "app/constants"
import { useAppSelector } from "app/store"
import type { Entry, ViewMode } from "app/types"
import { FeedEntryCompactHeader } from "components/content/header/FeedEntryCompactHeader"
import { FeedEntryHeader } from "components/content/header/FeedEntryHeader"
import { useMobile } from "hooks/useMobile"
import type React from "react" import type React from "react"
import { useSwipeable } from "react-swipeable" import { useSwipeable } from "react-swipeable"
import { tss } from "tss" import { Constants } from "@/app/constants"
import { useAppSelector } from "@/app/store"
import type { Entry, ViewMode } from "@/app/types"
import { FeedEntryCompactHeader } from "@/components/content/header/FeedEntryCompactHeader"
import { FeedEntryHeader } from "@/components/content/header/FeedEntryHeader"
import { useMobile } from "@/hooks/useMobile"
import { tss } from "@/tss"
import { FeedEntryBody } from "./FeedEntryBody" import { FeedEntryBody } from "./FeedEntryBody"
import { FeedEntryContextMenu } from "./FeedEntryContextMenu" import { FeedEntryContextMenu } from "./FeedEntryContextMenu"
import { FeedEntryFooter } from "./FeedEntryFooter" import { FeedEntryFooter } from "./FeedEntryFooter"

View File

@@ -1,6 +1,6 @@
import { Box } from "@mantine/core" import { Box } from "@mantine/core"
import { useAppSelector } from "app/store" import { useAppSelector } from "@/app/store"
import type { Entry } from "app/types" import type { Entry } from "@/app/types"
import { Content } from "./Content" import { Content } from "./Content"
import { Enclosure } from "./Enclosure" import { Enclosure } from "./Enclosure"
import { Media } from "./Media" import { Media } from "./Media"

View File

@@ -1,16 +1,16 @@
import { Trans } from "@lingui/react/macro" import { Trans } from "@lingui/react/macro"
import { Group } from "@mantine/core" import { Group } from "@mantine/core"
import { Constants } from "app/constants"
import { markEntriesUpToEntry, markEntry, starEntry } from "app/entries/thunks"
import { redirectToFeed } from "app/redirect/thunks"
import { useAppDispatch, useAppSelector } from "app/store"
import type { Entry } from "app/types"
import { truncate } from "app/utils"
import { useBrowserExtension } from "hooks/useBrowserExtension"
import { useColorScheme } from "hooks/useColorScheme"
import { Item, Menu, Separator } from "react-contexify" import { Item, Menu, Separator } from "react-contexify"
import { TbArrowBarToDown, TbExternalLink, TbMail, TbMailOpened, TbRss, TbStar, TbStarOff } from "react-icons/tb" import { TbArrowBarToDown, TbExternalLink, TbMail, TbMailOpened, TbRss, TbStar, TbStarOff } from "react-icons/tb"
import { tss } from "tss" import { Constants } from "@/app/constants"
import { markEntriesUpToEntry, markEntry, starEntry } from "@/app/entries/thunks"
import { redirectToFeed } from "@/app/redirect/thunks"
import { useAppDispatch, useAppSelector } from "@/app/store"
import type { Entry } from "@/app/types"
import { truncate } from "@/app/utils"
import { useBrowserExtension } from "@/hooks/useBrowserExtension"
import { useColorScheme } from "@/hooks/useColorScheme"
import { tss } from "@/tss"
interface FeedEntryContextMenuProps { interface FeedEntryContextMenuProps {
entry: Entry entry: Entry

View File

@@ -1,13 +1,13 @@
import { msg } from "@lingui/core/macro" import { msg } from "@lingui/core/macro"
import { useLingui } from "@lingui/react" import { useLingui } from "@lingui/react"
import { Group, Indicator, Popover, TagsInput } from "@mantine/core" import { Group, Indicator, Popover, TagsInput } from "@mantine/core"
import { markEntriesUpToEntry, markEntry, starEntry, tagEntry } from "app/entries/thunks"
import { useAppDispatch, useAppSelector } from "app/store"
import type { Entry } from "app/types"
import { ActionButton } from "components/ActionButton"
import { useActionButton } from "hooks/useActionButton"
import { useMobile } from "hooks/useMobile"
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"
import { ActionButton } from "@/components/ActionButton"
import { useActionButton } from "@/hooks/useActionButton"
import { useMobile } from "@/hooks/useMobile"
import { ShareButtons } from "./ShareButtons" import { ShareButtons } from "./ShareButtons"
interface FeedEntryFooterProps { interface FeedEntryFooterProps {

View File

@@ -1,4 +1,4 @@
import { ImageWithPlaceholderWhileLoading } from "components/ImageWithPlaceholderWhileLoading" import { ImageWithPlaceholderWhileLoading } from "@/components/ImageWithPlaceholderWhileLoading"
export interface FeedFaviconProps { export interface FeedFaviconProps {
url: string url: string

View File

@@ -1,8 +1,8 @@
import { Box } from "@mantine/core" import { Box } from "@mantine/core"
import { Constants } from "app/constants" import { Constants } from "@/app/constants"
import { calculatePlaceholderSize } from "app/utils" import { calculatePlaceholderSize } from "@/app/utils"
import { BasicHtmlStyles } from "components/content/BasicHtmlStyles" import { BasicHtmlStyles } from "@/components/content/BasicHtmlStyles"
import { ImageWithPlaceholderWhileLoading } from "components/ImageWithPlaceholderWhileLoading" import { ImageWithPlaceholderWhileLoading } from "@/components/ImageWithPlaceholderWhileLoading"
import { Content } from "./Content" import { Content } from "./Content"
export interface MediaProps { export interface MediaProps {

View File

@@ -1,13 +1,13 @@
import { Trans } from "@lingui/react/macro" import { Trans } from "@lingui/react/macro"
import { ActionIcon, Box, CopyButton, Divider, SimpleGrid } from "@mantine/core" import { ActionIcon, Box, CopyButton, Divider, SimpleGrid } from "@mantine/core"
import { Constants } from "app/constants"
import { useAppSelector } from "app/store"
import type { SharingSettings } from "app/types"
import { useBrowserExtension } from "hooks/useBrowserExtension"
import { useMobile } from "hooks/useMobile"
import type { IconType } from "react-icons" import type { IconType } from "react-icons"
import { TbCheck, TbCopy, TbDeviceDesktopShare, TbDeviceMobileShare } from "react-icons/tb" import { TbCheck, TbCopy, TbDeviceDesktopShare, TbDeviceMobileShare } from "react-icons/tb"
import { tss } from "tss" import { Constants } from "@/app/constants"
import { useAppSelector } from "@/app/store"
import type { SharingSettings } from "@/app/types"
import { useBrowserExtension } from "@/hooks/useBrowserExtension"
import { useMobile } from "@/hooks/useMobile"
import { tss } from "@/tss"
type Color = `#${string}` type Color = `#${string}`

View File

@@ -3,14 +3,14 @@ import { useLingui } from "@lingui/react"
import { Trans } from "@lingui/react/macro" import { Trans } from "@lingui/react/macro"
import { Box, Button, Group, Stack, TextInput } from "@mantine/core" import { Box, Button, Group, Stack, TextInput } from "@mantine/core"
import { useForm } from "@mantine/form" import { useForm } from "@mantine/form"
import { client, errorToStrings } from "app/client"
import { redirectToSelectedSource } from "app/redirect/thunks"
import { useAppDispatch } from "app/store"
import { reloadTree } from "app/tree/thunks"
import type { AddCategoryRequest } from "app/types"
import { Alert } from "components/Alert"
import { useAsyncCallback } from "react-async-hook" import { useAsyncCallback } from "react-async-hook"
import { TbFolderPlus } from "react-icons/tb" import { TbFolderPlus } from "react-icons/tb"
import { client, errorToStrings } from "@/app/client"
import { redirectToSelectedSource } from "@/app/redirect/thunks"
import { useAppDispatch } from "@/app/store"
import { reloadTree } from "@/app/tree/thunks"
import type { AddCategoryRequest } from "@/app/types"
import { Alert } from "@/components/Alert"
import { CategorySelect } from "./CategorySelect" import { CategorySelect } from "./CategorySelect"
export function AddCategory() { export function AddCategory() {

View File

@@ -2,10 +2,10 @@ import { msg } from "@lingui/core/macro"
import { useLingui } from "@lingui/react" import { useLingui } from "@lingui/react"
import { Select, type SelectProps } from "@mantine/core" import { Select, type SelectProps } from "@mantine/core"
import type { ComboboxItem } from "@mantine/core/lib/components/Combobox/Combobox.types" import type { ComboboxItem } from "@mantine/core/lib/components/Combobox/Combobox.types"
import { Constants } from "app/constants" import { Constants } from "@/app/constants"
import { useAppSelector } from "app/store" import { useAppSelector } from "@/app/store"
import type { Category } from "app/types" import type { Category } from "@/app/types"
import { flattenCategoryTree } from "app/utils" import { flattenCategoryTree } from "@/app/utils"
type CategorySelectProps = Partial<SelectProps> & { type CategorySelectProps = Partial<SelectProps> & {
withAll?: boolean withAll?: boolean

View File

@@ -3,13 +3,13 @@ import { useLingui } from "@lingui/react"
import { Trans } from "@lingui/react/macro" import { Trans } from "@lingui/react/macro"
import { Box, Button, FileInput, Group, Stack } from "@mantine/core" import { Box, Button, FileInput, Group, Stack } from "@mantine/core"
import { isNotEmpty, useForm } from "@mantine/form" import { isNotEmpty, useForm } from "@mantine/form"
import { client, errorToStrings } from "app/client"
import { redirectToSelectedSource } from "app/redirect/thunks"
import { useAppDispatch } from "app/store"
import { reloadTree } from "app/tree/thunks"
import { Alert } from "components/Alert"
import { useAsyncCallback } from "react-async-hook" import { useAsyncCallback } from "react-async-hook"
import { TbFileImport } from "react-icons/tb" import { TbFileImport } from "react-icons/tb"
import { client, errorToStrings } from "@/app/client"
import { redirectToSelectedSource } from "@/app/redirect/thunks"
import { useAppDispatch } from "@/app/store"
import { reloadTree } from "@/app/tree/thunks"
import { Alert } from "@/components/Alert"
export function ImportOpml() { export function ImportOpml() {
const dispatch = useAppDispatch() const dispatch = useAppDispatch()

View File

@@ -1,16 +1,16 @@
import { Trans } from "@lingui/react/macro" import { Trans } from "@lingui/react/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 { Constants } from "app/constants"
import { redirectToFeed, redirectToSelectedSource } from "app/redirect/thunks"
import { useAppDispatch } from "app/store"
import { reloadTree } from "app/tree/thunks"
import type { FeedInfoRequest, SubscribeRequest } from "app/types"
import { Alert } from "components/Alert"
import { useState } from "react" import { useState } from "react"
import { useAsyncCallback } from "react-async-hook" import { useAsyncCallback } from "react-async-hook"
import { TbRss } from "react-icons/tb" import { TbRss } from "react-icons/tb"
import { client, errorToStrings } from "@/app/client"
import { Constants } from "@/app/constants"
import { redirectToFeed, redirectToSelectedSource } from "@/app/redirect/thunks"
import { useAppDispatch } from "@/app/store"
import { reloadTree } from "@/app/tree/thunks"
import type { FeedInfoRequest, SubscribeRequest } from "@/app/types"
import { Alert } from "@/components/Alert"
import { CategorySelect } from "./CategorySelect" import { CategorySelect } from "./CategorySelect"
export function Subscribe() { export function Subscribe() {

View File

@@ -1,11 +1,11 @@
import { Box } from "@mantine/core" import { Box } from "@mantine/core"
import type { Entry } from "app/types" import type { Entry } from "@/app/types"
import { FeedFavicon } from "components/content/FeedFavicon" import { FeedFavicon } from "@/components/content/FeedFavicon"
import { OpenExternalLink } from "components/content/header/OpenExternalLink" import { OpenExternalLink } from "@/components/content/header/OpenExternalLink"
import { Star } from "components/content/header/Star" import { Star } from "@/components/content/header/Star"
import { RelativeDate } from "components/RelativeDate" import { RelativeDate } from "@/components/RelativeDate"
import { OnDesktop } from "components/responsive/OnDesktop" import { OnDesktop } from "@/components/responsive/OnDesktop"
import { tss } from "tss" import { tss } from "@/tss"
import { FeedEntryTitle } from "./FeedEntryTitle" import { FeedEntryTitle } from "./FeedEntryTitle"
export interface FeedEntryHeaderProps { export interface FeedEntryHeaderProps {

View File

@@ -1,10 +1,10 @@
import { Box, Flex, Space } from "@mantine/core" import { Box, Flex, Space } from "@mantine/core"
import type { Entry } from "app/types" import type { Entry } from "@/app/types"
import { FeedFavicon } from "components/content/FeedFavicon" import { FeedFavicon } from "@/components/content/FeedFavicon"
import { OpenExternalLink } from "components/content/header/OpenExternalLink" import { OpenExternalLink } from "@/components/content/header/OpenExternalLink"
import { Star } from "components/content/header/Star" import { Star } from "@/components/content/header/Star"
import { RelativeDate } from "components/RelativeDate" import { RelativeDate } from "@/components/RelativeDate"
import { tss } from "tss" import { tss } from "@/tss"
import { FeedEntryTitle } from "./FeedEntryTitle" import { FeedEntryTitle } from "./FeedEntryTitle"
export interface FeedEntryHeaderProps { export interface FeedEntryHeaderProps {

View File

@@ -1,6 +1,6 @@
import { Highlight } from "@mantine/core" import { Highlight } from "@mantine/core"
import { useAppSelector } from "app/store" import { useAppSelector } from "@/app/store"
import type { Entry } from "app/types" import type { Entry } from "@/app/types"
export interface FeedEntryTitleProps { export interface FeedEntryTitleProps {
entry: Entry entry: Entry

View File

@@ -1,10 +1,10 @@
import { Trans } from "@lingui/react/macro" import { Trans } from "@lingui/react/macro"
import { ActionIcon, Anchor, Tooltip } from "@mantine/core" import { ActionIcon, Anchor, Tooltip } from "@mantine/core"
import { Constants } from "app/constants"
import { markEntry } from "app/entries/thunks"
import { useAppDispatch } from "app/store"
import type { Entry } from "app/types"
import { TbExternalLink } from "react-icons/tb" import { TbExternalLink } from "react-icons/tb"
import { Constants } from "@/app/constants"
import { markEntry } from "@/app/entries/thunks"
import { useAppDispatch } from "@/app/store"
import type { Entry } from "@/app/types"
export function OpenExternalLink(props: { entry: Entry }) { export function OpenExternalLink(props: { entry: Entry }) {
const dispatch = useAppDispatch() const dispatch = useAppDispatch()

View File

@@ -1,10 +1,10 @@
import { Trans } from "@lingui/react/macro" import { Trans } from "@lingui/react/macro"
import { ActionIcon, Tooltip } from "@mantine/core" import { ActionIcon, Tooltip } from "@mantine/core"
import { Constants } from "app/constants"
import { starEntry } from "app/entries/thunks"
import { useAppDispatch } from "app/store"
import type { Entry } from "app/types"
import { TbStar, TbStarFilled } from "react-icons/tb" import { TbStar, TbStarFilled } from "react-icons/tb"
import { Constants } from "@/app/constants"
import { starEntry } from "@/app/entries/thunks"
import { useAppDispatch } from "@/app/store"
import type { Entry } from "@/app/types"
export function Star(props: { entry: Entry }) { export function Star(props: { entry: Entry }) {
const dispatch = useAppDispatch() const dispatch = useAppDispatch()

View File

@@ -2,14 +2,6 @@ import { msg } from "@lingui/core/macro"
import { useLingui } from "@lingui/react" import { useLingui } from "@lingui/react"
import { Box, Center, CloseButton, Divider, Group, Indicator, Popover, TextInput } from "@mantine/core" import { Box, Center, CloseButton, Divider, Group, Indicator, Popover, TextInput } from "@mantine/core"
import { useForm } from "@mantine/form" import { useForm } from "@mantine/form"
import { markAllAsReadWithConfirmationIfRequired, reloadEntries, search, selectNextEntry, selectPreviousEntry } from "app/entries/thunks"
import { useAppDispatch, useAppSelector } from "app/store"
import { changeReadingMode, changeReadingOrder } from "app/user/thunks"
import { ActionButton } from "components/ActionButton"
import { Loader } from "components/Loader"
import { useActionButton } from "hooks/useActionButton"
import { useBrowserExtension } from "hooks/useBrowserExtension"
import { useMobile } from "hooks/useMobile"
import { useEffect } from "react" import { useEffect } from "react"
import { import {
TbArrowDown, TbArrowDown,
@@ -25,6 +17,14 @@ import {
TbSortDescending, TbSortDescending,
TbUser, TbUser,
} from "react-icons/tb" } from "react-icons/tb"
import { markAllAsReadWithConfirmationIfRequired, reloadEntries, search, selectNextEntry, selectPreviousEntry } from "@/app/entries/thunks"
import { useAppDispatch, useAppSelector } from "@/app/store"
import { changeReadingMode, changeReadingOrder } from "@/app/user/thunks"
import { ActionButton } from "@/components/ActionButton"
import { Loader } from "@/components/Loader"
import { useActionButton } from "@/hooks/useActionButton"
import { useBrowserExtension } from "@/hooks/useBrowserExtension"
import { useMobile } from "@/hooks/useMobile"
import { ProfileMenu } from "./ProfileMenu" import { ProfileMenu } from "./ProfileMenu"
function HeaderDivider() { function HeaderDivider() {

View File

@@ -11,14 +11,7 @@ import {
useMantineColorScheme, useMantineColorScheme,
} from "@mantine/core" } from "@mantine/core"
import { showNotification } from "@mantine/notifications" import { showNotification } from "@mantine/notifications"
import { client } from "app/client"
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"
import { reloadProfile } from "app/user/thunks"
import dayjs from "dayjs" import dayjs from "dayjs"
import { useNow } from "hooks/useNow"
import { type ReactNode, useState } from "react" import { type ReactNode, useState } from "react"
import { import {
TbChartLine, TbChartLine,
@@ -36,6 +29,13 @@ import {
TbUsers, TbUsers,
TbWorldDownload, TbWorldDownload,
} from "react-icons/tb" } from "react-icons/tb"
import { client } from "@/app/client"
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"
import { reloadProfile } from "@/app/user/thunks"
import { useNow } from "@/hooks/useNow"
interface ProfileMenuProps { interface ProfileMenuProps {
control: React.ReactElement control: React.ReactElement

View File

@@ -1,5 +1,5 @@
import { NumberFormatter } from "@mantine/core" import { NumberFormatter } from "@mantine/core"
import type { MetricGauge } from "app/types" import type { MetricGauge } from "@/app/types"
interface MeterProps { interface MeterProps {
gauge: MetricGauge gauge: MetricGauge

View File

@@ -1,5 +1,5 @@
import { Box } from "@mantine/core" import { Box } from "@mantine/core"
import type { MetricMeter } from "app/types" import type { MetricMeter } from "@/app/types"
interface MeterProps { interface MeterProps {
meter: MetricMeter meter: MetricMeter

View File

@@ -1,5 +1,5 @@
import { Box } from "@mantine/core" import { Box } from "@mantine/core"
import type { MetricTimer } from "app/types" import type { MetricTimer } from "@/app/types"
interface MetricTimerProps { interface MetricTimerProps {
timer: MetricTimer timer: MetricTimer

View File

@@ -1,6 +1,6 @@
import { Box } from "@mantine/core" import { Box } from "@mantine/core"
import { useMobile } from "hooks/useMobile"
import type React from "react" import type React from "react"
import { useMobile } from "@/hooks/useMobile"
export function OnDesktop(props: { children: React.ReactNode }) { export function OnDesktop(props: { children: React.ReactNode }) {
const mobile = useMobile() const mobile = useMobile()

View File

@@ -1,6 +1,6 @@
import { Box } from "@mantine/core" import { Box } from "@mantine/core"
import { useMobile } from "hooks/useMobile"
import type React from "react" import type React from "react"
import { useMobile } from "@/hooks/useMobile"
export function OnMobile(props: { children: React.ReactNode }) { export function OnMobile(props: { children: React.ReactNode }) {
const mobile = useMobile() const mobile = useMobile()

View File

@@ -1,15 +1,15 @@
import { Trans } from "@lingui/react/macro" import { Trans } from "@lingui/react/macro"
import { Anchor, Box, Button, Group, Stack } from "@mantine/core" import { Anchor, Box, Button, Group, Stack } from "@mantine/core"
import { useForm } from "@mantine/form" import { useForm } from "@mantine/form"
import { client, errorToStrings } from "app/client"
import { Constants } from "app/constants"
import { redirectToSelectedSource } from "app/redirect/thunks"
import { useAppDispatch, useAppSelector } from "app/store"
import { Alert } from "components/Alert"
import { CodeEditor } from "components/code/CodeEditor"
import { useEffect } from "react" import { useEffect } from "react"
import { useAsyncCallback } from "react-async-hook" import { useAsyncCallback } from "react-async-hook"
import { TbDeviceFloppy } from "react-icons/tb" import { TbDeviceFloppy } from "react-icons/tb"
import { client, errorToStrings } from "@/app/client"
import { Constants } from "@/app/constants"
import { redirectToSelectedSource } from "@/app/redirect/thunks"
import { useAppDispatch, useAppSelector } from "@/app/store"
import { Alert } from "@/components/Alert"
import { CodeEditor } from "@/components/code/CodeEditor"
interface FormData { interface FormData {
customCss: string customCss: string

View File

@@ -3,9 +3,10 @@ import { useLingui } from "@lingui/react"
import { Trans } from "@lingui/react/macro" import { Trans } from "@lingui/react/macro"
import { Box, Divider, Group, NumberInput, Radio, Select, type SelectProps, SimpleGrid, Stack, Switch } from "@mantine/core" import { Box, Divider, Group, NumberInput, Radio, Select, type SelectProps, SimpleGrid, Stack, Switch } from "@mantine/core"
import type { ComboboxData } from "@mantine/core/lib/components/Combobox/Combobox.types" import type { ComboboxData } from "@mantine/core/lib/components/Combobox/Combobox.types"
import { Constants } from "app/constants" import type { ReactNode } from "react"
import { useAppDispatch, useAppSelector } from "app/store" import { Constants } from "@/app/constants"
import type { IconDisplayMode, ScrollMode, SharingSettings } from "app/types" import { useAppDispatch, useAppSelector } from "@/app/store"
import type { IconDisplayMode, ScrollMode, SharingSettings } from "@/app/types"
import { import {
changeCustomContextMenu, changeCustomContextMenu,
changeEntriesToKeepOnTopWhenScrolling, changeEntriesToKeepOnTopWhenScrolling,
@@ -23,9 +24,8 @@ import {
changeStarIconDisplayMode, changeStarIconDisplayMode,
changeUnreadCountFavicon, changeUnreadCountFavicon,
changeUnreadCountTitle, changeUnreadCountTitle,
} from "app/user/thunks" } from "@/app/user/thunks"
import { locales } from "i18n" import { locales } from "@/i18n"
import type { ReactNode } from "react"
export function DisplaySettings() { export function DisplaySettings() {
const language = useAppSelector(state => state.user.settings?.language) const language = useAppSelector(state => state.user.settings?.language)

View File

@@ -4,15 +4,15 @@ 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 { useForm } from "@mantine/form"
import { openConfirmModal } from "@mantine/modals" import { openConfirmModal } from "@mantine/modals"
import { client, errorToStrings } from "app/client"
import { redirectToLogin, redirectToSelectedSource } from "app/redirect/thunks"
import { useAppDispatch, useAppSelector } from "app/store"
import type { ProfileModificationRequest } from "app/types"
import { reloadProfile } from "app/user/thunks"
import { Alert } from "components/Alert"
import { useEffect } from "react" import { useEffect } from "react"
import { useAsyncCallback } from "react-async-hook" import { useAsyncCallback } from "react-async-hook"
import { TbDeviceFloppy, TbTrash } from "react-icons/tb" import { TbDeviceFloppy, TbTrash } from "react-icons/tb"
import { client, errorToStrings } from "@/app/client"
import { redirectToLogin, redirectToSelectedSource } from "@/app/redirect/thunks"
import { useAppDispatch, useAppSelector } from "@/app/store"
import type { ProfileModificationRequest } from "@/app/types"
import { reloadProfile } from "@/app/user/thunks"
import { Alert } from "@/components/Alert"
interface FormData extends ProfileModificationRequest { interface FormData extends ProfileModificationRequest {
newPasswordConfirmation?: string newPasswordConfirmation?: string

View File

@@ -1,6 +1,8 @@
import { Trans } from "@lingui/react/macro" import { Trans } from "@lingui/react/macro"
import { Box, Stack } from "@mantine/core" import { Box, Stack } from "@mantine/core"
import { Constants } from "app/constants" import React from "react"
import { TbChevronDown, TbChevronRight, TbInbox, TbStar, TbTag } from "react-icons/tb"
import { Constants } from "@/app/constants"
import { import {
redirectToCategory, redirectToCategory,
redirectToCategoryDetails, redirectToCategoryDetails,
@@ -8,16 +10,14 @@ import {
redirectToFeedDetails, redirectToFeedDetails,
redirectToTag, redirectToTag,
redirectToTagDetails, redirectToTagDetails,
} from "app/redirect/thunks" } from "@/app/redirect/thunks"
import { useAppDispatch, useAppSelector } from "app/store" import { useAppDispatch, useAppSelector } from "@/app/store"
import type { TreeSubscription } from "app/tree/slice" import type { TreeSubscription } from "@/app/tree/slice"
import { collapseTreeCategory } from "app/tree/thunks" import { collapseTreeCategory } from "@/app/tree/thunks"
import type { Category, Subscription } from "app/types" import type { Category, Subscription } from "@/app/types"
import { categoryHasNewEntries, categoryUnreadCount, flattenCategoryTree } from "app/utils" import { categoryHasNewEntries, categoryUnreadCount, flattenCategoryTree } from "@/app/utils"
import { Loader } from "components/Loader" import { Loader } from "@/components/Loader"
import { OnDesktop } from "components/responsive/OnDesktop" import { OnDesktop } from "@/components/responsive/OnDesktop"
import React from "react"
import { TbChevronDown, TbChevronRight, TbInbox, TbStar, TbTag } from "react-icons/tb"
import { TreeNode } from "./TreeNode" import { TreeNode } from "./TreeNode"
import { TreeSearch } from "./TreeSearch" import { TreeSearch } from "./TreeSearch"

View File

@@ -1,8 +1,8 @@
import { Box, Center } from "@mantine/core" import { Box, Center } from "@mantine/core"
import type { EntrySourceType } from "app/entries/slice"
import { FeedFavicon } from "components/content/FeedFavicon"
import type React from "react" import type React from "react"
import { tss } from "tss" import type { EntrySourceType } from "@/app/entries/slice"
import { FeedFavicon } from "@/components/content/FeedFavicon"
import { tss } from "@/tss"
import { UnreadCount } from "./UnreadCount" import { UnreadCount } from "./UnreadCount"
interface TreeNodeProps { interface TreeNodeProps {

View File

@@ -3,12 +3,12 @@ import { useLingui } from "@lingui/react"
import { Trans } from "@lingui/react/macro" import { Trans } from "@lingui/react/macro"
import { Box, TextInput } from "@mantine/core" import { Box, TextInput } from "@mantine/core"
import { Spotlight, type SpotlightActionData, spotlight } from "@mantine/spotlight" import { Spotlight, type SpotlightActionData, spotlight } from "@mantine/spotlight"
import { redirectToFeed } from "app/redirect/thunks"
import { useAppDispatch } from "app/store"
import type { Subscription } from "app/types"
import { FeedFavicon } from "components/content/FeedFavicon"
import { useMousetrap } from "hooks/useMousetrap"
import { TbSearch } from "react-icons/tb" import { TbSearch } from "react-icons/tb"
import { redirectToFeed } from "@/app/redirect/thunks"
import { useAppDispatch } from "@/app/store"
import type { Subscription } from "@/app/types"
import { FeedFavicon } from "@/components/content/FeedFavicon"
import { useMousetrap } from "@/hooks/useMousetrap"
export interface TreeSearchProps { export interface TreeSearchProps {
feeds: Subscription[] feeds: Subscription[]

View File

@@ -1,6 +1,6 @@
import { Badge, Indicator, Tooltip } from "@mantine/core" import { Badge, Indicator, Tooltip } from "@mantine/core"
import { Constants } from "app/constants" import { Constants } from "@/app/constants"
import { tss } from "tss" import { tss } from "@/tss"
const useStyles = tss.create(() => ({ const useStyles = tss.create(() => ({
badge: { badge: {

View File

@@ -1,5 +1,5 @@
import { useMantineTheme } from "@mantine/core" import { useMantineTheme } from "@mantine/core"
import { useMobile } from "hooks/useMobile" import { useMobile } from "@/hooks/useMobile"
export const useActionButton = () => { export const useActionButton = () => {
const theme = useMantineTheme() const theme = useMantineTheme()

View File

@@ -1,6 +1,6 @@
import { msg } from "@lingui/core/macro" import { msg } from "@lingui/core/macro"
import { useLingui } from "@lingui/react" import { useLingui } from "@lingui/react"
import { useAppSelector } from "app/store" import { useAppSelector } from "@/app/store"
interface Step { interface Step {
label: string label: string

View File

@@ -1,5 +1,5 @@
import { useMediaQuery } from "@mantine/hooks" import { useMediaQuery } from "@mantine/hooks"
import { Constants } from "app/constants" import { Constants } from "@/app/constants"
export const useMobile = (breakpoint: string | number = Constants.layout.mobileBreakpoint) => { export const useMobile = (breakpoint: string | number = Constants.layout.mobileBreakpoint) => {
const bp = typeof breakpoint === "number" ? `${breakpoint}px` : breakpoint const bp = typeof breakpoint === "number" ? `${breakpoint}px` : breakpoint

View File

@@ -1,8 +1,8 @@
import { setWebSocketConnected } from "app/server/slice"
import { type AppDispatch, useAppDispatch, useAppSelector } from "app/store"
import { newFeedEntriesDiscovered } from "app/tree/thunks"
import { useEffect } from "react" import { useEffect } from "react"
import WebsocketHeartbeatJs from "websocket-heartbeat-js" import WebsocketHeartbeatJs from "websocket-heartbeat-js"
import { setWebSocketConnected } from "@/app/server/slice"
import { type AppDispatch, useAppDispatch, useAppSelector } from "@/app/store"
import { newFeedEntriesDiscovered } from "@/app/tree/thunks"
const handleMessage = (dispatch: AppDispatch, message: string) => { const handleMessage = (dispatch: AppDispatch, message: string) => {
const parts = message.split(":") const parts = message.split(":")

View File

@@ -1,7 +1,7 @@
import { i18n, type Messages } from "@lingui/core" import { i18n, type Messages } from "@lingui/core"
import { useAppSelector } from "app/store"
import dayjs from "dayjs" import dayjs from "dayjs"
import { useEffect } from "react" import { useEffect } from "react"
import { useAppSelector } from "@/app/store"
interface Locale { interface Locale {
key: string key: string
@@ -12,34 +12,146 @@ interface Locale {
// add an object to the array to add a new locale // add an object to the array to add a new locale
// don't forget to also add it to the 'locales' array in lingui.config.ts // don't forget to also add it to the 'locales' array in lingui.config.ts
export const locales: Locale[] = [ export const locales: Locale[] = [
{ key: "ar", label: "العربية", dayjsImportFn: async () => await import("dayjs/locale/ar") }, {
{ key: "ca", label: "Català", dayjsImportFn: async () => await import("dayjs/locale/ca") }, key: "ar",
{ key: "cs", label: "Čeština", dayjsImportFn: async () => await import("dayjs/locale/cs") }, label: "العربية",
{ key: "cy", label: "Cymraeg", dayjsImportFn: async () => await import("dayjs/locale/cy") }, dayjsImportFn: async () => await import("dayjs/locale/ar"),
{ key: "da", label: "Danish", dayjsImportFn: async () => await import("dayjs/locale/da") }, },
{ key: "de", label: "Deutsch", dayjsImportFn: async () => await import("dayjs/locale/de") }, {
{ key: "en", label: "English", dayjsImportFn: async () => await import("dayjs/locale/en") }, key: "ca",
{ key: "es", label: "Español", dayjsImportFn: async () => await import("dayjs/locale/es") }, label: "Català",
{ key: "fa", label: "فارسی", dayjsImportFn: async () => await import("dayjs/locale/fa") }, dayjsImportFn: async () => await import("dayjs/locale/ca"),
{ key: "fi", label: "Suomi", dayjsImportFn: async () => await import("dayjs/locale/fi") }, },
{ key: "fr", label: "Français", dayjsImportFn: async () => await import("dayjs/locale/fr") }, {
{ key: "gl", label: "Galician", dayjsImportFn: async () => await import("dayjs/locale/gl") }, key: "cs",
{ key: "hu", label: "Magyar", dayjsImportFn: async () => await import("dayjs/locale/hu") }, label: "Čeština",
{ key: "id", label: "Indonesian", dayjsImportFn: async () => await import("dayjs/locale/id") }, dayjsImportFn: async () => await import("dayjs/locale/cs"),
{ key: "it", label: "Italiano", dayjsImportFn: async () => await import("dayjs/locale/it") }, },
{ key: "ja", label: "日本語", dayjsImportFn: async () => await import("dayjs/locale/ja") }, {
{ key: "ko", label: "한국어", dayjsImportFn: async () => await import("dayjs/locale/ko") }, key: "cy",
{ key: "ms", label: "Bahasa Malaysian", dayjsImportFn: async () => await import("dayjs/locale/ms") }, label: "Cymraeg",
{ key: "nb", label: "Norsk (bokmål)", dayjsImportFn: async () => await import("dayjs/locale/nb") }, dayjsImportFn: async () => await import("dayjs/locale/cy"),
{ key: "nl", label: "Nederlands", dayjsImportFn: async () => await import("dayjs/locale/nl") }, },
{ key: "nn", label: "Norsk (nynorsk)", dayjsImportFn: async () => await import("dayjs/locale/nn") }, {
{ key: "pl", label: "Polski", dayjsImportFn: async () => await import("dayjs/locale/pl") }, key: "da",
{ key: "pt", label: "Português", dayjsImportFn: async () => await import("dayjs/locale/pt") }, label: "Danish",
{ key: "ru", label: "Русский", dayjsImportFn: async () => await import("dayjs/locale/ru") }, dayjsImportFn: async () => await import("dayjs/locale/da"),
{ key: "sk", label: "Slovenčina", dayjsImportFn: async () => await import("dayjs/locale/sk") }, },
{ key: "sv", label: "Svenska", dayjsImportFn: async () => await import("dayjs/locale/sv") }, {
{ key: "tr", label: "Türkçe", dayjsImportFn: async () => await import("dayjs/locale/tr") }, key: "de",
{ key: "zh", label: "简体中文", dayjsImportFn: async () => await import("dayjs/locale/zh") }, label: "Deutsch",
dayjsImportFn: async () => await import("dayjs/locale/de"),
},
{
key: "en",
label: "English",
dayjsImportFn: async () => await import("dayjs/locale/en"),
},
{
key: "es",
label: "Español",
dayjsImportFn: async () => await import("dayjs/locale/es"),
},
{
key: "fa",
label: "فارسی",
dayjsImportFn: async () => await import("dayjs/locale/fa"),
},
{
key: "fi",
label: "Suomi",
dayjsImportFn: async () => await import("dayjs/locale/fi"),
},
{
key: "fr",
label: "Français",
dayjsImportFn: async () => await import("dayjs/locale/fr"),
},
{
key: "gl",
label: "Galician",
dayjsImportFn: async () => await import("dayjs/locale/gl"),
},
{
key: "hu",
label: "Magyar",
dayjsImportFn: async () => await import("dayjs/locale/hu"),
},
{
key: "id",
label: "Indonesian",
dayjsImportFn: async () => await import("dayjs/locale/id"),
},
{
key: "it",
label: "Italiano",
dayjsImportFn: async () => await import("dayjs/locale/it"),
},
{
key: "ja",
label: "日本語",
dayjsImportFn: async () => await import("dayjs/locale/ja"),
},
{
key: "ko",
label: "한국어",
dayjsImportFn: async () => await import("dayjs/locale/ko"),
},
{
key: "ms",
label: "Bahasa Malaysian",
dayjsImportFn: async () => await import("dayjs/locale/ms"),
},
{
key: "nb",
label: "Norsk (bokmål)",
dayjsImportFn: async () => await import("dayjs/locale/nb"),
},
{
key: "nl",
label: "Nederlands",
dayjsImportFn: async () => await import("dayjs/locale/nl"),
},
{
key: "nn",
label: "Norsk (nynorsk)",
dayjsImportFn: async () => await import("dayjs/locale/nn"),
},
{
key: "pl",
label: "Polski",
dayjsImportFn: async () => await import("dayjs/locale/pl"),
},
{
key: "pt",
label: "Português",
dayjsImportFn: async () => await import("dayjs/locale/pt"),
},
{
key: "ru",
label: "Русский",
dayjsImportFn: async () => await import("dayjs/locale/ru"),
},
{
key: "sk",
label: "Slovenčina",
dayjsImportFn: async () => await import("dayjs/locale/sk"),
},
{
key: "sv",
label: "Svenska",
dayjsImportFn: async () => await import("dayjs/locale/sv"),
},
{
key: "tr",
label: "Türkçe",
dayjsImportFn: async () => await import("dayjs/locale/tr"),
},
{
key: "zh",
label: "简体中文",
dayjsImportFn: async () => await import("dayjs/locale/zh"),
},
] ]
function activateLocale(locale: string) { function activateLocale(locale: string) {

View File

@@ -3,13 +3,13 @@ import "@mantine/core/styles.css"
import "@mantine/notifications/styles.css" import "@mantine/notifications/styles.css"
import "@mantine/spotlight/styles.css" import "@mantine/spotlight/styles.css"
import "react-contexify/ReactContexify.css" import "react-contexify/ReactContexify.css"
import { App } from "App"
import { store } from "app/store"
import dayjs from "dayjs" import dayjs from "dayjs"
import duration from "dayjs/plugin/duration" import duration from "dayjs/plugin/duration"
import relativeTime from "dayjs/plugin/relativeTime" import relativeTime from "dayjs/plugin/relativeTime"
import ReactDOM from "react-dom/client" import ReactDOM from "react-dom/client"
import { Provider } from "react-redux" import { Provider } from "react-redux"
import { App } from "@/App"
import { store } from "@/app/store"
dayjs.extend(relativeTime) dayjs.extend(relativeTime)
dayjs.extend(duration) dayjs.extend(duration)

View File

@@ -1,7 +1,7 @@
import { Trans } from "@lingui/react/macro" import { Trans } from "@lingui/react/macro"
import { Box, Button, Container, Group, Text, Title } from "@mantine/core" import { Box, Button, Container, Group, Text, Title } from "@mantine/core"
import { TbRefresh } from "react-icons/tb" import { TbRefresh } from "react-icons/tb"
import { tss } from "tss" import { tss } from "@/tss"
import { PageTitle } from "./PageTitle" import { PageTitle } from "./PageTitle"
const useStyles = tss.create(({ theme }) => ({ const useStyles = tss.create(({ theme }) => ({

View File

@@ -1,5 +1,5 @@
import { Center, Container, RingProgress, Text, useMantineTheme } from "@mantine/core" import { Center, Container, RingProgress, Text, useMantineTheme } from "@mantine/core"
import { useAppLoading } from "hooks/useAppLoading" import { useAppLoading } from "@/hooks/useAppLoading"
import { PageTitle } from "./PageTitle" import { PageTitle } from "./PageTitle"
export function LoadingPage() { export function LoadingPage() {

View File

@@ -1,5 +1,5 @@
import { Center, Title } from "@mantine/core" import { Center, Title } from "@mantine/core"
import { Logo } from "components/Logo" import { Logo } from "@/components/Logo"
export function PageTitle() { export function PageTitle() {
return ( return (

View File

@@ -1,16 +1,16 @@
import { msg } from "@lingui/core/macro" import { msg } from "@lingui/core/macro"
import { Anchor, Box, Center, Container, Divider, Group, Image, Space, Title, useMantineColorScheme } from "@mantine/core" import { Anchor, Box, Center, Container, Divider, Group, Image, Space, Title, useMantineColorScheme } from "@mantine/core"
import { client } from "app/client"
import { redirectToApiDocumentation, redirectToLogin, redirectToRegistration, redirectToRootCategory } from "app/redirect/thunks"
import { useAppDispatch, useAppSelector } from "app/store"
import welcomePageDark from "assets/welcome_page_dark.png"
import welcomePageLight from "assets/welcome_page_light.png"
import { ActionButton } from "components/ActionButton"
import { useBrowserExtension } from "hooks/useBrowserExtension"
import { useMobile } from "hooks/useMobile"
import { useAsyncCallback } from "react-async-hook" import { useAsyncCallback } from "react-async-hook"
import { SiGithub, SiX } from "react-icons/si" import { SiGithub, SiX } from "react-icons/si"
import { TbClock, TbKey, TbMoon, TbSettings, TbSun, TbUserPlus } from "react-icons/tb" import { TbClock, TbKey, TbMoon, TbSettings, TbSun, TbUserPlus } from "react-icons/tb"
import { client } from "@/app/client"
import { redirectToApiDocumentation, redirectToLogin, redirectToRegistration, redirectToRootCategory } from "@/app/redirect/thunks"
import { useAppDispatch, useAppSelector } from "@/app/store"
import welcomePageDark from "@/assets/welcome_page_dark.png"
import welcomePageLight from "@/assets/welcome_page_light.png"
import { ActionButton } from "@/components/ActionButton"
import { useBrowserExtension } from "@/hooks/useBrowserExtension"
import { useMobile } from "@/hooks/useMobile"
import { PageTitle } from "./PageTitle" import { PageTitle } from "./PageTitle"
const iconSize = 18 const iconSize = 18

View File

@@ -1,15 +1,15 @@
import { Trans } from "@lingui/react/macro" import { Trans } from "@lingui/react/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 type { UserModel } from "app/types"
import { Alert } from "components/Alert"
import { UserEdit } from "components/admin/UserEdit"
import { Loader } from "components/Loader"
import { RelativeDate } from "components/RelativeDate"
import type { ReactNode } from "react" import type { 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"
import { client, errorToStrings } from "@/app/client"
import type { UserModel } from "@/app/types"
import { Alert } from "@/components/Alert"
import { UserEdit } from "@/components/admin/UserEdit"
import { Loader } from "@/components/Loader"
import { RelativeDate } from "@/components/RelativeDate"
function BooleanIcon({ value }: { value: boolean }) { function BooleanIcon({ value }: { value: boolean }) {
return value ? <TbCheck size={18} /> : <TbX size={18} /> return value ? <TbCheck size={18} /> : <TbX size={18} />

View File

@@ -1,11 +1,11 @@
import { Accordion, Box } from "@mantine/core" import { Accordion, Box } from "@mantine/core"
import { client } from "app/client"
import { Loader } from "components/Loader"
import { Gauge } from "components/metrics/Gauge"
import { Meter } from "components/metrics/Meter"
import { MetricAccordionItem } from "components/metrics/MetricAccordionItem"
import { useEffect } from "react" import { useEffect } from "react"
import { useAsync } from "react-async-hook" import { useAsync } from "react-async-hook"
import { client } from "@/app/client"
import { Loader } from "@/components/Loader"
import { Gauge } from "@/components/metrics/Gauge"
import { Meter } from "@/components/metrics/Meter"
import { MetricAccordionItem } from "@/components/metrics/MetricAccordionItem"
const shownMeters: Record<string, string> = { const shownMeters: Record<string, string> = {
"com.commafeed.backend.feed.FeedRefreshEngine.refill": "Feed queue refill rate", "com.commafeed.backend.feed.FeedRefreshEngine.refill": "Feed queue refill rate",

View File

@@ -2,16 +2,16 @@ import { msg } from "@lingui/core/macro"
import { useLingui } from "@lingui/react" import { useLingui } from "@lingui/react"
import { Trans } from "@lingui/react/macro" import { Trans } from "@lingui/react/macro"
import { Anchor, Box, Container, List, NativeSelect, SimpleGrid, Title } from "@mantine/core" import { Anchor, Box, Container, List, NativeSelect, SimpleGrid, Title } from "@mantine/core"
import { Constants } from "app/constants"
import { redirectToApiDocumentation } from "app/redirect/thunks"
import { useAppDispatch, useAppSelector } from "app/store"
import { CategorySelect } from "components/content/add/CategorySelect"
import { KeyboardShortcutsHelp } from "components/KeyboardShortcutsHelp"
import { useBrowserExtension } from "hooks/useBrowserExtension"
import type React from "react" import type React from "react"
import { useState } from "react" import { useState } from "react"
import { TbHelp, TbKeyboard, TbPuzzle, TbRocket } from "react-icons/tb" import { TbHelp, TbKeyboard, TbPuzzle, TbRocket } from "react-icons/tb"
import { tss } from "tss" import { Constants } from "@/app/constants"
import { redirectToApiDocumentation } from "@/app/redirect/thunks"
import { useAppDispatch, useAppSelector } from "@/app/store"
import { CategorySelect } from "@/components/content/add/CategorySelect"
import { KeyboardShortcutsHelp } from "@/components/KeyboardShortcutsHelp"
import { useBrowserExtension } from "@/hooks/useBrowserExtension"
import { tss } from "@/tss"
const useStyles = tss.create(() => ({ const useStyles = tss.create(() => ({
sectionTitle: { sectionTitle: {

View File

@@ -1,9 +1,9 @@
import { Trans } from "@lingui/react/macro" import { Trans } from "@lingui/react/macro"
import { Container, Tabs } from "@mantine/core" import { Container, Tabs } from "@mantine/core"
import { AddCategory } from "components/content/add/AddCategory"
import { ImportOpml } from "components/content/add/ImportOpml"
import { Subscribe } from "components/content/add/Subscribe"
import { TbFileImport, TbFolderPlus, TbRss } from "react-icons/tb" import { TbFileImport, TbFolderPlus, TbRss } from "react-icons/tb"
import { AddCategory } from "@/components/content/add/AddCategory"
import { ImportOpml } from "@/components/content/add/ImportOpml"
import { Subscribe } from "@/components/content/add/Subscribe"
export function AddPage() { export function AddPage() {
return ( return (

View File

@@ -4,20 +4,20 @@ import { Trans } from "@lingui/react/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"
import { client, errorToStrings } from "app/client"
import { Constants } from "app/constants"
import { redirectToRootCategory, redirectToSelectedSource } from "app/redirect/thunks"
import { useAppDispatch, useAppSelector } from "app/store"
import { reloadTree } from "app/tree/thunks"
import type { Category, CategoryModificationRequest } from "app/types"
import { flattenCategoryTree } from "app/utils"
import { Alert } from "components/Alert"
import { CategorySelect } from "components/content/add/CategorySelect"
import { Loader } from "components/Loader"
import { useEffect } from "react" import { useEffect } from "react"
import { useAsync, useAsyncCallback } from "react-async-hook" import { useAsync, useAsyncCallback } from "react-async-hook"
import { TbDeviceFloppy, TbTrash } from "react-icons/tb" import { TbDeviceFloppy, TbTrash } from "react-icons/tb"
import { useParams } from "react-router-dom" import { useParams } from "react-router-dom"
import { client, errorToStrings } from "@/app/client"
import { Constants } from "@/app/constants"
import { redirectToRootCategory, redirectToSelectedSource } from "@/app/redirect/thunks"
import { useAppDispatch, useAppSelector } from "@/app/store"
import { reloadTree } from "@/app/tree/thunks"
import type { Category, CategoryModificationRequest } from "@/app/types"
import { flattenCategoryTree } from "@/app/utils"
import { Alert } from "@/components/Alert"
import { CategorySelect } from "@/components/content/add/CategorySelect"
import { Loader } from "@/components/Loader"
export function CategoryDetailsPage() { export function CategoryDetailsPage() {
const { id = Constants.categories.all.id } = useParams() const { id = Constants.categories.all.id } = useParams()

View File

@@ -1,7 +1,7 @@
import { Trans } from "@lingui/react/macro" import { Trans } from "@lingui/react/macro"
import { Anchor, Box, Code, Container, Group, List, Title } from "@mantine/core" import { Anchor, Box, Code, Container, Group, List, Title } from "@mantine/core"
import { Constants } from "app/constants"
import { TbBrandGithub, TbBrandPaypal, TbCoinBitcoin, TbHeartFilled } from "react-icons/tb" import { TbBrandGithub, TbBrandPaypal, TbCoinBitcoin, TbHeartFilled } from "react-icons/tb"
import { Constants } from "@/app/constants"
const iconSize = 24 const iconSize = 24

View File

@@ -2,19 +2,19 @@ import { Trans } from "@lingui/react/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"
import { client, errorToStrings } from "app/client"
import { redirectToRootCategory, redirectToSelectedSource } from "app/redirect/thunks"
import { useAppDispatch, useAppSelector } from "app/store"
import { reloadTree } from "app/tree/thunks"
import type { FeedModificationRequest } from "app/types"
import { Alert } from "components/Alert"
import { CategorySelect } from "components/content/add/CategorySelect"
import { Loader } from "components/Loader"
import { RelativeDate } from "components/RelativeDate"
import { useEffect } from "react" import { useEffect } from "react"
import { useAsync, useAsyncCallback } from "react-async-hook" import { useAsync, useAsyncCallback } from "react-async-hook"
import { TbDeviceFloppy, TbTrash } from "react-icons/tb" import { TbDeviceFloppy, TbTrash } from "react-icons/tb"
import { useParams } from "react-router-dom" import { useParams } from "react-router-dom"
import { client, errorToStrings } from "@/app/client"
import { redirectToRootCategory, redirectToSelectedSource } from "@/app/redirect/thunks"
import { useAppDispatch, useAppSelector } from "@/app/store"
import { reloadTree } from "@/app/tree/thunks"
import type { FeedModificationRequest } from "@/app/types"
import { Alert } from "@/components/Alert"
import { CategorySelect } from "@/components/content/add/CategorySelect"
import { Loader } from "@/components/Loader"
import { RelativeDate } from "@/components/RelativeDate"
function FilteringExpressionDescription() { function FilteringExpressionDescription() {
const example = <Code>url.contains('youtube') or (author eq 'athou' and title.contains('github'))</Code> const example = <Code>url.contains('youtube') or (author eq 'athou' and title.contains('github'))</Code>

View File

@@ -1,17 +1,17 @@
import { Trans } from "@lingui/react/macro" import { Trans } from "@lingui/react/macro"
import { ActionIcon, Box, Center, Divider, Group, Title, useMantineTheme } from "@mantine/core" import { ActionIcon, 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 type { EntrySourceType } from "app/entries/slice"
import { loadEntries } from "app/entries/thunks"
import { redirectToCategoryDetails, redirectToFeedDetails, redirectToTagDetails } from "app/redirect/thunks"
import { useAppDispatch, useAppSelector } from "app/store"
import { flattenCategoryTree } from "app/utils"
import { FeedEntries } from "components/content/FeedEntries"
import { useEffect } from "react" import { useEffect } from "react"
import { TbEdit } from "react-icons/tb" import { TbEdit } from "react-icons/tb"
import { useLocation, useParams } from "react-router-dom" import { useLocation, useParams } from "react-router-dom"
import { tss } from "tss" import { Constants } from "@/app/constants"
import type { EntrySourceType } from "@/app/entries/slice"
import { loadEntries } from "@/app/entries/thunks"
import { redirectToCategoryDetails, redirectToFeedDetails, redirectToTagDetails } from "@/app/redirect/thunks"
import { useAppDispatch, useAppSelector } from "@/app/store"
import { flattenCategoryTree } from "@/app/utils"
import { FeedEntries } from "@/components/content/FeedEntries"
import { tss } from "@/tss"
function NoSubscriptionHelp() { function NoSubscriptionHelp() {
return ( return (

View File

@@ -1,30 +1,30 @@
import { msg } from "@lingui/core/macro" import { msg } from "@lingui/core/macro"
import { ActionIcon, AppShell, Box, Center, Group, ScrollArea, Title, useMantineTheme } from "@mantine/core" import { ActionIcon, AppShell, Box, Center, Group, ScrollArea, Title, useMantineTheme } from "@mantine/core"
import { Constants } from "app/constants"
import { redirectToAdd, redirectToRootCategory } from "app/redirect/thunks"
import { useAppDispatch, useAppSelector } from "app/store"
import { setMobileMenuOpen } from "app/tree/slice"
import { reloadTree } from "app/tree/thunks"
import { setSidebarWidth } from "app/user/slice"
import { reloadProfile, reloadSettings, reloadTags } from "app/user/thunks"
import { ActionButton } from "components/ActionButton"
import { AnnouncementDialog } from "components/AnnouncementDialog"
import { Loader } from "components/Loader"
import { Logo } from "components/Logo"
import { MarkAllAsReadConfirmationDialog } from "components/MarkAllAsReadConfirmationDialog"
import { OnDesktop } from "components/responsive/OnDesktop"
import { OnMobile } from "components/responsive/OnMobile"
import { useAppLoading } from "hooks/useAppLoading"
import { useBrowserExtension } from "hooks/useBrowserExtension"
import { useMobile } from "hooks/useMobile"
import { useWebSocket } from "hooks/useWebSocket"
import { LoadingPage } from "pages/LoadingPage"
import { type ReactNode, type RefObject, Suspense, useEffect, useRef } from "react" import { type ReactNode, type RefObject, Suspense, useEffect, useRef } from "react"
import Draggable from "react-draggable" import Draggable from "react-draggable"
import { TbMenu2, TbPlus, TbX } from "react-icons/tb" import { TbMenu2, TbPlus, TbX } from "react-icons/tb"
import { Outlet } from "react-router-dom" import { Outlet } from "react-router-dom"
import { useSwipeable } from "react-swipeable" import { useSwipeable } from "react-swipeable"
import { tss } from "tss" import { Constants } from "@/app/constants"
import { redirectToAdd, redirectToRootCategory } from "@/app/redirect/thunks"
import { useAppDispatch, useAppSelector } from "@/app/store"
import { setMobileMenuOpen } from "@/app/tree/slice"
import { reloadTree } from "@/app/tree/thunks"
import { setSidebarWidth } from "@/app/user/slice"
import { reloadProfile, reloadSettings, reloadTags } from "@/app/user/thunks"
import { ActionButton } from "@/components/ActionButton"
import { AnnouncementDialog } from "@/components/AnnouncementDialog"
import { Loader } from "@/components/Loader"
import { Logo } from "@/components/Logo"
import { MarkAllAsReadConfirmationDialog } from "@/components/MarkAllAsReadConfirmationDialog"
import { OnDesktop } from "@/components/responsive/OnDesktop"
import { OnMobile } from "@/components/responsive/OnMobile"
import { useAppLoading } from "@/hooks/useAppLoading"
import { useBrowserExtension } from "@/hooks/useBrowserExtension"
import { useMobile } from "@/hooks/useMobile"
import { useWebSocket } from "@/hooks/useWebSocket"
import { LoadingPage } from "@/pages/LoadingPage"
import { tss } from "@/tss"
interface LayoutProps { interface LayoutProps {
sidebar: ReactNode sidebar: ReactNode

View File

@@ -1,9 +1,9 @@
import { Trans } from "@lingui/react/macro" import { Trans } from "@lingui/react/macro"
import { Container, Tabs } from "@mantine/core" import { Container, Tabs } from "@mantine/core"
import { CustomCodeSettings } from "components/settings/CustomCodeSettings"
import { DisplaySettings } from "components/settings/DisplaySettings"
import { ProfileSettings } from "components/settings/ProfileSettings"
import { TbCode, TbPhoto, TbUser } from "react-icons/tb" import { TbCode, TbPhoto, TbUser } from "react-icons/tb"
import { CustomCodeSettings } from "@/components/settings/CustomCodeSettings"
import { DisplaySettings } from "@/components/settings/DisplaySettings"
import { ProfileSettings } from "@/components/settings/ProfileSettings"
export function SettingsPage() { export function SettingsPage() {
return ( return (

View File

@@ -1,10 +1,10 @@
import { Trans } from "@lingui/react/macro" import { Trans } from "@lingui/react/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 { redirectToSelectedSource } from "app/redirect/thunks"
import { useAppDispatch, useAppSelector } from "app/store"
import { useParams } from "react-router-dom" import { useParams } from "react-router-dom"
import { Constants } from "@/app/constants"
import { redirectToSelectedSource } from "@/app/redirect/thunks"
import { useAppDispatch, useAppSelector } from "@/app/store"
export function TagDetailsPage() { export function TagDetailsPage() {
const { id = Constants.categories.all.id } = useParams() const { id = Constants.categories.all.id } = useParams()

View File

@@ -3,14 +3,14 @@ import { useLingui } from "@lingui/react"
import { Trans } from "@lingui/react/macro" import { Trans } from "@lingui/react/macro"
import { Anchor, Box, Button, Center, Container, Group, Paper, PasswordInput, Stack, TextInput, Title } from "@mantine/core" import { Anchor, Box, Button, Center, Container, Group, Paper, PasswordInput, Stack, TextInput, Title } from "@mantine/core"
import { useForm } from "@mantine/form" import { useForm } from "@mantine/form"
import { client, errorToStrings } from "app/client"
import { redirectToRootCategory } from "app/redirect/thunks"
import { useAppDispatch, useAppSelector } from "app/store"
import type { LoginRequest } from "app/types"
import { Alert } from "components/Alert"
import { PageTitle } from "pages/PageTitle"
import { useAsyncCallback } from "react-async-hook" import { useAsyncCallback } from "react-async-hook"
import { Link } from "react-router-dom" import { Link } from "react-router-dom"
import { client, errorToStrings } from "@/app/client"
import { redirectToRootCategory } from "@/app/redirect/thunks"
import { useAppDispatch, useAppSelector } from "@/app/store"
import type { LoginRequest } from "@/app/types"
import { Alert } from "@/components/Alert"
import { PageTitle } from "@/pages/PageTitle"
export function LoginPage() { export function LoginPage() {
const serverInfos = useAppSelector(state => state.server.serverInfos) const serverInfos = useAppSelector(state => state.server.serverInfos)

View File

@@ -3,13 +3,13 @@ import { useLingui } from "@lingui/react"
import { Trans } from "@lingui/react/macro" import { Trans } from "@lingui/react/macro"
import { Anchor, Box, Button, Center, Container, Group, Paper, Stack, TextInput, Title } from "@mantine/core" import { Anchor, Box, Button, Center, Container, Group, Paper, Stack, TextInput, Title } from "@mantine/core"
import { useForm } from "@mantine/form" import { useForm } from "@mantine/form"
import { client, errorToStrings } from "app/client"
import type { PasswordResetRequest } from "app/types"
import { Alert } from "components/Alert"
import { PageTitle } from "pages/PageTitle"
import { useState } from "react" import { useState } from "react"
import { useAsyncCallback } from "react-async-hook" import { useAsyncCallback } from "react-async-hook"
import { Link } from "react-router-dom" import { Link } from "react-router-dom"
import { client, errorToStrings } from "@/app/client"
import type { PasswordResetRequest } from "@/app/types"
import { Alert } from "@/components/Alert"
import { PageTitle } from "@/pages/PageTitle"
export function PasswordRecoveryPage() { export function PasswordRecoveryPage() {
const [message, setMessage] = useState("") const [message, setMessage] = useState("")

View File

@@ -3,14 +3,14 @@ import { useLingui } from "@lingui/react"
import { Trans } from "@lingui/react/macro" import { Trans } from "@lingui/react/macro"
import { Anchor, Box, Button, Center, Container, Group, Paper, PasswordInput, Stack, TextInput, Title } from "@mantine/core" import { Anchor, Box, Button, Center, Container, Group, Paper, PasswordInput, Stack, TextInput, Title } from "@mantine/core"
import { useForm } from "@mantine/form" import { useForm } from "@mantine/form"
import { client, errorToStrings } from "app/client"
import { redirectToRootCategory } from "app/redirect/thunks"
import { useAppDispatch, useAppSelector } from "app/store"
import type { RegistrationRequest } from "app/types"
import { Alert } from "components/Alert"
import { PageTitle } from "pages/PageTitle"
import { useAsyncCallback } from "react-async-hook" import { useAsyncCallback } from "react-async-hook"
import { Link } from "react-router-dom" import { Link } from "react-router-dom"
import { client, errorToStrings } from "@/app/client"
import { redirectToRootCategory } from "@/app/redirect/thunks"
import { useAppDispatch, useAppSelector } from "@/app/store"
import type { RegistrationRequest } from "@/app/types"
import { Alert } from "@/components/Alert"
import { PageTitle } from "@/pages/PageTitle"
export function RegistrationPage() { export function RegistrationPage() {
const serverInfos = useAppSelector(state => state.server.serverInfos) const serverInfos = useAppSelector(state => state.server.serverInfos)

View File

@@ -1,6 +1,6 @@
import "@testing-library/jest-dom" import "@testing-library/jest-dom"
import { Constants } from "app/constants"
import { vi } from "vitest" import { vi } from "vitest"
import { Constants } from "@/app/constants"
// reduce delay for faster tests // reduce delay for faster tests
Constants.tooltip.delay = 10 Constants.tooltip.delay = 10

View File

@@ -1,6 +1,6 @@
import { useMantineTheme } from "@mantine/core" import { useMantineTheme } from "@mantine/core"
import { useColorScheme } from "hooks/useColorScheme"
import { createTss } from "tss-react" import { createTss } from "tss-react"
import { useColorScheme } from "@/hooks/useColorScheme"
const useContext = () => { const useContext = () => {
// return anything here that will be accessible in tss.create() // return anything here that will be accessible in tss.create()

View File

@@ -1,6 +1,8 @@
{ {
"compilerOptions": { "compilerOptions": {
"baseUrl": "./src", "paths": {
"@/*": ["./src/*"]
},
"target": "ESNext", "target": "ESNext",
"useDefineForClassFields": true, "useDefineForClassFields": true,
"lib": ["DOM", "DOM.Iterable", "ESNext"], "lib": ["DOM", "DOM.Iterable", "ESNext"],