Skip to content

Commit

Permalink
Merge pull request #1069 from Thenlie/develop
Browse files Browse the repository at this point in the history
v2.0.6
  • Loading branch information
Thenlie authored Nov 1, 2024
2 parents fd3c67a + b04716d commit 32dca34
Show file tree
Hide file tree
Showing 12 changed files with 767 additions and 539 deletions.
4 changes: 2 additions & 2 deletions TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@
| [src/__tests__/screens/DiscoverScreen.test.tsx](src/__tests__/screens/DiscoverScreen.test.tsx#L29) | 29 | #851 Create global sections variable |
| [src/screens/auth/LoginScreen.tsx](src/screens/auth/LoginScreen.tsx#L89) | 89 | We could try to get the AuthApiError type and use 'cause' instead |
| [src/screens/dashboard/DashboardGalleryScreen.tsx](src/screens/dashboard/DashboardGalleryScreen.tsx#L38) | 38 | If profile does not return after a few seconds, |
| [src/screens/discover/DiscoverDetailScreen.tsx](src/screens/discover/DiscoverDetailScreen.tsx#L38) | 38 | paginate data #838 |
| [src/screens/discover/DiscoverDetailScreen.tsx](src/screens/discover/DiscoverDetailScreen.tsx#L88) | 88 | Create loader #839 |
| [src/screens/discover/DiscoverDetailScreen.tsx](src/screens/discover/DiscoverDetailScreen.tsx#L39) | 39 | paginate data #838 |
| [src/screens/discover/DiscoverDetailScreen.tsx](src/screens/discover/DiscoverDetailScreen.tsx#L107) | 107 | Create loader #839r |
| [src/screens/discover/discoverRequests.ts](src/screens/discover/discoverRequests.ts#L54) | 54 | #613 Dynamic date range |
643 changes: 376 additions & 267 deletions package-lock.json

Large diffs are not rendered by default.

10 changes: 5 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
"@testing-library/user-event": "^14.5.2",
"@types/jest": "^29.5.13",
"@types/node": "^22.5.4",
"@types/react": "^18.3.8",
"@types/react": "^18.3.12",
"@types/react-dom": "^18.3.0",
"@types/yargs": "^17.0.33",
"@typescript-eslint/eslint-plugin": "^7.13.1",
Expand All @@ -71,20 +71,20 @@
"dotenv": "^16.4.5",
"eslint": "^8.57.0",
"eslint-plugin-prettier": "^5.2.1",
"eslint-plugin-react": "^7.34.3",
"eslint-plugin-react": "^7.37.2",
"eslint-plugin-storybook": "^0.9.0",
"jsdom": "^25.0.1",
"leasot": "^14.4.0",
"markdownlint-cli": "^0.41.0",
"markdownlint-cli": "^0.42.0",
"patch-package": "^8.0.0",
"postcss": "^8.4.41",
"prettier": "^3.3.3",
"storybook": "^8.2.8",
"storybook-addon-remix-react-router": "^3.0.1",
"supabase": "^1.191.3",
"supabase": "^1.207.9",
"tailwindcss": "^3.4.4",
"tsx": "^4.17.0",
"typescript": "^5.5.4",
"typescript": "^5.6.3",
"vite": "^5.4.8",
"vitest": "^1.6.0",
"yargs": "^17.7.2"
Expand Down
File renamed without changes.
260 changes: 260 additions & 0 deletions src/components/SortFilterHeader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,260 @@
import React, { Dispatch, SetStateAction, useEffect, useState } from 'react';
import ViewModule from '@mui/icons-material/ViewModule';
import ViewList from '@mui/icons-material/ViewList';
import FilterAltOff from '@mui/icons-material/FilterAltOff';
import Tv from '@mui/icons-material/Tv';
import Movie from '@mui/icons-material/Movie';
import ToggleButtonGroup from '@mui/material/ToggleButtonGroup';
import ToggleButton from '@mui/material/ToggleButton';
import SvgIcon from '@mui/material/SvgIcon';
import Typ from '@mui/material/Typography';
import { useWindowSize } from '../hooks';
import { sortShowsAlphaAsc, sortShowsAlphaDesc, filterShowsByType } from '../helpers';
import Logger from '../logger';
import { ShowData } from '../types';

const LOG = new Logger('SearchResultsHeader');

interface SearchResultsHeaderProps {
/**
* String the user entered as a search
*/
query?: string;
/**
* Local storage key that for view state
*/
viewStateKey: string;
/**
* Current state of users view
*/
viewState?: 'list' | 'grid';
/**
* Function to set the react state of the view
*/
setViewState?: Dispatch<SetStateAction<'list' | 'grid'>>;
/**
* Data of shows returned from the requested search
*/
showDetails?: ShowData[] | null;
/**
* Function to set the react state of the show data
*/
setShowDetails?: Dispatch<SetStateAction<ShowData[] | null>>;
/**
* Function to trigger a re-render in the search results screen
*/
setHash?: Dispatch<SetStateAction<number>>;
/**
* All controls are disabled when `true`, defaults to `false`
*/
disableControls?: boolean;

/**
* Disable the filter options of the filter items, defaults to `false`
*/
disableAlphabeticOrderFilter?: boolean;

/**
* Disable the order filter for the type of results, defaults to `false`
*/
disableResultTypeFilter?: boolean;
}

interface FilterProps {
showType: 'tv' | 'movie' | 'none';
// more filters to add
}

/**
* Heading of the screen showing the search query
* and containing the view toggle button.
*/
const SortFilterHeader: React.FC<SearchResultsHeaderProps> = ({
query,
viewStateKey,
viewState,
setViewState,
setShowDetails,
setHash,
disableControls = false,
disableAlphabeticOrderFilter = false,
disableResultTypeFilter = false,
}) => {
const [sortState, setSortState] = useState<'alpha' | 'rev' | 'none'>('none');
const [filterState, setFilterState] = useState<FilterProps>({ showType: 'none' });
const windowSize = useWindowSize();
const unsortedShows = localStorage.getItem('streamabilityUnsortedResults');

useEffect(() => {
if (!unsortedShows) {
// should never be false
LOG.error('No original data in local storage');
return;
}
if (filterState.showType === 'none' && sortState === 'none') {
// resets to original data if neither are selected
setShowDetails?.(JSON.parse(unsortedShows));
return;
}

const results: ShowData[] = [];
if (filterState.showType !== 'none') {
// If filter is selected, push to `results` which will become a sorted/unsorted showDetails
const filteredShows = filterShowsByType(
JSON.parse(unsortedShows),
filterState.showType
);
results.push(...filteredShows);
}

if (sortState === 'alpha' && results.length > 0) {
// If sort AND filter
setShowDetails?.(sortShowsAlphaAsc(results));
} else if (sortState === 'rev' && results.length > 0) {
// if sort AND filter
setShowDetails?.(sortShowsAlphaDesc(results));
} else if (sortState === 'alpha') {
// If ONLY sort
setShowDetails?.(sortShowsAlphaAsc(JSON.parse(unsortedShows)));
} else if (sortState === 'rev') {
// If ONLY sort
setShowDetails?.(sortShowsAlphaDesc(JSON.parse(unsortedShows)));
}

if (results.length > 0) setShowDetails?.(results);
}, [sortState, filterState.showType]);

const handleViewToggle = (view: 'grid' | 'list') => {
setViewState?.(view);
localStorage.setItem(viewStateKey, view);
setHash?.(Math.random());
};

return (
<div
className={`flex flex-col md:flex-row flex-wrap ${query ? 'justify-between' : 'justify-end'} align-middle w-full p-3`}
>
{query && (
<Typ variant='h5' alignSelf='center' margin={1}>
Search results for: <span className='underline'>{query}</span>
</Typ>
)}
<div>
{!disableResultTypeFilter && (
<ToggleButtonGroup
value={filterState.showType}
exclusive
sx={{ marginRight: 2, marginBottom: 0.5 }}
disabled={disableResultTypeFilter}
>
<ToggleButton
value='tv'
aria-label='filter by tv shows'
onClick={() => setFilterState({ showType: 'tv' })}
disabled={disableControls}
>
<Tv />
</ToggleButton>
<ToggleButton
value='movie'
aria-label='filter by movies'
onClick={() => setFilterState({ showType: 'movie' })}
disabled={disableControls}
>
<Movie />
</ToggleButton>
<ToggleButton
value='none'
aria-label='Remove filter'
onClick={() => setFilterState({ showType: 'none' })}
disabled={disableControls}
>
<FilterAltOff />
</ToggleButton>
</ToggleButtonGroup>
)}

{!disableAlphabeticOrderFilter && (
<ToggleButtonGroup value={sortState} exclusive sx={{ marginRight: 2 }}>
<ToggleButton
value='alpha'
aria-label='sort results alphabetically'
onClick={() => setSortState('alpha')}
disabled={disableControls}
>
<SvgIcon>
<svg
xmlns='http://www.w3.org/2000/svg'
viewBox='0 0 24 24'
className='fill-text'
>
<title>Sort alphabetical ascending</title>
<path d='M19 17H22L18 21L14 17H17V3H19M11 13V15L7.67 19H11V21H5V19L8.33 15H5V13M9 3H7C5.9 3 5 3.9 5 5V11H7V9H9V11H11V5C11 3.9 10.11 3 9 3M9 7H7V5H9Z' />
</svg>
</SvgIcon>
</ToggleButton>
<ToggleButton
value='rev'
aria-label='sort results reverse alphabetically'
onClick={() => setSortState('rev')}
disabled={disableControls}
>
<SvgIcon>
<svg
xmlns='http://www.w3.org/2000/svg'
viewBox='0 0 24 24'
className='fill-text'
>
<title>Sort alphabetical descending</title>
<path d='M19 7H22L18 3L14 7H17V21H19M11 13V15L7.67 19H11V21H5V19L8.33 15H5V13M9 3H7C5.9 3 5 3.9 5 5V11H7V9H9V11H11V5C11 3.9 10.11 3 9 3M9 7H7V5H9Z' />
</svg>
</SvgIcon>
</ToggleButton>
<ToggleButton
value={'none'}
aria-label='Remove sort'
onClick={() => setSortState('none')}
disabled={disableControls}
>
<SvgIcon>
<svg
xmlns='http://www.w3.org/2000/svg'
viewBox='0 0 24 24'
className='fill-text'
>
<title>Remove sort</title>
<path d='M3 13H15V11H3M3 6V8H21V6M3 18H9V16H3V18M22.54 16.88L20.41 19L22.54 21.12L21.12 22.54L19 20.41L16.88 22.54L15.47 21.12L17.59 19L15.47 16.88L16.88 15.47L19 17.59L21.12 15.46L22.54 16.88' />
</svg>
</SvgIcon>
</ToggleButton>
</ToggleButtonGroup>
)}
<ToggleButtonGroup
value={viewState}
exclusive
sx={windowSize.width && windowSize.width < 750 ? { display: 'none' } : {}}
disabled={disableControls}
>
<ToggleButton
value='grid'
aria-label='switch to grid view'
onClick={() => handleViewToggle('grid')}
disabled={disableControls}
>
<ViewModule />
</ToggleButton>
<ToggleButton
value='list'
aria-label='switch to list view'
onClick={() => handleViewToggle('list')}
disabled={disableControls}
>
<ViewList />
</ToggleButton>
</ToggleButtonGroup>
</div>
</div>
);
};

export default SortFilterHeader;
60 changes: 60 additions & 0 deletions src/screens/DetailScreen.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import React from 'react';
import { ShowData } from '../types';
import { OfflineSnackbar } from '../components';
import { SearchResultsHeader } from './search_results';
import LoadingIndicator from '../components/LoadingIndicator';

interface Props {
query?: string;
viewStateKey: string;
viewState: 'grid' | 'list';
setViewState: React.Dispatch<React.SetStateAction<'grid' | 'list'>>;
data: ShowData[] | null;
setData: React.Dispatch<React.SetStateAction<ShowData[] | null>>;
setHash: React.Dispatch<React.SetStateAction<number>>;
cards: JSX.Element;
moreToFetch?: boolean;
loadMoreRef?: (node: HTMLDivElement) => void;
disableAlphabeticOrderFilter?: boolean;
disableResultTypeFilter?: boolean;
}

const DetailScreen: React.FC<Props> = ({
query,
viewStateKey,
viewState,
setViewState,
data,
setData,
setHash,
cards,
moreToFetch,
loadMoreRef,
disableAlphabeticOrderFilter,
disableResultTypeFilter,
}) => {
return (
<>
<SearchResultsHeader
query={query}
viewStateKey={viewStateKey}
viewState={viewState}
setViewState={setViewState}
showDetails={data}
setShowDetails={setData}
setHash={setHash}
disableAlphabeticOrderFilter={disableAlphabeticOrderFilter}
disableResultTypeFilter={disableResultTypeFilter}
/>
<div>
{cards}
{moreToFetch && <LoadingIndicator />}{' '}
{/* Show the indicator while more data is available */}
<div ref={loadMoreRef}></div>
</div>
<OfflineSnackbar />
</>
);
};

export default DetailScreen;
Loading

0 comments on commit 32dca34

Please sign in to comment.