Skip to content

Commit

Permalink
feat: resize panel and add store version (#49)
Browse files Browse the repository at this point in the history
Signed-off-by: Innei <i@innei.in>
  • Loading branch information
Innei authored Jun 10, 2024
1 parent 66ae2d2 commit dca43f5
Show file tree
Hide file tree
Showing 9 changed files with 149 additions and 61 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
"react-helmet-async": "2.0.5",
"react-hook-form": "7.51.5",
"react-intersection-observer": "9.10.2",
"react-resizable-layout": "0.7.2",
"react-router-dom": "6.23.1",
"react-virtuoso": "4.7.11",
"rehype-infer-description-meta": "2.0.0",
Expand Down
14 changes: 14 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

47 changes: 40 additions & 7 deletions src/renderer/src/pages/(main)/(context)/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,15 @@ import { EntryColumn } from "@renderer/components/entry-column"
import { EntryContent } from "@renderer/components/entry-content"
import { views } from "@renderer/lib/constants"
import { cn } from "@renderer/lib/utils"
import { feedActions, useFeedStore } from "@renderer/store"
import {
feedActions,
uiActions,
useFeedStore,
useUIStore,
} from "@renderer/store"
import { AnimatePresence } from "framer-motion"
import { useEffect } from "react"
import { useEffect, useMemo, useRef } from "react"
import { useResizable } from "react-resizable-layout"
import { useShallow } from "zustand/react/shallow"

export function Component() {
Expand All @@ -18,24 +24,51 @@ export function Component() {
useEffect(() => {
setActiveEntry(null)
}, [activeList?.id])
const containerRef = useRef<HTMLDivElement>(null)

// Memo this initial value to avoid re-render
// eslint-disable-next-line react-compiler/react-compiler
const entryColWidth = useMemo(() => useUIStore.getState().entryColWidth, [])

const { position, separatorProps } = useResizable({
axis: "x",
min: 300,
max: 450,
initial: entryColWidth,
containerRef,
onResizeEnd({ position }) {
uiActions.setEntryColWidth(position)
},
})

const inWideMode = activeList && views[activeList.view].wideMode
return (
<>
<div ref={containerRef} className="flex grow">
<div
className={cn(
"h-full shrink-0 overflow-y-auto border-r",
activeList && views[activeList.view].wideMode ? "flex-1" : "w-[340px]",
"h-full shrink-0 overflow-y-auto",
inWideMode ? "flex-1" : "border-r",
"will-change-[width]",
)}
style={{
width: position,
}}
>
<EntryColumn />
</div>
{!inWideMode && (
<div
{...separatorProps}
className="h-full w-px shrink-0 cursor-ew-resize hover:bg-border"
/>
)}
<AnimatePresence>
{!(activeList && views[activeList.view].wideMode) && (
{!inWideMode && (
<div className="flex-1">
<EntryContent entryId={activeEntry} />
</div>
)}
</AnimatePresence>
</>
</div>
)
}
13 changes: 5 additions & 8 deletions src/renderer/src/store/entry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,9 @@ import { apiClient } from "@renderer/lib/api-fetch"
import { getEntriesParams } from "@renderer/lib/utils"
import type { EntryModel } from "@renderer/models"
import { produce } from "immer"
import { omit } from "lodash-es"

import { unreadActions } from "./unread"
import { createZustandStore } from "./utils/helper"
import { createZustandStore, getStoreActions } from "./utils/helper"

type FeedId = string
type EntryId = string
Expand Down Expand Up @@ -41,6 +40,9 @@ interface EntryActions {

export const useEntryStore = createZustandStore<EntryState & EntryActions>(
"entry",
{
version: 0,
},
)((set, get) => ({
entries: {},
flatMapEntries: {},
Expand Down Expand Up @@ -140,12 +142,7 @@ export const useEntryStore = createZustandStore<EntryState & EntryActions>(
},
}))

export const entryActions = {
...(omit(useEntryStore.getState(), [
"entries",
"flatMapEntries",
]) as EntryActions),
}
export const entryActions = getStoreActions(useEntryStore)

export const useEntriesByFeedId = (feedId: string) =>
useEntryStore((state) => {
Expand Down
1 change: 1 addition & 0 deletions src/renderer/src/store/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * from "./entry"
export * from "./feed"
export * from "./subscription"
export * from "./ui"
export * from "./unread"
75 changes: 37 additions & 38 deletions src/renderer/src/store/subscription.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,10 @@ import { apiClient } from "@renderer/lib/api-fetch"
import type { FeedViewType } from "@renderer/lib/enum"
import type { SubscriptionModel } from "@renderer/models"
import { produce } from "immer"
import { omit } from "lodash-es"

import { entryActions } from "./entry"
import { unreadActions } from "./unread"
import { createZustandStore } from "./utils/helper"
import { createZustandStore, getStoreActions } from "./utils/helper"

type FeedId = string
interface SubscriptionState {
Expand All @@ -20,47 +19,47 @@ interface SubscriptionActions {
}
export const useSubscriptionStore = createZustandStore<
SubscriptionState & SubscriptionActions
>("subscription")((set, get) => ({
data: {},
internal_reset() {
set({ data: {} })
},
async fetchByView(view) {
const res = await apiClient.subscriptions.$get({
query: { view: String(view) },
})
>("subscription", {
version: 0,
})((set, get) => ({
data: {},
internal_reset() {
set({ data: {} })
},
async fetchByView(view) {
const res = await apiClient.subscriptions.$get({
query: { view: String(view) },
})

get().internal_reset()
res.data.forEach((subscription) => {
get().internal_reset()
res.data.forEach((subscription) => {
set((state) =>
produce(state, (state) => {
state.data[subscription.feeds.id] = subscription
return state
}),
)
})

return res.data
},
upsert: (feedId, subscription) => {
set((state) =>
produce(state, (state) => {
state.data[subscription.feeds.id] = subscription
state.data[feedId] = subscription
return state
}),
)
})

return res.data
},
upsert: (feedId, subscription) => {
set((state) =>
produce(state, (state) => {
state.data[feedId] = subscription
return state
}),
)
},
markReadByView(view) {
const state = get()
for (const feedId in state.data) {
if (state.data[feedId].view === view) {
unreadActions.updateByFeedId(feedId, 0)
entryActions.optimisticUpdateManyByFeedId(feedId, { read: true })
},
markReadByView(view) {
const state = get()
for (const feedId in state.data) {
if (state.data[feedId].view === view) {
unreadActions.updateByFeedId(feedId, 0)
entryActions.optimisticUpdateManyByFeedId(feedId, { read: true })
}
}
}
},
}))
},
}))

export const subscriptionActions = {
...omit(useSubscriptionStore.getState(), ["data"]),
}
export const subscriptionActions = getStoreActions(useSubscriptionStore)
21 changes: 21 additions & 0 deletions src/renderer/src/store/ui.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { createZustandStore, getStoreActions } from "./utils/helper"

interface UIState {
entryColWidth: number
}
interface UIActions {
setEntryColWidth: (width: number) => void
}
export const useUIStore = createZustandStore<UIState & UIActions>(
"ui",
{
version: 0,
},
)((set) => ({
entryColWidth: 450,
setEntryColWidth(width) {
set({ entryColWidth: width })
},
}))

export const uiActions = getStoreActions(useUIStore)
10 changes: 5 additions & 5 deletions src/renderer/src/store/unread.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { apiClient } from "@renderer/lib/api-fetch"
import type { FeedViewType } from "@renderer/lib/enum"
import { produce } from "immer"
import { omit } from "lodash-es"

import { createZustandStore } from "./utils/helper"
import { createZustandStore, getStoreActions } from "./utils/helper"

interface UnreadState {
data: Record<string, number>
Expand All @@ -17,6 +16,9 @@ interface UnreadActions {
}
export const useUnreadStore = createZustandStore<UnreadState & UnreadActions>(
"unread",
{
version: 0,
},
)((set, get) => ({
data: {},

Expand Down Expand Up @@ -62,6 +64,4 @@ export const useUnreadStore = createZustandStore<UnreadState & UnreadActions>(
},
}))

export const unreadActions = {
...omit(useUnreadStore.getState(), ["data"]),
}
export const unreadActions = getStoreActions(useUnreadStore)
28 changes: 25 additions & 3 deletions src/renderer/src/store/utils/helper.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { del, get, set } from "idb-keyval"
import type { StateCreator } from "zustand"
import type { StateStorage } from "zustand/middleware"
import type { PersistOptions, StateStorage } from "zustand/middleware"
import { createJSONStorage, persist } from "zustand/middleware"
import { shallow } from "zustand/shallow"
import { createWithEqualityFn } from "zustand/traditional"
Expand Down Expand Up @@ -28,18 +28,40 @@ export const createZustandStore =
> = StateCreator<S, [["zustand/persist", unknown]], []>,
>(
name: string,
options?: Partial<PersistOptions<S>>,
) =>
(store: T) => {
const newStore = createWithEqualityFn(
persist<S>(store, { name, storage: zustandStorage }),
persist<S>(store, {
name,
storage: zustandStorage,
...options,
}),
shallow,
)

// const newStore = create(persist(store, { name, storage: zustandStorage }))
Object.assign(window, {
[`__${name}`]() {
return newStore.getState()
},
})
return newStore
}
type FunctionKeys<T> = {
[K in keyof T]: T[K] extends (...args: any[]) => any ? K : never;
}[keyof T]

type FunctionProps<T> = Pick<T, FunctionKeys<T>>
export const getStoreActions = <T extends { getState: () => any }>(
store: T,
): FunctionProps<ReturnType<T["getState"]>> => {
const actions = {}
const state = store.getState()
for (const key in state) {
if (typeof state[key] === "function") {
actions[key] = state[key]
}
}

return actions as any
}

0 comments on commit dca43f5

Please sign in to comment.