Skip to content

Commit

Permalink
feat: reorder feeds
Browse files Browse the repository at this point in the history
  • Loading branch information
Xstoudi committed Jul 28, 2023
1 parent 71e0bb9 commit aac893c
Show file tree
Hide file tree
Showing 8 changed files with 251 additions and 40 deletions.
99 changes: 95 additions & 4 deletions package-lock.json

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

6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@
"test": "npm run eslint && npm run prettier && npm run check-types"
},
"dependencies": {
"@dnd-kit/core": "^6.0.8",
"@dnd-kit/modifiers": "^6.0.1",
"@dnd-kit/sortable": "^7.0.2",
"@dnd-kit/utilities": "^3.2.1",
"@emotion/css": "^11.11.2",
"@emotion/react": "^11.11.1",
"@emotion/styled": "^11.11.0",
Expand All @@ -33,7 +37,7 @@
"jszip": "^3.10.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-icons": "^4.9.0",
"react-icons": "^4.10.1",
"react-router-dom": "^6.13.0",
"react-use": "^17.4.0",
"sanitize-html": "^2.11.0",
Expand Down
2 changes: 2 additions & 0 deletions src/components/layout/Article.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { useSortable } from '@dnd-kit/sortable';

Check failure on line 1 in src/components/layout/Article.tsx

View workflow job for this annotation

GitHub Actions / lint

'useSortable' is defined but never used
import { CSS } from '@dnd-kit/utilities';

Check failure on line 2 in src/components/layout/Article.tsx

View workflow job for this annotation

GitHub Actions / lint

'CSS' is defined but never used
import clsx from 'clsx';
import { memo, useCallback } from 'react';
import { useLocation, useNavigate } from 'react-router-dom';
Expand Down
63 changes: 46 additions & 17 deletions src/components/layout/Feed.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { useSortable } from '@dnd-kit/sortable';
import { CSS } from '@dnd-kit/utilities';
import clsx from 'clsx';
import { memo, useCallback, useMemo, MouseEvent } from 'react';
import { FaEdit } from 'react-icons/fa';
import { RiDraggable } from 'react-icons/ri';
import { useNavigate } from 'react-router-dom';

import useEditMode from '../../hooks/useEditMode';
Expand Down Expand Up @@ -55,33 +58,59 @@ function Feed(props: FeedProps) {
[identifier, view.activeFeed],
);

const {
setNodeRef,
attributes,
listeners,
transition,
transform,
setActivatorNodeRef,
} = useSortable({ id: identifier });
const style = {
transform: CSS.Transform.toString(transform),
transition,
};

return (
<div
className={clsx(
'flex justify-between align-middle px-3 py-3 hover:bg-slate-300 hover:dark:bg-zinc-700 cursor-pointer',
'flex justify-between align-middle px-3 py-3 hover:bg-slate-300 hover:dark:bg-zinc-700 gap-4 cursor-pointer select-none',
active && 'bg-slate-300 dark:bg-zinc-700 text-black dark:text-white',
)}
role="button"
onClick={selectFeed}
style={style}
ref={setNodeRef}
>
<div className="flex flex-row gap-4 items-center text-xl leading-8">
{isEditing && (
<Button
variant="primary"
className="dark:text-white text-base leading-6"
onClick={editFeed}
>
<FaEdit />
</Button>
)}
<div className="flex flex-row justify-between w-full gap-4">
<div className="flex flex-row gap-4 items-center text-xl leading-8">
{isEditing && (
<Button
variant="primary"
className="dark:text-white text-base leading-6"
onClick={editFeed}
>
<FaEdit />
</Button>
)}

<FeedIcon feed={props} />
<FeedIcon feed={props} />

<div>{displayName}</div>
<div>{displayName}</div>
</div>
{unread > 0 && (
<div className="flex justify-center items-center px-2 bg-orange-400 rounded-full text-black dark:text-white">
{unread}
</div>
)}
</div>
{unread > 0 && (
<div className="flex justify-center items-center px-2 bg-orange-400 rounded-full text-black dark:text-white">
{unread}
{isEditing && (
<div
className="flex justify-center items-center"
ref={setActivatorNodeRef}
{...attributes}
{...listeners}
>
<RiDraggable className="w-6 h-6" />
</div>
)}
</div>
Expand Down
89 changes: 71 additions & 18 deletions src/components/layout/FeedList.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,27 @@
import {
closestCenter,
DndContext,
DragEndEvent,
PointerSensor,
UniqueIdentifier,
useSensor,
useSensors,
} from '@dnd-kit/core';
import { restrictToVerticalAxis } from '@dnd-kit/modifiers';
import {
SortableContext,
verticalListSortingStrategy,
} from '@dnd-kit/sortable';
import clsx from 'clsx';
import { memo, useCallback } from 'react';
import { memo, useCallback, useMemo } from 'react';
import { FaCogs, FaEdit, FaPlus } from 'react-icons/fa';

import useData from '../../hooks/useData';
import useDataDispatch from '../../hooks/useDataDispatch';
import useEditMode from '../../hooks/useEditMode';
import useModal from '../../hooks/useModal';
import usePreference from '../../hooks/usePreference';
import { REORDER_FEED } from '../../state/data/DataActionType';
import { PreferenceState } from '../../state/preference/PreferenceReducer';
import SyncAllButton from '../SyncAllButton';
import IconButton from '../form/IconButton';
Expand All @@ -15,6 +31,7 @@ import Feed from './Feed';

function FeedList() {
const data = useData();
const dataDispatch = useDataDispatch();
const preference = usePreference();
const { toggleEditMode, isEditing } = useEditMode();

Expand All @@ -28,24 +45,60 @@ function FeedList() {
openPreference(preference);
}, [openPreference, preference]);

const identifiers: UniqueIdentifier[] = useMemo(
() => data.feeds.map(({ identifier }) => identifier),
[data.feeds],
);

const sensors = useSensors(useSensor(PointerSensor));
const modifiers = useMemo(() => [restrictToVerticalAxis], []);

const handleDragEnd = useCallback(
({ active, over }: DragEndEvent) => {
if (over === null) {
return;
}
dataDispatch({
type: REORDER_FEED,
payload: {
fromIdentifier: active.id as string,
toIdentifier: over.id as string,
},
});
},
[dataDispatch],
);

return (
<div className="flex-[3_3_0%] bg-neutral-200 dark:bg-zinc-600 flex flex-col shadow-custom-big">
<div className="overflow-y-auto flex-1">
{data.feeds.map((feed) => (
<Feed key={feed.identifier} {...feed} />
))}
</div>
<div className="flex justify-around items-center h-12 shrink-0">
<IconButton Icon={FaPlus} onClick={handleOpenAddFeed} />
<SyncAllButton />
<IconButton
Icon={FaEdit}
onClick={toggleEditMode}
className={clsx(isEditing && 'text-black dark:text-white')}
/>
<IconButton Icon={FaCogs} onClick={handleOpenPreference} />
</div>
</div>
<DndContext
sensors={sensors}
collisionDetection={closestCenter}
modifiers={modifiers}
onDragEnd={handleDragEnd}
>
<SortableContext
items={identifiers}
strategy={verticalListSortingStrategy}
>
<div className="flex-[3_3_0%] bg-neutral-200 dark:bg-zinc-600 flex flex-col shadow-custom-big">
<div className="overflow-y-auto flex-1">
{data.feeds.map((feed) => (
<Feed key={feed.identifier} {...feed} />
))}
</div>
<div className="flex justify-around items-center h-12 shrink-0">
<IconButton Icon={FaPlus} onClick={handleOpenAddFeed} />
<SyncAllButton />
<IconButton
Icon={FaEdit}
onClick={toggleEditMode}
className={clsx(isEditing && 'text-black dark:text-white')}
/>
<IconButton Icon={FaCogs} onClick={handleOpenPreference} />
</div>
</div>
</SortableContext>
</DndContext>
);
}

Expand Down
1 change: 1 addition & 0 deletions src/state/data/DataActionType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export type DataActionType<Action, Payload = void> = Payload extends void
export const LOAD = 'LOAD';
export const ADD_FEED = 'ADD_FEED';
export const UPDATE_FEED = 'UPDATE_FEED';
export const REORDER_FEED = 'REORDER_FEED';
export const UPDATE_CONTENT = 'UPDATE_CONTENT';
export const UPDATE_MULTIPLE_CONTENT = 'UPDATE_MULTIPLE_CONTENT';
export const READ_ARTICLE = 'READ_ARTICLE';
Expand Down
5 changes: 5 additions & 0 deletions src/state/data/DataReducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
LOAD,
READ_ARTICLE,
REMOVE_FEED,
REORDER_FEED,
UPDATE_CONTENT,
UPDATE_FEED,
UPDATE_MULTIPLE_CONTENT,
Expand All @@ -17,6 +18,7 @@ import {
AddFeedAction,
ReadArticleAction,
RemoveFeedAction,
ReorderFeedAction,
UpdateContentAction,
UpdateFeedAction,
UpdateMultipleContentAction,
Expand All @@ -35,6 +37,7 @@ export const initialDataState: DataState = {
export type DataActions =
| AddFeedAction
| UpdateFeedAction
| ReorderFeedAction
| ReadArticleAction
| RemoveFeedAction
| LoadAction
Expand All @@ -49,6 +52,8 @@ function innerDataReducer(draft: Draft<DataState>, action: DataActions) {
return FeedActions.addFeed(draft, action.payload);
case UPDATE_FEED:
return FeedActions.updateFeed(draft, action.payload);
case REORDER_FEED:
return FeedActions.reorderFeed(draft, action.payload);
case READ_ARTICLE:
return FeedActions.readArticle(draft, action.payload);
case REMOVE_FEED:
Expand Down

0 comments on commit aac893c

Please sign in to comment.