[Glitch] Wrapstodon: Add nav modal
Port e206b0d0dead4cf48e211f9e57724ae63d3d8b08 to glitch-soc Signed-off-by: Claire <claire.github-309c@sitedethib.com>
This commit is contained in:
parent
febd6241bf
commit
e7c383251b
@ -11,7 +11,7 @@ export interface AnnualReportAnnouncementProps {
|
|||||||
year: string;
|
year: string;
|
||||||
state: Exclude<ApiAnnualReportState, 'ineligible'>;
|
state: Exclude<ApiAnnualReportState, 'ineligible'>;
|
||||||
onRequestBuild: () => void;
|
onRequestBuild: () => void;
|
||||||
onOpen: () => void;
|
onOpen?: () => void; // This is optional when inside the modal, as it won't be shown then.
|
||||||
onDismiss: () => void;
|
onDismiss: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -16,6 +16,7 @@
|
|||||||
var(--color-bg-primary);
|
var(--color-bg-primary);
|
||||||
border-bottom: 1px solid var(--color-border-primary);
|
border-bottom: 1px solid var(--color-border-primary);
|
||||||
position: relative;
|
position: relative;
|
||||||
|
pointer-events: all;
|
||||||
|
|
||||||
h2 {
|
h2 {
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
@ -34,4 +35,8 @@
|
|||||||
right: 8px;
|
right: 8px;
|
||||||
margin-inline: 0;
|
margin-inline: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:global(.modal-root__modal) & {
|
||||||
|
border-radius: 16px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -332,3 +332,7 @@ $mobile-breakpoint: 540px;
|
|||||||
left: 0;
|
left: 0;
|
||||||
mix-blend-mode: screen;
|
mix-blend-mode: screen;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.navItemBadge {
|
||||||
|
background: var(--color-bg-brand-soft);
|
||||||
|
}
|
||||||
|
|||||||
@ -11,7 +11,11 @@ import { closeModal } from '@/flavours/glitch/actions/modal';
|
|||||||
import { IconButton } from '@/flavours/glitch/components/icon_button';
|
import { IconButton } from '@/flavours/glitch/components/icon_button';
|
||||||
import { LoadingIndicator } from '@/flavours/glitch/components/loading_indicator';
|
import { LoadingIndicator } from '@/flavours/glitch/components/loading_indicator';
|
||||||
import { me } from '@/flavours/glitch/initial_state';
|
import { me } from '@/flavours/glitch/initial_state';
|
||||||
import { useAppDispatch, useAppSelector } from '@/flavours/glitch/store';
|
import {
|
||||||
|
createAppSelector,
|
||||||
|
useAppDispatch,
|
||||||
|
useAppSelector,
|
||||||
|
} from '@/flavours/glitch/store';
|
||||||
import CloseIcon from '@/material-icons/400-24px/close.svg?react';
|
import CloseIcon from '@/material-icons/400-24px/close.svg?react';
|
||||||
|
|
||||||
import { Archetype } from './archetype';
|
import { Archetype } from './archetype';
|
||||||
@ -23,21 +27,26 @@ import { NewPosts } from './new_posts';
|
|||||||
|
|
||||||
const moduleClassNames = classNames.bind(styles);
|
const moduleClassNames = classNames.bind(styles);
|
||||||
|
|
||||||
|
const accountSelector = createAppSelector(
|
||||||
|
[(state) => state.accounts, (state) => state.annualReport.report],
|
||||||
|
(accounts, report) => {
|
||||||
|
if (me) {
|
||||||
|
return accounts.get(me);
|
||||||
|
}
|
||||||
|
if (report?.schema_version === 2) {
|
||||||
|
return accounts.get(report.account_id);
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
export const AnnualReport: FC<{ context?: 'modal' | 'standalone' }> = ({
|
export const AnnualReport: FC<{ context?: 'modal' | 'standalone' }> = ({
|
||||||
context = 'standalone',
|
context = 'standalone',
|
||||||
}) => {
|
}) => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const report = useAppSelector((state) => state.annualReport.report);
|
const report = useAppSelector((state) => state.annualReport.report);
|
||||||
const account = useAppSelector((state) => {
|
const account = useAppSelector(accountSelector);
|
||||||
if (me) {
|
|
||||||
return state.accounts.get(me);
|
|
||||||
}
|
|
||||||
if (report?.schema_version === 2) {
|
|
||||||
return state.accounts.get(report.account_id);
|
|
||||||
}
|
|
||||||
return undefined;
|
|
||||||
});
|
|
||||||
|
|
||||||
const close = useCallback(() => {
|
const close = useCallback(() => {
|
||||||
dispatch(closeModal({ modalType: 'ANNUAL_REPORT', ignoreFocus: false }));
|
dispatch(closeModal({ modalType: 'ANNUAL_REPORT', ignoreFocus: false }));
|
||||||
|
|||||||
@ -1,11 +1,17 @@
|
|||||||
|
import type { MouseEventHandler } from 'react';
|
||||||
import { useCallback, useEffect } from 'react';
|
import { useCallback, useEffect } from 'react';
|
||||||
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
|
||||||
import { closeModal } from '@/flavours/glitch/actions/modal';
|
import { closeModal } from '@/flavours/glitch/actions/modal';
|
||||||
import { useAppDispatch } from '@/flavours/glitch/store';
|
import {
|
||||||
|
generateReport,
|
||||||
|
selectWrapstodonYear,
|
||||||
|
} from '@/flavours/glitch/reducers/slices/annual_report';
|
||||||
|
import { useAppDispatch, useAppSelector } from '@/flavours/glitch/store';
|
||||||
|
|
||||||
import { AnnualReport } from '.';
|
import { AnnualReport } from '.';
|
||||||
|
import { AnnualReportAnnouncement } from './announcement';
|
||||||
import styles from './index.module.scss';
|
import styles from './index.module.scss';
|
||||||
|
|
||||||
const AnnualReportModal: React.FC<{
|
const AnnualReportModal: React.FC<{
|
||||||
@ -15,17 +21,42 @@ const AnnualReportModal: React.FC<{
|
|||||||
onChangeBackgroundColor('var(--color-bg-media-base)');
|
onChangeBackgroundColor('var(--color-bg-media-base)');
|
||||||
}, [onChangeBackgroundColor]);
|
}, [onChangeBackgroundColor]);
|
||||||
|
|
||||||
|
const { state } = useAppSelector((state) => state.annualReport);
|
||||||
|
const year = useAppSelector(selectWrapstodonYear);
|
||||||
|
|
||||||
|
const showAnnouncement = year && state && state !== 'available';
|
||||||
|
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const handleCloseModal = useCallback<React.MouseEventHandler<HTMLDivElement>>(
|
|
||||||
|
const handleBuildRequest = useCallback(() => {
|
||||||
|
void dispatch(generateReport());
|
||||||
|
}, [dispatch]);
|
||||||
|
|
||||||
|
const handleClose = useCallback(() => {
|
||||||
|
dispatch(closeModal({ modalType: 'ANNUAL_REPORT', ignoreFocus: false }));
|
||||||
|
}, [dispatch]);
|
||||||
|
|
||||||
|
const handleCloseModal: MouseEventHandler = useCallback(
|
||||||
(e) => {
|
(e) => {
|
||||||
if (e.target === e.currentTarget)
|
if (e.target === e.currentTarget) {
|
||||||
dispatch(
|
handleClose();
|
||||||
closeModal({ modalType: 'ANNUAL_REPORT', ignoreFocus: false }),
|
}
|
||||||
);
|
|
||||||
},
|
},
|
||||||
[dispatch],
|
[handleClose],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Auto-close if ineligible
|
||||||
|
useEffect(() => {
|
||||||
|
if (state === 'ineligible') {
|
||||||
|
handleClose();
|
||||||
|
}
|
||||||
|
}, [handleClose, state]);
|
||||||
|
|
||||||
|
if (state === 'ineligible') {
|
||||||
|
// Not sure how you got here, but don't show anything.
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
// It's fine not to provide a keyboard handler here since there is a global
|
// It's fine not to provide a keyboard handler here since there is a global
|
||||||
// [Esc] key listener that will close open modals.
|
// [Esc] key listener that will close open modals.
|
||||||
@ -40,7 +71,16 @@ const AnnualReportModal: React.FC<{
|
|||||||
)}
|
)}
|
||||||
onClick={handleCloseModal}
|
onClick={handleCloseModal}
|
||||||
>
|
>
|
||||||
<AnnualReport context='modal' />
|
{!showAnnouncement ? (
|
||||||
|
<AnnualReport context='modal' />
|
||||||
|
) : (
|
||||||
|
<AnnualReportAnnouncement
|
||||||
|
year={year.toString()}
|
||||||
|
state={state}
|
||||||
|
onDismiss={handleClose}
|
||||||
|
onRequestBuild={handleBuildRequest}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -0,0 +1,55 @@
|
|||||||
|
import { useCallback } from 'react';
|
||||||
|
import type { FC } from 'react';
|
||||||
|
|
||||||
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
|
import { openModal } from '@/flavours/glitch/actions/modal';
|
||||||
|
import { Icon } from '@/flavours/glitch/components/icon';
|
||||||
|
import { selectWrapstodonYear } from '@/flavours/glitch/reducers/slices/annual_report';
|
||||||
|
import {
|
||||||
|
createAppSelector,
|
||||||
|
useAppDispatch,
|
||||||
|
useAppSelector,
|
||||||
|
} from '@/flavours/glitch/store';
|
||||||
|
import IconPlanet from '@/images/icons/icon_planet.svg?react';
|
||||||
|
|
||||||
|
import classes from './index.module.scss';
|
||||||
|
|
||||||
|
const selectReportModalOpen = createAppSelector(
|
||||||
|
[(state) => state.modal.getIn(['stack', 0, 'modalType'])],
|
||||||
|
(modalType) => modalType === 'ANNUAL_REPORT',
|
||||||
|
);
|
||||||
|
|
||||||
|
export const AnnualReportNavItem: FC = () => {
|
||||||
|
const { state } = useAppSelector((state) => state.annualReport);
|
||||||
|
const year = useAppSelector(selectWrapstodonYear);
|
||||||
|
const active = useAppSelector(selectReportModalOpen);
|
||||||
|
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
const handleClick = useCallback(() => {
|
||||||
|
dispatch(openModal({ modalType: 'ANNUAL_REPORT', modalProps: {} }));
|
||||||
|
}, [dispatch]);
|
||||||
|
|
||||||
|
if (!year || !state || state === 'ineligible') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
type='button'
|
||||||
|
className={classNames('column-link column-link--transparent', { active })}
|
||||||
|
onClick={handleClick}
|
||||||
|
>
|
||||||
|
<Icon icon={IconPlanet} id='wrapstodon-planet' width='24' height='24' />
|
||||||
|
<span>Wrapstodon {year}</span>
|
||||||
|
<span className={classNames('column-link__badge', classes.navItemBadge)}>
|
||||||
|
<FormattedMessage
|
||||||
|
id='annual_report.nav_item.badge'
|
||||||
|
defaultMessage='New'
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -51,6 +51,8 @@ import { canViewFeed } from 'flavours/glitch/permissions';
|
|||||||
import { selectUnreadNotificationGroupsCount } from 'flavours/glitch/selectors/notifications';
|
import { selectUnreadNotificationGroupsCount } from 'flavours/glitch/selectors/notifications';
|
||||||
import { useAppSelector, useAppDispatch } from 'flavours/glitch/store';
|
import { useAppSelector, useAppDispatch } from 'flavours/glitch/store';
|
||||||
|
|
||||||
|
import { AnnualReportNavItem } from '../annual_report/nav_item';
|
||||||
|
|
||||||
import { DisabledAccountBanner } from './components/disabled_account_banner';
|
import { DisabledAccountBanner } from './components/disabled_account_banner';
|
||||||
import { FollowedTagsPanel } from './components/followed_tags_panel';
|
import { FollowedTagsPanel } from './components/followed_tags_panel';
|
||||||
import { ListPanel } from './components/list_panel';
|
import { ListPanel } from './components/list_panel';
|
||||||
@ -318,6 +320,8 @@ export const NavigationPanel: React.FC<{ multiColumn?: boolean }> = ({
|
|||||||
|
|
||||||
<FollowRequestsLink />
|
<FollowRequestsLink />
|
||||||
|
|
||||||
|
<AnnualReportNavItem />
|
||||||
|
|
||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
<ListPanel />
|
<ListPanel />
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user