|
|
@ -1,16 +1,15 @@
|
|
|
|
'use strict';
|
|
|
|
'use strict';
|
|
|
|
|
|
|
|
|
|
|
|
import React, { useState, useEffect, useRef } from 'react';
|
|
|
|
import clsx from 'clsx';
|
|
|
|
|
|
|
|
import React, { useEffect, useRef } from 'react';
|
|
|
|
import { HotKeys } from 'react-hotkeys';
|
|
|
|
import { HotKeys } from 'react-hotkeys';
|
|
|
|
import { useIntl } from 'react-intl';
|
|
|
|
|
|
|
|
import { Switch, useHistory, useLocation, Redirect } from 'react-router-dom';
|
|
|
|
import { Switch, useHistory, useLocation, Redirect } from 'react-router-dom';
|
|
|
|
|
|
|
|
|
|
|
|
import { fetchFollowRequests } from 'soapbox/actions/accounts';
|
|
|
|
import { fetchFollowRequests } from 'soapbox/actions/accounts';
|
|
|
|
import { fetchReports, fetchUsers, fetchConfig } from 'soapbox/actions/admin';
|
|
|
|
import { fetchReports, fetchUsers, fetchConfig } from 'soapbox/actions/admin';
|
|
|
|
import { fetchAnnouncements } from 'soapbox/actions/announcements';
|
|
|
|
import { fetchAnnouncements } from 'soapbox/actions/announcements';
|
|
|
|
import { uploadCompose, resetCompose } from 'soapbox/actions/compose';
|
|
|
|
import { resetCompose } from 'soapbox/actions/compose';
|
|
|
|
import { fetchCustomEmojis } from 'soapbox/actions/custom-emojis';
|
|
|
|
import { fetchCustomEmojis } from 'soapbox/actions/custom-emojis';
|
|
|
|
import { uploadEventBanner } from 'soapbox/actions/events';
|
|
|
|
|
|
|
|
import { fetchFilters } from 'soapbox/actions/filters';
|
|
|
|
import { fetchFilters } from 'soapbox/actions/filters';
|
|
|
|
import { fetchMarker } from 'soapbox/actions/markers';
|
|
|
|
import { fetchMarker } from 'soapbox/actions/markers';
|
|
|
|
import { openModal } from 'soapbox/actions/modals';
|
|
|
|
import { openModal } from 'soapbox/actions/modals';
|
|
|
@ -26,7 +25,7 @@ import SidebarNavigation from 'soapbox/components/sidebar-navigation';
|
|
|
|
import ThumbNavigation from 'soapbox/components/thumb-navigation';
|
|
|
|
import ThumbNavigation from 'soapbox/components/thumb-navigation';
|
|
|
|
import { Layout } from 'soapbox/components/ui';
|
|
|
|
import { Layout } from 'soapbox/components/ui';
|
|
|
|
import { useStatContext } from 'soapbox/contexts/stat-context';
|
|
|
|
import { useStatContext } from 'soapbox/contexts/stat-context';
|
|
|
|
import { useAppDispatch, useAppSelector, useOwnAccount, useSoapboxConfig, useFeatures, useInstance } from 'soapbox/hooks';
|
|
|
|
import { useAppDispatch, useAppSelector, useOwnAccount, useSoapboxConfig, useFeatures, useInstance, useDraggedFiles } from 'soapbox/hooks';
|
|
|
|
import AdminPage from 'soapbox/pages/admin-page';
|
|
|
|
import AdminPage from 'soapbox/pages/admin-page';
|
|
|
|
import ChatsPage from 'soapbox/pages/chats-page';
|
|
|
|
import ChatsPage from 'soapbox/pages/chats-page';
|
|
|
|
import DefaultPage from 'soapbox/pages/default-page';
|
|
|
|
import DefaultPage from 'soapbox/pages/default-page';
|
|
|
@ -101,7 +100,6 @@ import {
|
|
|
|
FollowRecommendations,
|
|
|
|
FollowRecommendations,
|
|
|
|
Directory,
|
|
|
|
Directory,
|
|
|
|
SidebarMenu,
|
|
|
|
SidebarMenu,
|
|
|
|
UploadArea,
|
|
|
|
|
|
|
|
ProfileHoverCard,
|
|
|
|
ProfileHoverCard,
|
|
|
|
StatusHoverCard,
|
|
|
|
StatusHoverCard,
|
|
|
|
Share,
|
|
|
|
Share,
|
|
|
@ -387,16 +385,12 @@ interface IUI {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const UI: React.FC<IUI> = ({ children }) => {
|
|
|
|
const UI: React.FC<IUI> = ({ children }) => {
|
|
|
|
const intl = useIntl();
|
|
|
|
|
|
|
|
const history = useHistory();
|
|
|
|
const history = useHistory();
|
|
|
|
const dispatch = useAppDispatch();
|
|
|
|
const dispatch = useAppDispatch();
|
|
|
|
const { data: pendingPolicy } = usePendingPolicy();
|
|
|
|
const { data: pendingPolicy } = usePendingPolicy();
|
|
|
|
const instance = useInstance();
|
|
|
|
const instance = useInstance();
|
|
|
|
const statContext = useStatContext();
|
|
|
|
const statContext = useStatContext();
|
|
|
|
|
|
|
|
|
|
|
|
const [draggingOver, setDraggingOver] = useState<boolean>(false);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const dragTargets = useRef<EventTarget[]>([]);
|
|
|
|
|
|
|
|
const disconnect = useRef<any>(null);
|
|
|
|
const disconnect = useRef<any>(null);
|
|
|
|
const node = useRef<HTMLDivElement | null>(null);
|
|
|
|
const node = useRef<HTMLDivElement | null>(null);
|
|
|
|
const hotkeys = useRef<HTMLDivElement | null>(null);
|
|
|
|
const hotkeys = useRef<HTMLDivElement | null>(null);
|
|
|
@ -411,74 +405,7 @@ const UI: React.FC<IUI> = ({ children }) => {
|
|
|
|
const streamingUrl = instance.urls.get('streaming_api');
|
|
|
|
const streamingUrl = instance.urls.get('streaming_api');
|
|
|
|
const standalone = useAppSelector(isStandalone);
|
|
|
|
const standalone = useAppSelector(isStandalone);
|
|
|
|
|
|
|
|
|
|
|
|
const handleDragEnter = (e: DragEvent) => {
|
|
|
|
const { isDragging } = useDraggedFiles(node);
|
|
|
|
e.preventDefault();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (e.target && !dragTargets.current.includes(e.target)) {
|
|
|
|
|
|
|
|
dragTargets.current.push(e.target);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (e.dataTransfer && Array.from(e.dataTransfer.types).includes('Files')) {
|
|
|
|
|
|
|
|
setDraggingOver(true);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const handleDragOver = (e: DragEvent) => {
|
|
|
|
|
|
|
|
if (dataTransferIsText(e.dataTransfer)) return false;
|
|
|
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
|
|
|
e.stopPropagation();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
if (e.dataTransfer) {
|
|
|
|
|
|
|
|
e.dataTransfer.dropEffect = 'copy';
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
} catch (err) {
|
|
|
|
|
|
|
|
// Do nothing
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const handleDrop = (e: DragEvent) => {
|
|
|
|
|
|
|
|
if (!me) return;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (dataTransferIsText(e.dataTransfer)) return;
|
|
|
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
setDraggingOver(false);
|
|
|
|
|
|
|
|
dragTargets.current = [];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
dispatch((_, getState) => {
|
|
|
|
|
|
|
|
if (e.dataTransfer && e.dataTransfer.files.length >= 1) {
|
|
|
|
|
|
|
|
const modals = getState().modals;
|
|
|
|
|
|
|
|
const isModalOpen = modals.last()?.modalType === 'COMPOSE';
|
|
|
|
|
|
|
|
const isEventsModalOpen = modals.last()?.modalType === 'COMPOSE_EVENT';
|
|
|
|
|
|
|
|
if (isEventsModalOpen) dispatch(uploadEventBanner(e.dataTransfer.files[0], intl));
|
|
|
|
|
|
|
|
else dispatch(uploadCompose(isModalOpen ? 'compose-modal' : 'home', e.dataTransfer.files, intl));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const handleDragLeave = (e: DragEvent) => {
|
|
|
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
|
|
|
e.stopPropagation();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
dragTargets.current = dragTargets.current.filter(el => el !== e.target && node.current?.contains(el as Node));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (dragTargets.current.length > 0) {
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
setDraggingOver(false);
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const dataTransferIsText = (dataTransfer: DataTransfer | null) => {
|
|
|
|
|
|
|
|
return (dataTransfer && Array.from(dataTransfer.types).includes('text/plain') && dataTransfer.items.length === 1);
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const closeUploadModal = () => {
|
|
|
|
|
|
|
|
setDraggingOver(false);
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const handleServiceWorkerPostMessage = ({ data }: MessageEvent) => {
|
|
|
|
const handleServiceWorkerPostMessage = ({ data }: MessageEvent) => {
|
|
|
|
if (data.type === 'navigate') {
|
|
|
|
if (data.type === 'navigate') {
|
|
|
@ -501,6 +428,11 @@ const UI: React.FC<IUI> = ({ children }) => {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const handleDragEnter = (e: DragEvent) => e.preventDefault();
|
|
|
|
|
|
|
|
const handleDragLeave = (e: DragEvent) => e.preventDefault();
|
|
|
|
|
|
|
|
const handleDragOver = (e: DragEvent) => e.preventDefault();
|
|
|
|
|
|
|
|
const handleDrop = (e: DragEvent) => e.preventDefault();
|
|
|
|
|
|
|
|
|
|
|
|
/** Load initial data when a user is logged in */
|
|
|
|
/** Load initial data when a user is logged in */
|
|
|
|
const loadAccountData = () => {
|
|
|
|
const loadAccountData = () => {
|
|
|
|
if (!account) return;
|
|
|
|
if (!account) return;
|
|
|
@ -535,11 +467,6 @@ const UI: React.FC<IUI> = ({ children }) => {
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
useEffect(() => {
|
|
|
|
document.addEventListener('dragenter', handleDragEnter, false);
|
|
|
|
|
|
|
|
document.addEventListener('dragover', handleDragOver, false);
|
|
|
|
|
|
|
|
document.addEventListener('drop', handleDrop, false);
|
|
|
|
|
|
|
|
document.addEventListener('dragleave', handleDragLeave, false);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if ('serviceWorker' in navigator) {
|
|
|
|
if ('serviceWorker' in navigator) {
|
|
|
|
navigator.serviceWorker.addEventListener('message', handleServiceWorkerPostMessage);
|
|
|
|
navigator.serviceWorker.addEventListener('message', handleServiceWorkerPostMessage);
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -548,12 +475,21 @@ const UI: React.FC<IUI> = ({ children }) => {
|
|
|
|
window.setTimeout(() => Notification.requestPermission(), 120 * 1000);
|
|
|
|
window.setTimeout(() => Notification.requestPermission(), 120 * 1000);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return () => {
|
|
|
|
|
|
|
|
disconnectStreaming();
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
|
|
|
document.addEventListener('dragenter', handleDragEnter);
|
|
|
|
|
|
|
|
document.addEventListener('dragleave', handleDragLeave);
|
|
|
|
|
|
|
|
document.addEventListener('dragover', handleDragOver);
|
|
|
|
|
|
|
|
document.addEventListener('drop', handleDrop);
|
|
|
|
return () => {
|
|
|
|
return () => {
|
|
|
|
document.removeEventListener('dragenter', handleDragEnter);
|
|
|
|
document.removeEventListener('dragenter', handleDragEnter);
|
|
|
|
|
|
|
|
document.removeEventListener('dragleave', handleDragLeave);
|
|
|
|
document.removeEventListener('dragover', handleDragOver);
|
|
|
|
document.removeEventListener('dragover', handleDragOver);
|
|
|
|
document.removeEventListener('drop', handleDrop);
|
|
|
|
document.removeEventListener('drop', handleDrop);
|
|
|
|
document.removeEventListener('dragleave', handleDragLeave);
|
|
|
|
|
|
|
|
disconnectStreaming();
|
|
|
|
|
|
|
|
};
|
|
|
|
};
|
|
|
|
}, []);
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
|
@ -697,6 +633,12 @@ const UI: React.FC<IUI> = ({ children }) => {
|
|
|
|
return (
|
|
|
|
return (
|
|
|
|
<HotKeys keyMap={keyMap} handlers={me ? handlers : undefined} ref={setHotkeysRef} attach={window} focused>
|
|
|
|
<HotKeys keyMap={keyMap} handlers={me ? handlers : undefined} ref={setHotkeysRef} attach={window} focused>
|
|
|
|
<div ref={node} style={style}>
|
|
|
|
<div ref={node} style={style}>
|
|
|
|
|
|
|
|
<div
|
|
|
|
|
|
|
|
className={clsx('pointer-events-none fixed z-[9000] h-screen w-screen transition', {
|
|
|
|
|
|
|
|
'backdrop-blur': isDragging,
|
|
|
|
|
|
|
|
})}
|
|
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
|
|
<BackgroundShapes />
|
|
|
|
<BackgroundShapes />
|
|
|
|
|
|
|
|
|
|
|
|
<div className='z-10 flex flex-col'>
|
|
|
|
<div className='z-10 flex flex-col'>
|
|
|
@ -718,10 +660,6 @@ const UI: React.FC<IUI> = ({ children }) => {
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
)}
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
|
|
<BundleContainer fetchComponent={UploadArea}>
|
|
|
|
|
|
|
|
{Component => <Component active={draggingOver} onClose={closeUploadModal} />}
|
|
|
|
|
|
|
|
</BundleContainer>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
{me && (
|
|
|
|
{me && (
|
|
|
|
<BundleContainer fetchComponent={SidebarMenu}>
|
|
|
|
<BundleContainer fetchComponent={SidebarMenu}>
|
|
|
|
{Component => <Component />}
|
|
|
|
{Component => <Component />}
|
|
|
|