Skip to content

Commit

Permalink
fix: split entry history api and accurate count
Browse files Browse the repository at this point in the history
Signed-off-by: Innei <i@innei.in>
  • Loading branch information
Innei committed Sep 21, 2024
1 parent cea7f4a commit 8f3acd6
Show file tree
Hide file tree
Showing 8 changed files with 118 additions and 89 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@ import {
HoverCardTrigger,
} from "@radix-ui/react-hover-card"
import clsx from "clsx"
import { LayoutGroup, m } from "framer-motion"
import { memo, useEffect, useState } from "react"
import { m } from "framer-motion"
import { memo } from "react"

import { useWhoami } from "~/atoms/user"
import { Avatar, AvatarFallback, AvatarImage } from "~/components/ui/avatar"
import { Tooltip, TooltipContent, TooltipPortal, TooltipTrigger } from "~/components/ui/tooltip"
import { useAuthQuery } from "~/hooks/common"
import { useAppLayoutGridContainerWidth } from "~/providers/app-grid-layout-container-provider"
import { Queries } from "~/queries"
import { useEntryReadHistory } from "~/store/entry"
import { useUserById } from "~/store/user"
Expand All @@ -22,57 +23,47 @@ export const EntryReadHistory: Component<{ entryId: string }> = ({ entryId }) =>
const me = useWhoami()
const entryHistory = useEntryReadHistory(entryId)

const [isEnabledPolling, setIsEnabledPolling] = useState(false)
useAuthQuery(Queries.entries.entryReadingHistory(entryId), {
const { data } = useAuthQuery(Queries.entries.entryReadingHistory(entryId), {
refetchInterval: 1000 * 60,
enabled: isEnabledPolling,
})
useEffect(() => {
const timer = setTimeout(() => {
setIsEnabledPolling(true)
}, 1000 * 60)
const totalCount = data?.total || 0

return () => {
clearTimeout(timer)
}
}, [entryId])
const appGirdContainerWidth = useAppLayoutGridContainerWidth()

const LIMIT = appGirdContainerWidth > 600 ? 10 : 5

if (!entryHistory) return null
if (!me) return null
if (!entryHistory.userIds) return null

return (
<div className="hidden items-center duration-200 animate-in fade-in @md:flex">
<LayoutGroup>
{entryHistory.userIds
.filter((id) => id !== me?.id)
.slice(0, 10)
{entryHistory.userIds
.filter((id) => id !== me?.id)
.slice(0, LIMIT)

.map((userId, i) => (
<EntryUser userId={userId} i={i} key={userId} />
))}
</LayoutGroup>
.map((userId, i) => (
<EntryUser userId={userId} i={i} key={userId} />
))}

{entryHistory.readCount &&
entryHistory.readCount > 10 &&
entryHistory.userIds &&
entryHistory.userIds.length >= 10 && (
<HoverCard>
<HoverCardTrigger asChild>
<button
type="button"
style={{
right: "80px",
zIndex: 11,
}}
className="no-drag-region relative flex size-7 items-center justify-center rounded-full border border-border bg-muted ring ring-background"
>
<span className="text-[10px] font-medium text-muted-foreground">
+{Math.min(entryHistory.readCount - 10, 99)}
</span>
</button>
</HoverCardTrigger>
{!!totalCount && totalCount > LIMIT && (
<HoverCard open>
<HoverCardTrigger asChild>
<button
type="button"
style={{
right: "80px",
zIndex: 11,
}}
className="no-drag-region relative flex size-7 items-center justify-center rounded-full border border-border bg-muted ring ring-background"
>
<span className="text-[10px] font-medium text-muted-foreground">
+{Math.min(totalCount - LIMIT, 99)}
</span>
</button>
</HoverCardTrigger>

{totalCount > LIMIT && (
<HoverCardPortal>
<HoverCardContent
sideOffset={12}
Expand All @@ -88,15 +79,16 @@ export const EntryReadHistory: Component<{ entryId: string }> = ({ entryId }) =>
<ul>
{entryHistory.userIds
.filter((id) => id !== me?.id)
.slice(10)
.slice(LIMIT)
.map((userId) => (
<EntryUserRow userId={userId} key={userId} />
))}
</ul>
</HoverCardContent>
</HoverCardPortal>
</HoverCard>
)}
)}
</HoverCard>
)}
</div>
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,21 @@ import { useParams } from "react-router-dom"
import { ROUTE_ENTRY_PENDING, views } from "~/constants"
import { useRouteView } from "~/hooks/biz/useRouteParams"
import { EntryContent } from "~/modules/entry-content"
import { AppLayoutGridContainerProvider } from "~/providers/app-grid-layout-container-provider"

export const Component = () => {
const { entryId } = useParams()
const view = useRouteView()
const inWideMode = view ? views[view].wideMode : false
return (
<AnimatePresence>
{!inWideMode && (
<div className="flex min-w-0 flex-1 flex-col">
<EntryContent entryId={entryId === ROUTE_ENTRY_PENDING ? "" : entryId} />
</div>
)}
<AppLayoutGridContainerProvider>
{!inWideMode && (
<div className="flex min-w-0 flex-1 flex-col">
<EntryContent entryId={entryId === ROUTE_ENTRY_PENDING ? "" : entryId} />
</div>
)}
</AppLayoutGridContainerProvider>
</AnimatePresence>
)
}
10 changes: 7 additions & 3 deletions apps/renderer/src/pages/(main)/(layer)/feeds/[feedId]/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@ import { useMemo, useRef } from "react"
import { useResizable } from "react-resizable-layout"
import { Outlet } from "react-router-dom"

import { getUISettings, setUISetting } from "~/atoms/settings/ui"
import { getUISettings, setUISetting, useUISettingKey } from "~/atoms/settings/ui"
import { PanelSplitter } from "~/components/ui/divider"
import { views } from "~/constants"
import { useRouteParams } from "~/hooks/biz/useRouteParams"
import { cn, isSafari } from "~/lib/utils"
import { EntryColumn } from "~/modules/entry-column"
import { AppLayoutGridContainerProvider } from "~/providers/app-grid-layout-container-provider"

export function Component() {
const containerRef = useRef<HTMLDivElement>(null)
Expand All @@ -17,11 +18,12 @@ export function Component() {
const entryColWidth = useMemo(() => getUISettings().entryColWidth, [])
const { view } = useRouteParams()
const inWideMode = view ? views[view].wideMode : false
const feedColumnWidth = useUISettingKey("feedColWidth")
const { position, separatorProps, isDragging, separatorCursor } = useResizable({
axis: "x",
// FIXME: Less than this width causes grid images to overflow on safari
min: isSafari() ? 356 : 300,
max: 450,
max: Math.max((window.innerWidth - feedColumnWidth) / 2, 600),
initial: entryColWidth,
containerRef,
onResizeEnd({ position }) {
Expand All @@ -37,7 +39,9 @@ export function Component() {
width: position,
}}
>
<EntryColumn />
<AppLayoutGridContainerProvider>
<EntryColumn />
</AppLayoutGridContainerProvider>
</div>
{!inWideMode && (
<PanelSplitter {...separatorProps} cursor={separatorCursor} isDragging={isDragging} />
Expand Down
20 changes: 12 additions & 8 deletions apps/renderer/src/pages/(main)/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import { useShortcutsModal } from "~/modules/modal/shortcuts"
import { CmdF } from "~/modules/panel/cmdf"
import { SearchCmdK } from "~/modules/panel/cmdk"
import { CmdNTrigger } from "~/modules/panel/cmdn"
import { AppLayoutGridContainerProvider } from "~/providers/app-grid-layout-container-provider"

const FooterInfo = () => {
const { t } = useTranslation()
Expand Down Expand Up @@ -84,16 +85,19 @@ export function Component() {
>
{!import.meta.env.PROD && <EnvironmentIndicator />}

<FeedResponsiveResizerContainer containerRef={containerRef}>
<FeedColumn>
<CornerPlayer />
<FooterInfo />
<AppLayoutGridContainerProvider>
<FeedResponsiveResizerContainer containerRef={containerRef}>
<FeedColumn>
<CornerPlayer />
<FooterInfo />

{ELECTRON && <AutoUpdater />}
{ELECTRON && <AutoUpdater />}

<NetworkStatusIndicator />
</FeedColumn>
</FeedResponsiveResizerContainer>
</AppLayoutGridContainerProvider>

<NetworkStatusIndicator />
</FeedColumn>
</FeedResponsiveResizerContainer>
<main
ref={setMainContainerElement}
className="flex min-w-0 flex-1 bg-theme-background pt-[calc(var(--fo-window-padding-top)_-10px)] !outline-none"
Expand Down
42 changes: 42 additions & 0 deletions apps/renderer/src/providers/app-grid-layout-container-provider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { throttle } from "lodash-es"
import type { FC, PropsWithChildren } from "react"
import { createContext, useContext, useLayoutEffect, useRef, useState } from "react"


const AppLayoutGridContainerWidthContext = createContext<number>(0)

export const AppLayoutGridContainerProvider: FC<PropsWithChildren> = ({ children }) => {
const [width, setWidth] = useState(0)
const ref = useRef<HTMLDivElement>(null)
useLayoutEffect(() => {
const $first = ref.current?.firstElementChild
if (!$first) return
const handler = throttle(() => {
if (!$first) return

const { width } = $first.getBoundingClientRect()
setWidth(width)
}, 100)

handler()

const resizeObserver = new ResizeObserver(handler)
resizeObserver.observe($first)
return () => {
resizeObserver.disconnect()
}
}, [])

return (
<AppLayoutGridContainerWidthContext.Provider value={width}>
<div ref={ref} className="contents">
{children}
</div>
</AppLayoutGridContainerWidthContext.Provider>
)
}

export const useAppLayoutGridContainerWidth = () => {
const width = useContext(AppLayoutGridContainerWidthContext)
return width
}
24 changes: 12 additions & 12 deletions apps/renderer/src/store/entry-history/action.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
import { apiClient } from "~/lib/api-fetch"
import type { UserModel } from "~/models"

import { entryActions } from "../entry/store"
import { userActions } from "../user"

class EntryHistoryActions {
async fetchEntryHistory(entryId: string) {
const res = await apiClient.entries["read-histories"][entryId].$get()

const data = res.data as {
users: Record<string, UserModel>
entryReadHistories: {
entryId: string
userIds: string[]
readCount: number
}
}
const { data } = await apiClient.entries["read-histories"][":id"].$get({
param: {
id: entryId,
},
query: {
page: "1",
size: "100",
},
})

userActions.upsert(data.users)
entryActions.updateReadHistory(entryId, data.entryReadHistories)
if (data.entryReadHistories) {
entryActions.updateReadHistory(entryId, data.entryReadHistories)
}

return data
}
Expand Down
7 changes: 1 addition & 6 deletions apps/renderer/src/store/entry/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,12 @@ import { isNil, merge, omit } from "lodash-es"
import { runTransactionInScope } from "~/database"
import { apiClient } from "~/lib/api-fetch"
import { getEntriesParams, omitObjectUndefinedValue } from "~/lib/utils"
import type { CombinedEntryModel, EntryModel, FeedModel, UserModel } from "~/models"
import type { CombinedEntryModel, EntryModel, FeedModel } from "~/models"
import { EntryService } from "~/services"

import { feedActions } from "../feed"
import { imageActions } from "../image"
import { feedUnreadActions } from "../unread"
import { userActions } from "../user"
import { createZustandStore, doMutationAndTransaction } from "../utils/helper"
import { internal_batchMarkRead } from "./helper"
import type { EntryState, FlatEntryModel } from "./types"
Expand Down Expand Up @@ -63,10 +62,6 @@ class EntryActions {
omit(data, "read") as any,
])
feedActions.upsertMany([data.feeds])
userActions.upsert(data.users as Record<string, UserModel>)
if (data.entryReadHistories) {
this.updateReadHistory(entryId, data.entryReadHistories)
}
}

return data
Expand Down
13 changes: 1 addition & 12 deletions packages/shared/src/hono.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3218,6 +3218,7 @@ declare const _routes: hono_hono_base.HonoBase<Env, {
handle: string | null;
};
};
total: number;
entryReadHistories: {
userIds: string[];
readCount: number;
Expand Down Expand Up @@ -3391,18 +3392,6 @@ declare const _routes: hono_hono_base.HonoBase<Env, {
handle: string | null;
}[];
};
users: {
[x: string]: {
name: string | null;
id: string;
image: string | null;
handle: string | null;
};
};
entryReadHistories: {
userIds: string[];
readCount: number;
} | null;
} | undefined;
};
outputFormat: "json" | "text";
Expand Down

0 comments on commit 8f3acd6

Please sign in to comment.