2023-02-11 03:37:42 +03:00
|
|
|
import './app.css';
|
|
|
|
|
2023-01-20 20:04:27 +03:00
|
|
|
import {
|
|
|
|
useEffect,
|
|
|
|
useLayoutEffect,
|
|
|
|
useMemo,
|
|
|
|
useRef,
|
|
|
|
useState,
|
|
|
|
} from 'preact/hooks';
|
2023-02-09 17:27:49 +03:00
|
|
|
import {
|
|
|
|
matchPath,
|
|
|
|
Route,
|
|
|
|
Routes,
|
|
|
|
useLocation,
|
|
|
|
useNavigate,
|
2023-04-18 14:47:21 +03:00
|
|
|
useParams,
|
2023-02-09 17:27:49 +03:00
|
|
|
} from 'react-router-dom';
|
2023-04-25 15:41:08 +03:00
|
|
|
import 'swiped-events';
|
2022-12-10 12:14:48 +03:00
|
|
|
import { useSnapshot } from 'valtio';
|
|
|
|
|
2023-03-11 09:05:56 +03:00
|
|
|
import AccountSheet from './components/account-sheet';
|
2022-12-10 12:14:48 +03:00
|
|
|
import Compose from './components/compose';
|
2023-01-13 10:30:09 +03:00
|
|
|
import Drafts from './components/drafts';
|
2023-08-13 12:15:49 +03:00
|
|
|
import Icon, { ICONS } from './components/icon';
|
2022-12-10 12:14:48 +03:00
|
|
|
import Loader from './components/loader';
|
2023-01-29 10:23:53 +03:00
|
|
|
import MediaModal from './components/media-modal';
|
2022-12-10 12:14:48 +03:00
|
|
|
import Modal from './components/modal';
|
2023-02-16 12:51:54 +03:00
|
|
|
import Shortcuts from './components/shortcuts';
|
|
|
|
import ShortcutsSettings from './components/shortcuts-settings';
|
2023-01-28 13:52:18 +03:00
|
|
|
import NotFound from './pages/404';
|
2023-01-29 18:37:13 +03:00
|
|
|
import AccountStatuses from './pages/account-statuses';
|
2023-03-07 19:32:33 +03:00
|
|
|
import Accounts from './pages/accounts';
|
2023-01-20 19:23:59 +03:00
|
|
|
import Bookmarks from './pages/bookmarks';
|
2023-01-28 13:52:18 +03:00
|
|
|
import Favourites from './pages/favourites';
|
2023-02-11 11:48:47 +03:00
|
|
|
import FollowedHashtags from './pages/followed-hashtags';
|
2023-02-03 16:08:08 +03:00
|
|
|
import Following from './pages/following';
|
2023-02-18 15:48:24 +03:00
|
|
|
import Hashtag from './pages/hashtag';
|
2022-12-10 12:14:48 +03:00
|
|
|
import Home from './pages/home';
|
2023-04-17 14:00:41 +03:00
|
|
|
import HttpRoute from './pages/HttpRoute';
|
2023-02-10 19:05:18 +03:00
|
|
|
import List from './pages/list';
|
2023-01-28 13:52:18 +03:00
|
|
|
import Lists from './pages/lists';
|
2022-12-10 12:14:48 +03:00
|
|
|
import Login from './pages/login';
|
2023-04-06 14:32:26 +03:00
|
|
|
import Mentions from './pages/mentions';
|
2022-12-10 12:14:48 +03:00
|
|
|
import Notifications from './pages/notifications';
|
2023-01-28 13:52:18 +03:00
|
|
|
import Public from './pages/public';
|
2023-02-10 17:10:13 +03:00
|
|
|
import Search from './pages/search';
|
2022-12-10 12:14:48 +03:00
|
|
|
import Settings from './pages/settings';
|
|
|
|
import Status from './pages/status';
|
2023-04-05 20:14:38 +03:00
|
|
|
import Trending from './pages/trending';
|
2022-12-10 12:14:48 +03:00
|
|
|
import Welcome from './pages/welcome';
|
2023-02-09 18:59:57 +03:00
|
|
|
import {
|
|
|
|
api,
|
|
|
|
initAccount,
|
|
|
|
initClient,
|
|
|
|
initInstance,
|
|
|
|
initPreferences,
|
|
|
|
} from './utils/api';
|
2022-12-10 12:14:48 +03:00
|
|
|
import { getAccessToken } from './utils/auth';
|
2023-05-06 12:12:13 +03:00
|
|
|
import openCompose from './utils/open-compose';
|
2023-02-26 19:55:04 +03:00
|
|
|
import showToast from './utils/show-toast';
|
2023-02-06 11:35:03 +03:00
|
|
|
import states, { getStatus, saveStatus } from './utils/states';
|
2022-12-10 12:14:48 +03:00
|
|
|
import store from './utils/store';
|
2023-02-05 19:17:19 +03:00
|
|
|
import { getCurrentAccount } from './utils/store-utils';
|
2023-02-28 10:27:42 +03:00
|
|
|
import useInterval from './utils/useInterval';
|
2023-02-12 12:38:50 +03:00
|
|
|
import usePageVisibility from './utils/usePageVisibility';
|
2022-12-10 12:14:48 +03:00
|
|
|
|
2022-12-13 15:42:09 +03:00
|
|
|
window.__STATES__ = states;
|
2022-12-10 12:14:48 +03:00
|
|
|
|
2023-08-13 12:15:49 +03:00
|
|
|
// Preload icons
|
|
|
|
// There's probably a better way to do this
|
|
|
|
// Related: https://github.com/vitejs/vite/issues/10600
|
|
|
|
setTimeout(() => {
|
|
|
|
for (const icon in ICONS) {
|
|
|
|
if (Array.isArray(ICONS[icon])) {
|
|
|
|
ICONS[icon][0]?.();
|
|
|
|
} else {
|
|
|
|
ICONS[icon]?.();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}, 5000);
|
|
|
|
|
2022-12-31 20:46:08 +03:00
|
|
|
function App() {
|
2022-12-10 12:14:48 +03:00
|
|
|
const snapStates = useSnapshot(states);
|
|
|
|
const [isLoggedIn, setIsLoggedIn] = useState(false);
|
2022-12-21 15:34:24 +03:00
|
|
|
const [uiState, setUIState] = useState('loading');
|
2023-01-20 19:23:59 +03:00
|
|
|
const navigate = useNavigate();
|
2022-12-10 12:14:48 +03:00
|
|
|
|
|
|
|
useLayoutEffect(() => {
|
|
|
|
const theme = store.local.get('theme');
|
|
|
|
if (theme) {
|
|
|
|
document.documentElement.classList.add(`is-${theme}`);
|
|
|
|
document
|
|
|
|
.querySelector('meta[name="color-scheme"]')
|
2023-02-05 19:17:19 +03:00
|
|
|
.setAttribute('content', theme === 'auto' ? 'dark light' : theme);
|
2022-12-10 12:14:48 +03:00
|
|
|
}
|
2023-03-08 12:17:23 +03:00
|
|
|
const textSize = store.local.get('textSize');
|
|
|
|
if (textSize) {
|
|
|
|
document.documentElement.style.setProperty(
|
|
|
|
'--text-size',
|
|
|
|
`${textSize}px`,
|
|
|
|
);
|
|
|
|
}
|
2022-12-10 12:14:48 +03:00
|
|
|
}, []);
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
const instanceURL = store.local.get('instanceURL');
|
2023-05-20 05:08:41 +03:00
|
|
|
const code = decodeURIComponent(
|
|
|
|
(window.location.search.match(/code=([^&]+)/) || [, ''])[1],
|
|
|
|
);
|
2022-12-10 12:14:48 +03:00
|
|
|
|
|
|
|
if (code) {
|
|
|
|
console.log({ code });
|
|
|
|
// Clear the code from the URL
|
|
|
|
window.history.replaceState({}, document.title, '/');
|
|
|
|
|
|
|
|
const clientID = store.session.get('clientID');
|
|
|
|
const clientSecret = store.session.get('clientSecret');
|
|
|
|
|
|
|
|
(async () => {
|
|
|
|
setUIState('loading');
|
2023-02-05 19:17:19 +03:00
|
|
|
const { access_token: accessToken } = await getAccessToken({
|
2022-12-10 12:14:48 +03:00
|
|
|
instanceURL,
|
|
|
|
client_id: clientID,
|
|
|
|
client_secret: clientSecret,
|
|
|
|
code,
|
|
|
|
});
|
|
|
|
|
2023-02-05 19:17:19 +03:00
|
|
|
const masto = initClient({ instance: instanceURL, accessToken });
|
|
|
|
await Promise.allSettled([
|
2023-05-09 13:48:19 +03:00
|
|
|
initInstance(masto, instanceURL),
|
2023-02-05 19:17:19 +03:00
|
|
|
initAccount(masto, instanceURL, accessToken),
|
|
|
|
]);
|
2023-02-09 18:59:57 +03:00
|
|
|
initPreferences(masto);
|
2022-12-10 12:14:48 +03:00
|
|
|
|
|
|
|
setIsLoggedIn(true);
|
|
|
|
setUIState('default');
|
|
|
|
})();
|
2022-12-21 15:34:24 +03:00
|
|
|
} else {
|
2023-02-05 19:17:19 +03:00
|
|
|
const account = getCurrentAccount();
|
|
|
|
if (account) {
|
|
|
|
store.session.set('currentAccount', account.info.id);
|
2023-05-09 13:48:19 +03:00
|
|
|
const { masto, instance } = api({ account });
|
2023-02-12 12:38:50 +03:00
|
|
|
console.log('masto', masto);
|
2023-02-09 18:59:57 +03:00
|
|
|
initPreferences(masto);
|
2023-02-10 06:35:06 +03:00
|
|
|
setUIState('loading');
|
2023-02-07 19:31:46 +03:00
|
|
|
(async () => {
|
2023-02-10 06:35:06 +03:00
|
|
|
try {
|
2023-05-09 13:48:19 +03:00
|
|
|
await initInstance(masto, instance);
|
2023-02-10 06:35:06 +03:00
|
|
|
} catch (e) {
|
|
|
|
} finally {
|
|
|
|
setIsLoggedIn(true);
|
|
|
|
setUIState('default');
|
|
|
|
}
|
2023-02-07 19:31:46 +03:00
|
|
|
})();
|
2023-02-10 07:29:07 +03:00
|
|
|
} else {
|
|
|
|
setUIState('default');
|
2023-02-05 19:17:19 +03:00
|
|
|
}
|
2022-12-10 12:14:48 +03:00
|
|
|
}
|
|
|
|
}, []);
|
|
|
|
|
2023-01-20 19:23:59 +03:00
|
|
|
let location = useLocation();
|
2023-01-27 15:54:18 +03:00
|
|
|
states.currentLocation = location.pathname;
|
|
|
|
|
2022-12-30 15:37:57 +03:00
|
|
|
const focusDeck = () => {
|
|
|
|
let timer = setTimeout(() => {
|
2023-03-01 10:47:19 +03:00
|
|
|
const columns = document.getElementById('columns');
|
|
|
|
if (columns) {
|
|
|
|
// Focus first column
|
2023-04-07 15:22:32 +03:00
|
|
|
// columns.querySelector('.deck-container')?.focus?.();
|
2023-03-01 10:47:19 +03:00
|
|
|
} else {
|
2023-03-10 11:49:16 +03:00
|
|
|
const backDrop = document.querySelector('.deck-backdrop');
|
|
|
|
if (backDrop) return;
|
2023-03-01 10:47:19 +03:00
|
|
|
// Focus last deck
|
|
|
|
const pages = document.querySelectorAll('.deck-container');
|
|
|
|
const page = pages[pages.length - 1]; // last one
|
|
|
|
if (page && page.tabIndex === -1) {
|
|
|
|
console.log('FOCUS', page);
|
|
|
|
page.focus();
|
|
|
|
}
|
2022-12-30 15:37:57 +03:00
|
|
|
}
|
|
|
|
}, 100);
|
|
|
|
return () => clearTimeout(timer);
|
|
|
|
};
|
2023-01-20 19:23:59 +03:00
|
|
|
useEffect(focusDeck, [location]);
|
2023-02-16 12:51:54 +03:00
|
|
|
const showModal =
|
|
|
|
snapStates.showCompose ||
|
|
|
|
snapStates.showSettings ||
|
2023-03-07 19:32:33 +03:00
|
|
|
snapStates.showAccounts ||
|
2023-02-16 12:51:54 +03:00
|
|
|
snapStates.showAccount ||
|
|
|
|
snapStates.showDrafts ||
|
|
|
|
snapStates.showMediaModal ||
|
|
|
|
snapStates.showShortcutsSettings;
|
2022-12-30 15:37:57 +03:00
|
|
|
useEffect(() => {
|
2023-02-16 12:51:54 +03:00
|
|
|
if (!showModal) focusDeck();
|
2023-02-13 05:43:12 +03:00
|
|
|
}, [showModal]);
|
2022-12-10 12:14:48 +03:00
|
|
|
|
2023-01-21 05:08:55 +03:00
|
|
|
const { prevLocation } = snapStates;
|
|
|
|
const backgroundLocation = useRef(prevLocation || null);
|
2023-02-09 11:03:10 +03:00
|
|
|
const isModalPage =
|
|
|
|
matchPath('/:instance/s/:id', location.pathname) ||
|
|
|
|
matchPath('/s/:id', location.pathname);
|
2023-01-21 05:08:55 +03:00
|
|
|
if (isModalPage) {
|
|
|
|
if (!backgroundLocation.current) backgroundLocation.current = prevLocation;
|
|
|
|
} else {
|
|
|
|
backgroundLocation.current = null;
|
|
|
|
}
|
|
|
|
console.debug({
|
|
|
|
backgroundLocation: backgroundLocation.current,
|
|
|
|
location,
|
|
|
|
});
|
2023-01-20 19:23:59 +03:00
|
|
|
|
2023-04-17 13:56:09 +03:00
|
|
|
if (/\/https?:/.test(location.pathname)) {
|
|
|
|
return <HttpRoute />;
|
|
|
|
}
|
|
|
|
|
2023-01-20 19:23:59 +03:00
|
|
|
const nonRootLocation = useMemo(() => {
|
|
|
|
const { pathname } = location;
|
2023-02-05 19:17:19 +03:00
|
|
|
return !/^\/(login|welcome)/.test(pathname);
|
2023-01-20 19:23:59 +03:00
|
|
|
}, [location]);
|
|
|
|
|
2023-04-18 12:46:40 +03:00
|
|
|
// Change #app dataset based on snapStates.settings.shortcutsViewMode
|
2023-04-02 10:18:08 +03:00
|
|
|
useEffect(() => {
|
|
|
|
const $app = document.getElementById('app');
|
|
|
|
if ($app) {
|
|
|
|
$app.dataset.shortcutsViewMode = snapStates.settings.shortcutsViewMode;
|
|
|
|
}
|
|
|
|
}, [snapStates.settings.shortcutsViewMode]);
|
|
|
|
|
2023-04-23 07:08:41 +03:00
|
|
|
// Add/Remove cloak class to body
|
|
|
|
useEffect(() => {
|
|
|
|
const $body = document.body;
|
|
|
|
$body.classList.toggle('cloak', snapStates.settings.cloakMode);
|
|
|
|
}, [snapStates.settings.cloakMode]);
|
|
|
|
|
2022-12-10 12:14:48 +03:00
|
|
|
return (
|
|
|
|
<>
|
2023-01-20 19:23:59 +03:00
|
|
|
<Routes location={nonRootLocation || location}>
|
|
|
|
<Route
|
|
|
|
path="/"
|
|
|
|
element={
|
|
|
|
isLoggedIn ? (
|
|
|
|
<Home />
|
|
|
|
) : uiState === 'loading' ? (
|
2023-08-19 14:21:51 +03:00
|
|
|
<Loader id="loader-root" />
|
2023-01-20 19:23:59 +03:00
|
|
|
) : (
|
|
|
|
<Welcome />
|
|
|
|
)
|
2022-12-10 12:14:48 +03:00
|
|
|
}
|
2023-01-20 19:23:59 +03:00
|
|
|
/>
|
|
|
|
<Route path="/login" element={<Login />} />
|
|
|
|
<Route path="/welcome" element={<Welcome />} />
|
|
|
|
</Routes>
|
2023-01-20 20:04:27 +03:00
|
|
|
<Routes location={backgroundLocation.current || location}>
|
2023-01-20 19:23:59 +03:00
|
|
|
{isLoggedIn && (
|
|
|
|
<Route path="/notifications" element={<Notifications />} />
|
|
|
|
)}
|
2023-04-06 14:32:26 +03:00
|
|
|
{isLoggedIn && <Route path="/mentions" element={<Mentions />} />}
|
2023-02-18 16:37:34 +03:00
|
|
|
{isLoggedIn && <Route path="/following" element={<Following />} />}
|
2023-01-28 13:52:18 +03:00
|
|
|
{isLoggedIn && <Route path="/b" element={<Bookmarks />} />}
|
|
|
|
{isLoggedIn && <Route path="/f" element={<Favourites />} />}
|
2023-02-10 19:05:18 +03:00
|
|
|
{isLoggedIn && (
|
|
|
|
<Route path="/l">
|
|
|
|
<Route index element={<Lists />} />
|
|
|
|
<Route path=":id" element={<List />} />
|
|
|
|
</Route>
|
|
|
|
)}
|
2023-02-11 11:48:47 +03:00
|
|
|
{isLoggedIn && <Route path="/ft" element={<FollowedHashtags />} />}
|
2023-02-18 15:48:24 +03:00
|
|
|
<Route path="/:instance?/t/:hashtag" element={<Hashtag />} />
|
2023-02-06 14:54:18 +03:00
|
|
|
<Route path="/:instance?/a/:id" element={<AccountStatuses />} />
|
2023-02-06 15:17:07 +03:00
|
|
|
<Route path="/:instance?/p">
|
|
|
|
<Route index element={<Public />} />
|
|
|
|
<Route path="l" element={<Public local />} />
|
|
|
|
</Route>
|
2023-04-05 20:14:38 +03:00
|
|
|
<Route path="/:instance?/trending" element={<Trending />} />
|
2023-02-10 17:10:13 +03:00
|
|
|
<Route path="/:instance?/search" element={<Search />} />
|
2023-01-28 13:52:18 +03:00
|
|
|
{/* <Route path="/:anything" element={<NotFound />} /> */}
|
2023-01-20 19:23:59 +03:00
|
|
|
</Routes>
|
2023-04-18 14:47:21 +03:00
|
|
|
{uiState === 'default' && (
|
|
|
|
<Routes>
|
|
|
|
<Route path="/:instance?/s/:id" element={<StatusRoute />} />
|
|
|
|
</Routes>
|
|
|
|
)}
|
2023-05-08 07:53:27 +03:00
|
|
|
{isLoggedIn && (
|
|
|
|
<button
|
|
|
|
type="button"
|
|
|
|
id="compose-button"
|
|
|
|
onClick={(e) => {
|
|
|
|
if (e.shiftKey) {
|
|
|
|
const newWin = openCompose();
|
|
|
|
if (!newWin) {
|
|
|
|
alert('Looks like your browser is blocking popups.');
|
2023-05-06 12:12:13 +03:00
|
|
|
states.showCompose = true;
|
|
|
|
}
|
2023-05-08 07:53:27 +03:00
|
|
|
} else {
|
|
|
|
states.showCompose = true;
|
|
|
|
}
|
|
|
|
}}
|
|
|
|
>
|
|
|
|
<Icon icon="quill" size="xl" alt="Compose" />
|
|
|
|
</button>
|
|
|
|
)}
|
|
|
|
{isLoggedIn &&
|
|
|
|
!snapStates.settings.shortcutsColumnsMode &&
|
|
|
|
snapStates.settings.shortcutsViewMode !== 'multi-column' && (
|
|
|
|
<Shortcuts />
|
2023-05-07 17:19:19 +03:00
|
|
|
)}
|
2022-12-10 12:14:48 +03:00
|
|
|
{!!snapStates.showCompose && (
|
|
|
|
<Modal>
|
|
|
|
<Compose
|
|
|
|
replyToStatus={
|
|
|
|
typeof snapStates.showCompose !== 'boolean'
|
|
|
|
? snapStates.showCompose.replyToStatus
|
2023-01-02 07:03:06 +03:00
|
|
|
: window.__COMPOSE__?.replyToStatus || null
|
|
|
|
}
|
|
|
|
editStatus={
|
|
|
|
states.showCompose?.editStatus ||
|
|
|
|
window.__COMPOSE__?.editStatus ||
|
|
|
|
null
|
|
|
|
}
|
|
|
|
draftStatus={
|
|
|
|
states.showCompose?.draftStatus ||
|
|
|
|
window.__COMPOSE__?.draftStatus ||
|
|
|
|
null
|
2022-12-10 12:14:48 +03:00
|
|
|
}
|
2022-12-13 16:54:16 +03:00
|
|
|
onClose={(results) => {
|
2023-02-23 11:44:01 +03:00
|
|
|
const { newStatus, instance } = results || {};
|
2022-12-10 12:14:48 +03:00
|
|
|
states.showCompose = false;
|
2023-01-02 07:03:06 +03:00
|
|
|
window.__COMPOSE__ = null;
|
2022-12-13 16:54:16 +03:00
|
|
|
if (newStatus) {
|
2022-12-10 12:14:48 +03:00
|
|
|
states.reloadStatusPage++;
|
2023-02-26 19:55:04 +03:00
|
|
|
showToast({
|
2023-04-29 17:22:07 +03:00
|
|
|
text: 'Post published. Check it out.',
|
2023-02-26 19:55:04 +03:00
|
|
|
delay: 1000,
|
|
|
|
duration: 10_000, // 10 seconds
|
|
|
|
onClick: (toast) => {
|
|
|
|
toast.hideToast();
|
|
|
|
states.prevLocation = location;
|
|
|
|
navigate(
|
|
|
|
instance
|
|
|
|
? `/${instance}/s/${newStatus.id}`
|
|
|
|
: `/s/${newStatus.id}`,
|
|
|
|
);
|
|
|
|
},
|
|
|
|
});
|
2022-12-10 12:14:48 +03:00
|
|
|
}
|
|
|
|
}}
|
|
|
|
/>
|
|
|
|
</Modal>
|
|
|
|
)}
|
|
|
|
{!!snapStates.showSettings && (
|
|
|
|
<Modal
|
|
|
|
onClick={(e) => {
|
|
|
|
if (e.target === e.currentTarget) {
|
|
|
|
states.showSettings = false;
|
|
|
|
}
|
|
|
|
}}
|
|
|
|
>
|
|
|
|
<Settings
|
|
|
|
onClose={() => {
|
|
|
|
states.showSettings = false;
|
|
|
|
}}
|
|
|
|
/>
|
|
|
|
</Modal>
|
|
|
|
)}
|
2023-03-07 19:32:33 +03:00
|
|
|
{!!snapStates.showAccounts && (
|
|
|
|
<Modal
|
|
|
|
onClick={(e) => {
|
|
|
|
if (e.target === e.currentTarget) {
|
|
|
|
states.showAccounts = false;
|
|
|
|
}
|
|
|
|
}}
|
|
|
|
>
|
|
|
|
<Accounts
|
|
|
|
onClose={() => {
|
|
|
|
states.showAccounts = false;
|
|
|
|
}}
|
|
|
|
/>
|
|
|
|
</Modal>
|
|
|
|
)}
|
2022-12-10 12:14:48 +03:00
|
|
|
{!!snapStates.showAccount && (
|
|
|
|
<Modal
|
|
|
|
class="light"
|
|
|
|
onClick={(e) => {
|
|
|
|
if (e.target === e.currentTarget) {
|
|
|
|
states.showAccount = false;
|
|
|
|
}
|
|
|
|
}}
|
|
|
|
>
|
2023-03-11 09:05:56 +03:00
|
|
|
<AccountSheet
|
2023-02-05 19:17:19 +03:00
|
|
|
account={snapStates.showAccount?.account || snapStates.showAccount}
|
|
|
|
instance={snapStates.showAccount?.instance}
|
2023-03-11 13:13:53 +03:00
|
|
|
onClose={({ destination }) => {
|
2023-01-29 18:37:13 +03:00
|
|
|
states.showAccount = false;
|
2023-03-11 13:13:53 +03:00
|
|
|
if (destination) {
|
|
|
|
states.showAccounts = false;
|
|
|
|
}
|
2023-01-29 18:37:13 +03:00
|
|
|
}}
|
|
|
|
/>
|
2022-12-10 12:14:48 +03:00
|
|
|
</Modal>
|
|
|
|
)}
|
2023-01-13 10:30:09 +03:00
|
|
|
{!!snapStates.showDrafts && (
|
|
|
|
<Modal
|
|
|
|
onClick={(e) => {
|
|
|
|
if (e.target === e.currentTarget) {
|
|
|
|
states.showDrafts = false;
|
|
|
|
}
|
|
|
|
}}
|
|
|
|
>
|
2023-04-20 11:10:57 +03:00
|
|
|
<Drafts onClose={() => (states.showDrafts = false)} />
|
2023-01-13 10:30:09 +03:00
|
|
|
</Modal>
|
|
|
|
)}
|
2023-01-29 10:23:53 +03:00
|
|
|
{!!snapStates.showMediaModal && (
|
|
|
|
<Modal
|
|
|
|
onClick={(e) => {
|
|
|
|
if (
|
|
|
|
e.target === e.currentTarget ||
|
|
|
|
e.target.classList.contains('media')
|
|
|
|
) {
|
|
|
|
states.showMediaModal = false;
|
|
|
|
}
|
|
|
|
}}
|
|
|
|
>
|
|
|
|
<MediaModal
|
|
|
|
mediaAttachments={snapStates.showMediaModal.mediaAttachments}
|
2023-02-05 19:17:19 +03:00
|
|
|
instance={snapStates.showMediaModal.instance}
|
2023-01-29 10:23:53 +03:00
|
|
|
index={snapStates.showMediaModal.index}
|
|
|
|
statusID={snapStates.showMediaModal.statusID}
|
|
|
|
onClose={() => {
|
|
|
|
states.showMediaModal = false;
|
|
|
|
}}
|
|
|
|
/>
|
|
|
|
</Modal>
|
|
|
|
)}
|
2023-02-16 12:51:54 +03:00
|
|
|
{!!snapStates.showShortcutsSettings && (
|
|
|
|
<Modal
|
2023-04-08 17:16:58 +03:00
|
|
|
class="light"
|
2023-02-16 12:51:54 +03:00
|
|
|
onClick={(e) => {
|
|
|
|
if (e.target === e.currentTarget) {
|
|
|
|
states.showShortcutsSettings = false;
|
|
|
|
}
|
|
|
|
}}
|
|
|
|
>
|
2023-04-20 11:10:57 +03:00
|
|
|
<ShortcutsSettings
|
|
|
|
onClose={() => (states.showShortcutsSettings = false)}
|
|
|
|
/>
|
2023-02-16 12:51:54 +03:00
|
|
|
</Modal>
|
|
|
|
)}
|
2023-03-27 10:05:50 +03:00
|
|
|
<BackgroundService isLoggedIn={isLoggedIn} />
|
2022-12-10 12:14:48 +03:00
|
|
|
</>
|
|
|
|
);
|
|
|
|
}
|
2022-12-31 20:46:08 +03:00
|
|
|
|
2023-03-27 10:05:50 +03:00
|
|
|
function BackgroundService({ isLoggedIn }) {
|
|
|
|
// Notifications service
|
|
|
|
// - WebSocket to receive notifications when page is visible
|
|
|
|
const [visible, setVisible] = useState(true);
|
|
|
|
usePageVisibility(setVisible);
|
|
|
|
const notificationStream = useRef();
|
|
|
|
useEffect(() => {
|
|
|
|
if (isLoggedIn && visible) {
|
|
|
|
const { masto, instance } = api();
|
|
|
|
(async () => {
|
|
|
|
// 1. Get the latest notification
|
|
|
|
if (states.notificationsLast) {
|
|
|
|
const notificationsIterator = masto.v1.notifications.list({
|
|
|
|
limit: 1,
|
|
|
|
since_id: states.notificationsLast.id,
|
|
|
|
});
|
|
|
|
const { value: notifications } = await notificationsIterator.next();
|
|
|
|
if (notifications?.length) {
|
|
|
|
states.notificationsShowNew = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 2. Start streaming
|
|
|
|
notificationStream.current = await masto.ws.stream(
|
|
|
|
'/api/v1/streaming',
|
|
|
|
{
|
|
|
|
stream: 'user:notification',
|
|
|
|
},
|
|
|
|
);
|
|
|
|
console.log('🎏 Streaming notification', notificationStream.current);
|
|
|
|
|
|
|
|
notificationStream.current.on('notification', (notification) => {
|
|
|
|
console.log('🔔🔔 Notification', notification);
|
|
|
|
if (notification.status) {
|
|
|
|
saveStatus(notification.status, instance, {
|
|
|
|
skipThreading: true,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
states.notificationsShowNew = true;
|
|
|
|
});
|
|
|
|
|
|
|
|
notificationStream.current.ws.onclose = () => {
|
|
|
|
console.log('🔔🔔 Notification stream closed');
|
|
|
|
};
|
|
|
|
})();
|
|
|
|
}
|
|
|
|
return () => {
|
|
|
|
if (notificationStream.current) {
|
|
|
|
notificationStream.current.ws.close();
|
|
|
|
notificationStream.current = null;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}, [visible, isLoggedIn]);
|
|
|
|
|
|
|
|
// Check for updates service
|
|
|
|
const lastCheckDate = useRef();
|
|
|
|
const checkForUpdates = () => {
|
|
|
|
lastCheckDate.current = Date.now();
|
|
|
|
console.log('✨ Check app update');
|
|
|
|
fetch('./version.json')
|
|
|
|
.then((r) => r.json())
|
|
|
|
.then((info) => {
|
|
|
|
if (info) states.appVersion = info;
|
|
|
|
})
|
|
|
|
.catch((e) => {
|
|
|
|
console.error(e);
|
|
|
|
});
|
|
|
|
};
|
2023-05-05 12:53:34 +03:00
|
|
|
useInterval(checkForUpdates, visible && 1000 * 60 * 30); // 30 minutes
|
2023-03-27 10:05:50 +03:00
|
|
|
usePageVisibility((visible) => {
|
|
|
|
if (visible) {
|
|
|
|
if (!lastCheckDate.current) {
|
|
|
|
checkForUpdates();
|
|
|
|
} else {
|
|
|
|
const diff = Date.now() - lastCheckDate.current;
|
|
|
|
if (diff > 1000 * 60 * 60) {
|
|
|
|
// 1 hour
|
|
|
|
checkForUpdates();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2023-04-18 14:47:21 +03:00
|
|
|
function StatusRoute() {
|
|
|
|
const params = useParams();
|
|
|
|
const { id, instance } = params;
|
|
|
|
return <Status id={id} instance={instance} />;
|
|
|
|
}
|
|
|
|
|
2022-12-31 20:46:08 +03:00
|
|
|
export { App };
|