Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Remember the open state of the category, Ensure that each View'… #709

Merged
merged 2 commits into from
Sep 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 21 additions & 18 deletions apps/renderer/src/modules/feed-column/category.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ type FeedId = string
interface FeedCategoryProps {
data: FeedId[]
view?: number
expansion: boolean
categoryOpenStateData: Record<string, boolean>
}

function FeedCategoryImpl({ data: ids, view, expansion }: FeedCategoryProps) {
function FeedCategoryImpl({ data: ids, view, categoryOpenStateData }: FeedCategoryProps) {
const { t } = useTranslation()

const sortByUnreadFeedList = useFeedUnreadStore((state) =>
Expand All @@ -46,17 +46,29 @@ function FeedCategoryImpl({ data: ids, view, expansion }: FeedCategoryProps) {
const folderName = subscription?.category || subscription.defaultCategory

const showCollapse = sortByUnreadFeedList.length > 1 || subscription?.category
const [open, setOpen] = useState(!showCollapse)
const open = folderName ? categoryOpenStateData[folderName] : true

const shouldOpen = useRouteParamsSelector(
(s) => typeof s.feedId === "string" && ids.includes(s.feedId),
)
const shouldOpen =
useRouteParamsSelector((s) => typeof s.feedId === "string" && ids.includes(s.feedId)) ||
ids.length === 1

const itemsRef = useRef<HTMLDivElement>(null)

const toggleCategoryOpenState = (e) => {
e.stopPropagation()
if (!isCategoryEditing) {
setCategoryActive()
}
if (view !== undefined && folderName) {
subscriptionActions.toggleCategoryOpenState(view, folderName)
}
}

useEffect(() => {
if (shouldOpen) {
setOpen(true)
if (!open && view !== undefined && folderName) {
subscriptionActions.toggleCategoryOpenState(view, folderName)
}

const $items = itemsRef.current

Expand All @@ -66,12 +78,7 @@ function FeedCategoryImpl({ data: ids, view, expansion }: FeedCategoryProps) {
behavior: "smooth",
})
}
}, [shouldOpen])
useEffect(() => {
if (showCollapse) {
setOpen(expansion)
}
}, [expansion])
}, [shouldOpen, open, view, folderName])

const setCategoryActive = () => {
if (view !== undefined) {
Expand Down Expand Up @@ -200,11 +207,7 @@ function FeedCategoryImpl({ data: ids, view, expansion }: FeedCategoryProps) {
<div className="flex w-full min-w-0 items-center">
<button
type="button"
onClick={(e) => {
if (isCategoryEditing) return
e.stopPropagation()
setOpen(!open)
}}
onClick={toggleCategoryOpenState}
data-state={open ? "open" : "close"}
className={cn(
"flex h-8 items-center [&_.i-mgc-right-cute-fi]:data-[state=open]:rotate-90",
Expand Down
54 changes: 42 additions & 12 deletions apps/renderer/src/modules/feed-column/list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,12 @@ import type { FeedViewType } from "~/lib/enum"
import { cn, sortByAlphabet } from "~/lib/utils"
import { Queries } from "~/queries"
import { getPreferredTitle, useFeedStore } from "~/store/feed"
import { getSubscriptionByFeedId, useSubscriptionByView } from "~/store/subscription"
import {
getSubscriptionByFeedId,
subscriptionActions,
useCategoryOpenStateByView,
useSubscriptionByView,
} from "~/store/subscription"
import { useFeedUnreadStore } from "~/store/unread"

import {
Expand Down Expand Up @@ -79,10 +84,10 @@ const useUpdateUnreadCount = () => {
}

function FeedListImpl({ className, view }: { className?: string; view: number }) {
const [expansion, setExpansion] = useState(false)
const feedsData = useFeedsGroupedData(view)
const listsData = useListsGroupedData(view)

const categoryOpenStateData = useCategoryOpenStateByView(view)
const expansion = Object.values(categoryOpenStateData).every((value) => value === true)
useUpdateUnreadCount()

const totalUnread = useFeedUnreadStore((state) => {
Expand Down Expand Up @@ -124,9 +129,15 @@ function FeedListImpl({ className, view }: { className?: string; view: number })
<div className="ml-2 flex items-center gap-3 text-sm text-theme-vibrancyFg">
<SortButton />
{expansion ? (
<i className="i-mgc-list-collapse-cute-re" onClick={() => setExpansion(false)} />
<i
className="i-mgc-list-collapse-cute-re"
onClick={() => subscriptionActions.expandCategoryOpenStateByView(view, false)}
/>
) : (
<i className="i-mgc-list-expansion-cute-re" onClick={() => setExpansion(true)} />
<i
className="i-mgc-list-expansion-cute-re"
onClick={() => subscriptionActions.expandCategoryOpenStateByView(view, true)}
/>
)}
<UnreadNumber unread={totalUnread} className="text-xs !text-inherit" />
</div>
Expand Down Expand Up @@ -157,7 +168,12 @@ function FeedListImpl({ className, view }: { className?: string; view: number })
<div className="mt-1 flex h-6 w-full shrink-0 items-center rounded-md px-2.5 text-xs font-semibold text-theme-vibrancyFg transition-colors">
{t("words.lists")}
</div>
<SortableList view={view} expansion={expansion} data={listsData} by="alphabetical" />
<SortableList
view={view}
data={listsData}
categoryOpenStateData={categoryOpenStateData}
by="alphabetical"
/>
</>
)}
<div
Expand All @@ -169,7 +185,11 @@ function FeedListImpl({ className, view }: { className?: string; view: number })
{t("words.feeds")}
</div>
{hasData ? (
<SortableList view={view} expansion={expansion} data={feedsData} />
<SortableList
view={view}
data={feedsData}
categoryOpenStateData={categoryOpenStateData}
/>
) : (
<div className="flex h-full flex-1 items-center font-normal text-zinc-500">
<Link
Expand Down Expand Up @@ -279,10 +299,10 @@ const SortButton = () => {

type FeedListProps = {
view: number
expansion: boolean
data: Record<string, string[]>
categoryOpenStateData: Record<string, boolean>
}
const SortByUnreadList = ({ view, expansion, data }: FeedListProps) => {
const SortByUnreadList = ({ view, data, categoryOpenStateData }: FeedListProps) => {
const isDesc = useFeedListSortSelector((s) => s.order === "desc")

const sortedByUnread = useFeedUnreadStore((state) => {
Expand All @@ -309,13 +329,18 @@ const SortByUnreadList = ({ view, expansion, data }: FeedListProps) => {
return (
<Fragment>
{sortedByUnread?.map(([category, ids]) => (
<FeedCategory key={category} data={ids} view={view} expansion={expansion} />
<FeedCategory
key={category}
data={ids}
view={view}
categoryOpenStateData={categoryOpenStateData}
/>
))}
</Fragment>
)
}

const SortByAlphabeticalList = ({ view, expansion, data }: FeedListProps) => {
const SortByAlphabeticalList = ({ view, data, categoryOpenStateData }: FeedListProps) => {
const categoryName2RealDisplayNameMap = useFeedStore((state) => {
const map = {} as Record<string, string>
for (const categoryName in data) {
Expand Down Expand Up @@ -353,7 +378,12 @@ const SortByAlphabeticalList = ({ view, expansion, data }: FeedListProps) => {
return (
<Fragment>
{sortedByAlphabetical.map((category) => (
<FeedCategory key={category} data={data[category]} view={view} expansion={expansion} />
<FeedCategory
key={category}
data={data[category]}
view={view}
categoryOpenStateData={categoryOpenStateData}
/>
))}
</Fragment>
)
Expand Down
4 changes: 4 additions & 0 deletions apps/renderer/src/store/subscription/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ import { useSubscriptionStore } from "../subscription"
type FeedId = string
export const useFeedIdByView = (view: FeedViewType) =>
useSubscriptionStore((state) => state.feedIdByView[view] || [])

export const useCategoryOpenStateByView = (view: FeedViewType) =>
useSubscriptionStore((state) => state.categoryOpenStateByView[view])

export const useSubscriptionByView = (view: FeedViewType) =>
useSubscriptionStore((state) => state.feedIdByView[view].map((id) => state.data[id]))

Expand Down
50 changes: 50 additions & 0 deletions apps/renderer/src/store/subscription/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ interface SubscriptionState {
* Value: FeedId[]
*/
feedIdByView: Record<FeedViewType, FeedId[]>
/**
* Key: FeedViewType
* Value: Record<string, boolean>
*/
categoryOpenStateByView: Record<FeedViewType, Record<string, boolean>>
}

function morphResponseData(data: SubscriptionModel[]): SubscriptionFlatModel[] {
Expand Down Expand Up @@ -64,10 +69,19 @@ const emptyDataIdByView: Record<FeedViewType, FeedId[]> = {
[FeedViewType.SocialMedia]: [],
[FeedViewType.Videos]: [],
}
const emptyCategoryOpenStateByView: Record<FeedViewType, Record<string, boolean>> = {
[FeedViewType.Articles]: {},
[FeedViewType.Audios]: {},
[FeedViewType.Notifications]: {},
[FeedViewType.Pictures]: {},
[FeedViewType.SocialMedia]: {},
[FeedViewType.Videos]: {},
}

export const useSubscriptionStore = createZustandStore<SubscriptionState>("subscription")(() => ({
data: {},
feedIdByView: { ...emptyDataIdByView },
categoryOpenStateByView: { ...emptyCategoryOpenStateByView },
}))

const set = useSubscriptionStore.setState
Expand All @@ -91,16 +105,20 @@ class SubscriptionActions {
set((state) => ({
...state,
feedIdByView: { ...state.feedIdByView, [view]: [] },
categoryOpenStateByView: { ...state.categoryOpenStateByView },
}))
} else {
set((state) => ({
...state,
feedIdByView: { ...emptyDataIdByView },
categoryOpenStateByView: { ...state.categoryOpenStateByView },
}))
}

const transformedData = morphResponseData(res.data)

this.upsertMany(transformedData)
this.updateCategoryOpenState(transformedData.filter((s) => s.category || s.defaultCategory))
feedActions.upsertMany(res.data.map((s) => ("feeds" in s ? s.feeds : s.lists)))

return res.data
Expand All @@ -115,13 +133,44 @@ class SubscriptionActions {
subscriptions.forEach((subscription) => {
state.data[subscription.feedId] = omit(subscription, "feeds")
state.feedIdByView[subscription.view].push(subscription.feedId)
return state
})
}),
)
}

updateCategoryOpenState(subscriptions: SubscriptionFlatModel[]) {
set((state) =>
produce(state, (state) => {
subscriptions.forEach((subscription) => {
const folderName = subscription.category || subscription.defaultCategory
state.categoryOpenStateByView[subscription.view][folderName] =
state.categoryOpenStateByView[subscription.view][folderName] || false
return state
})
}),
)
}

toggleCategoryOpenState(view: FeedViewType, category: string) {
set((state) =>
produce(state, (state) => {
state.categoryOpenStateByView[view][category] =
!state.categoryOpenStateByView[view][category]
}),
)
}

expandCategoryOpenStateByView(view: FeedViewType, isOpen: boolean) {
set((state) =>
produce(state, (state) => {
for (const category in state.categoryOpenStateByView[view]) {
state.categoryOpenStateByView[view][category] = isOpen
}
}),
)
}

async markReadByView(view: FeedViewType, filter?: MarkReadFilter) {
doMutationAndTransaction(
() =>
Expand Down Expand Up @@ -195,6 +244,7 @@ class SubscriptionActions {
set({
data: {},
feedIdByView: { ...emptyDataIdByView },
categoryOpenStateByView: { ...emptyCategoryOpenStateByView },
})
}

Expand Down
Loading