Skip to content

Commit

Permalink
feat(home): implement sort by publish date #22
Browse files Browse the repository at this point in the history
  • Loading branch information
AXeL-dev committed Sep 19, 2022
1 parent 0e50a03 commit 28dced7
Show file tree
Hide file tree
Showing 14 changed files with 390 additions and 5 deletions.
48 changes: 48 additions & 0 deletions src/store/reducers/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
Settings,
VideosSeniority,
ViewFilters,
ViewSorting,
} from 'types';

const { REACT_APP_YOUTUBE_API_KEY } = process.env;
Expand All @@ -26,6 +27,15 @@ export const defaultSettings = {
archived: true,
others: true,
},
allViewSorting: {
publishDate: false,
},
recentViewSorting: {
publishDate: true,
},
watchLaterViewSorting: {
publishDate: false,
},
homeDisplayOptions: {
hiddenViews: [],
},
Expand Down Expand Up @@ -86,6 +96,43 @@ export const settingsSlice = createSlice({
return state;
}
},
setViewSorting: (
state,
action: PayloadAction<{
view: HomeView;
sorting: Partial<ViewSorting>;
}>,
) => {
const { view, sorting } = action.payload;
switch (view) {
case HomeView.All:
return {
...state,
allViewSorting: {
...state.allViewSorting,
...sorting,
},
};
case HomeView.Recent:
return {
...state,
recentViewSorting: {
...state.recentViewSorting,
...sorting,
},
};
case HomeView.WatchLater:
return {
...state,
watchLaterViewSorting: {
...state.watchLaterViewSorting,
...sorting,
},
};
default:
return state;
}
},
setHomeDisplayOptions: (
state,
action: PayloadAction<Partial<HomeDisplayOptions>>,
Expand All @@ -110,6 +157,7 @@ export const {
setSettings,
resetSettings,
setViewFilters,
setViewSorting,
setHomeDisplayOptions,
} = settingsSlice.actions;

Expand Down
22 changes: 22 additions & 0 deletions src/store/selectors/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,28 @@ export const selectViewFilters = (view: HomeView) =>
}
});

export const selectViewSorting = (view: HomeView) =>
createSelector(selectSettings, (settings) => {
switch (view) {
case HomeView.Recent:
return {
...defaultSettings.recentViewSorting,
...settings.recentViewSorting,
};
case HomeView.WatchLater:
return {
...defaultSettings.watchLaterViewSorting,
...settings.watchLaterViewSorting,
};
case HomeView.All:
default:
return {
...defaultSettings.allViewSorting,
...settings.allViewSorting,
};
}
});

export const selectHomeDisplayOptions = createSelector(
selectSettings,
(settings) => settings.homeDisplayOptions,
Expand Down
7 changes: 7 additions & 0 deletions src/types/Settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ export interface Settings {
recentVideosSeniority: VideosSeniority;
recentViewFilters: RecentViewFilters;
watchLaterViewFilters: WatchLaterViewFilters;
allViewSorting: ViewSorting;
recentViewSorting: ViewSorting;
watchLaterViewSorting: ViewSorting;
homeDisplayOptions: HomeDisplayOptions;
enableNotifications: boolean;
queryTimeout: number;
Expand All @@ -17,6 +20,10 @@ export interface HomeDisplayOptions {
hiddenViews: HomeView[];
}

export interface ViewSorting {
publishDate: boolean;
}

export interface RecentViewFilters {
seen: boolean;
watchLater: boolean;
Expand Down
3 changes: 1 addition & 2 deletions src/ui/components/pages/Channels/ChannelResults/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import React from 'react';
import { Stack, Divider } from '@mui/material';
import { Alert, ProgressBar } from 'ui/components/shared';
import { useFindChannelByNameQuery } from 'store/services/youtube';
import { Channel } from 'types';
import PickChannelCard from './PickChannelCard';

interface ChannelResultsProps {
Expand All @@ -23,7 +22,7 @@ export default function ChannelResults(props: ChannelResultsProps) {
<ProgressBar variant="indeterminate" />
) : (
<Stack sx={{ px: 3, overflow: 'auto' }} divider={<Divider />}>
{results.map((channel: Channel, index: number) => (
{results.map((channel, index) => (
<PickChannelCard key={index} channel={channel} />
))}
</Stack>
Expand Down
2 changes: 1 addition & 1 deletion src/ui/components/pages/Home/ChannelsWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ function ChannelsWrapper(props: ChannelsWrapperProps) {
pt: 3,
}}
>
{channels.map((channel: Channel, index: number) => (
{channels.map((channel, index) => (
<ChannelRenderer
key={index}
view={view}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import React, { useState } from 'react';
import { IconButton, ListSubheader } from '@mui/material';
import { StyledMenu, CheckableMenuItem } from 'ui/components/shared';
import { useAppDispatch, useAppSelector } from 'store';
import SortIcon from '@mui/icons-material/Sort';
import { selectViewSorting } from 'store/selectors/settings';
import { setViewSorting } from 'store/reducers/settings';
import { HomeView, Nullable, ViewSorting } from 'types';

const options: {
label: string;
value: keyof ViewSorting;
}[] = [
{
label: 'Publish date',
value: 'publishDate',
},
];

interface AllViewSortingProps {}

function AllViewSorting(props: AllViewSortingProps) {
const sorting = useAppSelector(selectViewSorting(HomeView.All));
const dispatch = useAppDispatch();
const [anchorEl, setAnchorEl] = useState<Nullable<HTMLElement>>(null);
const open = Boolean(anchorEl);

const handleClick = (event: React.MouseEvent<HTMLElement>) => {
setAnchorEl(event.currentTarget);
};

const handleClose = () => {
setAnchorEl(null);
};

const handleSortToggle = (key: keyof ViewSorting) => {
dispatch(
setViewSorting({
view: HomeView.All,
sorting: {
[key]: !sorting[key],
},
}),
);
};

return (
<>
<IconButton
id="sort-button"
aria-label="sort"
aria-controls={open ? 'sort-menu' : undefined}
aria-expanded={open ? 'true' : undefined}
aria-haspopup="true"
onClick={handleClick}
>
<SortIcon />
</IconButton>
<StyledMenu
id="sort-menu"
MenuListProps={{
'aria-labelledby': 'sort-button',
dense: true,
subheader: <ListSubheader component="div">Sort by</ListSubheader>,
}}
anchorEl={anchorEl}
open={open}
onClose={handleClose}
transformOrigin={{ horizontal: 'right', vertical: 'top' }}
anchorOrigin={{ horizontal: 'right', vertical: 'bottom' }}
>
{options.map(({ label, value }, index) => (
<CheckableMenuItem
key={index}
checked={!!sorting[value]}
onClick={() => handleSortToggle(value)}
>
{label}
</CheckableMenuItem>
))}
</StyledMenu>
</>
);
}

export default AllViewSorting;
21 changes: 21 additions & 0 deletions src/ui/components/pages/Home/TabActions/AllViewActions/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import React from 'react';
import { Box } from '@mui/material';
import AllViewSorting from './AllViewSorting';

interface AllViewActionsProps {}

function AllViewActions(props: AllViewActionsProps) {
return (
<Box
sx={{
display: 'flex',
alignItems: 'center',
gap: 2,
}}
>
<AllViewSorting />
</Box>
);
}

export default AllViewActions;
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import React, { useState } from 'react';
import { IconButton, ListSubheader } from '@mui/material';
import { StyledMenu, CheckableMenuItem } from 'ui/components/shared';
import { useAppDispatch, useAppSelector } from 'store';
import SortIcon from '@mui/icons-material/Sort';
import { selectViewSorting } from 'store/selectors/settings';
import { setViewSorting } from 'store/reducers/settings';
import { HomeView, Nullable, ViewSorting } from 'types';

const options: {
label: string;
value: keyof ViewSorting;
}[] = [
{
label: 'Publish date',
value: 'publishDate',
},
];

interface RecentViewSortingProps {}

function RecentViewSorting(props: RecentViewSortingProps) {
const sorting = useAppSelector(selectViewSorting(HomeView.Recent));
const dispatch = useAppDispatch();
const [anchorEl, setAnchorEl] = useState<Nullable<HTMLElement>>(null);
const open = Boolean(anchorEl);

const handleClick = (event: React.MouseEvent<HTMLElement>) => {
setAnchorEl(event.currentTarget);
};

const handleClose = () => {
setAnchorEl(null);
};

const handleSortToggle = (key: keyof ViewSorting) => {
dispatch(
setViewSorting({
view: HomeView.Recent,
sorting: {
[key]: !sorting[key],
},
}),
);
};

return (
<>
<IconButton
id="sort-button"
aria-label="sort"
aria-controls={open ? 'sort-menu' : undefined}
aria-expanded={open ? 'true' : undefined}
aria-haspopup="true"
onClick={handleClick}
>
<SortIcon />
</IconButton>
<StyledMenu
id="sort-menu"
MenuListProps={{
'aria-labelledby': 'sort-button',
dense: true,
subheader: <ListSubheader component="div">Sort by</ListSubheader>,
}}
anchorEl={anchorEl}
open={open}
onClose={handleClose}
transformOrigin={{ horizontal: 'right', vertical: 'top' }}
anchorOrigin={{ horizontal: 'right', vertical: 'bottom' }}
>
{options.map(({ label, value }, index) => (
<CheckableMenuItem
key={index}
checked={!!sorting[value]}
onClick={() => handleSortToggle(value)}
>
{label}
</CheckableMenuItem>
))}
</StyledMenu>
</>
);
}

export default RecentViewSorting;
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Box } from '@mui/material';
import RecentViewFilters from './RecentViewFilters';
import RecentVideosSeniority from './RecentVideosSeniority';
import RecentViewOptions from './RecentViewOptions';
import RecentViewSorting from './RecentViewSorting';

interface RecentViewActionsProps {
videosCount: number;
Expand All @@ -19,6 +20,7 @@ function RecentViewActions(props: RecentViewActionsProps) {
gap: 2,
}}
>
<RecentViewSorting />
<RecentViewFilters />
<RecentVideosSeniority />
{videosCount > 0 ? <RecentViewOptions /> : null}
Expand Down
Loading

0 comments on commit 28dced7

Please sign in to comment.