mirror of
https://github.com/cheeaun/phanpy.git
synced 2025-01-04 00:27:19 +03:00
231 lines
6.3 KiB
JavaScript
231 lines
6.3 KiB
JavaScript
import './notifications-menu.css';
|
|
|
|
import { ControlledMenu } from '@szhsin/react-menu';
|
|
import { memo } from 'preact/compat';
|
|
import { useEffect, useRef, useState } from 'preact/hooks';
|
|
import { useSnapshot } from 'valtio';
|
|
|
|
import Columns from '../components/columns';
|
|
import Icon from '../components/icon';
|
|
import Link from '../components/link';
|
|
import Loader from '../components/loader';
|
|
import Notification from '../components/notification';
|
|
import { api } from '../utils/api';
|
|
import db from '../utils/db';
|
|
import groupNotifications from '../utils/group-notifications';
|
|
import openCompose from '../utils/open-compose';
|
|
import states, { saveStatus } from '../utils/states';
|
|
import { getCurrentAccountNS } from '../utils/store-utils';
|
|
|
|
import Following from './following';
|
|
|
|
function Home() {
|
|
const snapStates = useSnapshot(states);
|
|
useEffect(() => {
|
|
(async () => {
|
|
const keys = await db.drafts.keys();
|
|
if (keys.length) {
|
|
const ns = getCurrentAccountNS();
|
|
const ownKeys = keys.filter((key) => key.startsWith(ns));
|
|
if (ownKeys.length) {
|
|
states.showDrafts = true;
|
|
}
|
|
}
|
|
})();
|
|
}, []);
|
|
|
|
return (
|
|
<>
|
|
{(snapStates.settings.shortcutsColumnsMode ||
|
|
snapStates.settings.shortcutsViewMode === 'multi-column') &&
|
|
!!snapStates.shortcuts?.length ? (
|
|
<Columns />
|
|
) : (
|
|
<Following
|
|
title="Home"
|
|
path="/"
|
|
id="home"
|
|
headerStart={false}
|
|
headerEnd={<NotificationsLink />}
|
|
/>
|
|
)}
|
|
{/* <button
|
|
// hidden={scrollDirection === 'end' && !nearReachStart}
|
|
type="button"
|
|
id="compose-button"
|
|
onClick={(e) => {
|
|
if (e.shiftKey) {
|
|
const newWin = openCompose();
|
|
if (!newWin) {
|
|
alert('Looks like your browser is blocking popups.');
|
|
states.showCompose = true;
|
|
}
|
|
} else {
|
|
states.showCompose = true;
|
|
}
|
|
}}
|
|
>
|
|
<Icon icon="quill" size="xl" alt="Compose" />
|
|
</button> */}
|
|
</>
|
|
);
|
|
}
|
|
|
|
function NotificationsLink() {
|
|
const snapStates = useSnapshot(states);
|
|
const notificationLinkRef = useRef();
|
|
const [menuState, setMenuState] = useState(undefined);
|
|
return (
|
|
<>
|
|
<Link
|
|
ref={notificationLinkRef}
|
|
to="/notifications"
|
|
class={`button plain notifications-button ${
|
|
snapStates.notificationsShowNew ? 'has-badge' : ''
|
|
} ${menuState}`}
|
|
onClick={(e) => {
|
|
e.stopPropagation();
|
|
if (window.matchMedia('(min-width: calc(40em))').matches) {
|
|
e.preventDefault();
|
|
setMenuState((state) => (!state ? 'open' : undefined));
|
|
}
|
|
}}
|
|
>
|
|
<Icon icon="notification" size="l" alt="Notifications" />
|
|
</Link>
|
|
<NotificationsMenu
|
|
state={menuState}
|
|
anchorRef={notificationLinkRef}
|
|
onClose={() => setMenuState(undefined)}
|
|
/>
|
|
</>
|
|
);
|
|
}
|
|
|
|
const NOTIFICATIONS_LIMIT = 30;
|
|
const NOTIFICATIONS_DISPLAY_LIMIT = 5;
|
|
function NotificationsMenu({ anchorRef, state, onClose }) {
|
|
const { masto, instance } = api();
|
|
const snapStates = useSnapshot(states);
|
|
const [uiState, setUIState] = useState('default');
|
|
|
|
const notificationsIterator = masto.v1.notifications.list({
|
|
limit: NOTIFICATIONS_LIMIT,
|
|
});
|
|
|
|
async function fetchNotifications() {
|
|
const allNotifications = await notificationsIterator.next();
|
|
const notifications = allNotifications.value;
|
|
|
|
if (notifications?.length) {
|
|
notifications.forEach((notification) => {
|
|
saveStatus(notification.status, instance, {
|
|
skipThreading: true,
|
|
});
|
|
});
|
|
|
|
const groupedNotifications = groupNotifications(notifications);
|
|
|
|
states.notificationsLast = notifications[0];
|
|
states.notifications = groupedNotifications;
|
|
}
|
|
|
|
states.notificationsShowNew = false;
|
|
states.notificationsLastFetchTime = Date.now();
|
|
return allNotifications;
|
|
}
|
|
|
|
const [hasFollowRequests, setHasFollowRequests] = useState(false);
|
|
function fetchFollowRequests() {
|
|
return masto.v1.followRequests.list({
|
|
limit: 1,
|
|
});
|
|
}
|
|
|
|
function loadNotifications() {
|
|
setUIState('loading');
|
|
(async () => {
|
|
try {
|
|
await fetchNotifications();
|
|
const followRequests = await fetchFollowRequests();
|
|
setHasFollowRequests(!!followRequests?.length);
|
|
setUIState('default');
|
|
} catch (e) {
|
|
setUIState('error');
|
|
}
|
|
})();
|
|
}
|
|
|
|
useEffect(() => {
|
|
if (state === 'open') loadNotifications();
|
|
}, [state]);
|
|
|
|
return (
|
|
<ControlledMenu
|
|
menuClassName="notifications-menu"
|
|
state={state}
|
|
anchorRef={anchorRef}
|
|
onClose={onClose}
|
|
portal={{
|
|
target: document.body,
|
|
}}
|
|
overflow="auto"
|
|
viewScroll="close"
|
|
position="anchor"
|
|
align="center"
|
|
boundingBoxPadding="8 8 8 8"
|
|
>
|
|
<header>
|
|
<h2>Notifications</h2>
|
|
</header>
|
|
<main>
|
|
{snapStates.notifications.length ? (
|
|
<>
|
|
{snapStates.notifications
|
|
.slice(0, NOTIFICATIONS_DISPLAY_LIMIT)
|
|
.map((notification) => (
|
|
<Notification
|
|
key={notification.id}
|
|
instance={instance}
|
|
notification={notification}
|
|
/>
|
|
))}
|
|
</>
|
|
) : uiState === 'loading' ? (
|
|
<div class="ui-state">
|
|
<Loader abrupt />
|
|
</div>
|
|
) : (
|
|
uiState === 'error' && (
|
|
<div class="ui-state">
|
|
<p>Unable to fetch notifications.</p>
|
|
<p>
|
|
<button type="button" onClick={loadNotifications}>
|
|
Try again
|
|
</button>
|
|
</p>
|
|
</div>
|
|
)
|
|
)}
|
|
</main>
|
|
<footer>
|
|
<Link to="/mentions" class="button plain">
|
|
<Icon icon="at" /> <span>Mentions</span>
|
|
</Link>
|
|
<Link to="/notifications" class="button plain2">
|
|
{hasFollowRequests ? (
|
|
<>
|
|
<span class="tag collapsed">New</span>{' '}
|
|
<span>Follow Requests</span>
|
|
</>
|
|
) : (
|
|
<b>See all</b>
|
|
)}{' '}
|
|
<Icon icon="arrow-right" />
|
|
</Link>
|
|
</footer>
|
|
</ControlledMenu>
|
|
);
|
|
}
|
|
|
|
export default memo(Home);
|