mirror of
https://github.com/Qortal/Qortal-Hub.git
synced 2025-06-07 08:56:58 +00:00
Merge pull request #2 from nbenaglia/feature/remove-is-mobile-check
Remove is mobile check
This commit is contained in:
commit
d2e81fda44
218
src/App.tsx
218
src/App.tsx
@ -118,9 +118,7 @@ import {
|
||||
import { useAppFullScreen } from './useAppFullscreen';
|
||||
import { NotAuthenticated } from './ExtStates/NotAuthenticated';
|
||||
import { handleGetFileFromIndexedDB } from './utils/indexedDB';
|
||||
import { CoreSyncStatus } from './components/CoreSyncStatus';
|
||||
import { Wallets } from './Wallets';
|
||||
import { RandomSentenceGenerator } from './utils/seedPhrase/RandomSentenceGenerator';
|
||||
import { useFetchResources } from './common/useFetchResources';
|
||||
import { Tutorials } from './components/Tutorials/Tutorials';
|
||||
import { useHandleTutorials } from './components/Tutorials/useHandleTutorials';
|
||||
@ -182,28 +180,6 @@ const defaultValues: MyContextInterface = {
|
||||
message: '',
|
||||
},
|
||||
};
|
||||
export let isMobile = false;
|
||||
|
||||
const isMobileDevice = () => {
|
||||
const userAgent = navigator.userAgent || navigator.vendor || window.opera;
|
||||
|
||||
if (/android/i.test(userAgent)) {
|
||||
return true; // Android device
|
||||
}
|
||||
|
||||
if (/iPad|iPhone|iPod/.test(userAgent) && !window.MSStream) {
|
||||
return true; // iOS device
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
if (isMobileDevice()) {
|
||||
isMobile = true;
|
||||
console.log('Running on a mobile device');
|
||||
} else {
|
||||
console.log('Running on a desktop');
|
||||
}
|
||||
|
||||
export const allQueues = {
|
||||
requestQueueCommentCount: requestQueueCommentCount,
|
||||
@ -436,16 +412,20 @@ function App() {
|
||||
const [isOpenMinting, setIsOpenMinting] = useState(false);
|
||||
const { toggleFullScreen } = useAppFullScreen(setFullScreen);
|
||||
const generatorRef = useRef(null);
|
||||
|
||||
const exportSeedphrase = () => {
|
||||
const seedPhrase = generatorRef.current.parsedString;
|
||||
saveSeedPhraseToDisk(seedPhrase);
|
||||
};
|
||||
|
||||
const passwordRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (extState === 'wallet-dropped' && passwordRef.current) {
|
||||
passwordRef.current.focus();
|
||||
}
|
||||
}, [extState]);
|
||||
|
||||
useEffect(() => {
|
||||
const isDevModeFromStorage = localStorage.getItem('isEnabledDevMode');
|
||||
if (isDevModeFromStorage) {
|
||||
@ -491,31 +471,38 @@ function App() {
|
||||
);
|
||||
};
|
||||
}, [toggleFullScreen]);
|
||||
|
||||
//resets for recoil
|
||||
const resetAtomSortablePinnedAppsAtom = useResetRecoilState(
|
||||
sortablePinnedAppsAtom
|
||||
);
|
||||
|
||||
const resetAtomIsUsingImportExportSettingsAtom = useResetRecoilState(
|
||||
isUsingImportExportSettingsAtom
|
||||
);
|
||||
const resetAtomCanSaveSettingToQdnAtom = useResetRecoilState(
|
||||
canSaveSettingToQdnAtom
|
||||
);
|
||||
|
||||
const resetAtomSettingsQDNLastUpdatedAtom = useResetRecoilState(
|
||||
settingsQDNLastUpdatedAtom
|
||||
);
|
||||
|
||||
const resetAtomSettingsLocalLastUpdatedAtom = useResetRecoilState(
|
||||
settingsLocalLastUpdatedAtom
|
||||
);
|
||||
|
||||
const resetAtomOldPinnedAppsAtom = useResetRecoilState(oldPinnedAppsAtom);
|
||||
const resetAtomQMailLastEnteredTimestampAtom = useResetRecoilState(
|
||||
qMailLastEnteredTimestampAtom
|
||||
);
|
||||
|
||||
const resetAtomMailsAtom = useResetRecoilState(mailsAtom);
|
||||
const resetGroupPropertiesAtom = useResetRecoilState(groupsPropertiesAtom);
|
||||
const resetLastPaymentSeenTimestampAtom = useResetRecoilState(
|
||||
lastPaymentSeenTimestampAtom
|
||||
);
|
||||
|
||||
const resetAllRecoil = () => {
|
||||
resetAtomSortablePinnedAppsAtom();
|
||||
resetAtomCanSaveSettingToQdnAtom();
|
||||
@ -528,34 +515,11 @@ function App() {
|
||||
resetGroupPropertiesAtom();
|
||||
resetLastPaymentSeenTimestampAtom();
|
||||
};
|
||||
useEffect(() => {
|
||||
if (!isMobile) return;
|
||||
// Function to set the height of the app to the viewport height
|
||||
const resetHeight = () => {
|
||||
const height = window.visualViewport
|
||||
? window.visualViewport.height
|
||||
: window.innerHeight;
|
||||
// Set the height to the root element (usually #root)
|
||||
document.getElementById('root').style.height = height + 'px';
|
||||
setRootHeight(height + 'px');
|
||||
};
|
||||
|
||||
// Set the initial height
|
||||
resetHeight();
|
||||
|
||||
// Add event listeners for resize and visualViewport changes
|
||||
window.addEventListener('resize', resetHeight);
|
||||
window.visualViewport?.addEventListener('resize', resetHeight);
|
||||
|
||||
// Clean up the event listeners when the component unmounts
|
||||
return () => {
|
||||
window.removeEventListener('resize', resetHeight);
|
||||
window.visualViewport?.removeEventListener('resize', resetHeight);
|
||||
};
|
||||
}, []);
|
||||
const handleSetGlobalApikey = (key) => {
|
||||
globalApiKey = key;
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
@ -1232,14 +1196,6 @@ function App() {
|
||||
// Handler for when the window gains focus
|
||||
const handleFocus = () => {
|
||||
setIsFocused(true);
|
||||
if (isMobile) {
|
||||
window.sendMessage('clearAllNotifications', {}).catch((error) => {
|
||||
console.error(
|
||||
'Failed to clear notifications:',
|
||||
error.message || 'An error occurred'
|
||||
);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Handler for when the window loses focus
|
||||
@ -1255,14 +1211,6 @@ function App() {
|
||||
const handleVisibilityChange = () => {
|
||||
if (document.visibilityState === 'visible') {
|
||||
setIsFocused(true);
|
||||
if (isMobile) {
|
||||
window.sendMessage('clearAllNotifications', {}).catch((error) => {
|
||||
console.error(
|
||||
'Failed to clear notifications:',
|
||||
error.message || 'An error occurred'
|
||||
);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
setIsFocused(false);
|
||||
}
|
||||
@ -1315,7 +1263,7 @@ function App() {
|
||||
return (
|
||||
<AuthenticatedContainerInnerLeft
|
||||
sx={{
|
||||
overflowY: isMobile && 'auto',
|
||||
overflowY: 'auto',
|
||||
padding: '0px 20px',
|
||||
minWidth: '225px',
|
||||
}}
|
||||
@ -1568,27 +1516,9 @@ function App() {
|
||||
backgroundColor: theme.palette.background.default,
|
||||
display: 'flex',
|
||||
justifyContent: 'flex-end',
|
||||
width: isMobile ? '100vw' : 'auto',
|
||||
width: 'auto',
|
||||
}}
|
||||
>
|
||||
{isMobile && (
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
justifyContent: 'flex-end',
|
||||
padding: '10px',
|
||||
}}
|
||||
>
|
||||
<CloseIcon
|
||||
onClick={() => {
|
||||
setIsOpenDrawerProfile(false);
|
||||
}}
|
||||
sx={{
|
||||
cursor: 'pointer',
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
{desktopViewMode !== 'apps' &&
|
||||
desktopViewMode !== 'dev' &&
|
||||
desktopViewMode !== 'chat' && <>{renderProfileLeft()}</>}
|
||||
@ -1609,51 +1539,46 @@ function App() {
|
||||
>
|
||||
<Spacer height="20px" />
|
||||
|
||||
{!isMobile && (
|
||||
<>
|
||||
<Spacer height="20px" />
|
||||
<Tooltip
|
||||
title={
|
||||
<span
|
||||
style={{
|
||||
fontSize: '14px',
|
||||
fontWeight: 700,
|
||||
}}
|
||||
>
|
||||
LOG OUT
|
||||
</span>
|
||||
}
|
||||
placement="left"
|
||||
arrow
|
||||
sx={{ fontSize: '24' }}
|
||||
slotProps={{
|
||||
tooltip: {
|
||||
sx: {
|
||||
color: theme.palette.text.primary,
|
||||
backgroundColor: theme.palette.background.default,
|
||||
},
|
||||
},
|
||||
arrow: {
|
||||
sx: {
|
||||
color: theme.palette.text.primary,
|
||||
},
|
||||
},
|
||||
<Tooltip
|
||||
title={
|
||||
<span
|
||||
style={{
|
||||
fontSize: '14px',
|
||||
fontWeight: 700,
|
||||
}}
|
||||
>
|
||||
<Logout
|
||||
style={{
|
||||
cursor: 'pointer',
|
||||
width: '20px',
|
||||
height: 'auto',
|
||||
}}
|
||||
onClick={() => {
|
||||
logoutFunc();
|
||||
setIsOpenDrawerProfile(false);
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
</>
|
||||
)}
|
||||
LOG OUT
|
||||
</span>
|
||||
}
|
||||
placement="left"
|
||||
arrow
|
||||
sx={{ fontSize: '24' }}
|
||||
slotProps={{
|
||||
tooltip: {
|
||||
sx: {
|
||||
color: theme.palette.text.primary,
|
||||
backgroundColor: theme.palette.background.default,
|
||||
},
|
||||
},
|
||||
arrow: {
|
||||
sx: {
|
||||
color: theme.palette.text.primary,
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Logout
|
||||
style={{
|
||||
cursor: 'pointer',
|
||||
width: '20px',
|
||||
height: 'auto',
|
||||
}}
|
||||
onClick={() => {
|
||||
logoutFunc();
|
||||
setIsOpenDrawerProfile(false);
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
|
||||
<Spacer height="20px" />
|
||||
|
||||
@ -2009,7 +1934,7 @@ function App() {
|
||||
return (
|
||||
<AppContainer
|
||||
sx={{
|
||||
height: isMobile ? '100%' : '100vh',
|
||||
height: '100vh',
|
||||
// backgroundImage: desktopViewMode === "apps" && 'url("appsBg.svg")',
|
||||
// backgroundSize: desktopViewMode === "apps" && "cover",
|
||||
// backgroundPosition: desktopViewMode === "apps" && "center",
|
||||
@ -2075,51 +2000,50 @@ function App() {
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
width: '100vw',
|
||||
height: isMobile ? '100%' : '100vh',
|
||||
display: 'flex',
|
||||
flexDirection: isMobile ? 'column' : 'row',
|
||||
overflow: isMobile && 'hidden',
|
||||
flexDirection: 'row',
|
||||
height: '100vh',
|
||||
width: '100vw',
|
||||
}}
|
||||
>
|
||||
<Group
|
||||
logoutFunc={logoutFunc}
|
||||
balance={balance}
|
||||
userInfo={userInfo}
|
||||
myAddress={address}
|
||||
desktopViewMode={desktopViewMode}
|
||||
isFocused={isFocused}
|
||||
isMain={isMain}
|
||||
isOpenDrawerProfile={isOpenDrawerProfile}
|
||||
setIsOpenDrawerProfile={setIsOpenDrawerProfile}
|
||||
desktopViewMode={desktopViewMode}
|
||||
logoutFunc={logoutFunc}
|
||||
myAddress={address}
|
||||
setDesktopViewMode={setDesktopViewMode}
|
||||
setIsOpenDrawerProfile={setIsOpenDrawerProfile}
|
||||
userInfo={userInfo}
|
||||
/>
|
||||
{!isMobile && renderProfile()}
|
||||
renderProfile()
|
||||
</Box>
|
||||
</MyContext.Provider>
|
||||
)}
|
||||
{isOpenSendQort && isMainWindow && (
|
||||
<Box
|
||||
sx={{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
position: 'fixed',
|
||||
alignItems: 'center',
|
||||
background: theme.palette.background.default,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
height: '100%',
|
||||
position: 'fixed',
|
||||
width: '100%',
|
||||
zIndex: 10000,
|
||||
}}
|
||||
>
|
||||
<Spacer height="22px" />
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
width: '100%',
|
||||
justifyContent: 'flex-start',
|
||||
paddingLeft: '22px',
|
||||
boxSizing: 'border-box',
|
||||
display: 'flex',
|
||||
justifyContent: 'flex-start',
|
||||
maxWidth: '700px',
|
||||
paddingLeft: '22px',
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
<Return
|
||||
@ -2130,7 +2054,9 @@ function App() {
|
||||
onClick={returnToMain}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
<Spacer height="35px" />
|
||||
|
||||
<QortPayment
|
||||
balance={balance}
|
||||
show={show}
|
||||
|
26
src/assets/Icons/Search.tsx
Normal file
26
src/assets/Icons/Search.tsx
Normal file
@ -0,0 +1,26 @@
|
||||
import { useTheme } from '@mui/material';
|
||||
import { SVGProps } from './interfaces';
|
||||
|
||||
export const Search: React.FC<SVGProps> = ({ color, opacity, ...children }) => {
|
||||
const theme = useTheme();
|
||||
|
||||
const setColor = color ? color : theme.palette.text.primary;
|
||||
const setOpacity = opacity ? opacity : 1;
|
||||
|
||||
return (
|
||||
<svg
|
||||
{...children}
|
||||
width="14"
|
||||
height="14"
|
||||
viewBox="0 0 14 14"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M6.08728 0.00158245C2.72507 0.00158245 0 2.7262 0 6.08784C0 9.44948 2.72507 12.1741 6.08728 12.1741C7.62099 12.1741 9.02317 11.6043 10.0947 10.6668L13.3088 13.8803C13.3881 13.9596 13.4911 14 13.595 14C13.6988 14 13.8018 13.9596 13.8811 13.8803C14.0396 13.7218 14.0396 13.4643 13.8811 13.3066L10.667 10.093C11.6047 9.02162 12.1746 7.62202 12.1746 6.08626C12.1746 2.72461 9.44951 0 6.0873 0L6.08728 0.00158245ZM6.08728 11.3626C3.17756 11.3626 0.811637 8.99707 0.811637 6.08784C0.811637 3.17861 3.17756 0.813083 6.08728 0.813083C8.997 0.813083 11.3629 3.17861 11.3629 6.08784C11.3629 8.99707 8.997 11.3626 6.08728 11.3626Z"
|
||||
fill={setColor}
|
||||
opacity={setOpacity}
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
};
|
@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="35.93" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 228"><path fill="#00D8FF" d="M210.483 73.824a171.49 171.49 0 0 0-8.24-2.597c.465-1.9.893-3.777 1.273-5.621c6.238-30.281 2.16-54.676-11.769-62.708c-13.355-7.7-35.196.329-57.254 19.526a171.23 171.23 0 0 0-6.375 5.848a155.866 155.866 0 0 0-4.241-3.917C100.759 3.829 77.587-4.822 63.673 3.233C50.33 10.957 46.379 33.89 51.995 62.588a170.974 170.974 0 0 0 1.892 8.48c-3.28.932-6.445 1.924-9.474 2.98C17.309 83.498 0 98.307 0 113.668c0 15.865 18.582 31.778 46.812 41.427a145.52 145.52 0 0 0 6.921 2.165a167.467 167.467 0 0 0-2.01 9.138c-5.354 28.2-1.173 50.591 12.134 58.266c13.744 7.926 36.812-.22 59.273-19.855a145.567 145.567 0 0 0 5.342-4.923a168.064 168.064 0 0 0 6.92 6.314c21.758 18.722 43.246 26.282 56.54 18.586c13.731-7.949 18.194-32.003 12.4-61.268a145.016 145.016 0 0 0-1.535-6.842c1.62-.48 3.21-.974 4.76-1.488c29.348-9.723 48.443-25.443 48.443-41.52c0-15.417-17.868-30.326-45.517-39.844Zm-6.365 70.984c-1.4.463-2.836.91-4.3 1.345c-3.24-10.257-7.612-21.163-12.963-32.432c5.106-11 9.31-21.767 12.459-31.957c2.619.758 5.16 1.557 7.61 2.4c23.69 8.156 38.14 20.213 38.14 29.504c0 9.896-15.606 22.743-40.946 31.14Zm-10.514 20.834c2.562 12.94 2.927 24.64 1.23 33.787c-1.524 8.219-4.59 13.698-8.382 15.893c-8.067 4.67-25.32-1.4-43.927-17.412a156.726 156.726 0 0 1-6.437-5.87c7.214-7.889 14.423-17.06 21.459-27.246c12.376-1.098 24.068-2.894 34.671-5.345a134.17 134.17 0 0 1 1.386 6.193ZM87.276 214.515c-7.882 2.783-14.16 2.863-17.955.675c-8.075-4.657-11.432-22.636-6.853-46.752a156.923 156.923 0 0 1 1.869-8.499c10.486 2.32 22.093 3.988 34.498 4.994c7.084 9.967 14.501 19.128 21.976 27.15a134.668 134.668 0 0 1-4.877 4.492c-9.933 8.682-19.886 14.842-28.658 17.94ZM50.35 144.747c-12.483-4.267-22.792-9.812-29.858-15.863c-6.35-5.437-9.555-10.836-9.555-15.216c0-9.322 13.897-21.212 37.076-29.293c2.813-.98 5.757-1.905 8.812-2.773c3.204 10.42 7.406 21.315 12.477 32.332c-5.137 11.18-9.399 22.249-12.634 32.792a134.718 134.718 0 0 1-6.318-1.979Zm12.378-84.26c-4.811-24.587-1.616-43.134 6.425-47.789c8.564-4.958 27.502 2.111 47.463 19.835a144.318 144.318 0 0 1 3.841 3.545c-7.438 7.987-14.787 17.08-21.808 26.988c-12.04 1.116-23.565 2.908-34.161 5.309a160.342 160.342 0 0 1-1.76-7.887Zm110.427 27.268a347.8 347.8 0 0 0-7.785-12.803c8.168 1.033 15.994 2.404 23.343 4.08c-2.206 7.072-4.956 14.465-8.193 22.045a381.151 381.151 0 0 0-7.365-13.322Zm-45.032-43.861c5.044 5.465 10.096 11.566 15.065 18.186a322.04 322.04 0 0 0-30.257-.006c4.974-6.559 10.069-12.652 15.192-18.18ZM82.802 87.83a323.167 323.167 0 0 0-7.227 13.238c-3.184-7.553-5.909-14.98-8.134-22.152c7.304-1.634 15.093-2.97 23.209-3.984a321.524 321.524 0 0 0-7.848 12.897Zm8.081 65.352c-8.385-.936-16.291-2.203-23.593-3.793c2.26-7.3 5.045-14.885 8.298-22.6a321.187 321.187 0 0 0 7.257 13.246c2.594 4.48 5.28 8.868 8.038 13.147Zm37.542 31.03c-5.184-5.592-10.354-11.779-15.403-18.433c4.902.192 9.899.29 14.978.29c5.218 0 10.376-.117 15.453-.343c-4.985 6.774-10.018 12.97-15.028 18.486Zm52.198-57.817c3.422 7.8 6.306 15.345 8.596 22.52c-7.422 1.694-15.436 3.058-23.88 4.071a382.417 382.417 0 0 0 7.859-13.026a347.403 347.403 0 0 0 7.425-13.565Zm-16.898 8.101a358.557 358.557 0 0 1-12.281 19.815a329.4 329.4 0 0 1-23.444.823c-7.967 0-15.716-.248-23.178-.732a310.202 310.202 0 0 1-12.513-19.846h.001a307.41 307.41 0 0 1-10.923-20.627a310.278 310.278 0 0 1 10.89-20.637l-.001.001a307.318 307.318 0 0 1 12.413-19.761c7.613-.576 15.42-.876 23.31-.876H128c7.926 0 15.743.303 23.354.883a329.357 329.357 0 0 1 12.335 19.695a358.489 358.489 0 0 1 11.036 20.54a329.472 329.472 0 0 1-11 20.722Zm22.56-122.124c8.572 4.944 11.906 24.881 6.52 51.026c-.344 1.668-.73 3.367-1.15 5.09c-10.622-2.452-22.155-4.275-34.23-5.408c-7.034-10.017-14.323-19.124-21.64-27.008a160.789 160.789 0 0 1 5.888-5.4c18.9-16.447 36.564-22.941 44.612-18.3ZM128 90.808c12.625 0 22.86 10.235 22.86 22.86s-10.235 22.86-22.86 22.86s-22.86-10.235-22.86-22.86s10.235-22.86 22.86-22.86Z"></path></svg>
|
Before Width: | Height: | Size: 4.0 KiB |
@ -1,5 +1,4 @@
|
||||
// @ts-nocheck
|
||||
|
||||
import './qortalRequests';
|
||||
import { isArray } from 'lodash';
|
||||
import {
|
||||
@ -30,7 +29,6 @@ import { RequestQueueWithPromise } from './utils/queue/queue';
|
||||
import { validateAddress } from './utils/validateAddress';
|
||||
import { Sha256 } from 'asmcrypto.js';
|
||||
import { TradeBotRespondMultipleRequest } from './transactions/TradeBotRespondMultipleRequest';
|
||||
|
||||
import { RESOURCE_TYPE_NUMBER_GROUP_CHAT_REACTIONS } from './constants/resourceTypes';
|
||||
import {
|
||||
addDataPublishesCase,
|
||||
@ -111,6 +109,7 @@ export let groupSecretkeys = {};
|
||||
export function cleanUrl(url) {
|
||||
return url?.replace(/^(https?:\/\/)?(www\.)?/, '');
|
||||
}
|
||||
|
||||
export function getProtocol(url) {
|
||||
if (url?.startsWith('https://')) {
|
||||
return 'https';
|
||||
@ -130,7 +129,6 @@ export const groupApiLocal = 'http://127.0.0.1:12391';
|
||||
export const groupApiSocketLocal = 'ws://127.0.0.1:12391';
|
||||
const timeDifferenceForNotificationChatsBackground = 86400000;
|
||||
const requestQueueAnnouncements = new RequestQueueWithPromise(1);
|
||||
let isMobile = true;
|
||||
|
||||
function handleNotificationClick(notificationId) {
|
||||
// Decode the notificationId if it was encoded
|
||||
@ -193,26 +191,6 @@ function handleNotificationClick(notificationId) {
|
||||
}
|
||||
}
|
||||
|
||||
const isMobileDevice = () => {
|
||||
const userAgent = navigator.userAgent || navigator.vendor || window.opera;
|
||||
|
||||
if (/android/i.test(userAgent)) {
|
||||
return true; // Android device
|
||||
}
|
||||
|
||||
if (/iPad|iPhone|iPod/.test(userAgent) && !window.MSStream) {
|
||||
return true; // iOS device
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
if (isMobileDevice()) {
|
||||
isMobile = true;
|
||||
console.log('Running on a mobile device');
|
||||
} else {
|
||||
console.log('Running on a desktop');
|
||||
}
|
||||
const allQueues = {
|
||||
requestQueueAnnouncements: requestQueueAnnouncements,
|
||||
};
|
||||
@ -264,7 +242,9 @@ export const getForeignKey = async (foreignBlockchain) => {
|
||||
};
|
||||
|
||||
export const pauseAllQueues = () => controlAllQueues('pause');
|
||||
|
||||
export const resumeAllQueues = () => controlAllQueues('resume');
|
||||
|
||||
export const checkDifference = (
|
||||
createdTimestamp,
|
||||
diff = timeDifferenceForNotificationChatsBackground
|
||||
@ -302,6 +282,7 @@ export const getBaseApi = async (customApi?: string) => {
|
||||
return groupApi;
|
||||
}
|
||||
};
|
||||
|
||||
export const isUsingLocal = async () => {
|
||||
const apiKey = await getApiKeyFromStorage(); // Retrieve apiKey asynchronously
|
||||
if (apiKey?.url) {
|
||||
@ -345,13 +326,13 @@ const proxyAccountAddress = 'QXPejUe5Za1KD3zCMViWCX35AreMQ9H7ku';
|
||||
const proxyAccountPublicKey = '5hP6stDWybojoDw5t8z9D51nV945oMPX7qBd29rhX1G7';
|
||||
const pendingResponses = new Map();
|
||||
let groups = null;
|
||||
|
||||
let socket;
|
||||
let timeoutId;
|
||||
let groupSocketTimeout;
|
||||
let socketTimeout: any;
|
||||
let interval;
|
||||
let intervalThreads;
|
||||
|
||||
// Function to check each API endpoint
|
||||
export async function findUsableApi() {
|
||||
for (const endpoint of apiEndpoints) {
|
||||
@ -435,6 +416,7 @@ async function checkWebviewFocus() {
|
||||
window.addEventListener('message', handleMessage);
|
||||
});
|
||||
}
|
||||
|
||||
const worker = new ChatComputePowWorker();
|
||||
|
||||
export async function performPowTask(chatBytes, difficulty) {
|
||||
@ -584,6 +566,7 @@ const handleNotificationDirect = async (directs) => {
|
||||
setChatHeadsDirect(dataDirects);
|
||||
}
|
||||
};
|
||||
|
||||
async function getThreadActivity(): Promise<any | null> {
|
||||
const wallet = await getSaveWallet();
|
||||
const address = wallet.address0;
|
||||
@ -852,6 +835,7 @@ export async function getNameInfoForOthers(address) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
export async function getAddressInfo(address) {
|
||||
const validApi = await getBaseApi();
|
||||
const response = await fetch(validApi + '/addresses/' + address);
|
||||
@ -932,6 +916,7 @@ async function getTradeInfo(qortalAtAddress) {
|
||||
const data = await response.json();
|
||||
return data;
|
||||
}
|
||||
|
||||
async function getTradesInfo(qortalAtAddresses) {
|
||||
// Use Promise.all to fetch data for all addresses concurrently
|
||||
const trades = await Promise.all(
|
||||
@ -951,6 +936,7 @@ export async function getBalanceInfo() {
|
||||
const data = await response.json();
|
||||
return data;
|
||||
}
|
||||
|
||||
export async function getLTCBalance() {
|
||||
const wallet = await getSaveWallet();
|
||||
let _url = `${buyTradeNodeBaseUrl}/crosschain/ltc/walletbalance`;
|
||||
@ -1075,6 +1061,7 @@ const transaction = async (
|
||||
data: res,
|
||||
};
|
||||
};
|
||||
|
||||
const makeTransactionRequest = async (
|
||||
receiver,
|
||||
lastRef,
|
||||
@ -1113,6 +1100,7 @@ export const getLastRef = async () => {
|
||||
const data = await response.text();
|
||||
return data;
|
||||
};
|
||||
|
||||
export const sendQortFee = async (): Promise<number> => {
|
||||
const validApi = await getBaseApi();
|
||||
const response = await fetch(
|
||||
@ -1386,6 +1374,7 @@ export async function signChatFunc(
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
function sbrk(size, heap) {
|
||||
let brk = 512 * 1024; // stack top
|
||||
let old = brk;
|
||||
|
@ -2,7 +2,6 @@ import React, { useEffect, useMemo, useState } from 'react';
|
||||
import {
|
||||
AppCircle,
|
||||
AppCircleContainer,
|
||||
AppCircleLabel,
|
||||
AppDownloadButton,
|
||||
AppDownloadButtonText,
|
||||
AppInfoAppName,
|
||||
@ -17,14 +16,11 @@ import {
|
||||
AppsCategoryInfoValue,
|
||||
AppsInfoDescription,
|
||||
AppsLibraryContainer,
|
||||
AppsParent,
|
||||
AppsWidthLimiter,
|
||||
} from './Apps-styles';
|
||||
import { Avatar, Box, ButtonBase, InputBase } from '@mui/material';
|
||||
import { Add } from '@mui/icons-material';
|
||||
import { getBaseApiReact, isMobile } from '../../App';
|
||||
import { Avatar, Box } from '@mui/material';
|
||||
import { getBaseApiReact } from '../../App';
|
||||
import LogoSelected from '../../assets/svgs/LogoSelected.svg';
|
||||
|
||||
import { Spacer } from '../../common/Spacer';
|
||||
import { executeEvent } from '../../utils/events';
|
||||
import { AppRating } from './AppRating';
|
||||
@ -51,9 +47,8 @@ export const AppInfo = ({ app, myName }) => {
|
||||
return (
|
||||
<AppsLibraryContainer
|
||||
sx={{
|
||||
height: !isMobile && '100%',
|
||||
justifyContent: !isMobile && 'flex-start',
|
||||
alignItems: isMobile && 'center',
|
||||
height: '100%',
|
||||
justifyContent: 'flex-start',
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
@ -64,7 +59,8 @@ export const AppInfo = ({ app, myName }) => {
|
||||
width: '90%',
|
||||
}}
|
||||
>
|
||||
{!isMobile && <Spacer height="30px" />}
|
||||
<Spacer height="30px" />
|
||||
|
||||
<AppsWidthLimiter>
|
||||
<AppInfoSnippetContainer>
|
||||
<AppInfoSnippetLeft
|
||||
@ -172,15 +168,9 @@ export const AppInfo = ({ app, myName }) => {
|
||||
}}
|
||||
>
|
||||
<AppDownloadButtonText>
|
||||
{!isMobile ? (
|
||||
<>
|
||||
{isSelectedAppPinned
|
||||
? 'Unpin from dashboard'
|
||||
: 'Pin to dashboard'}
|
||||
</>
|
||||
) : (
|
||||
<>{isSelectedAppPinned ? 'Unpin' : 'Pin'}</>
|
||||
)}
|
||||
{isSelectedAppPinned
|
||||
? 'Unpin from dashboard'
|
||||
: 'Pin to dashboard'}
|
||||
</AppDownloadButtonText>
|
||||
</AppDownloadButton>
|
||||
<AppDownloadButton
|
||||
|
@ -12,9 +12,8 @@ import {
|
||||
AppInfoUserName,
|
||||
} from './Apps-styles';
|
||||
import { Avatar, ButtonBase } from '@mui/material';
|
||||
import { getBaseApiReact, isMobile } from '../../App';
|
||||
import { getBaseApiReact } from '../../App';
|
||||
import LogoSelected from '../../assets/svgs/LogoSelected.svg';
|
||||
|
||||
import { Spacer } from '../../common/Spacer';
|
||||
import { executeEvent } from '../../utils/events';
|
||||
import { AppRating } from './AppRating';
|
||||
@ -124,52 +123,49 @@ export const AppInfoSnippet = ({
|
||||
gap: '10px',
|
||||
}}
|
||||
>
|
||||
{!isMobile && (
|
||||
<AppDownloadButton
|
||||
onClick={() => {
|
||||
setSortablePinnedApps((prev) => {
|
||||
let updatedApps;
|
||||
<AppDownloadButton
|
||||
onClick={() => {
|
||||
setSortablePinnedApps((prev) => {
|
||||
let updatedApps;
|
||||
|
||||
if (isSelectedAppPinned) {
|
||||
// Remove the selected app if it is pinned
|
||||
updatedApps = prev.filter(
|
||||
(item) =>
|
||||
!(
|
||||
item?.name === app?.name &&
|
||||
item?.service === app?.service
|
||||
)
|
||||
);
|
||||
} else {
|
||||
// Add the selected app if it is not pinned
|
||||
updatedApps = [
|
||||
...prev,
|
||||
{
|
||||
name: app?.name,
|
||||
service: app?.service,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
saveToLocalStorage(
|
||||
'ext_saved_settings',
|
||||
'sortablePinnedApps',
|
||||
updatedApps
|
||||
if (isSelectedAppPinned) {
|
||||
// Remove the selected app if it is pinned
|
||||
updatedApps = prev.filter(
|
||||
(item) =>
|
||||
!(
|
||||
item?.name === app?.name && item?.service === app?.service
|
||||
)
|
||||
);
|
||||
return updatedApps;
|
||||
});
|
||||
setSettingsLocalLastUpdated(Date.now());
|
||||
}}
|
||||
sx={{
|
||||
backgroundColor: '#359ff7ff',
|
||||
opacity: isSelectedAppPinned ? 0.6 : 1,
|
||||
}}
|
||||
>
|
||||
<AppDownloadButtonText>
|
||||
{' '}
|
||||
{isSelectedAppPinned ? 'Unpin' : 'Pin'}
|
||||
</AppDownloadButtonText>
|
||||
</AppDownloadButton>
|
||||
)}
|
||||
} else {
|
||||
// Add the selected app if it is not pinned
|
||||
updatedApps = [
|
||||
...prev,
|
||||
{
|
||||
name: app?.name,
|
||||
service: app?.service,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
saveToLocalStorage(
|
||||
'ext_saved_settings',
|
||||
'sortablePinnedApps',
|
||||
updatedApps
|
||||
);
|
||||
return updatedApps;
|
||||
});
|
||||
setSettingsLocalLastUpdated(Date.now());
|
||||
}}
|
||||
sx={{
|
||||
backgroundColor: '#359ff7ff',
|
||||
opacity: isSelectedAppPinned ? 0.6 : 1,
|
||||
}}
|
||||
>
|
||||
<AppDownloadButtonText>
|
||||
{' '}
|
||||
{isSelectedAppPinned ? 'Unpin' : 'Pin'}
|
||||
</AppDownloadButtonText>
|
||||
</AppDownloadButton>
|
||||
|
||||
<AppDownloadButton
|
||||
onClick={() => {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { useContext, useEffect, useMemo, useState } from "react";
|
||||
import React, { useContext, useEffect, useState } from 'react';
|
||||
import {
|
||||
AppCircle,
|
||||
AppCircleContainer,
|
||||
@ -19,90 +19,74 @@ import {
|
||||
PublishQAppCTAButton,
|
||||
PublishQAppChoseFile,
|
||||
PublishQAppInfo,
|
||||
} from "./Apps-styles";
|
||||
import {
|
||||
Avatar,
|
||||
Box,
|
||||
ButtonBase,
|
||||
InputBase,
|
||||
InputLabel,
|
||||
MenuItem,
|
||||
Select,
|
||||
} from "@mui/material";
|
||||
import {
|
||||
Select as BaseSelect,
|
||||
SelectProps,
|
||||
selectClasses,
|
||||
SelectRootSlotProps,
|
||||
} from "@mui/base/Select";
|
||||
import { Option as BaseOption, optionClasses } from "@mui/base/Option";
|
||||
import { styled } from "@mui/system";
|
||||
import UnfoldMoreRoundedIcon from "@mui/icons-material/UnfoldMoreRounded";
|
||||
import { Add } from "@mui/icons-material";
|
||||
import { MyContext, getBaseApiReact, isMobile } from "../../App";
|
||||
import LogoSelected from "../../assets/svgs/LogoSelected.svg";
|
||||
|
||||
import { Spacer } from "../../common/Spacer";
|
||||
import { executeEvent } from "../../utils/events";
|
||||
import { useDropzone } from "react-dropzone";
|
||||
import { LoadingSnackbar } from "../Snackbar/LoadingSnackbar";
|
||||
import { CustomizedSnackbars } from "../Snackbar/Snackbar";
|
||||
import { getFee } from "../../background";
|
||||
import { fileToBase64 } from "../../utils/fileReading";
|
||||
} from './Apps-styles';
|
||||
import { InputBase, InputLabel, MenuItem, Select } from '@mui/material';
|
||||
import { styled } from '@mui/system';
|
||||
import UnfoldMoreRoundedIcon from '@mui/icons-material/UnfoldMoreRounded';
|
||||
import { Add } from '@mui/icons-material';
|
||||
import { MyContext, getBaseApiReact } from '../../App';
|
||||
import LogoSelected from '../../assets/svgs/LogoSelected.svg';
|
||||
import { Spacer } from '../../common/Spacer';
|
||||
import { executeEvent } from '../../utils/events';
|
||||
import { useDropzone } from 'react-dropzone';
|
||||
import { LoadingSnackbar } from '../Snackbar/LoadingSnackbar';
|
||||
import { CustomizedSnackbars } from '../Snackbar/Snackbar';
|
||||
import { getFee } from '../../background';
|
||||
import { fileToBase64 } from '../../utils/fileReading';
|
||||
|
||||
const CustomSelect = styled(Select)({
|
||||
border: "0.5px solid var(--50-white, #FFFFFF80)",
|
||||
padding: "0px 15px",
|
||||
borderRadius: "5px",
|
||||
height: "36px",
|
||||
width: "100%",
|
||||
maxWidth: "450px",
|
||||
"& .MuiSelect-select": {
|
||||
padding: "0px",
|
||||
border: '0.5px solid var(--50-white, #FFFFFF80)',
|
||||
padding: '0px 15px',
|
||||
borderRadius: '5px',
|
||||
height: '36px',
|
||||
width: '100%',
|
||||
maxWidth: '450px',
|
||||
'& .MuiSelect-select': {
|
||||
padding: '0px',
|
||||
},
|
||||
"&:hover": {
|
||||
borderColor: "none", // Border color on hover
|
||||
'&:hover': {
|
||||
borderColor: 'none', // Border color on hover
|
||||
},
|
||||
"&.Mui-focused .MuiOutlinedInput-notchedOutline": {
|
||||
borderColor: "none", // Border color when focused
|
||||
'&.Mui-focused .MuiOutlinedInput-notchedOutline': {
|
||||
borderColor: 'none', // Border color when focused
|
||||
},
|
||||
"&.Mui-disabled": {
|
||||
'&.Mui-disabled': {
|
||||
opacity: 0.5, // Lower opacity when disabled
|
||||
},
|
||||
"& .MuiSvgIcon-root": {
|
||||
color: "var(--50-white, #FFFFFF80)",
|
||||
'& .MuiSvgIcon-root': {
|
||||
color: 'var(--50-white, #FFFFFF80)',
|
||||
},
|
||||
});
|
||||
|
||||
const CustomMenuItem = styled(MenuItem)({
|
||||
backgroundColor: "#1f1f1f", // Background for dropdown items
|
||||
color: "#ccc",
|
||||
"&:hover": {
|
||||
backgroundColor: "#333", // Darker background on hover
|
||||
backgroundColor: '#1f1f1f', // Background for dropdown items
|
||||
color: '#ccc',
|
||||
'&:hover': {
|
||||
backgroundColor: '#333', // Darker background on hover
|
||||
},
|
||||
});
|
||||
|
||||
export const AppPublish = ({ names, categories }) => {
|
||||
const [name, setName] = useState("");
|
||||
const [title, setTitle] = useState("");
|
||||
const [description, setDescription] = useState("");
|
||||
const [category, setCategory] = useState("");
|
||||
const [appType, setAppType] = useState("APP");
|
||||
const [name, setName] = useState('');
|
||||
const [title, setTitle] = useState('');
|
||||
const [description, setDescription] = useState('');
|
||||
const [category, setCategory] = useState('');
|
||||
const [appType, setAppType] = useState('APP');
|
||||
const [file, setFile] = useState(null);
|
||||
const { show } = useContext(MyContext);
|
||||
|
||||
const [tag1, setTag1] = useState("");
|
||||
const [tag2, setTag2] = useState("");
|
||||
const [tag3, setTag3] = useState("");
|
||||
const [tag4, setTag4] = useState("");
|
||||
const [tag5, setTag5] = useState("");
|
||||
const [tag1, setTag1] = useState('');
|
||||
const [tag2, setTag2] = useState('');
|
||||
const [tag3, setTag3] = useState('');
|
||||
const [tag4, setTag4] = useState('');
|
||||
const [tag5, setTag5] = useState('');
|
||||
const [openSnack, setOpenSnack] = useState(false);
|
||||
const [infoSnack, setInfoSnack] = useState(null);
|
||||
const [isLoading, setIsLoading] = useState("");
|
||||
const maxFileSize = appType === "APP" ? 50 * 1024 * 1024 : 400 * 1024 * 1024; // 50MB or 400MB
|
||||
const [isLoading, setIsLoading] = useState('');
|
||||
const maxFileSize = appType === 'APP' ? 50 * 1024 * 1024 : 400 * 1024 * 1024; // 50MB or 400MB
|
||||
const { getRootProps, getInputProps } = useDropzone({
|
||||
accept: {
|
||||
"application/zip": [".zip"], // Only accept zip files
|
||||
'application/zip': ['.zip'], // Only accept zip files
|
||||
},
|
||||
maxSize: maxFileSize, // Set the max size based on appType
|
||||
multiple: false, // Disable multiple file uploads
|
||||
@ -114,7 +98,7 @@ export const AppPublish = ({ names, categories }) => {
|
||||
onDropRejected: (fileRejections) => {
|
||||
fileRejections.forEach(({ file, errors }) => {
|
||||
errors.forEach((error) => {
|
||||
if (error.code === "file-too-large") {
|
||||
if (error.code === 'file-too-large') {
|
||||
console.error(
|
||||
`File ${file.name} is too large. Max size allowed is ${
|
||||
maxFileSize / (1024 * 1024)
|
||||
@ -128,13 +112,13 @@ export const AppPublish = ({ names, categories }) => {
|
||||
|
||||
const getQapp = React.useCallback(async (name, appType) => {
|
||||
try {
|
||||
setIsLoading("Loading app information");
|
||||
setIsLoading('Loading app information');
|
||||
const url = `${getBaseApiReact()}/arbitrary/resources/search?service=${appType}&mode=ALL&name=${name}&includemetadata=true`;
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: "GET",
|
||||
method: 'GET',
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
if (!response?.ok) return;
|
||||
@ -142,18 +126,18 @@ export const AppPublish = ({ names, categories }) => {
|
||||
|
||||
if (responseData?.length > 0) {
|
||||
const myApp = responseData[0];
|
||||
setTitle(myApp?.metadata?.title || "");
|
||||
setDescription(myApp?.metadata?.description || "");
|
||||
setCategory(myApp?.metadata?.category || "");
|
||||
setTag1(myApp?.metadata?.tags[0] || "");
|
||||
setTag2(myApp?.metadata?.tags[1] || "");
|
||||
setTag3(myApp?.metadata?.tags[2] || "");
|
||||
setTag4(myApp?.metadata?.tags[3] || "");
|
||||
setTag5(myApp?.metadata?.tags[4] || "");
|
||||
setTitle(myApp?.metadata?.title || '');
|
||||
setDescription(myApp?.metadata?.description || '');
|
||||
setCategory(myApp?.metadata?.category || '');
|
||||
setTag1(myApp?.metadata?.tags[0] || '');
|
||||
setTag2(myApp?.metadata?.tags[1] || '');
|
||||
setTag3(myApp?.metadata?.tags[2] || '');
|
||||
setTag4(myApp?.metadata?.tags[3] || '');
|
||||
setTag5(myApp?.metadata?.tags[4] || '');
|
||||
}
|
||||
} catch (error) {
|
||||
} finally {
|
||||
setIsLoading("");
|
||||
setIsLoading('');
|
||||
}
|
||||
}, []);
|
||||
|
||||
@ -173,12 +157,12 @@ export const AppPublish = ({ names, categories }) => {
|
||||
file,
|
||||
};
|
||||
const requiredFields = [
|
||||
"name",
|
||||
"title",
|
||||
"description",
|
||||
"category",
|
||||
"appType",
|
||||
"file",
|
||||
'name',
|
||||
'title',
|
||||
'description',
|
||||
'category',
|
||||
'appType',
|
||||
'file',
|
||||
];
|
||||
|
||||
const missingFields: string[] = [];
|
||||
@ -188,32 +172,33 @@ export const AppPublish = ({ names, categories }) => {
|
||||
}
|
||||
});
|
||||
if (missingFields.length > 0) {
|
||||
const missingFieldsString = missingFields.join(", ");
|
||||
const missingFieldsString = missingFields.join(', ');
|
||||
const errorMsg = `Missing fields: ${missingFieldsString}`;
|
||||
throw new Error(errorMsg);
|
||||
}
|
||||
const fee = await getFee("ARBITRARY");
|
||||
const fee = await getFee('ARBITRARY');
|
||||
|
||||
await show({
|
||||
message: "Would you like to publish this app?",
|
||||
publishFee: fee.fee + " QORT",
|
||||
message: 'Would you like to publish this app?',
|
||||
publishFee: fee.fee + ' QORT',
|
||||
});
|
||||
setIsLoading("Publishing... Please wait.");
|
||||
setIsLoading('Publishing... Please wait.');
|
||||
const fileBase64 = await fileToBase64(file);
|
||||
await new Promise((res, rej) => {
|
||||
window.sendMessage("publishOnQDN", {
|
||||
data: fileBase64,
|
||||
service: appType,
|
||||
title,
|
||||
description,
|
||||
category,
|
||||
tag1,
|
||||
tag2,
|
||||
tag3,
|
||||
tag4,
|
||||
tag5,
|
||||
uploadType: "zip",
|
||||
})
|
||||
window
|
||||
.sendMessage('publishOnQDN', {
|
||||
data: fileBase64,
|
||||
service: appType,
|
||||
title,
|
||||
description,
|
||||
category,
|
||||
tag1,
|
||||
tag2,
|
||||
tag3,
|
||||
tag4,
|
||||
tag5,
|
||||
uploadType: 'zip',
|
||||
})
|
||||
.then((response) => {
|
||||
if (!response?.error) {
|
||||
res(response);
|
||||
@ -222,14 +207,13 @@ export const AppPublish = ({ names, categories }) => {
|
||||
rej(response.error);
|
||||
})
|
||||
.catch((error) => {
|
||||
rej(error.message || "An error occurred");
|
||||
rej(error.message || 'An error occurred');
|
||||
});
|
||||
|
||||
});
|
||||
setInfoSnack({
|
||||
type: "success",
|
||||
type: 'success',
|
||||
message:
|
||||
"Successfully published. Please wait a couple minutes for the network to propogate the changes.",
|
||||
'Successfully published. Please wait a couple minutes for the network to propogate the changes.',
|
||||
});
|
||||
setOpenSnack(true);
|
||||
const dataObj = {
|
||||
@ -242,35 +226,49 @@ export const AppPublish = ({ names, categories }) => {
|
||||
},
|
||||
created: Date.now(),
|
||||
};
|
||||
executeEvent("addTab", {
|
||||
executeEvent('addTab', {
|
||||
data: dataObj,
|
||||
});
|
||||
} catch (error) {
|
||||
setInfoSnack({
|
||||
type: "error",
|
||||
message: error?.message || "Unable to publish app",
|
||||
type: 'error',
|
||||
message: error?.message || 'Unable to publish app',
|
||||
});
|
||||
setOpenSnack(true);
|
||||
} finally {
|
||||
setIsLoading("");
|
||||
setIsLoading('');
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<AppsLibraryContainer sx={{
|
||||
height: !isMobile ? '100%' : 'auto',
|
||||
paddingTop: !isMobile && '30px',
|
||||
alignItems: !isMobile && 'center'
|
||||
}}>
|
||||
<AppsWidthLimiter sx={{
|
||||
width: !isMobile ? 'auto' : '90%'
|
||||
}}>
|
||||
<AppsLibraryContainer
|
||||
sx={{
|
||||
alignItems: 'center',
|
||||
height: '100%',
|
||||
paddingTop: '30px',
|
||||
}}
|
||||
>
|
||||
<AppsWidthLimiter
|
||||
sx={{
|
||||
width: 'auto',
|
||||
}}
|
||||
>
|
||||
<AppLibrarySubTitle>Create Apps!</AppLibrarySubTitle>
|
||||
|
||||
<Spacer height="18px" />
|
||||
|
||||
<PublishQAppInfo>
|
||||
Note: Currently, only one App and Website is allowed per Name.
|
||||
</PublishQAppInfo>
|
||||
|
||||
<Spacer height="18px" />
|
||||
<InputLabel sx={{ color: '#888', fontSize: '14px', marginBottom: '2px' }}>Name/App</InputLabel>
|
||||
|
||||
<InputLabel
|
||||
sx={{ color: '#888', fontSize: '14px', marginBottom: '2px' }}
|
||||
>
|
||||
Name/App
|
||||
</InputLabel>
|
||||
|
||||
<CustomSelect
|
||||
placeholder="Select Name/App"
|
||||
displayEmpty
|
||||
@ -280,19 +278,26 @@ export const AppPublish = ({ names, categories }) => {
|
||||
<CustomMenuItem value="">
|
||||
<em
|
||||
style={{
|
||||
color: "var(--50-white, #FFFFFF80)",
|
||||
color: 'var(--50-white, #FFFFFF80)',
|
||||
}}
|
||||
>
|
||||
Select Name/App
|
||||
</em>{" "}
|
||||
</em>{' '}
|
||||
{/* This is the placeholder item */}
|
||||
</CustomMenuItem>
|
||||
{names.map((name) => {
|
||||
return <CustomMenuItem value={name}>{name}</CustomMenuItem>;
|
||||
})}
|
||||
</CustomSelect>
|
||||
|
||||
<Spacer height="15px" />
|
||||
<InputLabel sx={{ color: '#888', fontSize: '14px', marginBottom: '2px' }}>App service type</InputLabel>
|
||||
|
||||
<InputLabel
|
||||
sx={{ color: '#888', fontSize: '14px', marginBottom: '2px' }}
|
||||
>
|
||||
App service type
|
||||
</InputLabel>
|
||||
|
||||
<CustomSelect
|
||||
placeholder="SERVICE TYPE"
|
||||
displayEmpty
|
||||
@ -302,59 +307,79 @@ export const AppPublish = ({ names, categories }) => {
|
||||
<CustomMenuItem value="">
|
||||
<em
|
||||
style={{
|
||||
color: "var(--50-white, #FFFFFF80)",
|
||||
color: 'var(--50-white, #FFFFFF80)',
|
||||
}}
|
||||
>
|
||||
Select App Type
|
||||
</em>{" "}
|
||||
</em>{' '}
|
||||
{/* This is the placeholder item */}
|
||||
</CustomMenuItem>
|
||||
<CustomMenuItem value={"APP"}>App</CustomMenuItem>
|
||||
<CustomMenuItem value={"WEBSITE"}>Website</CustomMenuItem>
|
||||
<CustomMenuItem value={'APP'}>App</CustomMenuItem>
|
||||
<CustomMenuItem value={'WEBSITE'}>Website</CustomMenuItem>
|
||||
</CustomSelect>
|
||||
|
||||
<Spacer height="15px" />
|
||||
<InputLabel sx={{ color: '#888', fontSize: '14px', marginBottom: '2px' }}>Title</InputLabel>
|
||||
|
||||
<InputLabel
|
||||
sx={{ color: '#888', fontSize: '14px', marginBottom: '2px' }}
|
||||
>
|
||||
Title
|
||||
</InputLabel>
|
||||
|
||||
<InputBase
|
||||
value={title}
|
||||
onChange={(e) => setTitle(e.target.value)}
|
||||
sx={{
|
||||
border: "0.5px solid var(--50-white, #FFFFFF80)",
|
||||
padding: "0px 15px",
|
||||
borderRadius: "5px",
|
||||
height: "36px",
|
||||
width: "100%",
|
||||
maxWidth: "450px",
|
||||
border: '0.5px solid var(--50-white, #FFFFFF80)',
|
||||
padding: '0px 15px',
|
||||
borderRadius: '5px',
|
||||
height: '36px',
|
||||
width: '100%',
|
||||
maxWidth: '450px',
|
||||
}}
|
||||
placeholder="Title"
|
||||
inputProps={{
|
||||
"aria-label": "Title",
|
||||
fontSize: "14px",
|
||||
fontWeight: 400,
|
||||
}}
|
||||
/>
|
||||
<Spacer height="15px" />
|
||||
<InputLabel sx={{ color: '#888', fontSize: '14px', marginBottom: '2px' }}>Description</InputLabel>
|
||||
<InputBase
|
||||
value={description}
|
||||
onChange={(e) => setDescription(e.target.value)}
|
||||
sx={{
|
||||
border: "0.5px solid var(--50-white, #FFFFFF80)",
|
||||
padding: "0px 15px",
|
||||
borderRadius: "5px",
|
||||
height: "36px",
|
||||
width: "100%",
|
||||
maxWidth: "450px",
|
||||
}}
|
||||
placeholder="Description"
|
||||
inputProps={{
|
||||
"aria-label": "Description",
|
||||
fontSize: "14px",
|
||||
'aria-label': 'Title',
|
||||
fontSize: '14px',
|
||||
fontWeight: 400,
|
||||
}}
|
||||
/>
|
||||
|
||||
<Spacer height="15px" />
|
||||
<InputLabel sx={{ color: '#888', fontSize: '14px', marginBottom: '2px' }}>Category</InputLabel>
|
||||
|
||||
<InputLabel
|
||||
sx={{ color: '#888', fontSize: '14px', marginBottom: '2px' }}
|
||||
>
|
||||
Description
|
||||
</InputLabel>
|
||||
|
||||
<InputBase
|
||||
value={description}
|
||||
onChange={(e) => setDescription(e.target.value)}
|
||||
sx={{
|
||||
border: '0.5px solid var(--50-white, #FFFFFF80)',
|
||||
padding: '0px 15px',
|
||||
borderRadius: '5px',
|
||||
height: '36px',
|
||||
width: '100%',
|
||||
maxWidth: '450px',
|
||||
}}
|
||||
placeholder="Description"
|
||||
inputProps={{
|
||||
'aria-label': 'Description',
|
||||
fontSize: '14px',
|
||||
fontWeight: 400,
|
||||
}}
|
||||
/>
|
||||
|
||||
<Spacer height="15px" />
|
||||
|
||||
<InputLabel
|
||||
sx={{ color: '#888', fontSize: '14px', marginBottom: '2px' }}
|
||||
>
|
||||
Category
|
||||
</InputLabel>
|
||||
|
||||
<CustomSelect
|
||||
displayEmpty
|
||||
placeholder="Select Category"
|
||||
@ -364,11 +389,11 @@ export const AppPublish = ({ names, categories }) => {
|
||||
<CustomMenuItem value="">
|
||||
<em
|
||||
style={{
|
||||
color: "var(--50-white, #FFFFFF80)",
|
||||
color: 'var(--50-white, #FFFFFF80)',
|
||||
}}
|
||||
>
|
||||
Select Category
|
||||
</em>{" "}
|
||||
</em>{' '}
|
||||
{/* This is the placeholder item */}
|
||||
</CustomMenuItem>
|
||||
{categories?.map((category) => {
|
||||
@ -379,23 +404,30 @@ export const AppPublish = ({ names, categories }) => {
|
||||
);
|
||||
})}
|
||||
</CustomSelect>
|
||||
|
||||
<Spacer height="15px" />
|
||||
<InputLabel sx={{ color: '#888', fontSize: '14px', marginBottom: '2px' }}>Tags</InputLabel>
|
||||
|
||||
<InputLabel
|
||||
sx={{ color: '#888', fontSize: '14px', marginBottom: '2px' }}
|
||||
>
|
||||
Tags
|
||||
</InputLabel>
|
||||
|
||||
<AppPublishTagsContainer>
|
||||
<InputBase
|
||||
value={tag1}
|
||||
onChange={(e) => setTag1(e.target.value)}
|
||||
sx={{
|
||||
border: "0.5px solid var(--50-white, #FFFFFF80)",
|
||||
padding: "0px 15px",
|
||||
borderRadius: "5px",
|
||||
height: "36px",
|
||||
width: "100px",
|
||||
border: '0.5px solid var(--50-white, #FFFFFF80)',
|
||||
padding: '0px 15px',
|
||||
borderRadius: '5px',
|
||||
height: '36px',
|
||||
width: '100px',
|
||||
}}
|
||||
placeholder="Tag 1"
|
||||
inputProps={{
|
||||
"aria-label": "Tag 1",
|
||||
fontSize: "14px",
|
||||
'aria-label': 'Tag 1',
|
||||
fontSize: '14px',
|
||||
fontWeight: 400,
|
||||
}}
|
||||
/>
|
||||
@ -403,16 +435,16 @@ export const AppPublish = ({ names, categories }) => {
|
||||
value={tag2}
|
||||
onChange={(e) => setTag2(e.target.value)}
|
||||
sx={{
|
||||
border: "0.5px solid var(--50-white, #FFFFFF80)",
|
||||
padding: "0px 15px",
|
||||
borderRadius: "5px",
|
||||
height: "36px",
|
||||
width: "100px",
|
||||
border: '0.5px solid var(--50-white, #FFFFFF80)',
|
||||
padding: '0px 15px',
|
||||
borderRadius: '5px',
|
||||
height: '36px',
|
||||
width: '100px',
|
||||
}}
|
||||
placeholder="Tag 2"
|
||||
inputProps={{
|
||||
"aria-label": "Tag 2",
|
||||
fontSize: "14px",
|
||||
'aria-label': 'Tag 2',
|
||||
fontSize: '14px',
|
||||
fontWeight: 400,
|
||||
}}
|
||||
/>
|
||||
@ -420,16 +452,16 @@ export const AppPublish = ({ names, categories }) => {
|
||||
value={tag3}
|
||||
onChange={(e) => setTag3(e.target.value)}
|
||||
sx={{
|
||||
border: "0.5px solid var(--50-white, #FFFFFF80)",
|
||||
padding: "0px 15px",
|
||||
borderRadius: "5px",
|
||||
height: "36px",
|
||||
width: "100px",
|
||||
border: '0.5px solid var(--50-white, #FFFFFF80)',
|
||||
padding: '0px 15px',
|
||||
borderRadius: '5px',
|
||||
height: '36px',
|
||||
width: '100px',
|
||||
}}
|
||||
placeholder="Tag 3"
|
||||
inputProps={{
|
||||
"aria-label": "Tag 3",
|
||||
fontSize: "14px",
|
||||
'aria-label': 'Tag 3',
|
||||
fontSize: '14px',
|
||||
fontWeight: 400,
|
||||
}}
|
||||
/>
|
||||
@ -437,16 +469,16 @@ export const AppPublish = ({ names, categories }) => {
|
||||
value={tag4}
|
||||
onChange={(e) => setTag4(e.target.value)}
|
||||
sx={{
|
||||
border: "0.5px solid var(--50-white, #FFFFFF80)",
|
||||
padding: "0px 15px",
|
||||
borderRadius: "5px",
|
||||
height: "36px",
|
||||
width: "100px",
|
||||
border: '0.5px solid var(--50-white, #FFFFFF80)',
|
||||
padding: '0px 15px',
|
||||
borderRadius: '5px',
|
||||
height: '36px',
|
||||
width: '100px',
|
||||
}}
|
||||
placeholder="Tag 4"
|
||||
inputProps={{
|
||||
"aria-label": "Tag 4",
|
||||
fontSize: "14px",
|
||||
'aria-label': 'Tag 4',
|
||||
fontSize: '14px',
|
||||
fontWeight: 400,
|
||||
}}
|
||||
/>
|
||||
@ -454,27 +486,31 @@ export const AppPublish = ({ names, categories }) => {
|
||||
value={tag5}
|
||||
onChange={(e) => setTag5(e.target.value)}
|
||||
sx={{
|
||||
border: "0.5px solid var(--50-white, #FFFFFF80)",
|
||||
padding: "0px 15px",
|
||||
borderRadius: "5px",
|
||||
height: "36px",
|
||||
width: "100px",
|
||||
border: '0.5px solid var(--50-white, #FFFFFF80)',
|
||||
padding: '0px 15px',
|
||||
borderRadius: '5px',
|
||||
height: '36px',
|
||||
width: '100px',
|
||||
}}
|
||||
placeholder="Tag 5"
|
||||
inputProps={{
|
||||
"aria-label": "Tag 5",
|
||||
fontSize: "14px",
|
||||
'aria-label': 'Tag 5',
|
||||
fontSize: '14px',
|
||||
fontWeight: 400,
|
||||
}}
|
||||
/>
|
||||
</AppPublishTagsContainer>
|
||||
|
||||
<Spacer height="30px" />
|
||||
|
||||
<PublishQAppInfo>
|
||||
Select .zip file containing static content:{" "}
|
||||
Select .zip file containing static content:{' '}
|
||||
</PublishQAppInfo>
|
||||
|
||||
<Spacer height="10px" />
|
||||
|
||||
<PublishQAppInfo>{`(${
|
||||
appType === "APP" ? "50mb" : "400mb"
|
||||
appType === 'APP' ? '50mb' : '400mb'
|
||||
} MB maximum)`}</PublishQAppInfo>
|
||||
{file && (
|
||||
<>
|
||||
@ -484,21 +520,25 @@ export const AppPublish = ({ names, categories }) => {
|
||||
)}
|
||||
|
||||
<Spacer height="18px" />
|
||||
|
||||
<PublishQAppChoseFile {...getRootProps()}>
|
||||
{" "}
|
||||
{' '}
|
||||
<input {...getInputProps()} />
|
||||
Choose File
|
||||
</PublishQAppChoseFile>
|
||||
|
||||
<Spacer height="35px" />
|
||||
|
||||
<PublishQAppCTAButton
|
||||
sx={{
|
||||
alignSelf: "center",
|
||||
alignSelf: 'center',
|
||||
}}
|
||||
onClick={publishApp}
|
||||
>
|
||||
Publish
|
||||
</PublishQAppCTAButton>
|
||||
</AppsWidthLimiter>
|
||||
|
||||
<LoadingSnackbar
|
||||
open={!!isLoading}
|
||||
info={{
|
||||
@ -512,7 +552,6 @@ export const AppPublish = ({ names, categories }) => {
|
||||
info={infoSnack}
|
||||
setInfo={setInfoSnack}
|
||||
/>
|
||||
|
||||
</AppsLibraryContainer>
|
||||
);
|
||||
};
|
||||
|
@ -1,210 +1,249 @@
|
||||
import React, { useContext, useEffect, useMemo, useState } from "react";
|
||||
import React, { useContext, useEffect, useMemo, useState } from 'react';
|
||||
import { Box } from '@mui/material';
|
||||
import { MyContext, getBaseApiReact } from '../../App';
|
||||
import { subscribeToEvent, unsubscribeFromEvent } from '../../utils/events';
|
||||
import { useFrame } from 'react-frame-component';
|
||||
import { useQortalMessageListener } from './useQortalMessageListener';
|
||||
import { useThemeContext } from '../Theme/ThemeContext';
|
||||
|
||||
import { Box, } from "@mui/material";
|
||||
import { MyContext, getBaseApiReact, isMobile } from "../../App";
|
||||
export const AppViewer = React.forwardRef(
|
||||
({ app, hide, isDevMode, skipAuth }, iframeRef) => {
|
||||
const { rootHeight } = useContext(MyContext);
|
||||
// const iframeRef = useRef(null);
|
||||
const { window: frameWindow } = useFrame();
|
||||
const { path, history, changeCurrentIndex, resetHistory } =
|
||||
useQortalMessageListener(
|
||||
frameWindow,
|
||||
iframeRef,
|
||||
app?.tabId,
|
||||
isDevMode,
|
||||
app?.name,
|
||||
app?.service,
|
||||
skipAuth
|
||||
);
|
||||
const [url, setUrl] = useState('');
|
||||
const { themeMode } = useThemeContext();
|
||||
|
||||
import { subscribeToEvent, unsubscribeFromEvent } from "../../utils/events";
|
||||
import { useFrame } from "react-frame-component";
|
||||
import { useQortalMessageListener } from "./useQortalMessageListener";
|
||||
import { useThemeContext } from "../Theme/ThemeContext";
|
||||
useEffect(() => {
|
||||
if (app?.isPreview) return;
|
||||
if (isDevMode) {
|
||||
setUrl(app?.url);
|
||||
return;
|
||||
}
|
||||
let hasQueryParam = false;
|
||||
if (app?.path && app.path.includes('?')) {
|
||||
hasQueryParam = true;
|
||||
}
|
||||
|
||||
setUrl(
|
||||
`${getBaseApiReact()}/render/${app?.service}/${app?.name}${app?.path != null ? `/${app?.path}` : ''}${hasQueryParam ? '&' : '?'}theme=${themeMode}&identifier=${app?.identifier != null && app?.identifier != 'null' ? app?.identifier : ''}`
|
||||
);
|
||||
}, [app?.service, app?.name, app?.identifier, app?.path, app?.isPreview]);
|
||||
|
||||
useEffect(() => {
|
||||
if (app?.isPreview && app?.url) {
|
||||
resetHistory();
|
||||
setUrl(app.url);
|
||||
}
|
||||
}, [app?.url, app?.isPreview]);
|
||||
const defaultUrl = useMemo(() => {
|
||||
return url;
|
||||
}, [url, isDevMode]);
|
||||
|
||||
|
||||
export const AppViewer = React.forwardRef(({ app , hide, isDevMode, skipAuth}, iframeRef) => {
|
||||
const { rootHeight } = useContext(MyContext);
|
||||
// const iframeRef = useRef(null);
|
||||
const { window: frameWindow } = useFrame();
|
||||
const {path, history, changeCurrentIndex, resetHistory} = useQortalMessageListener(frameWindow, iframeRef, app?.tabId, isDevMode, app?.name, app?.service, skipAuth)
|
||||
const [url, setUrl] = useState('')
|
||||
const { themeMode } = useThemeContext();
|
||||
|
||||
useEffect(()=> {
|
||||
if(app?.isPreview) return
|
||||
if(isDevMode){
|
||||
setUrl(app?.url)
|
||||
return
|
||||
}
|
||||
let hasQueryParam = false
|
||||
if(app?.path && app.path.includes('?')){
|
||||
hasQueryParam = true
|
||||
}
|
||||
|
||||
setUrl(`${getBaseApiReact()}/render/${app?.service}/${app?.name}${app?.path != null ? `/${app?.path}` : ''}${hasQueryParam ? "&": "?" }theme=${themeMode}&identifier=${(app?.identifier != null && app?.identifier != 'null') ? app?.identifier : ''}`)
|
||||
}, [app?.service, app?.name, app?.identifier, app?.path, app?.isPreview])
|
||||
|
||||
useEffect(()=> {
|
||||
if(app?.isPreview && app?.url){
|
||||
resetHistory()
|
||||
setUrl(app.url)
|
||||
}
|
||||
}, [app?.url, app?.isPreview])
|
||||
const defaultUrl = useMemo(()=> {
|
||||
return url
|
||||
}, [url, isDevMode])
|
||||
|
||||
|
||||
const refreshAppFunc = (e) => {
|
||||
const {tabId} = e.detail
|
||||
if(tabId === app?.tabId){
|
||||
if(isDevMode){
|
||||
resetHistory()
|
||||
if(!app?.isPreview || app?.isPrivate){
|
||||
setUrl(app?.url + `?time=${Date.now()}`)
|
||||
const refreshAppFunc = (e) => {
|
||||
const { tabId } = e.detail;
|
||||
if (tabId === app?.tabId) {
|
||||
if (isDevMode) {
|
||||
resetHistory();
|
||||
if (!app?.isPreview || app?.isPrivate) {
|
||||
setUrl(app?.url + `?time=${Date.now()}`);
|
||||
}
|
||||
return;
|
||||
}
|
||||
return
|
||||
|
||||
const constructUrl = `${getBaseApiReact()}/render/${app?.service}/${app?.name}${path != null ? path : ''}?theme=${themeMode}&identifier=${app?.identifier != null ? app?.identifier : ''}&time=${new Date().getMilliseconds()}`;
|
||||
setUrl(constructUrl);
|
||||
}
|
||||
const constructUrl = `${getBaseApiReact()}/render/${app?.service}/${app?.name}${path != null ? path : ''}?theme=${themeMode}&identifier=${app?.identifier != null ? app?.identifier : ''}&time=${new Date().getMilliseconds()}`
|
||||
setUrl(constructUrl)
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
subscribeToEvent("refreshApp", refreshAppFunc);
|
||||
|
||||
return () => {
|
||||
unsubscribeFromEvent("refreshApp", refreshAppFunc);
|
||||
};
|
||||
}, [app, path, isDevMode]);
|
||||
|
||||
useEffect(()=> {
|
||||
if(!iframeRef?.current) return
|
||||
const targetOrigin = iframeRef.current ? new URL(iframeRef.current.src).origin : "*";
|
||||
// Send the navigation command after setting up the listener and timeout
|
||||
iframeRef.current.contentWindow.postMessage(
|
||||
{ action: 'THEME_CHANGED', theme: themeMode, requestedHandler: 'UI' }, targetOrigin
|
||||
);
|
||||
}, [themeMode])
|
||||
useEffect(() => {
|
||||
subscribeToEvent('refreshApp', refreshAppFunc);
|
||||
|
||||
const removeTrailingSlash = (str) => str.replace(/\/$/, '');
|
||||
const copyLinkFunc = (e) => {
|
||||
const {tabId} = e.detail
|
||||
if(tabId === app?.tabId){
|
||||
let link = 'qortal://' + app?.service + '/' + app?.name
|
||||
if(path && path.startsWith('/')){
|
||||
link = link + removeTrailingSlash(path)
|
||||
}
|
||||
if(path && !path.startsWith('/')){
|
||||
link = link + '/' + removeTrailingSlash(path)
|
||||
}
|
||||
navigator.clipboard.writeText(link)
|
||||
.then(() => {
|
||||
console.log("Path copied to clipboard:", path);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("Failed to copy path:", error);
|
||||
});
|
||||
}
|
||||
};
|
||||
return () => {
|
||||
unsubscribeFromEvent('refreshApp', refreshAppFunc);
|
||||
};
|
||||
}, [app, path, isDevMode]);
|
||||
|
||||
useEffect(() => {
|
||||
subscribeToEvent("copyLink", copyLinkFunc);
|
||||
|
||||
return () => {
|
||||
unsubscribeFromEvent("copyLink", copyLinkFunc);
|
||||
};
|
||||
}, [app, path]);
|
||||
|
||||
// Function to navigate back in iframe
|
||||
const navigateBackInIframe = async () => {
|
||||
if (iframeRef.current && iframeRef.current.contentWindow && history?.currentIndex > 0) {
|
||||
// Calculate the previous index and path
|
||||
const previousPageIndex = history.currentIndex - 1;
|
||||
const previousPath = history.customQDNHistoryPaths[previousPageIndex];
|
||||
const targetOrigin = iframeRef.current ? new URL(iframeRef.current.src).origin : "*";
|
||||
// Signal non-manual navigation
|
||||
iframeRef.current.contentWindow.postMessage(
|
||||
{ action: 'PERFORMING_NON_MANUAL', currentIndex: previousPageIndex },targetOrigin
|
||||
);
|
||||
// Update the current index locally
|
||||
changeCurrentIndex(previousPageIndex);
|
||||
|
||||
// Create a navigation promise with a 200ms timeout
|
||||
const navigationPromise = new Promise((resolve, reject) => {
|
||||
function handleNavigationSuccess(event) {
|
||||
if (event.data?.action === 'NAVIGATION_SUCCESS' && event.data.path === previousPath) {
|
||||
frameWindow.removeEventListener('message', handleNavigationSuccess);
|
||||
resolve();
|
||||
}
|
||||
}
|
||||
|
||||
frameWindow.addEventListener('message', handleNavigationSuccess);
|
||||
|
||||
// Timeout after 200ms if no response
|
||||
setTimeout(() => {
|
||||
window.removeEventListener('message', handleNavigationSuccess);
|
||||
reject(new Error("Navigation timeout"));
|
||||
}, 200);
|
||||
const targetOrigin = iframeRef.current ? new URL(iframeRef.current.src).origin : "*";
|
||||
useEffect(() => {
|
||||
if (!iframeRef?.current) return;
|
||||
const targetOrigin = iframeRef.current
|
||||
? new URL(iframeRef.current.src).origin
|
||||
: '*';
|
||||
// Send the navigation command after setting up the listener and timeout
|
||||
iframeRef.current.contentWindow.postMessage(
|
||||
{ action: 'NAVIGATE_TO_PATH', path: previousPath, requestedHandler: 'UI' }, targetOrigin
|
||||
{ action: 'THEME_CHANGED', theme: themeMode, requestedHandler: 'UI' },
|
||||
targetOrigin
|
||||
);
|
||||
});
|
||||
}, [themeMode]);
|
||||
|
||||
// Execute navigation promise and handle timeout fallback
|
||||
try {
|
||||
await navigationPromise;
|
||||
} catch (error) {
|
||||
if(isDevMode){
|
||||
setUrl(`${url}${previousPath != null ? previousPath : ''}?theme=${themeMode}&time=${new Date().getMilliseconds()}&isManualNavigation=false`)
|
||||
return
|
||||
}
|
||||
setUrl(`${getBaseApiReact()}/render/${app?.service}/${app?.name}${previousPath != null ? previousPath : ''}?theme=${themeMode}&identifier=${(app?.identifier != null && app?.identifier != 'null') ? app?.identifier : ''}&time=${new Date().getMilliseconds()}&isManualNavigation=false`)
|
||||
// iframeRef.current.contentWindow.location.href = previousPath; // Fallback URL update
|
||||
}
|
||||
} else {
|
||||
console.log('Iframe not accessible or does not have a content window.');
|
||||
}
|
||||
};
|
||||
const removeTrailingSlash = (str) => str.replace(/\/$/, '');
|
||||
|
||||
const navigateBackAppFunc = (e) => {
|
||||
|
||||
navigateBackInIframe()
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if(!app?.tabId) return
|
||||
subscribeToEvent(`navigateBackApp-${app?.tabId}`, navigateBackAppFunc);
|
||||
|
||||
return () => {
|
||||
unsubscribeFromEvent(`navigateBackApp-${app?.tabId}`, navigateBackAppFunc);
|
||||
const copyLinkFunc = (e) => {
|
||||
const { tabId } = e.detail;
|
||||
if (tabId === app?.tabId) {
|
||||
let link = 'qortal://' + app?.service + '/' + app?.name;
|
||||
if (path && path.startsWith('/')) {
|
||||
link = link + removeTrailingSlash(path);
|
||||
}
|
||||
if (path && !path.startsWith('/')) {
|
||||
link = link + '/' + removeTrailingSlash(path);
|
||||
}
|
||||
navigator.clipboard
|
||||
.writeText(link)
|
||||
.then(() => {
|
||||
console.log('Path copied to clipboard:', path);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Failed to copy path:', error);
|
||||
});
|
||||
}
|
||||
};
|
||||
}, [app, history]);
|
||||
|
||||
useEffect(() => {
|
||||
subscribeToEvent('copyLink', copyLinkFunc);
|
||||
|
||||
// Function to navigate back in iframe
|
||||
const navigateForwardInIframe = async () => {
|
||||
return () => {
|
||||
unsubscribeFromEvent('copyLink', copyLinkFunc);
|
||||
};
|
||||
}, [app, path]);
|
||||
|
||||
|
||||
if (iframeRef.current && iframeRef.current.contentWindow) {
|
||||
const targetOrigin = iframeRef.current ? new URL(iframeRef.current.src).origin : "*";
|
||||
iframeRef.current.contentWindow.postMessage(
|
||||
{ action: 'NAVIGATE_FORWARD'},
|
||||
// Function to navigate back in iframe
|
||||
const navigateBackInIframe = async () => {
|
||||
if (
|
||||
iframeRef.current &&
|
||||
iframeRef.current.contentWindow &&
|
||||
history?.currentIndex > 0
|
||||
) {
|
||||
// Calculate the previous index and path
|
||||
const previousPageIndex = history.currentIndex - 1;
|
||||
const previousPath = history.customQDNHistoryPaths[previousPageIndex];
|
||||
const targetOrigin = iframeRef.current
|
||||
? new URL(iframeRef.current.src).origin
|
||||
: '*';
|
||||
// Signal non-manual navigation
|
||||
iframeRef.current.contentWindow.postMessage(
|
||||
{ action: 'PERFORMING_NON_MANUAL', currentIndex: previousPageIndex },
|
||||
targetOrigin
|
||||
);
|
||||
} else {
|
||||
console.log('Iframe not accessible or does not have a content window.');
|
||||
);
|
||||
// Update the current index locally
|
||||
changeCurrentIndex(previousPageIndex);
|
||||
|
||||
// Create a navigation promise with a 200ms timeout
|
||||
const navigationPromise = new Promise((resolve, reject) => {
|
||||
function handleNavigationSuccess(event) {
|
||||
if (
|
||||
event.data?.action === 'NAVIGATION_SUCCESS' &&
|
||||
event.data.path === previousPath
|
||||
) {
|
||||
frameWindow.removeEventListener(
|
||||
'message',
|
||||
handleNavigationSuccess
|
||||
);
|
||||
resolve();
|
||||
}
|
||||
}
|
||||
|
||||
frameWindow.addEventListener('message', handleNavigationSuccess);
|
||||
|
||||
// Timeout after 200ms if no response
|
||||
setTimeout(() => {
|
||||
window.removeEventListener('message', handleNavigationSuccess);
|
||||
reject(new Error('Navigation timeout'));
|
||||
}, 200);
|
||||
const targetOrigin = iframeRef.current
|
||||
? new URL(iframeRef.current.src).origin
|
||||
: '*';
|
||||
// Send the navigation command after setting up the listener and timeout
|
||||
iframeRef.current.contentWindow.postMessage(
|
||||
{
|
||||
action: 'NAVIGATE_TO_PATH',
|
||||
path: previousPath,
|
||||
requestedHandler: 'UI',
|
||||
},
|
||||
targetOrigin
|
||||
);
|
||||
});
|
||||
|
||||
// Execute navigation promise and handle timeout fallback
|
||||
try {
|
||||
await navigationPromise;
|
||||
} catch (error) {
|
||||
if (isDevMode) {
|
||||
setUrl(
|
||||
`${url}${previousPath != null ? previousPath : ''}?theme=${themeMode}&time=${new Date().getMilliseconds()}&isManualNavigation=false`
|
||||
);
|
||||
return;
|
||||
}
|
||||
setUrl(
|
||||
`${getBaseApiReact()}/render/${app?.service}/${app?.name}${previousPath != null ? previousPath : ''}?theme=${themeMode}&identifier=${app?.identifier != null && app?.identifier != 'null' ? app?.identifier : ''}&time=${new Date().getMilliseconds()}&isManualNavigation=false`
|
||||
);
|
||||
// iframeRef.current.contentWindow.location.href = previousPath; // Fallback URL update
|
||||
}
|
||||
} else {
|
||||
console.log('Iframe not accessible or does not have a content window.');
|
||||
}
|
||||
};
|
||||
|
||||
const navigateBackAppFunc = (e) => {
|
||||
navigateBackInIframe();
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!app?.tabId) return;
|
||||
subscribeToEvent(`navigateBackApp-${app?.tabId}`, navigateBackAppFunc);
|
||||
|
||||
return () => {
|
||||
unsubscribeFromEvent(
|
||||
`navigateBackApp-${app?.tabId}`,
|
||||
navigateBackAppFunc
|
||||
);
|
||||
};
|
||||
}, [app, history]);
|
||||
|
||||
// Function to navigate back in iframe
|
||||
const navigateForwardInIframe = async () => {
|
||||
if (iframeRef.current && iframeRef.current.contentWindow) {
|
||||
const targetOrigin = iframeRef.current
|
||||
? new URL(iframeRef.current.src).origin
|
||||
: '*';
|
||||
iframeRef.current.contentWindow.postMessage(
|
||||
{ action: 'NAVIGATE_FORWARD' },
|
||||
targetOrigin
|
||||
);
|
||||
} else {
|
||||
console.log('Iframe not accessible or does not have a content window.');
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
}}
|
||||
>
|
||||
<iframe
|
||||
ref={iframeRef}
|
||||
style={{
|
||||
height: '100vh',
|
||||
border: 'none',
|
||||
width: '100%',
|
||||
}}
|
||||
id="browser-iframe"
|
||||
src={defaultUrl}
|
||||
sandbox="allow-scripts allow-same-origin allow-forms allow-downloads allow-modals"
|
||||
allow="fullscreen; clipboard-read; clipboard-write"
|
||||
></iframe>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
}}>
|
||||
|
||||
<iframe ref={iframeRef} style={{
|
||||
height: !isMobile ? '100vh' : `calc(${rootHeight} - 60px - 45px )`,
|
||||
border: 'none',
|
||||
width: '100%'
|
||||
}} id="browser-iframe" src={defaultUrl} sandbox="allow-scripts allow-same-origin allow-forms allow-downloads allow-modals"
|
||||
allow="fullscreen; clipboard-read; clipboard-write">
|
||||
|
||||
</iframe>
|
||||
</Box>
|
||||
|
||||
);
|
||||
});
|
||||
);
|
||||
|
@ -1,20 +1,19 @@
|
||||
import React, { useContext, } from 'react';
|
||||
import React, { useContext } from 'react';
|
||||
import { AppViewer } from './AppViewer';
|
||||
import Frame from 'react-frame-component';
|
||||
import { MyContext, isMobile } from '../../App';
|
||||
import { MyContext } from '../../App';
|
||||
|
||||
const AppViewerContainer = React.forwardRef(({ app, isSelected, hide, isDevMode, customHeight, skipAuth }, ref) => {
|
||||
const { rootHeight } = useContext(MyContext);
|
||||
const AppViewerContainer = React.forwardRef(
|
||||
({ app, isSelected, hide, isDevMode, customHeight, skipAuth }, ref) => {
|
||||
const { rootHeight } = useContext(MyContext);
|
||||
|
||||
|
||||
return (
|
||||
<Frame
|
||||
id={`browser-iframe-${app?.tabId}`}
|
||||
|
||||
head={
|
||||
<>
|
||||
<style>
|
||||
{`
|
||||
return (
|
||||
<Frame
|
||||
id={`browser-iframe-${app?.tabId}`}
|
||||
head={
|
||||
<>
|
||||
<style>
|
||||
{`
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
@ -28,24 +27,31 @@ const AppViewerContainer = React.forwardRef(({ app, isSelected, hide, isDevMode,
|
||||
}
|
||||
.frame-content {
|
||||
overflow: hidden;
|
||||
height: ${!isMobile ? '100vh' : `calc(${rootHeight} - 60px - 45px)`};
|
||||
height: '100vh';
|
||||
}
|
||||
`}
|
||||
</style>
|
||||
</>
|
||||
}
|
||||
style={{
|
||||
position: (!isSelected || hide) && 'fixed',
|
||||
left: (!isSelected || hide) && '-200vw',
|
||||
height: customHeight ? customHeight : !isMobile ? '100vh' : `calc(${rootHeight} - 60px - 45px)`,
|
||||
border: 'none',
|
||||
width: '100%',
|
||||
overflow: 'hidden',
|
||||
}}
|
||||
>
|
||||
<AppViewer skipAuth={skipAuth} app={app} ref={ref} hide={!isSelected || hide} isDevMode={isDevMode} />
|
||||
</Frame>
|
||||
);
|
||||
});
|
||||
</style>
|
||||
</>
|
||||
}
|
||||
style={{
|
||||
border: 'none',
|
||||
height: '100vh',
|
||||
left: (!isSelected || hide) && '-200vw',
|
||||
overflow: 'hidden',
|
||||
position: (!isSelected || hide) && 'fixed',
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
<AppViewer
|
||||
skipAuth={skipAuth}
|
||||
app={app}
|
||||
ref={ref}
|
||||
hide={!isSelected || hide}
|
||||
isDevMode={isDevMode}
|
||||
/>
|
||||
</Frame>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export default AppViewerContainer;
|
||||
|
@ -137,9 +137,9 @@ export const AppCircle = styled(Box)(({ theme }) => ({
|
||||
theme.palette.mode === 'dark'
|
||||
? 'rgb(209, 209, 209)'
|
||||
: 'rgba(41, 41, 43, 1)',
|
||||
borderWidth: '1px',
|
||||
borderRadius: '50%',
|
||||
borderStyle: 'solid',
|
||||
borderWidth: '1px',
|
||||
color: theme.palette.text.primary,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
|
@ -22,7 +22,7 @@ import {
|
||||
Input,
|
||||
} from '@mui/material';
|
||||
import { Add } from '@mui/icons-material';
|
||||
import { MyContext, getBaseApiReact, isMobile } from '../../App';
|
||||
import { MyContext, getBaseApiReact } from '../../App';
|
||||
import LogoSelected from '../../assets/svgs/LogoSelected.svg';
|
||||
import { executeEvent } from '../../utils/events';
|
||||
import { Spacer } from '../../common/Spacer';
|
||||
@ -279,7 +279,9 @@ export const AppsDevModeHome = ({
|
||||
Dev Mode Apps
|
||||
</AppLibrarySubTitle>
|
||||
</AppsContainer>
|
||||
|
||||
<Spacer height="45px" />
|
||||
|
||||
<AppsContainer
|
||||
sx={{
|
||||
gap: '75px',
|
||||
@ -293,7 +295,7 @@ export const AppsDevModeHome = ({
|
||||
>
|
||||
<AppCircleContainer
|
||||
sx={{
|
||||
gap: !isMobile ? '10px' : '5px',
|
||||
gap: '10px',
|
||||
}}
|
||||
>
|
||||
<AppCircle>
|
||||
@ -302,6 +304,7 @@ export const AppsDevModeHome = ({
|
||||
<AppCircleLabel>Server</AppCircleLabel>
|
||||
</AppCircleContainer>
|
||||
</ButtonBase>
|
||||
|
||||
<ButtonBase
|
||||
onClick={() => {
|
||||
addPreviewApp();
|
||||
@ -309,15 +312,17 @@ export const AppsDevModeHome = ({
|
||||
>
|
||||
<AppCircleContainer
|
||||
sx={{
|
||||
gap: !isMobile ? '10px' : '5px',
|
||||
gap: '10px',
|
||||
}}
|
||||
>
|
||||
<AppCircle>
|
||||
<Add>+</Add>
|
||||
</AppCircle>
|
||||
|
||||
<AppCircleLabel>Zip</AppCircleLabel>
|
||||
</AppCircleContainer>
|
||||
</ButtonBase>
|
||||
|
||||
<ButtonBase
|
||||
onClick={() => {
|
||||
addPreviewAppWithDirectory();
|
||||
@ -325,7 +330,7 @@ export const AppsDevModeHome = ({
|
||||
>
|
||||
<AppCircleContainer
|
||||
sx={{
|
||||
gap: !isMobile ? '10px' : '5px',
|
||||
gap: '10px',
|
||||
}}
|
||||
>
|
||||
<AppCircle>
|
||||
@ -334,6 +339,7 @@ export const AppsDevModeHome = ({
|
||||
<AppCircleLabel>Directory</AppCircleLabel>
|
||||
</AppCircleContainer>
|
||||
</ButtonBase>
|
||||
|
||||
<ButtonBase
|
||||
onClick={() => {
|
||||
executeEvent('appsDevModeAddTab', {
|
||||
@ -347,7 +353,7 @@ export const AppsDevModeHome = ({
|
||||
>
|
||||
<AppCircleContainer
|
||||
sx={{
|
||||
gap: !isMobile ? '10px' : '5px',
|
||||
gap: '10px',
|
||||
}}
|
||||
>
|
||||
<AppCircle>
|
||||
@ -371,9 +377,11 @@ export const AppsDevModeHome = ({
|
||||
/>
|
||||
</Avatar>
|
||||
</AppCircle>
|
||||
|
||||
<AppCircleLabel>Q-Sandbox</AppCircleLabel>
|
||||
</AppCircleContainer>
|
||||
</ButtonBase>
|
||||
|
||||
<ButtonBase
|
||||
onClick={() => {
|
||||
executeEvent('appsDevModeAddTab', {
|
||||
@ -387,7 +395,7 @@ export const AppsDevModeHome = ({
|
||||
>
|
||||
<AppCircleContainer
|
||||
sx={{
|
||||
gap: !isMobile ? '10px' : '5px',
|
||||
gap: '10px',
|
||||
}}
|
||||
>
|
||||
<AppCircle>
|
||||
@ -411,10 +419,12 @@ export const AppsDevModeHome = ({
|
||||
/>
|
||||
</Avatar>
|
||||
</AppCircle>
|
||||
|
||||
<AppCircleLabel>API</AppCircleLabel>
|
||||
</AppCircleContainer>
|
||||
</ButtonBase>
|
||||
</AppsContainer>
|
||||
|
||||
{isShow && (
|
||||
<Dialog
|
||||
open={isShow}
|
||||
@ -429,6 +439,7 @@ export const AppsDevModeHome = ({
|
||||
<DialogTitle id="alert-dialog-title">
|
||||
{'Add custom framework'}
|
||||
</DialogTitle>
|
||||
|
||||
<DialogContent>
|
||||
<Box
|
||||
sx={{
|
||||
@ -460,6 +471,7 @@ export const AppsDevModeHome = ({
|
||||
/>
|
||||
</Box>
|
||||
</DialogContent>
|
||||
|
||||
<DialogActions>
|
||||
<Button variant="contained" onClick={onCancel}>
|
||||
Close
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { useState } from 'react';
|
||||
import { useState } from 'react';
|
||||
import {
|
||||
AppCircle,
|
||||
AppCircleContainer,
|
||||
@ -8,7 +8,6 @@ import {
|
||||
} from './Apps-styles';
|
||||
import { Box, ButtonBase, Input, useTheme } from '@mui/material';
|
||||
import { Add } from '@mui/icons-material';
|
||||
import { isMobile } from '../../App';
|
||||
import { executeEvent } from '../../utils/events';
|
||||
import { Spacer } from '../../common/Spacer';
|
||||
import { SortablePinnedApps } from './SortablePinnedApps';
|
||||
@ -137,12 +136,13 @@ export const AppsHomeDesktop = ({
|
||||
>
|
||||
<AppCircleContainer
|
||||
sx={{
|
||||
gap: !isMobile ? '10px' : '5px',
|
||||
gap: '10px',
|
||||
}}
|
||||
>
|
||||
<AppCircle>
|
||||
<Add>+</Add>
|
||||
</AppCircle>
|
||||
|
||||
<AppCircleLabel>Library</AppCircleLabel>
|
||||
</AppCircleContainer>
|
||||
</ButtonBase>
|
||||
|
@ -1,11 +1,4 @@
|
||||
import React, {
|
||||
useCallback,
|
||||
useContext,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react';
|
||||
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import {
|
||||
AppCircle,
|
||||
AppCircleContainer,
|
||||
@ -32,17 +25,14 @@ import {
|
||||
Typography,
|
||||
styled,
|
||||
} from '@mui/material';
|
||||
import { Add } from '@mui/icons-material';
|
||||
import { MyContext, getBaseApiReact } from '../../App';
|
||||
import { getBaseApiReact } from '../../App';
|
||||
import LogoSelected from '../../assets/svgs/LogoSelected.svg';
|
||||
import IconSearch from '../../assets/svgs/Search.svg';
|
||||
import IconClearInput from '../../assets/svgs/ClearInput.svg';
|
||||
import qappDevelopText from '../../assets/svgs/qappDevelopText.svg';
|
||||
import qappLibraryText from '../../assets/svgs/qappLibraryText.svg';
|
||||
import RefreshIcon from '@mui/icons-material/Refresh';
|
||||
|
||||
import qappDots from '../../assets/svgs/qappDots.svg';
|
||||
|
||||
import { Spacer } from '../../common/Spacer';
|
||||
import { AppInfoSnippet } from './AppInfoSnippet';
|
||||
import { Virtuoso } from 'react-virtuoso';
|
||||
@ -57,6 +47,7 @@ import {
|
||||
MailIconImg,
|
||||
ShowMessageReturnButton,
|
||||
} from '../Group/Forum/Mail-styles';
|
||||
|
||||
const officialAppList = [
|
||||
'q-tube',
|
||||
'q-blog',
|
||||
|
@ -6,7 +6,6 @@ import {
|
||||
Dialog,
|
||||
DialogActions,
|
||||
DialogContent,
|
||||
DialogTitle,
|
||||
Input,
|
||||
MenuItem,
|
||||
Select,
|
||||
@ -32,7 +31,7 @@ import {
|
||||
PublishQAppInfo,
|
||||
} from './Apps-styles';
|
||||
import ImageUploader from '../../common/ImageUploader';
|
||||
import { isMobile, MyContext } from '../../App';
|
||||
import { MyContext } from '../../App';
|
||||
import { fileToBase64 } from '../../utils/fileReading';
|
||||
import { objectToBase64 } from '../../qdn/encryption/group-encryption';
|
||||
import { getFee } from '../../background';
|
||||
@ -67,6 +66,7 @@ export const AppsPrivate = ({ myName }) => {
|
||||
(group) => groupsProperties[group?.groupId]?.isOpen === false
|
||||
);
|
||||
}, [memberGroups, groupsProperties]);
|
||||
|
||||
const [privateAppValues, setPrivateAppValues] = useState({
|
||||
name: '',
|
||||
service: 'DOCUMENT',
|
||||
@ -230,7 +230,7 @@ export const AppsPrivate = ({ myName }) => {
|
||||
>
|
||||
<AppCircleContainer
|
||||
sx={{
|
||||
gap: !isMobile ? '10px' : '5px',
|
||||
gap: '10px',
|
||||
}}
|
||||
>
|
||||
<AppCircle>
|
||||
@ -267,9 +267,8 @@ export const AppsPrivate = ({ myName }) => {
|
||||
value={valueTabPrivateApp}
|
||||
onChange={handleChange}
|
||||
aria-label="basic tabs example"
|
||||
variant={isMobile ? 'scrollable' : 'fullWidth'} // Scrollable on mobile, full width on desktop
|
||||
variant={'fullWidth'}
|
||||
scrollButtons="auto"
|
||||
allowScrollButtonsMobile
|
||||
sx={{
|
||||
'& .MuiTabs-indicator': {
|
||||
backgroundColor: theme.palette.background.default,
|
||||
@ -283,7 +282,7 @@ export const AppsPrivate = ({ myName }) => {
|
||||
'&.Mui-selected': {
|
||||
color: theme.palette.text.primary,
|
||||
},
|
||||
fontSize: isMobile ? '0.75rem' : '1rem', // Adjust font size for mobile
|
||||
fontSize: '1rem',
|
||||
}}
|
||||
/>
|
||||
<Tab
|
||||
@ -293,7 +292,7 @@ export const AppsPrivate = ({ myName }) => {
|
||||
'&.Mui-selected': {
|
||||
color: theme.palette.text.primary,
|
||||
},
|
||||
fontSize: isMobile ? '0.75rem' : '1rem', // Adjust font size for mobile
|
||||
fontSize: '1rem',
|
||||
}}
|
||||
/>
|
||||
</Tabs>
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { useContext, useEffect, useState } from 'react';
|
||||
import { MyContext, isMobile } from '../../App';
|
||||
import { MyContext } from '../../App';
|
||||
import { Box, Typography } from '@mui/material';
|
||||
import { AdminSpaceInner } from './AdminSpaceInner';
|
||||
|
||||
@ -29,10 +29,9 @@ export const AdminSpace = ({
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
// reference to change height
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
height: isMobile ? `calc(${rootHeight} - 127px` : 'calc(100vh - 70px)',
|
||||
height: 'calc(100vh - 70px)',
|
||||
left: hide && '-1000px',
|
||||
opacity: hide ? 0 : 1,
|
||||
position: hide ? 'fixed' : 'relative',
|
||||
|
@ -21,7 +21,6 @@ import ArrowBackIcon from '@mui/icons-material/ArrowBack';
|
||||
import {
|
||||
getArbitraryEndpointReact,
|
||||
getBaseApiReact,
|
||||
isMobile,
|
||||
pauseAllQueues,
|
||||
resumeAllQueues,
|
||||
} from '../../App';
|
||||
@ -56,12 +55,6 @@ export const AnnouncementDiscussion = ({
|
||||
const clearEditorContent = () => {
|
||||
if (editorRef.current) {
|
||||
editorRef.current.chain().focus().clearContent().run();
|
||||
if (isMobile) {
|
||||
setTimeout(() => {
|
||||
editorRef.current?.chain().blur().run();
|
||||
setIsFocusedParent(false);
|
||||
}, 200);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -278,7 +271,7 @@ export const AnnouncementDiscussion = ({
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
height: isMobile ? '100%' : '100%',
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
|
@ -20,7 +20,6 @@ import { CustomizedSnackbars } from '../Snackbar/Snackbar';
|
||||
import {
|
||||
getBaseApiReact,
|
||||
getBaseApiReactSocket,
|
||||
isMobile,
|
||||
pauseAllQueues,
|
||||
resumeAllQueues,
|
||||
} from '../../App';
|
||||
@ -410,16 +409,6 @@ export const ChatDirect = ({
|
||||
if (editorRef.current) {
|
||||
setMessageSize(0);
|
||||
editorRef.current.chain().focus().clearContent().run();
|
||||
if (isMobile) {
|
||||
setTimeout(() => {
|
||||
editorRef.current?.chain().blur().run();
|
||||
setIsFocusedParent(false);
|
||||
executeEvent('sent-new-message-group', {});
|
||||
setTimeout(() => {
|
||||
triggerRerender();
|
||||
}, 300);
|
||||
}, 200);
|
||||
}
|
||||
}
|
||||
};
|
||||
useEffect(() => {
|
||||
@ -547,108 +536,41 @@ export const ChatDirect = ({
|
||||
background: theme.palette.background.default,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
height: isMobile ? '100%' : '100vh',
|
||||
height: '100vh',
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
{!isMobile && (
|
||||
<Box
|
||||
onClick={close}
|
||||
<Box
|
||||
onClick={close}
|
||||
sx={{
|
||||
alignItems: 'center',
|
||||
alignSelf: 'center',
|
||||
background: theme.palette.background.default,
|
||||
borderRadius: '3px',
|
||||
cursor: 'pointer',
|
||||
display: 'flex',
|
||||
gap: '5px',
|
||||
margin: '10px 0px',
|
||||
padding: '4px 6px',
|
||||
width: 'fit-content',
|
||||
}}
|
||||
>
|
||||
<ArrowBackIcon
|
||||
sx={{
|
||||
alignItems: 'center',
|
||||
alignSelf: 'center',
|
||||
background: theme.palette.background.default,
|
||||
borderRadius: '3px',
|
||||
cursor: 'pointer',
|
||||
display: 'flex',
|
||||
gap: '5px',
|
||||
margin: '10px 0px',
|
||||
padding: '4px 6px',
|
||||
width: 'fit-content',
|
||||
color: theme.palette.text.primary,
|
||||
fontSize: '20px',
|
||||
}}
|
||||
/>
|
||||
<Typography
|
||||
sx={{
|
||||
color: theme.palette.text.primary,
|
||||
fontSize: '14px',
|
||||
}}
|
||||
>
|
||||
<ArrowBackIcon
|
||||
sx={{
|
||||
color: theme.palette.text.primary,
|
||||
fontSize: '20px',
|
||||
}}
|
||||
/>
|
||||
<Typography
|
||||
sx={{
|
||||
color: theme.palette.text.primary,
|
||||
fontSize: '14px',
|
||||
}}
|
||||
>
|
||||
Close Direct Chat
|
||||
</Typography>
|
||||
</Box>
|
||||
)}
|
||||
{isMobile && (
|
||||
<Box
|
||||
sx={{
|
||||
alignItems: 'center',
|
||||
display: 'flex',
|
||||
height: '15px',
|
||||
justifyContent: 'center',
|
||||
marginTop: '14px',
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
alignItems: 'center',
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
width: '320px',
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
alignItems: 'center',
|
||||
display: 'flex',
|
||||
width: '50px',
|
||||
}}
|
||||
>
|
||||
<ButtonBase
|
||||
onClick={() => {
|
||||
close();
|
||||
}}
|
||||
>
|
||||
<ReturnIcon />
|
||||
</ButtonBase>
|
||||
</Box>
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: '14px',
|
||||
fontWeight: 600,
|
||||
}}
|
||||
>
|
||||
{isNewChat
|
||||
? ''
|
||||
: selectedDirect?.name ||
|
||||
selectedDirect?.address?.slice(0, 10) + '...'}
|
||||
</Typography>
|
||||
<Box
|
||||
sx={{
|
||||
alignItems: 'center',
|
||||
display: 'flex',
|
||||
justifyContent: 'flex-end',
|
||||
width: '50px',
|
||||
}}
|
||||
>
|
||||
<ButtonBase
|
||||
onClick={() => {
|
||||
setSelectedDirect(null);
|
||||
setMobileViewModeKeepOpen('');
|
||||
setNewChat(false);
|
||||
}}
|
||||
>
|
||||
<ExitIcon />
|
||||
</ButtonBase>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
)}
|
||||
Close Direct Chat
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
{isNewChat && (
|
||||
<>
|
||||
<Spacer height="30px" />
|
||||
@ -685,7 +607,7 @@ export const ChatDirect = ({
|
||||
flexShrink: 0,
|
||||
minHeight: '150px',
|
||||
overflow: 'hidden',
|
||||
padding: isMobile ? '10px' : '20px',
|
||||
padding: '20px',
|
||||
position: isFocusedParent ? 'fixed' : 'relative',
|
||||
top: isFocusedParent ? '0px' : 'unset',
|
||||
width: '100%',
|
||||
@ -753,7 +675,7 @@ export const ChatDirect = ({
|
||||
setEditorRef={setEditorRef}
|
||||
onEnter={sendMessage}
|
||||
isChat
|
||||
disableEnter={isMobile ? true : false}
|
||||
disableEnter={false}
|
||||
setIsFocusedParent={setIsFocusedParent}
|
||||
/>
|
||||
{messageSize > 750 && (
|
||||
|
@ -20,7 +20,6 @@ import { LoadingSnackbar } from '../Snackbar/LoadingSnackbar';
|
||||
import {
|
||||
getBaseApiReact,
|
||||
getBaseApiReactSocket,
|
||||
isMobile,
|
||||
MyContext,
|
||||
pauseAllQueues,
|
||||
resumeAllQueues,
|
||||
@ -746,16 +745,6 @@ export const ChatGroup = ({
|
||||
if (editorRef.current) {
|
||||
setMessageSize(0);
|
||||
editorRef.current.chain().focus().clearContent().run();
|
||||
if (isMobile) {
|
||||
setTimeout(() => {
|
||||
editorRef.current?.chain().blur().run();
|
||||
setIsFocusedParent(false);
|
||||
executeEvent('sent-new-message-group', {});
|
||||
setTimeout(() => {
|
||||
triggerRerender();
|
||||
}, 300);
|
||||
}, 200);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -1109,7 +1098,7 @@ export const ChatGroup = ({
|
||||
setEditorRef={setEditorRef}
|
||||
onEnter={sendMessage}
|
||||
isChat
|
||||
disableEnter={isMobile ? true : false}
|
||||
disableEnter={false}
|
||||
isFocusedParent={isFocusedParent}
|
||||
setIsFocusedParent={setIsFocusedParent}
|
||||
membersWithNames={members}
|
||||
|
@ -26,7 +26,6 @@ import {
|
||||
MyContext,
|
||||
getArbitraryEndpointReact,
|
||||
getBaseApiReact,
|
||||
isMobile,
|
||||
pauseAllQueues,
|
||||
resumeAllQueues,
|
||||
} from '../../App';
|
||||
@ -262,15 +261,6 @@ export const GroupAnnouncements = ({
|
||||
const clearEditorContent = () => {
|
||||
if (editorRef.current) {
|
||||
editorRef.current.chain().focus().clearContent().run();
|
||||
if (isMobile) {
|
||||
setTimeout(() => {
|
||||
editorRef.current?.chain().blur().run();
|
||||
setIsFocusedParent(false);
|
||||
setTimeout(() => {
|
||||
triggerRerender();
|
||||
}, 300);
|
||||
}, 200);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -529,12 +519,9 @@ export const GroupAnnouncements = ({
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
// reference to change height
|
||||
height: isMobile
|
||||
? `calc(${rootHeight} - 127px`
|
||||
: 'calc(100vh - 70px)',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
height: 'calc(100vh - 70px)',
|
||||
left: hide && '-1000px',
|
||||
position: hide && 'fixed',
|
||||
visibility: hide && 'hidden',
|
||||
@ -576,26 +563,24 @@ export const GroupAnnouncements = ({
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
{!isMobile && (
|
||||
<Box
|
||||
<Box
|
||||
sx={{
|
||||
alignItems: 'center',
|
||||
display: 'flex',
|
||||
fontSize: '20px',
|
||||
gap: '20px',
|
||||
justifyContent: 'center',
|
||||
padding: '25px',
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
<CampaignIcon
|
||||
sx={{
|
||||
alignItems: 'center',
|
||||
display: 'flex',
|
||||
fontSize: '20px',
|
||||
gap: '20px',
|
||||
justifyContent: 'center',
|
||||
padding: '25px',
|
||||
width: '100%',
|
||||
fontSize: '30px',
|
||||
}}
|
||||
>
|
||||
<CampaignIcon
|
||||
sx={{
|
||||
fontSize: '30px',
|
||||
}}
|
||||
/>
|
||||
Group Announcements
|
||||
</Box>
|
||||
)}
|
||||
/>
|
||||
Group Announcements
|
||||
</Box>
|
||||
|
||||
<Spacer height={'25px'} />
|
||||
</div>
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { useContext, useEffect, useState } from 'react';
|
||||
import { GroupMail } from '../Group/Forum/GroupMail';
|
||||
import { MyContext, isMobile } from '../../App';
|
||||
import { MyContext } from '../../App';
|
||||
|
||||
export const GroupForum = ({
|
||||
selectedGroup,
|
||||
|
@ -23,7 +23,6 @@ import DeveloperModeIcon from '@mui/icons-material/DeveloperMode';
|
||||
import Compressor from 'compressorjs';
|
||||
import Mention from '@tiptap/extension-mention';
|
||||
import ImageResize from 'tiptap-extension-resize-image'; // Import the ResizeImage extension
|
||||
import { isMobile } from '../../App';
|
||||
import tippy from 'tippy.js';
|
||||
import 'tippy.js/dist/tippy.css';
|
||||
import { ReactRenderer } from '@tiptap/react';
|
||||
@ -137,7 +136,7 @@ const MenuBar = ({
|
||||
color: editor.isActive('bold')
|
||||
? theme.palette.text.primary
|
||||
: theme.palette.text.secondary,
|
||||
padding: isMobile ? '5px' : 'revert',
|
||||
padding: 'revert',
|
||||
}}
|
||||
>
|
||||
<FormatBoldIcon />
|
||||
@ -149,7 +148,7 @@ const MenuBar = ({
|
||||
color: editor.isActive('italic')
|
||||
? theme.palette.text.primary
|
||||
: theme.palette.text.secondary,
|
||||
padding: isMobile ? '5px' : 'revert',
|
||||
padding: 'revert',
|
||||
}}
|
||||
>
|
||||
<FormatItalicIcon />
|
||||
@ -161,7 +160,7 @@ const MenuBar = ({
|
||||
color: editor.isActive('strike')
|
||||
? theme.palette.text.primary
|
||||
: theme.palette.text.secondary,
|
||||
padding: isMobile ? '5px' : 'revert',
|
||||
padding: 'revert',
|
||||
}}
|
||||
>
|
||||
<StrikethroughSIcon />
|
||||
@ -173,7 +172,7 @@ const MenuBar = ({
|
||||
color: editor.isActive('code')
|
||||
? theme.palette.text.primary
|
||||
: theme.palette.text.secondary,
|
||||
padding: isMobile ? '5px' : 'revert',
|
||||
padding: 'revert',
|
||||
}}
|
||||
>
|
||||
<CodeIcon />
|
||||
@ -188,7 +187,7 @@ const MenuBar = ({
|
||||
editor.isActive('code')
|
||||
? theme.palette.text.primary
|
||||
: theme.palette.text.secondary,
|
||||
padding: isMobile ? '5px' : 'revert',
|
||||
padding: 'revert',
|
||||
}}
|
||||
>
|
||||
<FormatClearIcon />
|
||||
@ -199,7 +198,7 @@ const MenuBar = ({
|
||||
color: editor.isActive('bulletList')
|
||||
? theme.palette.text.primary
|
||||
: theme.palette.text.secondary,
|
||||
padding: isMobile ? '5px' : 'revert',
|
||||
padding: 'revert',
|
||||
}}
|
||||
>
|
||||
<FormatListBulletedIcon />
|
||||
@ -210,7 +209,7 @@ const MenuBar = ({
|
||||
color: editor.isActive('orderedList')
|
||||
? theme.palette.text.primary
|
||||
: theme.palette.text.secondary,
|
||||
padding: isMobile ? '5px' : 'revert',
|
||||
padding: 'revert',
|
||||
}}
|
||||
>
|
||||
<FormatListNumberedIcon />
|
||||
@ -221,7 +220,7 @@ const MenuBar = ({
|
||||
color: editor.isActive('codeBlock')
|
||||
? theme.palette.text.primary
|
||||
: theme.palette.text.secondary,
|
||||
padding: isMobile ? '5px' : 'revert',
|
||||
padding: 'revert',
|
||||
}}
|
||||
>
|
||||
<DeveloperModeIcon />
|
||||
@ -232,7 +231,7 @@ const MenuBar = ({
|
||||
color: editor.isActive('blockquote')
|
||||
? theme.palette.text.primary
|
||||
: theme.palette.text.secondary,
|
||||
padding: isMobile ? '5px' : 'revert',
|
||||
padding: 'revert',
|
||||
}}
|
||||
>
|
||||
<FormatQuoteIcon />
|
||||
@ -240,7 +239,7 @@ const MenuBar = ({
|
||||
<IconButton
|
||||
onClick={() => editor.chain().focus().setHorizontalRule().run()}
|
||||
disabled={!editor.can().chain().focus().setHorizontalRule().run()}
|
||||
sx={{ color: 'gray', padding: isMobile ? '5px' : 'revert' }}
|
||||
sx={{ color: 'gray', padding: 'revert' }}
|
||||
>
|
||||
<HorizontalRuleIcon />
|
||||
</IconButton>
|
||||
@ -252,7 +251,7 @@ const MenuBar = ({
|
||||
color: editor.isActive('heading', { level: 1 })
|
||||
? theme.palette.text.primary
|
||||
: theme.palette.text.secondary,
|
||||
padding: isMobile ? '5px' : 'revert',
|
||||
padding: 'revert',
|
||||
}}
|
||||
>
|
||||
<FormatHeadingIcon fontSize="small" />
|
||||
@ -260,7 +259,7 @@ const MenuBar = ({
|
||||
<IconButton
|
||||
onClick={() => editor.chain().focus().undo().run()}
|
||||
disabled={!editor.can().chain().focus().undo().run()}
|
||||
sx={{ color: 'gray', padding: isMobile ? '5px' : 'revert' }}
|
||||
sx={{ color: 'gray', padding: 'revert' }}
|
||||
>
|
||||
<UndoIcon />
|
||||
</IconButton>
|
||||
@ -313,7 +312,7 @@ const MenuBar = ({
|
||||
onClick={triggerImageUpload}
|
||||
sx={{
|
||||
color: theme.palette.text.secondary,
|
||||
padding: isMobile ? '5px' : 'revert',
|
||||
padding: 'revert',
|
||||
}}
|
||||
>
|
||||
<ImageIcon />
|
||||
@ -398,11 +397,6 @@ export default ({
|
||||
usersRef.current = users; // Keep users up-to-date
|
||||
}, [users]);
|
||||
|
||||
const handleFocus = () => {
|
||||
if (!isMobile) return;
|
||||
setIsFocusedParent(true);
|
||||
};
|
||||
|
||||
const handleBlur = () => {
|
||||
const htmlContent = editorRef.current.getHTML();
|
||||
if (!htmlContent?.trim() || htmlContent?.trim() === '<p></p>') {
|
||||
@ -499,7 +493,7 @@ export default ({
|
||||
>
|
||||
<EditorProvider
|
||||
slotBefore={
|
||||
(isFocusedParent || !isMobile || overrideMobile) && (
|
||||
(isFocusedParent || overrideMobile) && (
|
||||
<MenuBar
|
||||
setEditorRef={setEditorRefFunc}
|
||||
isChat={isChat}
|
||||
@ -511,21 +505,15 @@ export default ({
|
||||
extensions={[...extensionsFiltered, ...additionalExtensions]}
|
||||
content={content}
|
||||
onCreate={({ editor }) => {
|
||||
editor.on('focus', handleFocus); // Listen for focus event
|
||||
editor.on('blur', handleBlur); // Listen for blur event
|
||||
}}
|
||||
onUpdate={({ editor }) => {
|
||||
editor.on('focus', handleFocus); // Ensure focus is updated
|
||||
editor.on('blur', handleBlur); // Ensure blur is updated
|
||||
}}
|
||||
editorProps={{
|
||||
attributes: {
|
||||
class: 'tiptap-prosemirror',
|
||||
style: isMobile
|
||||
? `overflow: auto; min-height: ${
|
||||
customEditorHeight ? '200px' : '0px'
|
||||
}; max-height:calc(100svh - ${customEditorHeight || '140px'})`
|
||||
: `overflow: auto; max-height: 250px`,
|
||||
style: `overflow: auto; max-height: 250px`,
|
||||
},
|
||||
handleKeyDown(view, event) {
|
||||
if (
|
||||
|
@ -1,6 +1,5 @@
|
||||
import Box from '@mui/material/Box';
|
||||
import Drawer from '@mui/material/Drawer';
|
||||
import { isMobile } from '../../App';
|
||||
export const DrawerComponent = ({ open, setOpen, children }) => {
|
||||
const toggleDrawer = (newOpen: boolean) => () => {
|
||||
setOpen(newOpen);
|
||||
@ -9,10 +8,7 @@ export const DrawerComponent = ({ open, setOpen, children }) => {
|
||||
return (
|
||||
<div>
|
||||
<Drawer open={open} onClose={toggleDrawer(false)}>
|
||||
<Box
|
||||
sx={{ width: isMobile ? '100vw' : '400px', height: '100%' }}
|
||||
role="presentation"
|
||||
>
|
||||
<Box sx={{ width: '400px', height: '100%' }} role="presentation">
|
||||
{children}
|
||||
</Box>
|
||||
</Drawer>
|
||||
|
@ -1,23 +1,25 @@
|
||||
import React, { useContext, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import { Box, IconButton, Slider } from '@mui/material'
|
||||
import { CircularProgress, Typography } from '@mui/material'
|
||||
import { Key } from 'ts-key-enum'
|
||||
import React, { useContext, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { Box, IconButton, Slider } from '@mui/material';
|
||||
import { CircularProgress, Typography } from '@mui/material';
|
||||
import { Key } from 'ts-key-enum';
|
||||
import {
|
||||
PlayArrow,
|
||||
Pause,
|
||||
VolumeUp,
|
||||
Fullscreen,
|
||||
PictureInPicture, VolumeOff, Calculate
|
||||
} from '@mui/icons-material'
|
||||
import { styled } from '@mui/system'
|
||||
import { Refresh } from '@mui/icons-material'
|
||||
PictureInPicture,
|
||||
VolumeOff,
|
||||
Calculate,
|
||||
} from '@mui/icons-material';
|
||||
import { styled } from '@mui/system';
|
||||
import { Refresh } from '@mui/icons-material';
|
||||
|
||||
import { Menu, MenuItem } from '@mui/material'
|
||||
import { MoreVert as MoreIcon } from '@mui/icons-material'
|
||||
import { GlobalContext, getBaseApiReact } from '../../App'
|
||||
import { resourceKeySelector } from '../../atoms/global'
|
||||
import { useRecoilValue } from 'recoil'
|
||||
import { Menu, MenuItem } from '@mui/material';
|
||||
import { MoreVert as MoreIcon } from '@mui/icons-material';
|
||||
import { GlobalContext, getBaseApiReact } from '../../App';
|
||||
import { resourceKeySelector } from '../../atoms/global';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
const VideoContainer = styled(Box)`
|
||||
position: relative;
|
||||
display: flex;
|
||||
@ -28,14 +30,14 @@ const VideoContainer = styled(Box)`
|
||||
height: 100%;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
`
|
||||
`;
|
||||
|
||||
const VideoElement = styled('video')`
|
||||
width: 100%;
|
||||
height: auto;
|
||||
max-height: calc(100vh - 150px);
|
||||
background: rgb(33, 33, 33);
|
||||
`
|
||||
`;
|
||||
|
||||
const ControlsContainer = styled(Box)`
|
||||
position: absolute;
|
||||
@ -47,18 +49,18 @@ const ControlsContainer = styled(Box)`
|
||||
right: 0;
|
||||
padding: 8px;
|
||||
background-color: rgba(0, 0, 0, 0.6);
|
||||
`
|
||||
`;
|
||||
|
||||
interface VideoPlayerProps {
|
||||
src?: string
|
||||
poster?: string
|
||||
name?: string
|
||||
identifier?: string
|
||||
service?: string
|
||||
autoplay?: boolean
|
||||
from?: string | null
|
||||
customStyle?: any
|
||||
user?: string
|
||||
src?: string;
|
||||
poster?: string;
|
||||
name?: string;
|
||||
identifier?: string;
|
||||
service?: string;
|
||||
autoplay?: boolean;
|
||||
from?: string | null;
|
||||
customStyle?: any;
|
||||
user?: string;
|
||||
}
|
||||
|
||||
export const VideoPlayer: React.FC<VideoPlayerProps> = ({
|
||||
@ -69,33 +71,30 @@ export const VideoPlayer: React.FC<VideoPlayerProps> = ({
|
||||
autoplay = true,
|
||||
from = null,
|
||||
customStyle = {},
|
||||
node
|
||||
node,
|
||||
}) => {
|
||||
|
||||
const keyIdentifier = useMemo(()=> {
|
||||
|
||||
if(name && identifier && service){
|
||||
return `${service}-${name}-${identifier}`
|
||||
const keyIdentifier = useMemo(() => {
|
||||
if (name && identifier && service) {
|
||||
return `${service}-${name}-${identifier}`;
|
||||
} else {
|
||||
return undefined
|
||||
return undefined;
|
||||
}
|
||||
}, [service, name, identifier])
|
||||
}, [service, name, identifier]);
|
||||
const download = useRecoilValue(resourceKeySelector(keyIdentifier));
|
||||
const { downloadResource } = useContext(GlobalContext);
|
||||
|
||||
const videoRef = useRef<HTMLVideoElement | null>(null)
|
||||
const [playing, setPlaying] = useState(false)
|
||||
const [volume, setVolume] = useState(1)
|
||||
const [mutedVolume, setMutedVolume] = useState(1)
|
||||
const [isMuted, setIsMuted] = useState(false)
|
||||
const [progress, setProgress] = useState(0)
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
const [canPlay, setCanPlay] = useState(false)
|
||||
const [startPlay, setStartPlay] = useState(false)
|
||||
const [isMobileView, setIsMobileView] = useState(false)
|
||||
const [playbackRate, setPlaybackRate] = useState(1)
|
||||
const [anchorEl, setAnchorEl] = useState(null)
|
||||
const reDownload = useRef<boolean>(false)
|
||||
const videoRef = useRef<HTMLVideoElement | null>(null);
|
||||
const [playing, setPlaying] = useState(false);
|
||||
const [volume, setVolume] = useState(1);
|
||||
const [mutedVolume, setMutedVolume] = useState(1);
|
||||
const [isMuted, setIsMuted] = useState(false);
|
||||
const [progress, setProgress] = useState(0);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [canPlay, setCanPlay] = useState(false);
|
||||
const [startPlay, setStartPlay] = useState(false);
|
||||
const [playbackRate, setPlaybackRate] = useState(1);
|
||||
const [anchorEl, setAnchorEl] = useState(null);
|
||||
const reDownload = useRef<boolean>(false);
|
||||
|
||||
const resetVideoState = () => {
|
||||
// Reset all states to their initial values
|
||||
@ -107,10 +106,9 @@ export const VideoPlayer: React.FC<VideoPlayerProps> = ({
|
||||
setIsLoading(false);
|
||||
setCanPlay(false);
|
||||
setStartPlay(false);
|
||||
setIsMobileView(false);
|
||||
setPlaybackRate(1);
|
||||
setAnchorEl(null);
|
||||
|
||||
|
||||
// Reset refs to their initial values
|
||||
if (videoRef.current) {
|
||||
videoRef.current.pause(); // Ensure the video is paused
|
||||
@ -120,18 +118,19 @@ export const VideoPlayer: React.FC<VideoPlayerProps> = ({
|
||||
};
|
||||
|
||||
const src = useMemo(() => {
|
||||
if(name && identifier && service){
|
||||
return `${node || getBaseApiReact()}/arbitrary/${service}/${name}/${identifier}`
|
||||
}
|
||||
return ''
|
||||
}, [service, name, identifier])
|
||||
if (name && identifier && service) {
|
||||
return `${node || getBaseApiReact()}/arbitrary/${service}/${name}/${identifier}`;
|
||||
}
|
||||
return '';
|
||||
}, [service, name, identifier]);
|
||||
|
||||
useEffect(() => {
|
||||
resetVideoState();
|
||||
}, [keyIdentifier]);
|
||||
|
||||
useEffect(()=> {
|
||||
resetVideoState()
|
||||
}, [keyIdentifier])
|
||||
const resourceStatus = useMemo(() => {
|
||||
return download?.status || {}
|
||||
}, [download])
|
||||
return download?.status || {};
|
||||
}, [download]);
|
||||
|
||||
const minSpeed = 0.25;
|
||||
const maxSpeed = 4.0;
|
||||
@ -139,306 +138,339 @@ export const VideoPlayer: React.FC<VideoPlayerProps> = ({
|
||||
|
||||
const updatePlaybackRate = (newSpeed: number) => {
|
||||
if (videoRef.current) {
|
||||
if (newSpeed > maxSpeed || newSpeed < minSpeed)
|
||||
newSpeed = minSpeed
|
||||
videoRef.current.playbackRate = newSpeed
|
||||
setPlaybackRate(newSpeed)
|
||||
if (newSpeed > maxSpeed || newSpeed < minSpeed) newSpeed = minSpeed;
|
||||
videoRef.current.playbackRate = newSpeed;
|
||||
setPlaybackRate(newSpeed);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const increaseSpeed = (wrapOverflow = true) => {
|
||||
const changedSpeed = playbackRate + speedChange
|
||||
let newSpeed = wrapOverflow ? changedSpeed : Math.min(changedSpeed, maxSpeed)
|
||||
|
||||
const changedSpeed = playbackRate + speedChange;
|
||||
let newSpeed = wrapOverflow
|
||||
? changedSpeed
|
||||
: Math.min(changedSpeed, maxSpeed);
|
||||
|
||||
if (videoRef.current) {
|
||||
updatePlaybackRate(newSpeed);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const decreaseSpeed = () => {
|
||||
if (videoRef.current) {
|
||||
updatePlaybackRate(playbackRate - speedChange);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
const togglePlay = async () => {
|
||||
if (!videoRef.current) return
|
||||
setStartPlay(true)
|
||||
if (!videoRef.current) return;
|
||||
setStartPlay(true);
|
||||
if (!src || resourceStatus?.status !== 'READY') {
|
||||
ReactDOM.flushSync(() => {
|
||||
setIsLoading(true)
|
||||
})
|
||||
getSrc()
|
||||
setIsLoading(true);
|
||||
});
|
||||
getSrc();
|
||||
}
|
||||
if (playing) {
|
||||
videoRef.current.pause()
|
||||
videoRef.current.pause();
|
||||
} else {
|
||||
videoRef.current.play()
|
||||
videoRef.current.play();
|
||||
}
|
||||
setPlaying(!playing)
|
||||
}
|
||||
|
||||
setPlaying(!playing);
|
||||
};
|
||||
|
||||
const onVolumeChange = (_: any, value: number | number[]) => {
|
||||
if (!videoRef.current) return
|
||||
videoRef.current.volume = value as number
|
||||
setVolume(value as number)
|
||||
setIsMuted(false)
|
||||
}
|
||||
if (!videoRef.current) return;
|
||||
videoRef.current.volume = value as number;
|
||||
setVolume(value as number);
|
||||
setIsMuted(false);
|
||||
};
|
||||
|
||||
const onProgressChange = (_: any, value: number | number[]) => {
|
||||
if (!videoRef.current) return
|
||||
videoRef.current.currentTime = value as number
|
||||
setProgress(value as number)
|
||||
if (!videoRef.current) return;
|
||||
videoRef.current.currentTime = value as number;
|
||||
setProgress(value as number);
|
||||
if (!playing) {
|
||||
videoRef.current.play()
|
||||
setPlaying(true)
|
||||
videoRef.current.play();
|
||||
setPlaying(true);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleEnded = () => {
|
||||
setPlaying(false)
|
||||
}
|
||||
setPlaying(false);
|
||||
};
|
||||
|
||||
const updateProgress = () => {
|
||||
if (!videoRef.current) return
|
||||
setProgress(videoRef.current.currentTime)
|
||||
}
|
||||
if (!videoRef.current) return;
|
||||
setProgress(videoRef.current.currentTime);
|
||||
};
|
||||
|
||||
const [isFullscreen, setIsFullscreen] = useState(false)
|
||||
const [isFullscreen, setIsFullscreen] = useState(false);
|
||||
|
||||
const enterFullscreen = () => {
|
||||
if (!videoRef.current) return
|
||||
if (!videoRef.current) return;
|
||||
if (videoRef.current.requestFullscreen) {
|
||||
videoRef.current.requestFullscreen()
|
||||
videoRef.current.requestFullscreen();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const exitFullscreen = () => {
|
||||
if (document.exitFullscreen) {
|
||||
document.exitFullscreen()
|
||||
document.exitFullscreen();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const toggleFullscreen = () => {
|
||||
isFullscreen ? exitFullscreen() : enterFullscreen()
|
||||
}
|
||||
|
||||
isFullscreen ? exitFullscreen() : enterFullscreen();
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const handleFullscreenChange = () => {
|
||||
setIsFullscreen(!!document.fullscreenElement)
|
||||
}
|
||||
setIsFullscreen(!!document.fullscreenElement);
|
||||
};
|
||||
|
||||
document.addEventListener('fullscreenchange', handleFullscreenChange)
|
||||
document.addEventListener('fullscreenchange', handleFullscreenChange);
|
||||
return () => {
|
||||
document.removeEventListener('fullscreenchange', handleFullscreenChange)
|
||||
}
|
||||
}, [])
|
||||
|
||||
|
||||
document.removeEventListener('fullscreenchange', handleFullscreenChange);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const handleCanPlay = () => {
|
||||
setIsLoading(false)
|
||||
setCanPlay(true)
|
||||
}
|
||||
setIsLoading(false);
|
||||
setCanPlay(true);
|
||||
};
|
||||
|
||||
const getSrc = React.useCallback(async () => {
|
||||
if (!name || !identifier || !service) return
|
||||
if (!name || !identifier || !service) return;
|
||||
try {
|
||||
downloadResource({
|
||||
name,
|
||||
service,
|
||||
identifier
|
||||
})
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
identifier,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}, [identifier, name, service])
|
||||
|
||||
|
||||
|
||||
}, [identifier, name, service]);
|
||||
|
||||
function formatTime(seconds: number): string {
|
||||
seconds = Math.floor(seconds)
|
||||
let minutes: number | string = Math.floor(seconds / 60)
|
||||
let hours: number | string = Math.floor(minutes / 60)
|
||||
seconds = Math.floor(seconds);
|
||||
let minutes: number | string = Math.floor(seconds / 60);
|
||||
let hours: number | string = Math.floor(minutes / 60);
|
||||
|
||||
let remainingSeconds: number | string = seconds % 60
|
||||
let remainingMinutes: number | string = minutes % 60
|
||||
let remainingSeconds: number | string = seconds % 60;
|
||||
let remainingMinutes: number | string = minutes % 60;
|
||||
|
||||
if (remainingSeconds < 10) {
|
||||
remainingSeconds = '0' + remainingSeconds
|
||||
remainingSeconds = '0' + remainingSeconds;
|
||||
}
|
||||
|
||||
if (remainingMinutes < 10) {
|
||||
remainingMinutes = '0' + remainingMinutes
|
||||
remainingMinutes = '0' + remainingMinutes;
|
||||
}
|
||||
|
||||
if (hours === 0) {
|
||||
hours = ''
|
||||
}
|
||||
else {
|
||||
hours = hours + ':'
|
||||
hours = '';
|
||||
} else {
|
||||
hours = hours + ':';
|
||||
}
|
||||
|
||||
return hours + remainingMinutes + ':' + remainingSeconds
|
||||
return hours + remainingMinutes + ':' + remainingSeconds;
|
||||
}
|
||||
|
||||
const reloadVideo = () => {
|
||||
if (!videoRef.current) return
|
||||
const currentTime = videoRef.current.currentTime
|
||||
videoRef.current.src = src
|
||||
videoRef.current.load()
|
||||
videoRef.current.currentTime = currentTime
|
||||
if (!videoRef.current) return;
|
||||
const currentTime = videoRef.current.currentTime;
|
||||
videoRef.current.src = src;
|
||||
videoRef.current.load();
|
||||
videoRef.current.currentTime = currentTime;
|
||||
if (playing) {
|
||||
videoRef.current.play()
|
||||
videoRef.current.play();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
resourceStatus?.status === 'DOWNLOADED' &&
|
||||
reDownload?.current === false
|
||||
) {
|
||||
getSrc()
|
||||
reDownload.current = true
|
||||
getSrc();
|
||||
reDownload.current = true;
|
||||
}
|
||||
}, [getSrc, resourceStatus])
|
||||
}, [getSrc, resourceStatus]);
|
||||
|
||||
const handleMenuOpen = (event: any) => {
|
||||
setAnchorEl(event.currentTarget)
|
||||
}
|
||||
setAnchorEl(event.currentTarget);
|
||||
};
|
||||
|
||||
const handleMenuClose = () => {
|
||||
setAnchorEl(null)
|
||||
}
|
||||
setAnchorEl(null);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const videoWidth = videoRef?.current?.offsetWidth
|
||||
if (videoWidth && videoWidth <= 600) {
|
||||
setIsMobileView(true)
|
||||
}
|
||||
}, [canPlay])
|
||||
const videoWidth = videoRef?.current?.offsetWidth;
|
||||
}, [canPlay]);
|
||||
|
||||
const getDownloadProgress = (current: number, total: number) => {
|
||||
const progress = current / total * 100;
|
||||
return Number.isNaN(progress) ? '' : progress.toFixed(0) + '%'
|
||||
}
|
||||
const progress = (current / total) * 100;
|
||||
return Number.isNaN(progress) ? '' : progress.toFixed(0) + '%';
|
||||
};
|
||||
const mute = () => {
|
||||
setIsMuted(true)
|
||||
setMutedVolume(volume)
|
||||
setVolume(0)
|
||||
if (videoRef.current) videoRef.current.volume = 0
|
||||
}
|
||||
setIsMuted(true);
|
||||
setMutedVolume(volume);
|
||||
setVolume(0);
|
||||
if (videoRef.current) videoRef.current.volume = 0;
|
||||
};
|
||||
const unMute = () => {
|
||||
setIsMuted(false)
|
||||
setVolume(mutedVolume)
|
||||
if (videoRef.current) videoRef.current.volume = mutedVolume
|
||||
}
|
||||
setIsMuted(false);
|
||||
setVolume(mutedVolume);
|
||||
if (videoRef.current) videoRef.current.volume = mutedVolume;
|
||||
};
|
||||
|
||||
const toggleMute = () => {
|
||||
isMuted ? unMute() : mute();
|
||||
}
|
||||
};
|
||||
|
||||
const changeVolume = (volumeChange: number) => {
|
||||
if (videoRef.current) {
|
||||
const minVolume = 0;
|
||||
const maxVolume = 1;
|
||||
|
||||
let newVolume = volumeChange + volume;
|
||||
|
||||
let newVolume = volumeChange + volume
|
||||
newVolume = Math.max(newVolume, minVolume);
|
||||
newVolume = Math.min(newVolume, maxVolume);
|
||||
|
||||
newVolume = Math.max(newVolume, minVolume)
|
||||
newVolume = Math.min(newVolume, maxVolume)
|
||||
|
||||
setIsMuted(false)
|
||||
setMutedVolume(newVolume)
|
||||
videoRef.current.volume = newVolume
|
||||
setIsMuted(false);
|
||||
setMutedVolume(newVolume);
|
||||
videoRef.current.volume = newVolume;
|
||||
setVolume(newVolume);
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
const setProgressRelative = (secondsChange: number) => {
|
||||
if (videoRef.current) {
|
||||
const currentTime = videoRef.current?.currentTime
|
||||
const minTime = 0
|
||||
const maxTime = videoRef.current?.duration || 100
|
||||
const currentTime = videoRef.current?.currentTime;
|
||||
const minTime = 0;
|
||||
const maxTime = videoRef.current?.duration || 100;
|
||||
|
||||
let newTime = currentTime + secondsChange;
|
||||
newTime = Math.max(newTime, minTime)
|
||||
newTime = Math.min(newTime, maxTime)
|
||||
newTime = Math.max(newTime, minTime);
|
||||
newTime = Math.min(newTime, maxTime);
|
||||
videoRef.current.currentTime = newTime;
|
||||
setProgress(newTime);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const setProgressAbsolute = (videoPercent: number) => {
|
||||
if (videoRef.current) {
|
||||
videoPercent = Math.min(videoPercent, 100)
|
||||
videoPercent = Math.max(videoPercent, 0)
|
||||
const finalTime = videoRef.current?.duration * videoPercent / 100
|
||||
videoRef.current.currentTime = finalTime
|
||||
videoPercent = Math.min(videoPercent, 100);
|
||||
videoPercent = Math.max(videoPercent, 0);
|
||||
const finalTime = (videoRef.current?.duration * videoPercent) / 100;
|
||||
videoRef.current.currentTime = finalTime;
|
||||
setProgress(finalTime);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
const keyboardShortcutsDown = (e: React.KeyboardEvent<HTMLDivElement>) => {
|
||||
e.preventDefault()
|
||||
e.preventDefault();
|
||||
|
||||
switch (e.key) {
|
||||
case Key.Add: increaseSpeed(false); break;
|
||||
case '+': increaseSpeed(false); break;
|
||||
case '>': increaseSpeed(false); break;
|
||||
case Key.Add:
|
||||
increaseSpeed(false);
|
||||
break;
|
||||
case '+':
|
||||
increaseSpeed(false);
|
||||
break;
|
||||
case '>':
|
||||
increaseSpeed(false);
|
||||
break;
|
||||
|
||||
case Key.Subtract: decreaseSpeed(); break;
|
||||
case '-': decreaseSpeed(); break;
|
||||
case '<': decreaseSpeed(); break;
|
||||
case Key.Subtract:
|
||||
decreaseSpeed();
|
||||
break;
|
||||
case '-':
|
||||
decreaseSpeed();
|
||||
break;
|
||||
case '<':
|
||||
decreaseSpeed();
|
||||
break;
|
||||
|
||||
case Key.ArrowLeft: {
|
||||
if (e.shiftKey) setProgressRelative(-300);
|
||||
else if (e.ctrlKey) setProgressRelative(-60);
|
||||
else if (e.altKey) setProgressRelative(-10);
|
||||
else setProgressRelative(-5);
|
||||
} break;
|
||||
case Key.ArrowLeft:
|
||||
{
|
||||
if (e.shiftKey) setProgressRelative(-300);
|
||||
else if (e.ctrlKey) setProgressRelative(-60);
|
||||
else if (e.altKey) setProgressRelative(-10);
|
||||
else setProgressRelative(-5);
|
||||
}
|
||||
break;
|
||||
|
||||
case Key.ArrowRight: {
|
||||
if (e.shiftKey) setProgressRelative(300);
|
||||
else if (e.ctrlKey) setProgressRelative(60);
|
||||
else if (e.altKey) setProgressRelative(10);
|
||||
else setProgressRelative(5);
|
||||
} break;
|
||||
case Key.ArrowRight:
|
||||
{
|
||||
if (e.shiftKey) setProgressRelative(300);
|
||||
else if (e.ctrlKey) setProgressRelative(60);
|
||||
else if (e.altKey) setProgressRelative(10);
|
||||
else setProgressRelative(5);
|
||||
}
|
||||
break;
|
||||
|
||||
case Key.ArrowDown: changeVolume(-0.05); break;
|
||||
case Key.ArrowUp: changeVolume(0.05); break;
|
||||
case Key.ArrowDown:
|
||||
changeVolume(-0.05);
|
||||
break;
|
||||
case Key.ArrowUp:
|
||||
changeVolume(0.05);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const keyboardShortcutsUp = (e: React.KeyboardEvent<HTMLDivElement>) => {
|
||||
e.preventDefault()
|
||||
e.preventDefault();
|
||||
|
||||
switch (e.key) {
|
||||
case ' ': togglePlay(); break;
|
||||
case 'm': toggleMute(); break;
|
||||
case ' ':
|
||||
togglePlay();
|
||||
break;
|
||||
case 'm':
|
||||
toggleMute();
|
||||
break;
|
||||
|
||||
case 'f': enterFullscreen(); break;
|
||||
case Key.Escape: exitFullscreen(); break;
|
||||
case 'f':
|
||||
enterFullscreen();
|
||||
break;
|
||||
case Key.Escape:
|
||||
exitFullscreen();
|
||||
break;
|
||||
|
||||
case '0': setProgressAbsolute(0); break;
|
||||
case '1': setProgressAbsolute(10); break;
|
||||
case '2': setProgressAbsolute(20); break;
|
||||
case '3': setProgressAbsolute(30); break;
|
||||
case '4': setProgressAbsolute(40); break;
|
||||
case '5': setProgressAbsolute(50); break;
|
||||
case '6': setProgressAbsolute(60); break;
|
||||
case '7': setProgressAbsolute(70); break;
|
||||
case '8': setProgressAbsolute(80); break;
|
||||
case '9': setProgressAbsolute(90); break;
|
||||
case '0':
|
||||
setProgressAbsolute(0);
|
||||
break;
|
||||
case '1':
|
||||
setProgressAbsolute(10);
|
||||
break;
|
||||
case '2':
|
||||
setProgressAbsolute(20);
|
||||
break;
|
||||
case '3':
|
||||
setProgressAbsolute(30);
|
||||
break;
|
||||
case '4':
|
||||
setProgressAbsolute(40);
|
||||
break;
|
||||
case '5':
|
||||
setProgressAbsolute(50);
|
||||
break;
|
||||
case '6':
|
||||
setProgressAbsolute(60);
|
||||
break;
|
||||
case '7':
|
||||
setProgressAbsolute(70);
|
||||
break;
|
||||
case '8':
|
||||
setProgressAbsolute(80);
|
||||
break;
|
||||
case '9':
|
||||
setProgressAbsolute(90);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<VideoContainer
|
||||
@ -451,7 +483,6 @@ export const VideoPlayer: React.FC<VideoPlayerProps> = ({
|
||||
height: '100%',
|
||||
}}
|
||||
>
|
||||
|
||||
{isLoading && (
|
||||
<Box
|
||||
position="absolute"
|
||||
@ -467,40 +498,44 @@ export const VideoPlayer: React.FC<VideoPlayerProps> = ({
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: '10px'
|
||||
gap: '10px',
|
||||
}}
|
||||
>
|
||||
<CircularProgress color="secondary" />
|
||||
|
||||
<Typography
|
||||
variant="subtitle2"
|
||||
component="div"
|
||||
sx={{
|
||||
color: 'white',
|
||||
fontSize: '15px',
|
||||
textAlign: 'center'
|
||||
}}
|
||||
>
|
||||
{resourceStatus?.status === 'REFETCHING' ? (
|
||||
<>
|
||||
<>
|
||||
{getDownloadProgress(resourceStatus?.localChunkCount, resourceStatus?.totalChunkCount)}
|
||||
</>
|
||||
|
||||
<> Refetching data in 25 seconds</>
|
||||
</>
|
||||
) : resourceStatus?.status === 'DOWNLOADED' ? (
|
||||
<>Download Completed: building tutorial video...</>
|
||||
) : resourceStatus?.status !== 'READY' ? (
|
||||
<Typography
|
||||
variant="subtitle2"
|
||||
component="div"
|
||||
sx={{
|
||||
color: 'white',
|
||||
fontSize: '15px',
|
||||
textAlign: 'center',
|
||||
}}
|
||||
>
|
||||
{resourceStatus?.status === 'REFETCHING' ? (
|
||||
<>
|
||||
<>
|
||||
{getDownloadProgress(resourceStatus?.localChunkCount || 0, resourceStatus?.totalChunkCount || 100)}
|
||||
|
||||
{getDownloadProgress(
|
||||
resourceStatus?.localChunkCount,
|
||||
resourceStatus?.totalChunkCount
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<>Fetching tutorial from the Qortal Network...</>
|
||||
)}
|
||||
</Typography>
|
||||
|
||||
|
||||
<> Refetching data in 25 seconds</>
|
||||
</>
|
||||
) : resourceStatus?.status === 'DOWNLOADED' ? (
|
||||
<>Download Completed: building tutorial video...</>
|
||||
) : resourceStatus?.status !== 'READY' ? (
|
||||
<>
|
||||
{getDownloadProgress(
|
||||
resourceStatus?.localChunkCount || 0,
|
||||
resourceStatus?.totalChunkCount || 100
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<>Fetching tutorial from the Qortal Network...</>
|
||||
)}
|
||||
</Typography>
|
||||
</Box>
|
||||
)}
|
||||
{((!src && !isLoading) || !startPlay) && (
|
||||
@ -516,59 +551,61 @@ export const VideoPlayer: React.FC<VideoPlayerProps> = ({
|
||||
zIndex={500}
|
||||
bgcolor="rgba(0, 0, 0, 0.6)"
|
||||
onClick={() => {
|
||||
togglePlay()
|
||||
togglePlay();
|
||||
}}
|
||||
sx={{
|
||||
cursor: 'pointer'
|
||||
cursor: 'pointer',
|
||||
}}
|
||||
>
|
||||
<PlayArrow
|
||||
sx={{
|
||||
width: '50px',
|
||||
height: '50px',
|
||||
color: 'white'
|
||||
color: 'white',
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
flexGrow: 1,
|
||||
width: '100%',
|
||||
height: 'calc(100% - 60px)',
|
||||
}}>
|
||||
<VideoElement
|
||||
id={identifier}
|
||||
ref={videoRef}
|
||||
src={!startPlay ? '' : resourceStatus?.status === 'READY' ? src : ''}
|
||||
poster={!startPlay ? poster : ""}
|
||||
onTimeUpdate={updateProgress}
|
||||
autoPlay={autoplay}
|
||||
onClick={togglePlay}
|
||||
onEnded={handleEnded}
|
||||
// onLoadedMetadata={handleLoadedMetadata}
|
||||
onCanPlay={handleCanPlay}
|
||||
preload="metadata"
|
||||
style={{
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexGrow: 1,
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
...customStyle
|
||||
height: 'calc(100% - 60px)',
|
||||
}}
|
||||
/>
|
||||
>
|
||||
<VideoElement
|
||||
id={identifier}
|
||||
ref={videoRef}
|
||||
src={!startPlay ? '' : resourceStatus?.status === 'READY' ? src : ''}
|
||||
poster={!startPlay ? poster : ''}
|
||||
onTimeUpdate={updateProgress}
|
||||
autoPlay={autoplay}
|
||||
onClick={togglePlay}
|
||||
onEnded={handleEnded}
|
||||
// onLoadedMetadata={handleLoadedMetadata}
|
||||
onCanPlay={handleCanPlay}
|
||||
preload="metadata"
|
||||
style={{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
...customStyle,
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
<ControlsContainer
|
||||
sx={{
|
||||
position: 'relative',
|
||||
background: 'var(--bg-primary)',
|
||||
width: '100%',
|
||||
flexShrink: 0
|
||||
flexShrink: 0,
|
||||
}}
|
||||
>
|
||||
{isMobileView && canPlay ? (
|
||||
{canPlay ? (
|
||||
<>
|
||||
<IconButton
|
||||
sx={{
|
||||
color: 'rgba(255, 255, 255, 0.7)'
|
||||
color: 'rgba(255, 255, 255, 0.7)',
|
||||
}}
|
||||
onClick={togglePlay}
|
||||
>
|
||||
@ -577,77 +614,7 @@ export const VideoPlayer: React.FC<VideoPlayerProps> = ({
|
||||
<IconButton
|
||||
sx={{
|
||||
color: 'rgba(255, 255, 255, 0.7)',
|
||||
marginLeft: '15px'
|
||||
}}
|
||||
onClick={reloadVideo}
|
||||
>
|
||||
<Refresh />
|
||||
</IconButton>
|
||||
<Slider
|
||||
value={progress}
|
||||
onChange={onProgressChange}
|
||||
min={0}
|
||||
max={videoRef.current?.duration || 100}
|
||||
sx={{ flexGrow: 1, mx: 2 }}
|
||||
/>
|
||||
<IconButton
|
||||
edge="end"
|
||||
color="inherit"
|
||||
aria-label="menu"
|
||||
onClick={handleMenuOpen}
|
||||
>
|
||||
<MoreIcon />
|
||||
</IconButton>
|
||||
<Menu
|
||||
id="simple-menu"
|
||||
anchorEl={anchorEl}
|
||||
keepMounted
|
||||
open={Boolean(anchorEl)}
|
||||
onClose={handleMenuClose}
|
||||
PaperProps={{
|
||||
style: {
|
||||
width: '250px'
|
||||
}
|
||||
}}
|
||||
>
|
||||
<MenuItem>
|
||||
<VolumeUp />
|
||||
<Slider
|
||||
value={volume}
|
||||
onChange={onVolumeChange}
|
||||
min={0}
|
||||
max={1}
|
||||
step={0.01} />
|
||||
</MenuItem>
|
||||
<MenuItem onClick={() => increaseSpeed()}>
|
||||
<Typography
|
||||
sx={{
|
||||
color: 'rgba(255, 255, 255, 0.7)',
|
||||
fontSize: '14px'
|
||||
}}
|
||||
>
|
||||
Speed: {playbackRate}x
|
||||
</Typography>
|
||||
</MenuItem>
|
||||
<MenuItem onClick={toggleFullscreen}>
|
||||
<Fullscreen />
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
</>
|
||||
) : canPlay ? (
|
||||
<>
|
||||
<IconButton
|
||||
sx={{
|
||||
color: 'rgba(255, 255, 255, 0.7)'
|
||||
}}
|
||||
onClick={togglePlay}
|
||||
>
|
||||
{playing ? <Pause /> : <PlayArrow />}
|
||||
</IconButton>
|
||||
<IconButton
|
||||
sx={{
|
||||
color: 'rgba(255, 255, 255, 0.7)',
|
||||
marginLeft: '15px'
|
||||
marginLeft: '15px',
|
||||
}}
|
||||
onClick={reloadVideo}
|
||||
>
|
||||
@ -669,7 +636,7 @@ export const VideoPlayer: React.FC<VideoPlayerProps> = ({
|
||||
!videoRef.current?.duration || !progress
|
||||
? 'hidden'
|
||||
: 'visible',
|
||||
flexShrink: 0
|
||||
flexShrink: 0,
|
||||
}}
|
||||
>
|
||||
{progress && videoRef.current?.duration && formatTime(progress)}/
|
||||
@ -680,7 +647,7 @@ export const VideoPlayer: React.FC<VideoPlayerProps> = ({
|
||||
<IconButton
|
||||
sx={{
|
||||
color: 'rgba(255, 255, 255, 0.7)',
|
||||
marginRight: '10px'
|
||||
marginRight: '10px',
|
||||
}}
|
||||
onClick={toggleMute}
|
||||
>
|
||||
@ -694,14 +661,14 @@ export const VideoPlayer: React.FC<VideoPlayerProps> = ({
|
||||
step={0.01}
|
||||
sx={{
|
||||
maxWidth: '100px',
|
||||
color: 'var(--Mail-Background)'
|
||||
color: 'var(--Mail-Background)',
|
||||
}}
|
||||
/>
|
||||
<IconButton
|
||||
sx={{
|
||||
color: 'rgba(255, 255, 255, 0.7)',
|
||||
fontSize: '14px',
|
||||
marginLeft: '5px'
|
||||
marginLeft: '5px',
|
||||
}}
|
||||
onClick={(e) => increaseSpeed()}
|
||||
>
|
||||
@ -709,7 +676,7 @@ export const VideoPlayer: React.FC<VideoPlayerProps> = ({
|
||||
</IconButton>
|
||||
<IconButton
|
||||
sx={{
|
||||
color: 'rgba(255, 255, 255, 0.7)'
|
||||
color: 'rgba(255, 255, 255, 0.7)',
|
||||
}}
|
||||
onClick={toggleFullscreen}
|
||||
>
|
||||
@ -719,5 +686,5 @@ export const VideoPlayer: React.FC<VideoPlayerProps> = ({
|
||||
) : null}
|
||||
</ControlsContainer>
|
||||
</VideoContainer>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
@ -26,7 +26,7 @@ import { AddGroupList } from './AddGroupList';
|
||||
import { UserListOfInvites } from './UserListOfInvites';
|
||||
import { CustomizedSnackbars } from '../Snackbar/Snackbar';
|
||||
import { getFee } from '../../background';
|
||||
import { MyContext, isMobile } from '../../App';
|
||||
import { MyContext } from '../../App';
|
||||
import { subscribeToEvent, unsubscribeFromEvent } from '../../utils/events';
|
||||
|
||||
export const Label = styled('label')`
|
||||
@ -231,7 +231,7 @@ export const AddGroup = ({ address, open, setOpen }) => {
|
||||
value={value}
|
||||
onChange={handleChange}
|
||||
aria-label="basic tabs example"
|
||||
variant={isMobile ? 'scrollable' : 'fullWidth'} // Scrollable on mobile, full width on desktop
|
||||
variant={'fullWidth'}
|
||||
scrollButtons="auto"
|
||||
allowScrollButtonsMobile
|
||||
sx={{
|
||||
@ -247,7 +247,7 @@ export const AddGroup = ({ address, open, setOpen }) => {
|
||||
'&.Mui-selected': {
|
||||
color: theme.palette.text.primary,
|
||||
},
|
||||
fontSize: isMobile ? '0.75rem' : '1rem', // Adjust font size for mobile
|
||||
fontSize: '1rem',
|
||||
}}
|
||||
/>
|
||||
<Tab
|
||||
@ -257,7 +257,7 @@ export const AddGroup = ({ address, open, setOpen }) => {
|
||||
'&.Mui-selected': {
|
||||
color: theme.palette.text.primary,
|
||||
},
|
||||
fontSize: isMobile ? '0.75rem' : '1rem', // Adjust font size for mobile
|
||||
fontSize: '1rem',
|
||||
}}
|
||||
/>
|
||||
<Tab
|
||||
@ -267,7 +267,7 @@ export const AddGroup = ({ address, open, setOpen }) => {
|
||||
'&.Mui-selected': {
|
||||
color: theme.palette.text.primary,
|
||||
},
|
||||
fontSize: isMobile ? '0.75rem' : '1rem', // Adjust font size for mobile
|
||||
fontSize: '1rem',
|
||||
}}
|
||||
/>
|
||||
</Tabs>
|
||||
|
@ -61,11 +61,7 @@ import {
|
||||
unsubscribeFromEvent,
|
||||
} from '../../../utils/events';
|
||||
import RefreshIcon from '@mui/icons-material/Refresh';
|
||||
import {
|
||||
getArbitraryEndpointReact,
|
||||
getBaseApiReact,
|
||||
isMobile,
|
||||
} from '../../../App';
|
||||
import { getArbitraryEndpointReact, getBaseApiReact } from '../../../App';
|
||||
import { WrapperUserAction } from '../../WrapperUserAction';
|
||||
import { addDataPublishesFunc, getDataPublishesFunc } from '../Group';
|
||||
const filterOptions = ['Recently active', 'Newest', 'Oldest'];
|
||||
@ -754,7 +750,6 @@ export const GroupMail = ({
|
||||
<ThreadSingleTitle
|
||||
sx={{
|
||||
fontWeight: shouldAppearLighter && 300,
|
||||
fontSize: isMobile && '18px',
|
||||
}}
|
||||
>
|
||||
{thread?.threadData?.title}
|
||||
|
@ -1,18 +1,9 @@
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
CircularProgress,
|
||||
Input,
|
||||
Typography,
|
||||
} from '@mui/material';
|
||||
import { Box, CircularProgress, Input } from '@mui/material';
|
||||
import ShortUniqueId from 'short-unique-id';
|
||||
import CloseIcon from '@mui/icons-material/Close';
|
||||
|
||||
import ModalCloseSVG from '../../../assets/svgs/ModalClose.svg';
|
||||
|
||||
import ComposeIconSVG from '../../../assets/svgs/ComposeIcon.svg';
|
||||
|
||||
import {
|
||||
AttachmentContainer,
|
||||
CloseContainer,
|
||||
@ -22,7 +13,6 @@ import {
|
||||
InstanceFooter,
|
||||
InstanceListContainer,
|
||||
InstanceListHeader,
|
||||
NewMessageAttachmentImg,
|
||||
NewMessageCloseImg,
|
||||
NewMessageHeaderP,
|
||||
NewMessageInputRow,
|
||||
@ -32,16 +22,9 @@ import {
|
||||
|
||||
import { ReusableModal } from './ReusableModal';
|
||||
import { Spacer } from '../../../common/Spacer';
|
||||
import { formatBytes } from '../../../utils/Size';
|
||||
import { CreateThreadIcon } from '../../../assets/Icons/CreateThreadIcon';
|
||||
import { SendNewMessage } from '../../../assets/Icons/SendNewMessage';
|
||||
import { TextEditor } from './TextEditor';
|
||||
import {
|
||||
MyContext,
|
||||
isMobile,
|
||||
pauseAllQueues,
|
||||
resumeAllQueues,
|
||||
} from '../../../App';
|
||||
import { MyContext, pauseAllQueues, resumeAllQueues } from '../../../App';
|
||||
import { getFee } from '../../../background';
|
||||
import TipTap from '../../Chat/TipTap';
|
||||
import { MessageDisplay } from '../../Chat/MessageDisplay';
|
||||
@ -411,8 +394,8 @@ export const NewThread = ({
|
||||
>
|
||||
<ComposeContainer
|
||||
sx={{
|
||||
padding: isMobile ? '5px' : '15px',
|
||||
justifyContent: isMobile ? 'flex-start' : 'revert',
|
||||
padding: '15px',
|
||||
justifyContent: 'revert',
|
||||
}}
|
||||
onClick={() => setIsOpen(true)}
|
||||
>
|
||||
@ -423,7 +406,7 @@ export const NewThread = ({
|
||||
<ReusableModal
|
||||
open={isOpen}
|
||||
customStyles={{
|
||||
maxHeight: isMobile ? '95svh' : '95vh',
|
||||
maxHeight: '95vh',
|
||||
maxWidth: '950px',
|
||||
height: '700px',
|
||||
borderRadius: '12px 12px 0px 0px',
|
||||
@ -434,8 +417,8 @@ export const NewThread = ({
|
||||
>
|
||||
<InstanceListHeader
|
||||
sx={{
|
||||
height: isMobile ? 'auto' : '50px',
|
||||
padding: isMobile ? '5px' : '20px 42px',
|
||||
height: '50px',
|
||||
padding: '20px 42px',
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
@ -457,7 +440,7 @@ export const NewThread = ({
|
||||
<InstanceListContainer
|
||||
sx={{
|
||||
backgroundColor: '#434448',
|
||||
padding: isMobile ? '5px' : '20px 42px',
|
||||
padding: '20px 42px',
|
||||
height: 'calc(100% - 165px)',
|
||||
flexShrink: 0,
|
||||
}}
|
||||
@ -481,7 +464,7 @@ export const NewThread = ({
|
||||
color: 'white',
|
||||
'& .MuiInput-input::placeholder': {
|
||||
color: 'rgba(255,255,255, 0.70) !important',
|
||||
fontSize: isMobile ? '14px' : '20px',
|
||||
fontSize: '20px',
|
||||
fontStyle: 'normal',
|
||||
fontWeight: 400,
|
||||
lineHeight: '120%', // 24px
|
||||
@ -509,7 +492,9 @@ export const NewThread = ({
|
||||
<MessageDisplay htmlContent={postReply?.textContentV2} />
|
||||
</Box>
|
||||
)}
|
||||
{!isMobile && <Spacer height="30px" />}
|
||||
|
||||
<Spacer height="30px" />
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
maxHeight: '40vh',
|
||||
@ -530,12 +515,13 @@ export const NewThread = ({
|
||||
/> */}
|
||||
</Box>
|
||||
</InstanceListContainer>
|
||||
|
||||
<InstanceFooter
|
||||
sx={{
|
||||
backgroundColor: '#434448',
|
||||
padding: isMobile ? '5px' : '20px 42px',
|
||||
padding: '20px 42px',
|
||||
alignItems: 'center',
|
||||
height: isMobile ? 'auto' : '90px',
|
||||
height: '90px',
|
||||
}}
|
||||
>
|
||||
<NewMessageSendButton onClick={sendMail}>
|
||||
@ -553,9 +539,11 @@ export const NewThread = ({
|
||||
<CircularProgress sx={{}} size={'12px'} />
|
||||
</Box>
|
||||
)}
|
||||
|
||||
<NewMessageSendP>
|
||||
{isMessage ? 'Post' : 'Create Thread'}
|
||||
</NewMessageSendP>
|
||||
|
||||
{isMessage ? (
|
||||
<SendNewMessage opacity={1} height="25px" width="25px" />
|
||||
) : (
|
||||
@ -564,6 +552,7 @@ export const NewThread = ({
|
||||
</NewMessageSendButton>
|
||||
</InstanceFooter>
|
||||
</ReusableModal>
|
||||
|
||||
<CustomizedSnackbars
|
||||
open={openSnack}
|
||||
setOpen={setOpenSnack}
|
||||
|
@ -1,13 +1,12 @@
|
||||
import React from 'react'
|
||||
import { Box, Modal, useTheme } from '@mui/material'
|
||||
import { isMobile } from '../../../App'
|
||||
import React from 'react';
|
||||
import { Box, Modal, useTheme } from '@mui/material';
|
||||
|
||||
interface MyModalProps {
|
||||
open: boolean
|
||||
onClose?: () => void
|
||||
onSubmit?: (obj: any) => Promise<void>
|
||||
children: any
|
||||
customStyles?: any
|
||||
open: boolean;
|
||||
onClose?: () => void;
|
||||
onSubmit?: (obj: any) => Promise<void>;
|
||||
children: any;
|
||||
customStyles?: any;
|
||||
}
|
||||
|
||||
export const ReusableModal: React.FC<MyModalProps> = ({
|
||||
@ -15,9 +14,10 @@ export const ReusableModal: React.FC<MyModalProps> = ({
|
||||
onClose,
|
||||
onSubmit,
|
||||
children,
|
||||
customStyles = {}
|
||||
customStyles = {},
|
||||
}) => {
|
||||
const theme = useTheme()
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<Modal
|
||||
open={open}
|
||||
@ -32,27 +32,27 @@ export const ReusableModal: React.FC<MyModalProps> = ({
|
||||
},
|
||||
}}
|
||||
disableAutoFocus
|
||||
disableEnforceFocus
|
||||
disableRestoreFocus
|
||||
disableEnforceFocus
|
||||
disableRestoreFocus
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
top: '50%',
|
||||
left: '50%',
|
||||
transform: 'translate(-50%, -50%)',
|
||||
width: isMobile ? '95%' : '75%',
|
||||
bgcolor: theme.palette.primary.main,
|
||||
boxShadow: 24,
|
||||
p: 4,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: 2,
|
||||
...customStyles
|
||||
left: '50%',
|
||||
p: 4,
|
||||
position: 'absolute',
|
||||
top: '50%',
|
||||
transform: 'translate(-50%, -50%)',
|
||||
width: '75%',
|
||||
...customStyles,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</Box>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
@ -41,11 +41,7 @@ import {
|
||||
import { LoadingSnackbar } from '../../Snackbar/LoadingSnackbar';
|
||||
import { subscribeToEvent, unsubscribeFromEvent } from '../../../utils/events';
|
||||
import RefreshIcon from '@mui/icons-material/Refresh';
|
||||
import {
|
||||
getArbitraryEndpointReact,
|
||||
getBaseApiReact,
|
||||
isMobile,
|
||||
} from '../../../App';
|
||||
import { getArbitraryEndpointReact, getBaseApiReact } from '../../../App';
|
||||
import {
|
||||
ArrowDownward as ArrowDownwardIcon,
|
||||
ArrowUpward as ArrowUpwardIcon,
|
||||
@ -602,23 +598,18 @@ export const Thread = ({
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
gap: isMobile ? '45px' : '35px',
|
||||
gap: '35px',
|
||||
alignItems: 'center',
|
||||
padding: isMobile && '5px',
|
||||
}}
|
||||
>
|
||||
<ShowMessageReturnButton
|
||||
sx={{
|
||||
padding: isMobile && '5px',
|
||||
minWidth: isMobile && '50px',
|
||||
}}
|
||||
onClick={() => {
|
||||
setMessages([]);
|
||||
closeThread();
|
||||
}}
|
||||
>
|
||||
<MailIconImg src={ReturnSVG} />
|
||||
{!isMobile && <ComposeP>Return to Threads</ComposeP>}
|
||||
<ComposeP>Return to Threads</ComposeP>
|
||||
</ShowMessageReturnButton>
|
||||
{/* Conditionally render the scroll buttons */}
|
||||
{showScrollButton &&
|
||||
@ -628,7 +619,7 @@ export const Thread = ({
|
||||
sx={{
|
||||
color: 'white',
|
||||
cursor: 'pointer',
|
||||
fontSize: isMobile ? '28px' : '36px',
|
||||
fontSize: '36px',
|
||||
}}
|
||||
/>
|
||||
</ButtonBase>
|
||||
@ -638,7 +629,7 @@ export const Thread = ({
|
||||
sx={{
|
||||
color: 'white',
|
||||
cursor: 'pointer',
|
||||
fontSize: isMobile ? '28px' : '36px',
|
||||
fontSize: '36px',
|
||||
}}
|
||||
/>
|
||||
</ButtonBase>
|
||||
@ -655,38 +646,30 @@ export const Thread = ({
|
||||
>
|
||||
<div ref={threadBeginningRef} />
|
||||
<ThreadContainer>
|
||||
<Spacer height={isMobile ? '10px' : '30px'} />
|
||||
<Spacer height={'30px'} />
|
||||
<Box
|
||||
sx={{
|
||||
width: '100%',
|
||||
alignItems: 'center',
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
<GroupNameP
|
||||
sx={{
|
||||
fontSize: isMobile && '18px',
|
||||
}}
|
||||
>
|
||||
{currentThread?.threadData?.title}
|
||||
</GroupNameP>
|
||||
<GroupNameP>{currentThread?.threadData?.title}</GroupNameP>
|
||||
</Box>
|
||||
<Spacer height={'15px'} />
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
width: '100%',
|
||||
alignItems: 'center',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
gap: '5px',
|
||||
justifyContent: 'center',
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
sx={{
|
||||
padding: isMobile && '5px',
|
||||
fontSize: isMobile && '14px',
|
||||
textTransformation: 'capitalize',
|
||||
}}
|
||||
onClick={() => {
|
||||
@ -705,8 +688,6 @@ export const Thread = ({
|
||||
</Button>
|
||||
<Button
|
||||
sx={{
|
||||
padding: isMobile && '5px',
|
||||
fontSize: isMobile && '14px',
|
||||
textTransformation: 'capitalize',
|
||||
}}
|
||||
onClick={() => {
|
||||
@ -725,8 +706,6 @@ export const Thread = ({
|
||||
</Button>
|
||||
<Button
|
||||
sx={{
|
||||
padding: isMobile && '5px',
|
||||
fontSize: isMobile && '14px',
|
||||
textTransformation: 'capitalize',
|
||||
}}
|
||||
onClick={() => {
|
||||
@ -745,8 +724,6 @@ export const Thread = ({
|
||||
</Button>
|
||||
<Button
|
||||
sx={{
|
||||
padding: isMobile && '5px',
|
||||
fontSize: isMobile && '14px',
|
||||
textTransformation: 'capitalize',
|
||||
}}
|
||||
onClick={() => {
|
||||
@ -764,7 +741,9 @@ export const Thread = ({
|
||||
Last
|
||||
</Button>
|
||||
</Box>
|
||||
<Spacer height={isMobile ? '10px' : '30px'} />
|
||||
|
||||
<Spacer height={'30px'} />
|
||||
|
||||
{combinedListTempAndReal.map((message, index, list) => {
|
||||
let fullMessage = message;
|
||||
|
||||
@ -780,17 +759,17 @@ export const Thread = ({
|
||||
>
|
||||
<Box
|
||||
style={{
|
||||
width: '100%',
|
||||
borderRadius: '8px',
|
||||
flexDirection: 'column',
|
||||
overflow: 'hidden',
|
||||
position: 'relative',
|
||||
flexDirection: 'column',
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
alignItems: 'flex-start',
|
||||
display: 'flex',
|
||||
gap: '10px',
|
||||
}}
|
||||
>
|
||||
@ -812,6 +791,7 @@ export const Thread = ({
|
||||
{message?.name?.charAt(0)}
|
||||
</Avatar>
|
||||
</WrapperUserAction>
|
||||
|
||||
<ThreadInfoColumn>
|
||||
<WrapperUserAction
|
||||
disabled={userInfo?.name === message?.name}
|
||||
@ -822,18 +802,20 @@ export const Thread = ({
|
||||
{message?.name}
|
||||
</ThreadInfoColumnNameP>
|
||||
</WrapperUserAction>
|
||||
|
||||
<ThreadInfoColumnTime>
|
||||
{formatTimestampForum(message?.created)}
|
||||
</ThreadInfoColumnTime>
|
||||
</ThreadInfoColumn>
|
||||
</Box>
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'center',
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
@ -876,17 +858,17 @@ export const Thread = ({
|
||||
>
|
||||
<Box
|
||||
style={{
|
||||
width: '100%',
|
||||
borderRadius: '8px',
|
||||
flexDirection: 'column',
|
||||
overflow: 'hidden',
|
||||
position: 'relative',
|
||||
flexDirection: 'column',
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
alignItems: 'flex-start',
|
||||
display: 'flex',
|
||||
gap: '10px',
|
||||
}}
|
||||
>
|
||||
@ -908,6 +890,7 @@ export const Thread = ({
|
||||
{message?.name?.charAt(0)}
|
||||
</Avatar>
|
||||
</WrapperUserAction>
|
||||
|
||||
<ThreadInfoColumn>
|
||||
<WrapperUserAction
|
||||
disabled={userInfo?.name === message?.name}
|
||||
@ -918,18 +901,20 @@ export const Thread = ({
|
||||
{message?.name}
|
||||
</ThreadInfoColumnNameP>
|
||||
</WrapperUserAction>
|
||||
|
||||
<ThreadInfoColumnTime>
|
||||
{formatTimestampForum(message?.created)}
|
||||
</ThreadInfoColumnTime>
|
||||
</ThreadInfoColumn>
|
||||
</Box>
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'center',
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
<CustomLoader />
|
||||
@ -952,9 +937,9 @@ export const Thread = ({
|
||||
<Spacer height="20px" />
|
||||
<Box
|
||||
sx={{
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
justifyContent: 'flex-end',
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
@ -997,8 +982,6 @@ export const Thread = ({
|
||||
>
|
||||
<Button
|
||||
sx={{
|
||||
padding: isMobile && '5px',
|
||||
fontSize: isMobile && '14px',
|
||||
textTransformation: 'capitalize',
|
||||
}}
|
||||
onClick={() => {
|
||||
@ -1017,8 +1000,6 @@ export const Thread = ({
|
||||
</Button>
|
||||
<Button
|
||||
sx={{
|
||||
padding: isMobile && '5px',
|
||||
fontSize: isMobile && '14px',
|
||||
textTransformation: 'capitalize',
|
||||
}}
|
||||
onClick={() => {
|
||||
@ -1037,8 +1018,6 @@ export const Thread = ({
|
||||
</Button>
|
||||
<Button
|
||||
sx={{
|
||||
padding: isMobile && '5px',
|
||||
fontSize: isMobile && '14px',
|
||||
textTransformation: 'capitalize',
|
||||
}}
|
||||
onClick={() => {
|
||||
@ -1057,8 +1036,6 @@ export const Thread = ({
|
||||
</Button>
|
||||
<Button
|
||||
sx={{
|
||||
padding: isMobile && '5px',
|
||||
fontSize: isMobile && '14px',
|
||||
textTransformation: 'capitalize',
|
||||
}}
|
||||
onClick={() => {
|
||||
|
@ -37,7 +37,6 @@ import {
|
||||
clearAllQueues,
|
||||
getArbitraryEndpointReact,
|
||||
getBaseApiReact,
|
||||
isMobile,
|
||||
pauseAllQueues,
|
||||
resumeAllQueues,
|
||||
} from '../../App';
|
||||
@ -1096,12 +1095,8 @@ export const Group = ({
|
||||
return;
|
||||
}
|
||||
if (findDirect) {
|
||||
if (!isMobile) {
|
||||
setDesktopSideView('directs');
|
||||
setDesktopViewMode('home');
|
||||
} else {
|
||||
setMobileViewModeKeepOpen('messaging');
|
||||
}
|
||||
setDesktopSideView('directs');
|
||||
setDesktopViewMode('home');
|
||||
setSelectedDirect(null);
|
||||
|
||||
setNewChat(false);
|
||||
@ -1136,11 +1131,7 @@ export const Group = ({
|
||||
);
|
||||
|
||||
if (findDirect) {
|
||||
if (!isMobile) {
|
||||
setDesktopSideView('directs');
|
||||
} else {
|
||||
setMobileViewModeKeepOpen('messaging');
|
||||
}
|
||||
setDesktopSideView('directs');
|
||||
setSelectedDirect(null);
|
||||
|
||||
setNewChat(false);
|
||||
@ -1162,11 +1153,7 @@ export const Group = ({
|
||||
getTimestampEnterChat();
|
||||
}, 200);
|
||||
} else {
|
||||
if (!isMobile) {
|
||||
setDesktopSideView('directs');
|
||||
} else {
|
||||
setMobileViewModeKeepOpen('messaging');
|
||||
}
|
||||
setDesktopSideView('directs');
|
||||
setNewChat(true);
|
||||
setTimeout(() => {
|
||||
executeEvent('setDirectToValueNewChat', {
|
||||
@ -1284,9 +1271,7 @@ export const Group = ({
|
||||
setupGroupWebsocketInterval.current = null;
|
||||
settimeoutForRefetchSecretKey.current = null;
|
||||
initiatedGetMembers.current = false;
|
||||
if (!isMobile) {
|
||||
setDesktopViewMode('home');
|
||||
}
|
||||
setDesktopViewMode('home');
|
||||
};
|
||||
|
||||
const logoutEventFunc = () => {
|
||||
@ -1303,34 +1288,7 @@ export const Group = ({
|
||||
}, []);
|
||||
|
||||
const openAppsMode = () => {
|
||||
if (isMobile) {
|
||||
setMobileViewMode('apps');
|
||||
}
|
||||
if (!isMobile) {
|
||||
setDesktopViewMode('apps');
|
||||
}
|
||||
if (isMobile) {
|
||||
setIsOpenSideViewDirects(false);
|
||||
setIsOpenSideViewGroups(false);
|
||||
setGroupSection('default');
|
||||
setSelectedGroup(null);
|
||||
setNewChat(false);
|
||||
setSelectedDirect(null);
|
||||
setSecretKey(null);
|
||||
setGroupOwner(null);
|
||||
lastFetchedSecretKey.current = null;
|
||||
initiatedGetMembers.current = false;
|
||||
setSecretKeyPublishDate(null);
|
||||
setAdmins([]);
|
||||
setSecretKeyDetails(null);
|
||||
setAdminsWithNames([]);
|
||||
setMembers([]);
|
||||
setMemberCountFromSecretKeyData(null);
|
||||
setTriedToFetchSecretKey(false);
|
||||
setFirstSecretKeyInCreation(false);
|
||||
setIsOpenSideViewDirects(false);
|
||||
setIsOpenSideViewGroups(false);
|
||||
}
|
||||
setDesktopViewMode('apps');
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
@ -1372,9 +1330,7 @@ export const Group = ({
|
||||
setTriedToFetchSecretKey(false);
|
||||
setFirstSecretKeyInCreation(false);
|
||||
setGroupSection('chat');
|
||||
if (!isMobile) {
|
||||
setDesktopViewMode('chat');
|
||||
}
|
||||
setDesktopViewMode('chat');
|
||||
|
||||
window
|
||||
.sendMessage('addTimestampEnterChat', {
|
||||
@ -1430,9 +1386,7 @@ export const Group = ({
|
||||
setTriedToFetchSecretKey(false);
|
||||
setFirstSecretKeyInCreation(false);
|
||||
setGroupSection('announcement');
|
||||
if (!isMobile) {
|
||||
setDesktopViewMode('chat');
|
||||
}
|
||||
setDesktopViewMode('chat');
|
||||
window
|
||||
.sendMessage('addGroupNotificationTimestamp', {
|
||||
timestamp: Date.now(),
|
||||
@ -1496,9 +1450,7 @@ export const Group = ({
|
||||
setFirstSecretKeyInCreation(false);
|
||||
setGroupSection('forum');
|
||||
setDefaultThread(data);
|
||||
if (!isMobile) {
|
||||
setDesktopViewMode('chat');
|
||||
}
|
||||
setDesktopViewMode('chat');
|
||||
setTimeout(() => {
|
||||
setSelectedGroup(findGroup);
|
||||
setMobileViewMode('group');
|
||||
@ -1521,12 +1473,6 @@ export const Group = ({
|
||||
};
|
||||
|
||||
const goToHome = async () => {
|
||||
if (isMobile) {
|
||||
setMobileViewMode('home');
|
||||
}
|
||||
if (!isMobile) {
|
||||
// TODO: empty block. Check it!
|
||||
}
|
||||
setDesktopViewMode('home');
|
||||
|
||||
await new Promise((res) => {
|
||||
@ -1614,26 +1560,38 @@ export const Group = ({
|
||||
borderRadius: '0px 15px 15px 0px',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
height: isMobile ? `calc(${rootHeight} - 45px)` : '100%',
|
||||
width: isMobile ? '100%' : '380px',
|
||||
height: '100%',
|
||||
width: '380px',
|
||||
}}
|
||||
>
|
||||
{!isMobile && (
|
||||
<Box
|
||||
sx={{
|
||||
alignItems: 'center',
|
||||
display: 'flex',
|
||||
gap: '10px',
|
||||
justifyContent: 'center',
|
||||
width: '100%',
|
||||
<Box
|
||||
sx={{
|
||||
alignItems: 'center',
|
||||
display: 'flex',
|
||||
gap: '10px',
|
||||
justifyContent: 'center',
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
<ButtonBase
|
||||
onClick={() => {
|
||||
setDesktopSideView('groups');
|
||||
}}
|
||||
>
|
||||
<ButtonBase
|
||||
onClick={() => {
|
||||
setDesktopSideView('groups');
|
||||
}}
|
||||
<IconWrapper
|
||||
color={
|
||||
groupChatHasUnread || groupsAnnHasUnread
|
||||
? 'var(--unread)'
|
||||
: desktopSideView === 'groups'
|
||||
? theme.palette.text.primary
|
||||
: theme.palette.text.secondary
|
||||
}
|
||||
label="Groups"
|
||||
selected={desktopSideView === 'groups'}
|
||||
customWidth="75px"
|
||||
>
|
||||
<IconWrapper
|
||||
<HubsIcon
|
||||
height={24}
|
||||
color={
|
||||
groupChatHasUnread || groupsAnnHasUnread
|
||||
? 'var(--unread)'
|
||||
@ -1641,29 +1599,28 @@ export const Group = ({
|
||||
? theme.palette.text.primary
|
||||
: theme.palette.text.secondary
|
||||
}
|
||||
label="Groups"
|
||||
selected={desktopSideView === 'groups'}
|
||||
customWidth="75px"
|
||||
>
|
||||
<HubsIcon
|
||||
height={24}
|
||||
color={
|
||||
groupChatHasUnread || groupsAnnHasUnread
|
||||
? 'var(--unread)'
|
||||
: desktopSideView === 'groups'
|
||||
? theme.palette.text.primary
|
||||
: theme.palette.text.secondary
|
||||
}
|
||||
/>
|
||||
</IconWrapper>
|
||||
</ButtonBase>
|
||||
<ButtonBase
|
||||
onClick={() => {
|
||||
setDesktopSideView('directs');
|
||||
}}
|
||||
/>
|
||||
</IconWrapper>
|
||||
</ButtonBase>
|
||||
<ButtonBase
|
||||
onClick={() => {
|
||||
setDesktopSideView('directs');
|
||||
}}
|
||||
>
|
||||
<IconWrapper
|
||||
customWidth="75px"
|
||||
color={
|
||||
directChatHasUnread
|
||||
? 'var(--unread)'
|
||||
: desktopSideView === 'directs'
|
||||
? theme.palette.text.primary
|
||||
: theme.palette.text.secondary
|
||||
}
|
||||
label="Messaging"
|
||||
selected={desktopSideView === 'directs'}
|
||||
>
|
||||
<IconWrapper
|
||||
customWidth="75px"
|
||||
<MessagingIcon
|
||||
height={24}
|
||||
color={
|
||||
directChatHasUnread
|
||||
? 'var(--unread)'
|
||||
@ -1671,23 +1628,10 @@ export const Group = ({
|
||||
? theme.palette.text.primary
|
||||
: theme.palette.text.secondary
|
||||
}
|
||||
label="Messaging"
|
||||
selected={desktopSideView === 'directs'}
|
||||
>
|
||||
<MessagingIcon
|
||||
height={24}
|
||||
color={
|
||||
directChatHasUnread
|
||||
? 'var(--unread)'
|
||||
: desktopSideView === 'directs'
|
||||
? theme.palette.text.primary
|
||||
: theme.palette.text.secondary
|
||||
}
|
||||
/>
|
||||
</IconWrapper>
|
||||
</ButtonBase>
|
||||
</Box>
|
||||
)}
|
||||
/>
|
||||
</IconWrapper>
|
||||
</ButtonBase>
|
||||
</Box>
|
||||
|
||||
<div
|
||||
style={{
|
||||
@ -1843,30 +1787,42 @@ export const Group = ({
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
width: isMobile ? '100%' : '380px',
|
||||
width: '380px',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'flex-start',
|
||||
height: isMobile ? `calc(${rootHeight} - 45px)` : '100%',
|
||||
background: !isMobile && theme.palette.background.default,
|
||||
borderRadius: !isMobile && '0px 15px 15px 0px',
|
||||
height: '100%',
|
||||
background: theme.palette.background.default,
|
||||
borderRadius: '0px 15px 15px 0px',
|
||||
}}
|
||||
>
|
||||
{!isMobile && (
|
||||
<Box
|
||||
sx={{
|
||||
width: '100%',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
display: 'flex',
|
||||
gap: '10px',
|
||||
<Box
|
||||
sx={{
|
||||
width: '100%',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
display: 'flex',
|
||||
gap: '10px',
|
||||
}}
|
||||
>
|
||||
<ButtonBase
|
||||
onClick={() => {
|
||||
setDesktopSideView('groups');
|
||||
}}
|
||||
>
|
||||
<ButtonBase
|
||||
onClick={() => {
|
||||
setDesktopSideView('groups');
|
||||
}}
|
||||
<IconWrapper
|
||||
color={
|
||||
groupChatHasUnread || groupsAnnHasUnread
|
||||
? 'var(--unread)'
|
||||
: desktopSideView === 'groups'
|
||||
? theme.palette.text.primary
|
||||
: theme.palette.text.secondary
|
||||
}
|
||||
label="Groups"
|
||||
selected={desktopSideView === 'groups'}
|
||||
customWidth="75px"
|
||||
>
|
||||
<IconWrapper
|
||||
<HubsIcon
|
||||
height={24}
|
||||
color={
|
||||
groupChatHasUnread || groupsAnnHasUnread
|
||||
? 'var(--unread)'
|
||||
@ -1874,29 +1830,28 @@ export const Group = ({
|
||||
? theme.palette.text.primary
|
||||
: theme.palette.text.secondary
|
||||
}
|
||||
label="Groups"
|
||||
selected={desktopSideView === 'groups'}
|
||||
customWidth="75px"
|
||||
>
|
||||
<HubsIcon
|
||||
height={24}
|
||||
color={
|
||||
groupChatHasUnread || groupsAnnHasUnread
|
||||
? 'var(--unread)'
|
||||
: desktopSideView === 'groups'
|
||||
? theme.palette.text.primary
|
||||
: theme.palette.text.secondary
|
||||
}
|
||||
/>
|
||||
</IconWrapper>
|
||||
</ButtonBase>
|
||||
<ButtonBase
|
||||
onClick={() => {
|
||||
setDesktopSideView('directs');
|
||||
}}
|
||||
/>
|
||||
</IconWrapper>
|
||||
</ButtonBase>
|
||||
<ButtonBase
|
||||
onClick={() => {
|
||||
setDesktopSideView('directs');
|
||||
}}
|
||||
>
|
||||
<IconWrapper
|
||||
customWidth="75px"
|
||||
color={
|
||||
directChatHasUnread
|
||||
? 'var(--unread)'
|
||||
: desktopSideView === 'directs'
|
||||
? theme.palette.text.primary
|
||||
: theme.palette.text.secondary
|
||||
}
|
||||
label="Messaging"
|
||||
selected={desktopSideView === 'directs'}
|
||||
>
|
||||
<IconWrapper
|
||||
customWidth="75px"
|
||||
<MessagingIcon
|
||||
height={24}
|
||||
color={
|
||||
directChatHasUnread
|
||||
? 'var(--unread)'
|
||||
@ -1904,23 +1859,10 @@ export const Group = ({
|
||||
? theme.palette.text.primary
|
||||
: theme.palette.text.secondary
|
||||
}
|
||||
label="Messaging"
|
||||
selected={desktopSideView === 'directs'}
|
||||
>
|
||||
<MessagingIcon
|
||||
height={24}
|
||||
color={
|
||||
directChatHasUnread
|
||||
? 'var(--unread)'
|
||||
: desktopSideView === 'directs'
|
||||
? theme.palette.text.primary
|
||||
: theme.palette.text.secondary
|
||||
}
|
||||
/>
|
||||
</IconWrapper>
|
||||
</ButtonBase>
|
||||
</Box>
|
||||
)}
|
||||
/>
|
||||
</IconWrapper>
|
||||
</ButtonBase>
|
||||
</Box>
|
||||
|
||||
<div
|
||||
style={{
|
||||
@ -2181,38 +2123,35 @@ export const Group = ({
|
||||
alignItems: 'flex-start',
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
height: isMobile ? '100%' : '100%',
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
{!isMobile &&
|
||||
((desktopViewMode !== 'apps' && desktopViewMode !== 'dev') ||
|
||||
isOpenSideViewGroups) && (
|
||||
<DesktopSideBar
|
||||
desktopViewMode={desktopViewMode}
|
||||
toggleSideViewGroups={toggleSideViewGroups}
|
||||
toggleSideViewDirects={toggleSideViewDirects}
|
||||
goToHome={goToHome}
|
||||
mode={appsMode}
|
||||
setMode={setAppsMode}
|
||||
setDesktopSideView={setDesktopSideView}
|
||||
hasUnreadDirects={directChatHasUnread}
|
||||
isApps={desktopViewMode === 'apps'}
|
||||
myName={userInfo?.name}
|
||||
isGroups={isOpenSideViewGroups}
|
||||
isDirects={isOpenSideViewDirects}
|
||||
hasUnreadGroups={groupChatHasUnread || groupsAnnHasUnread}
|
||||
setDesktopViewMode={setDesktopViewMode}
|
||||
/>
|
||||
)}
|
||||
{((desktopViewMode !== 'apps' && desktopViewMode !== 'dev') ||
|
||||
isOpenSideViewGroups) && (
|
||||
<DesktopSideBar
|
||||
desktopViewMode={desktopViewMode}
|
||||
toggleSideViewGroups={toggleSideViewGroups}
|
||||
toggleSideViewDirects={toggleSideViewDirects}
|
||||
goToHome={goToHome}
|
||||
mode={appsMode}
|
||||
setMode={setAppsMode}
|
||||
setDesktopSideView={setDesktopSideView}
|
||||
hasUnreadDirects={directChatHasUnread}
|
||||
isApps={desktopViewMode === 'apps'}
|
||||
myName={userInfo?.name}
|
||||
isGroups={isOpenSideViewGroups}
|
||||
isDirects={isOpenSideViewDirects}
|
||||
hasUnreadGroups={groupChatHasUnread || groupsAnnHasUnread}
|
||||
setDesktopViewMode={setDesktopViewMode}
|
||||
/>
|
||||
)}
|
||||
|
||||
{!isMobile &&
|
||||
desktopViewMode === 'chat' &&
|
||||
{desktopViewMode === 'chat' &&
|
||||
desktopSideView !== 'directs' &&
|
||||
renderGroups()}
|
||||
|
||||
{!isMobile &&
|
||||
desktopViewMode === 'chat' &&
|
||||
{desktopViewMode === 'chat' &&
|
||||
desktopSideView === 'directs' &&
|
||||
renderDirects()}
|
||||
|
||||
@ -2231,66 +2170,10 @@ export const Group = ({
|
||||
|
||||
{newChat && (
|
||||
<>
|
||||
{isMobile && (
|
||||
<Box
|
||||
sx={{
|
||||
alignItems: 'center',
|
||||
display: 'flex',
|
||||
height: '15px',
|
||||
justifyContent: 'center',
|
||||
marginTop: '14px',
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
alignItems: 'center',
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
width: '320px',
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
alignItems: 'center',
|
||||
display: 'flex',
|
||||
width: '50px',
|
||||
}}
|
||||
>
|
||||
<ButtonBase
|
||||
onClick={() => {
|
||||
close();
|
||||
}}
|
||||
>
|
||||
<ReturnIcon />
|
||||
</ButtonBase>
|
||||
</Box>
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
alignItems: 'center',
|
||||
display: 'flex',
|
||||
justifyContent: 'flex-end',
|
||||
width: '50px',
|
||||
}}
|
||||
>
|
||||
<ButtonBase
|
||||
onClick={() => {
|
||||
setSelectedDirect(null);
|
||||
setMobileViewModeKeepOpen('');
|
||||
}}
|
||||
>
|
||||
<ExitIcon />
|
||||
</ButtonBase>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
)}
|
||||
<Box
|
||||
sx={{
|
||||
background: theme.palette.background.default,
|
||||
bottom: !(desktopViewMode === 'chat') ? 'unset' : '0px',
|
||||
height: isMobile && `calc(${rootHeight} - 45px)`,
|
||||
left: !(desktopViewMode === 'chat') ? '-100000px' : '0px',
|
||||
opacity: !(desktopViewMode === 'chat') ? 0 : 1,
|
||||
position: 'absolute',
|
||||
@ -2352,7 +2235,7 @@ export const Group = ({
|
||||
: '0px',
|
||||
}}
|
||||
>
|
||||
{!isMobile && (
|
||||
{
|
||||
<DesktopHeader
|
||||
isPrivate={isPrivate}
|
||||
selectedGroup={selectedGroup}
|
||||
@ -2387,15 +2270,14 @@ export const Group = ({
|
||||
setGroupSection={setGroupSection}
|
||||
isForum={groupSection === 'forum'}
|
||||
/>
|
||||
)}
|
||||
}
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
position: 'relative',
|
||||
flexGrow: 1,
|
||||
display: 'flex',
|
||||
// reference to change height
|
||||
height: isMobile ? 'calc(100% - 82px)' : 'calc(100vh - 70px)',
|
||||
flexGrow: 1,
|
||||
height: 'calc(100vh - 70px)',
|
||||
position: 'relative',
|
||||
}}
|
||||
>
|
||||
{triedToFetchSecretKey && (
|
||||
@ -2449,15 +2331,13 @@ export const Group = ({
|
||||
(!secretKeyPublishDate && !firstSecretKeyInCreation) ? (
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
width: '100%',
|
||||
height: isMobile
|
||||
? `calc(${rootHeight} - 113px)`
|
||||
: 'calc(100vh - 70px)',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'flex-start',
|
||||
padding: '20px',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
height: 'calc(100vh - 70px)',
|
||||
overflow: 'auto',
|
||||
padding: '20px',
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
{' '}
|
||||
@ -2605,7 +2485,6 @@ export const Group = ({
|
||||
sx={{
|
||||
background: theme.palette.background.default,
|
||||
bottom: !(desktopViewMode === 'chat') ? 'unset' : '0px',
|
||||
height: isMobile && `calc(${rootHeight} - 45px)`,
|
||||
left: !(desktopViewMode === 'chat') ? '-100000px' : '0px',
|
||||
opacity: !(desktopViewMode === 'chat') ? 0 : 1,
|
||||
position: 'absolute',
|
||||
@ -2642,7 +2521,7 @@ export const Group = ({
|
||||
</>
|
||||
)}
|
||||
|
||||
{!isMobile && (
|
||||
{
|
||||
<AppsDesktop
|
||||
toggleSideViewGroups={toggleSideViewGroups}
|
||||
toggleSideViewDirects={toggleSideViewDirects}
|
||||
@ -2660,8 +2539,8 @@ export const Group = ({
|
||||
isApps={desktopViewMode === 'apps'}
|
||||
desktopViewMode={desktopViewMode}
|
||||
/>
|
||||
)}
|
||||
{!isMobile && (
|
||||
}
|
||||
{
|
||||
<AppsDevMode
|
||||
toggleSideViewGroups={toggleSideViewGroups}
|
||||
toggleSideViewDirects={toggleSideViewDirects}
|
||||
@ -2679,9 +2558,9 @@ export const Group = ({
|
||||
desktopViewMode={desktopViewMode}
|
||||
isApps={desktopViewMode === 'apps'}
|
||||
/>
|
||||
)}
|
||||
}
|
||||
|
||||
{!isMobile && (
|
||||
{
|
||||
<HomeDesktop
|
||||
name={userInfo?.name}
|
||||
refreshHomeDataFunc={refreshHomeDataFunc}
|
||||
@ -2699,7 +2578,7 @@ export const Group = ({
|
||||
setDesktopViewMode={setDesktopViewMode}
|
||||
desktopViewMode={desktopViewMode}
|
||||
/>
|
||||
)}
|
||||
}
|
||||
</Box>
|
||||
|
||||
<AuthenticatedContainerInnerRight
|
||||
@ -2708,7 +2587,6 @@ export const Group = ({
|
||||
width: '31px',
|
||||
padding: '5px',
|
||||
display:
|
||||
isMobile ||
|
||||
desktopViewMode === 'apps' ||
|
||||
desktopViewMode === 'dev' ||
|
||||
desktopViewMode === 'chat'
|
||||
|
@ -9,7 +9,7 @@ import { executeEvent } from '../../utils/events';
|
||||
import { Box, ButtonBase, Collapse, Typography, useTheme } from '@mui/material';
|
||||
import { getGroupNames } from './UserListOfInvites';
|
||||
import { CustomLoader } from '../../common/CustomLoader';
|
||||
import { getBaseApiReact, isMobile } from '../../App';
|
||||
import { getBaseApiReact } from '../../App';
|
||||
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
|
||||
import ExpandLessIcon from '@mui/icons-material/ExpandLess';
|
||||
|
||||
@ -97,7 +97,7 @@ export const GroupInvites = ({ myAddress, setOpenAddGroup }) => {
|
||||
borderRadius: '19px',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
height: isMobile ? '165px' : '250px',
|
||||
height: '250px',
|
||||
padding: '20px',
|
||||
width: '322px',
|
||||
}}
|
||||
|
@ -1,244 +1,277 @@
|
||||
import * as React from "react";
|
||||
import List from "@mui/material/List";
|
||||
import ListItem from "@mui/material/ListItem";
|
||||
import ListItemButton from "@mui/material/ListItemButton";
|
||||
import ListItemIcon from "@mui/material/ListItemIcon";
|
||||
import ListItemText from "@mui/material/ListItemText";
|
||||
import Checkbox from "@mui/material/Checkbox";
|
||||
import IconButton from "@mui/material/IconButton";
|
||||
import CommentIcon from "@mui/icons-material/Comment";
|
||||
import InfoIcon from "@mui/icons-material/Info";
|
||||
import { RequestQueueWithPromise } from "../../utils/queue/queue";
|
||||
import * as React from 'react';
|
||||
import List from '@mui/material/List';
|
||||
import ListItem from '@mui/material/ListItem';
|
||||
import ListItemButton from '@mui/material/ListItemButton';
|
||||
import ListItemText from '@mui/material/ListItemText';
|
||||
import IconButton from '@mui/material/IconButton';
|
||||
import { RequestQueueWithPromise } from '../../utils/queue/queue';
|
||||
import GroupAddIcon from '@mui/icons-material/GroupAdd';
|
||||
import { executeEvent } from "../../utils/events";
|
||||
import { Box, ButtonBase, Collapse, Typography } from "@mui/material";
|
||||
import { Spacer } from "../../common/Spacer";
|
||||
import { CustomLoader } from "../../common/CustomLoader";
|
||||
import { getBaseApi } from "../../background";
|
||||
import { MyContext, getBaseApiReact, isMobile } from "../../App";
|
||||
import { myGroupsWhereIAmAdminAtom } from "../../atoms/global";
|
||||
import { useSetRecoilState } from "recoil";
|
||||
import { executeEvent } from '../../utils/events';
|
||||
import { Box, ButtonBase, Collapse, Typography } from '@mui/material';
|
||||
import { CustomLoader } from '../../common/CustomLoader';
|
||||
import { MyContext, getBaseApiReact } from '../../App';
|
||||
import { myGroupsWhereIAmAdminAtom } from '../../atoms/global';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
|
||||
import ExpandLessIcon from '@mui/icons-material/ExpandLess';
|
||||
export const requestQueueGroupJoinRequests = new RequestQueueWithPromise(2)
|
||||
export const requestQueueGroupJoinRequests = new RequestQueueWithPromise(2);
|
||||
|
||||
export const GroupJoinRequests = ({ myAddress, groups, setOpenManageMembers, getTimestampEnterChat, setSelectedGroup, setGroupSection, setMobileViewMode, setDesktopViewMode }) => {
|
||||
const [isExpanded, setIsExpanded] = React.useState(false)
|
||||
|
||||
const [groupsWithJoinRequests, setGroupsWithJoinRequests] = React.useState([])
|
||||
const [loading, setLoading] = React.useState(true)
|
||||
const {txList, setTxList} = React.useContext(MyContext)
|
||||
const setMyGroupsWhereIAmAdmin = useSetRecoilState(
|
||||
myGroupsWhereIAmAdminAtom
|
||||
export const GroupJoinRequests = ({
|
||||
myAddress,
|
||||
groups,
|
||||
setOpenManageMembers,
|
||||
getTimestampEnterChat,
|
||||
setSelectedGroup,
|
||||
setGroupSection,
|
||||
setMobileViewMode,
|
||||
setDesktopViewMode,
|
||||
}) => {
|
||||
const [isExpanded, setIsExpanded] = React.useState(false);
|
||||
|
||||
const [groupsWithJoinRequests, setGroupsWithJoinRequests] = React.useState(
|
||||
[]
|
||||
);
|
||||
const [loading, setLoading] = React.useState(true);
|
||||
const { txList, setTxList } = React.useContext(MyContext);
|
||||
const setMyGroupsWhereIAmAdmin = useSetRecoilState(myGroupsWhereIAmAdminAtom);
|
||||
|
||||
|
||||
const getJoinRequests = async ()=> {
|
||||
const getJoinRequests = async () => {
|
||||
try {
|
||||
setLoading(true)
|
||||
|
||||
let groupsAsAdmin = []
|
||||
const getAllGroupsAsAdmin = groups.filter((item)=> item.groupId !== '0').map(async (group)=> {
|
||||
|
||||
const isAdminResponse = await requestQueueGroupJoinRequests.enqueue(()=> {
|
||||
return fetch(
|
||||
`${getBaseApiReact()}/groups/members/${group.groupId}?limit=0&onlyAdmins=true`
|
||||
setLoading(true);
|
||||
|
||||
let groupsAsAdmin = [];
|
||||
const getAllGroupsAsAdmin = groups
|
||||
.filter((item) => item.groupId !== '0')
|
||||
.map(async (group) => {
|
||||
const isAdminResponse = await requestQueueGroupJoinRequests.enqueue(
|
||||
() => {
|
||||
return fetch(
|
||||
`${getBaseApiReact()}/groups/members/${group.groupId}?limit=0&onlyAdmins=true`
|
||||
);
|
||||
}
|
||||
);
|
||||
})
|
||||
const isAdminData = await isAdminResponse.json()
|
||||
|
||||
const isAdminData = await isAdminResponse.json();
|
||||
|
||||
const findMyself = isAdminData?.members?.find((member)=> member.member === myAddress)
|
||||
|
||||
if(findMyself){
|
||||
groupsAsAdmin.push(group)
|
||||
}
|
||||
return true
|
||||
})
|
||||
const findMyself = isAdminData?.members?.find(
|
||||
(member) => member.member === myAddress
|
||||
);
|
||||
|
||||
|
||||
await Promise.all(getAllGroupsAsAdmin)
|
||||
setMyGroupsWhereIAmAdmin(groupsAsAdmin)
|
||||
const res = await Promise.all(groupsAsAdmin.map(async (group)=> {
|
||||
if (findMyself) {
|
||||
groupsAsAdmin.push(group);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
const joinRequestResponse = await requestQueueGroupJoinRequests.enqueue(()=> {
|
||||
return fetch(
|
||||
`${getBaseApiReact()}/groups/joinrequests/${group.groupId}`
|
||||
);
|
||||
})
|
||||
await Promise.all(getAllGroupsAsAdmin);
|
||||
setMyGroupsWhereIAmAdmin(groupsAsAdmin);
|
||||
const res = await Promise.all(
|
||||
groupsAsAdmin.map(async (group) => {
|
||||
const joinRequestResponse =
|
||||
await requestQueueGroupJoinRequests.enqueue(() => {
|
||||
return fetch(
|
||||
`${getBaseApiReact()}/groups/joinrequests/${group.groupId}`
|
||||
);
|
||||
});
|
||||
|
||||
const joinRequestData = await joinRequestResponse.json()
|
||||
return {
|
||||
group,
|
||||
data: joinRequestData
|
||||
}
|
||||
}))
|
||||
setGroupsWithJoinRequests(res)
|
||||
const joinRequestData = await joinRequestResponse.json();
|
||||
return {
|
||||
group,
|
||||
data: joinRequestData,
|
||||
};
|
||||
})
|
||||
);
|
||||
setGroupsWithJoinRequests(res);
|
||||
} catch (error) {
|
||||
|
||||
} finally {
|
||||
setLoading(false)
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
if (myAddress && groups.length > 0) {
|
||||
getJoinRequests()
|
||||
getJoinRequests();
|
||||
} else {
|
||||
setLoading(false)
|
||||
setLoading(false);
|
||||
}
|
||||
}, [myAddress, groups]);
|
||||
|
||||
const filteredJoinRequests = React.useMemo(()=> {
|
||||
return groupsWithJoinRequests.map((group)=> {
|
||||
const filteredGroupRequests = group?.data?.filter((gd)=> {
|
||||
const findJoinRequsetInTxList = txList?.find((tx)=> tx?.groupId === group?.group?.groupId && tx?.qortalAddress === gd?.joiner && tx?.type === 'join-request-accept')
|
||||
const filteredJoinRequests = React.useMemo(() => {
|
||||
return groupsWithJoinRequests.map((group) => {
|
||||
const filteredGroupRequests = group?.data?.filter((gd) => {
|
||||
const findJoinRequsetInTxList = txList?.find(
|
||||
(tx) =>
|
||||
tx?.groupId === group?.group?.groupId &&
|
||||
tx?.qortalAddress === gd?.joiner &&
|
||||
tx?.type === 'join-request-accept'
|
||||
);
|
||||
|
||||
if(findJoinRequsetInTxList) return false
|
||||
return true
|
||||
})
|
||||
if (findJoinRequsetInTxList) return false;
|
||||
return true;
|
||||
});
|
||||
return {
|
||||
...group,
|
||||
data: filteredGroupRequests
|
||||
}
|
||||
})
|
||||
}, [groupsWithJoinRequests, txList])
|
||||
|
||||
|
||||
data: filteredGroupRequests,
|
||||
};
|
||||
});
|
||||
}, [groupsWithJoinRequests, txList]);
|
||||
|
||||
return (
|
||||
<Box sx={{
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: 'center'
|
||||
}}>
|
||||
<Box
|
||||
sx={{
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<ButtonBase
|
||||
sx={{
|
||||
width: "322px",
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
width: '322px',
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
padding: '0px 20px',
|
||||
gap: '10px',
|
||||
justifyContent: 'flex-start'
|
||||
justifyContent: 'flex-start',
|
||||
}}
|
||||
onClick={()=> setIsExpanded((prev)=> !prev)}
|
||||
onClick={() => setIsExpanded((prev) => !prev)}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: "1rem",
|
||||
fontSize: '1rem',
|
||||
}}
|
||||
>
|
||||
Join Requests {filteredJoinRequests?.filter((group)=> group?.data?.length > 0)?.length > 0 && ` (${filteredJoinRequests?.filter((group)=> group?.data?.length > 0)?.length})`}
|
||||
Join Requests{' '}
|
||||
{filteredJoinRequests?.filter((group) => group?.data?.length > 0)
|
||||
?.length > 0 &&
|
||||
` (${filteredJoinRequests?.filter((group) => group?.data?.length > 0)?.length})`}
|
||||
</Typography>
|
||||
{isExpanded ? <ExpandLessIcon sx={{
|
||||
marginLeft: 'auto'
|
||||
}} /> : (
|
||||
<ExpandMoreIcon sx={{
|
||||
marginLeft: 'auto'
|
||||
}}/>
|
||||
)}
|
||||
{isExpanded ? (
|
||||
<ExpandLessIcon
|
||||
sx={{
|
||||
marginLeft: 'auto',
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<ExpandMoreIcon
|
||||
sx={{
|
||||
marginLeft: 'auto',
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</ButtonBase>
|
||||
<Collapse in={isExpanded} timeout="auto" unmountOnExit>
|
||||
<Box
|
||||
sx={{
|
||||
width: "322px",
|
||||
height: isMobile ? "165px" : "250px",
|
||||
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
bgcolor: "background.paper",
|
||||
padding: "20px",
|
||||
borderRadius: '19px'
|
||||
}}
|
||||
>
|
||||
{loading && filteredJoinRequests.length === 0 && (
|
||||
<Box sx={{
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
justifyContent: 'center'
|
||||
}}>
|
||||
<CustomLoader />
|
||||
</Box>
|
||||
)}
|
||||
{!loading && (filteredJoinRequests.length === 0 || filteredJoinRequests?.filter((group)=> group?.data?.length > 0).length === 0) && (
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: 'center',
|
||||
height: '100%',
|
||||
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: "11px",
|
||||
fontWeight: 400,
|
||||
color: 'rgba(255, 255, 255, 0.2)'
|
||||
}}
|
||||
>
|
||||
Nothing to display
|
||||
</Typography>
|
||||
</Box>
|
||||
)}
|
||||
<List className="scrollable-container" sx={{ width: "100%", maxWidth: 360, bgcolor: "background.paper", maxHeight: '300px', overflow: 'auto' }}>
|
||||
{filteredJoinRequests?.map((group)=> {
|
||||
if(group?.data?.length === 0) return null
|
||||
return (
|
||||
<ListItem
|
||||
key={group?.groupId}
|
||||
onClick={()=> {
|
||||
setSelectedGroup(group?.group)
|
||||
setMobileViewMode('group')
|
||||
getTimestampEnterChat()
|
||||
setGroupSection("announcement")
|
||||
setOpenManageMembers(true)
|
||||
if(!isMobile){
|
||||
setDesktopViewMode('chat')
|
||||
}
|
||||
setTimeout(() => {
|
||||
executeEvent("openGroupJoinRequest", {});
|
||||
|
||||
}, 300);
|
||||
}}
|
||||
<Collapse in={isExpanded} timeout="auto" unmountOnExit>
|
||||
<Box
|
||||
sx={{
|
||||
marginBottom: '20px'
|
||||
}}
|
||||
disablePadding
|
||||
secondaryAction={
|
||||
<IconButton edge="end" aria-label="comments">
|
||||
<GroupAddIcon
|
||||
sx={{
|
||||
color: "white",
|
||||
fontSize: '18px'
|
||||
}}
|
||||
/>
|
||||
</IconButton>
|
||||
}
|
||||
>
|
||||
<ListItemButton sx={{
|
||||
padding: "0px",
|
||||
}} disableRipple role={undefined} dense>
|
||||
|
||||
<ListItemText sx={{
|
||||
"& .MuiTypography-root": {
|
||||
fontSize: "13px",
|
||||
fontWeight: 400,
|
||||
},
|
||||
}} primary={`${group?.group?.groupName} has ${group?.data?.length} pending join requests.`} />
|
||||
</ListItemButton>
|
||||
</ListItem>
|
||||
)
|
||||
width: '322px',
|
||||
height: '250px',
|
||||
|
||||
})}
|
||||
|
||||
|
||||
|
||||
</List>
|
||||
</Box>
|
||||
</Collapse>
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
bgcolor: 'background.paper',
|
||||
padding: '20px',
|
||||
borderRadius: '19px',
|
||||
}}
|
||||
>
|
||||
{loading && filteredJoinRequests.length === 0 && (
|
||||
<Box
|
||||
sx={{
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
>
|
||||
<CustomLoader />
|
||||
</Box>
|
||||
)}
|
||||
{!loading &&
|
||||
(filteredJoinRequests.length === 0 ||
|
||||
filteredJoinRequests?.filter((group) => group?.data?.length > 0)
|
||||
.length === 0) && (
|
||||
<Box
|
||||
sx={{
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
height: '100%',
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: '11px',
|
||||
fontWeight: 400,
|
||||
color: 'rgba(255, 255, 255, 0.2)',
|
||||
}}
|
||||
>
|
||||
Nothing to display
|
||||
</Typography>
|
||||
</Box>
|
||||
)}
|
||||
<List
|
||||
className="scrollable-container"
|
||||
sx={{
|
||||
width: '100%',
|
||||
maxWidth: 360,
|
||||
bgcolor: 'background.paper',
|
||||
maxHeight: '300px',
|
||||
overflow: 'auto',
|
||||
}}
|
||||
>
|
||||
{filteredJoinRequests?.map((group) => {
|
||||
if (group?.data?.length === 0) return null;
|
||||
return (
|
||||
<ListItem
|
||||
key={group?.groupId}
|
||||
onClick={() => {
|
||||
setSelectedGroup(group?.group);
|
||||
setMobileViewMode('group');
|
||||
getTimestampEnterChat();
|
||||
setGroupSection('announcement');
|
||||
setOpenManageMembers(true);
|
||||
setDesktopViewMode('chat');
|
||||
setTimeout(() => {
|
||||
executeEvent('openGroupJoinRequest', {});
|
||||
}, 300);
|
||||
}}
|
||||
sx={{
|
||||
marginBottom: '20px',
|
||||
}}
|
||||
disablePadding
|
||||
secondaryAction={
|
||||
<IconButton edge="end" aria-label="comments">
|
||||
<GroupAddIcon
|
||||
sx={{
|
||||
color: 'white',
|
||||
fontSize: '18px',
|
||||
}}
|
||||
/>
|
||||
</IconButton>
|
||||
}
|
||||
>
|
||||
<ListItemButton
|
||||
sx={{
|
||||
padding: '0px',
|
||||
}}
|
||||
disableRipple
|
||||
role={undefined}
|
||||
dense
|
||||
>
|
||||
<ListItemText
|
||||
sx={{
|
||||
'& .MuiTypography-root': {
|
||||
fontSize: '13px',
|
||||
fontWeight: 400,
|
||||
},
|
||||
}}
|
||||
primary={`${group?.group?.groupName} has ${group?.data?.length} pending join requests.`}
|
||||
/>
|
||||
</ListItemButton>
|
||||
</ListItem>
|
||||
);
|
||||
})}
|
||||
</List>
|
||||
</Box>
|
||||
</Collapse>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
@ -16,19 +16,12 @@ import {
|
||||
DialogContent,
|
||||
DialogContentText,
|
||||
DialogTitle,
|
||||
ListItem,
|
||||
ListItemAvatar,
|
||||
ListItemButton,
|
||||
ListItemText,
|
||||
MenuItem,
|
||||
Popover,
|
||||
Select,
|
||||
TextField,
|
||||
Typography,
|
||||
} from '@mui/material';
|
||||
|
||||
import { getNameInfo } from './Group';
|
||||
import { getBaseApi, getFee } from '../../background';
|
||||
import { LoadingButton } from '@mui/lab';
|
||||
import LockIcon from '@mui/icons-material/Lock';
|
||||
import NoEncryptionGmailerrorredIcon from '@mui/icons-material/NoEncryptionGmailerrorred';
|
||||
@ -36,7 +29,6 @@ import {
|
||||
MyContext,
|
||||
getArbitraryEndpointReact,
|
||||
getBaseApiReact,
|
||||
isMobile,
|
||||
} from '../../App';
|
||||
import { Spacer } from '../../common/Spacer';
|
||||
import { CustomLoader } from '../../common/CustomLoader';
|
||||
@ -51,7 +43,6 @@ import { Label } from './AddGroup';
|
||||
import ShortUniqueId from 'short-unique-id';
|
||||
import { CustomizedSnackbars } from '../Snackbar/Snackbar';
|
||||
import { getGroupNames } from './UserListOfInvites';
|
||||
import { WrapperUserAction } from '../WrapperUserAction';
|
||||
import { useVirtualizer } from '@tanstack/react-virtual';
|
||||
import ErrorBoundary from '../../common/ErrorBoundary';
|
||||
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
|
||||
@ -390,6 +381,7 @@ export const ListOfGroupPromotions = () => {
|
||||
/>
|
||||
)}
|
||||
</ButtonBase>
|
||||
|
||||
<Box
|
||||
style={{
|
||||
width: '330px',
|
||||
@ -401,7 +393,7 @@ export const ListOfGroupPromotions = () => {
|
||||
<>
|
||||
<Box
|
||||
sx={{
|
||||
width: isMobile ? '320px' : '750px',
|
||||
width: '750px',
|
||||
maxWidth: '90%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
@ -422,6 +414,7 @@ export const ListOfGroupPromotions = () => {
|
||||
fontWeight: 600,
|
||||
}}
|
||||
></Typography>
|
||||
|
||||
<Button
|
||||
variant="contained"
|
||||
onClick={() => setIsShowModal(true)}
|
||||
@ -432,18 +425,19 @@ export const ListOfGroupPromotions = () => {
|
||||
Add Promotion
|
||||
</Button>
|
||||
</Box>
|
||||
|
||||
<Spacer height="10px" />
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
width: isMobile ? '320px' : '750px',
|
||||
maxWidth: '90%',
|
||||
maxHeight: '700px',
|
||||
bgcolor: 'background.paper',
|
||||
borderRadius: '19px',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
bgcolor: 'background.paper',
|
||||
maxHeight: '700px',
|
||||
maxWidth: '90%',
|
||||
padding: '20px 0px',
|
||||
borderRadius: '19px',
|
||||
width: '750px',
|
||||
}}
|
||||
>
|
||||
{loading && promotions.length === 0 && (
|
||||
@ -589,6 +583,7 @@ export const ListOfGroupPromotions = () => {
|
||||
>
|
||||
Group name: {` ${promotion?.groupName}`}
|
||||
</Typography>
|
||||
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: '13px',
|
||||
@ -598,6 +593,7 @@ export const ListOfGroupPromotions = () => {
|
||||
Number of members:{' '}
|
||||
{` ${promotion?.memberCount}`}
|
||||
</Typography>
|
||||
|
||||
{promotion?.description && (
|
||||
<Typography
|
||||
sx={{
|
||||
@ -608,6 +604,7 @@ export const ListOfGroupPromotions = () => {
|
||||
{promotion?.description}
|
||||
</Typography>
|
||||
)}
|
||||
|
||||
{promotion?.isOpen === false && (
|
||||
<Typography
|
||||
sx={{
|
||||
@ -620,7 +617,9 @@ export const ListOfGroupPromotions = () => {
|
||||
your request
|
||||
</Typography>
|
||||
)}
|
||||
|
||||
<Spacer height="5px" />
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
@ -638,6 +637,7 @@ export const ListOfGroupPromotions = () => {
|
||||
>
|
||||
Close
|
||||
</LoadingButton>
|
||||
|
||||
<LoadingButton
|
||||
loading={isLoadingJoinGroup}
|
||||
loadingPosition="start"
|
||||
@ -682,6 +682,7 @@ export const ListOfGroupPromotions = () => {
|
||||
>
|
||||
{promotion?.name?.charAt(0)}
|
||||
</Avatar>
|
||||
|
||||
<Typography
|
||||
sx={{
|
||||
fontWight: 600,
|
||||
@ -692,6 +693,7 @@ export const ListOfGroupPromotions = () => {
|
||||
{promotion?.name}
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Typography
|
||||
sx={{
|
||||
fontWight: 600,
|
||||
@ -702,7 +704,9 @@ export const ListOfGroupPromotions = () => {
|
||||
{promotion?.groupName}
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Spacer height="20px" />
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
@ -735,7 +739,9 @@ export const ListOfGroupPromotions = () => {
|
||||
: 'Private group'}
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Spacer height="20px" />
|
||||
|
||||
<Typography
|
||||
sx={{
|
||||
fontWight: 600,
|
||||
@ -745,7 +751,9 @@ export const ListOfGroupPromotions = () => {
|
||||
>
|
||||
{promotion?.data}
|
||||
</Typography>
|
||||
|
||||
<Spacer height="20px" />
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
@ -767,6 +775,7 @@ export const ListOfGroupPromotions = () => {
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
<Spacer height="50px" />
|
||||
</ErrorBoundary>
|
||||
</div>
|
||||
@ -779,6 +788,7 @@ export const ListOfGroupPromotions = () => {
|
||||
</Box>
|
||||
</>
|
||||
</Collapse>
|
||||
|
||||
<Spacer height="20px" />
|
||||
|
||||
{isShowModal && (
|
||||
|
@ -1,21 +1,14 @@
|
||||
import * as React from "react";
|
||||
import List from "@mui/material/List";
|
||||
import ListItem from "@mui/material/ListItem";
|
||||
import ListItemButton from "@mui/material/ListItemButton";
|
||||
import ListItemIcon from "@mui/material/ListItemIcon";
|
||||
import ListItemText from "@mui/material/ListItemText";
|
||||
import Checkbox from "@mui/material/Checkbox";
|
||||
import IconButton from "@mui/material/IconButton";
|
||||
import CommentIcon from "@mui/icons-material/Comment";
|
||||
import InfoIcon from "@mui/icons-material/Info";
|
||||
import GroupAddIcon from "@mui/icons-material/GroupAdd";
|
||||
import { executeEvent } from "../../utils/events";
|
||||
import { Box, Typography } from "@mui/material";
|
||||
import { Spacer } from "../../common/Spacer";
|
||||
import { getGroupNames } from "./UserListOfInvites";
|
||||
import { CustomLoader } from "../../common/CustomLoader";
|
||||
import VisibilityIcon from "@mui/icons-material/Visibility";
|
||||
import { isMobile } from "../../App";
|
||||
import * as React from 'react';
|
||||
import List from '@mui/material/List';
|
||||
import ListItem from '@mui/material/ListItem';
|
||||
import ListItemButton from '@mui/material/ListItemButton';
|
||||
import ListItemText from '@mui/material/ListItemText';
|
||||
import IconButton from '@mui/material/IconButton';
|
||||
import { executeEvent } from '../../utils/events';
|
||||
import { Box, Typography } from '@mui/material';
|
||||
import { Spacer } from '../../common/Spacer';
|
||||
import { CustomLoader } from '../../common/CustomLoader';
|
||||
import VisibilityIcon from '@mui/icons-material/Visibility';
|
||||
|
||||
export const ListOfThreadPostsWatched = () => {
|
||||
const [posts, setPosts] = React.useState([]);
|
||||
@ -24,33 +17,33 @@ export const ListOfThreadPostsWatched = () => {
|
||||
const getPosts = async () => {
|
||||
try {
|
||||
await new Promise((res, rej) => {
|
||||
window.sendMessage("getThreadActivity", {})
|
||||
.then((response) => {
|
||||
if (!response?.error) {
|
||||
if (!response) {
|
||||
res(null);
|
||||
window
|
||||
.sendMessage('getThreadActivity', {})
|
||||
.then((response) => {
|
||||
if (!response?.error) {
|
||||
if (!response) {
|
||||
res(null);
|
||||
return;
|
||||
}
|
||||
const uniquePosts = response.reduce((acc, current) => {
|
||||
const x = acc.find(
|
||||
(item) => item?.thread?.threadId === current?.thread?.threadId
|
||||
);
|
||||
if (!x) {
|
||||
return acc.concat([current]);
|
||||
} else {
|
||||
return acc;
|
||||
}
|
||||
}, []);
|
||||
setPosts(uniquePosts);
|
||||
res(uniquePosts);
|
||||
return;
|
||||
}
|
||||
const uniquePosts = response.reduce((acc, current) => {
|
||||
const x = acc.find(
|
||||
(item) => item?.thread?.threadId === current?.thread?.threadId
|
||||
);
|
||||
if (!x) {
|
||||
return acc.concat([current]);
|
||||
} else {
|
||||
return acc;
|
||||
}
|
||||
}, []);
|
||||
setPosts(uniquePosts);
|
||||
res(uniquePosts);
|
||||
return;
|
||||
}
|
||||
rej(response.error);
|
||||
})
|
||||
.catch((error) => {
|
||||
rej(error.message || "An error occurred");
|
||||
});
|
||||
|
||||
rej(response.error);
|
||||
})
|
||||
.catch((error) => {
|
||||
rej(error.message || 'An error occurred');
|
||||
});
|
||||
});
|
||||
} catch (error) {
|
||||
} finally {
|
||||
@ -63,49 +56,50 @@ export const ListOfThreadPostsWatched = () => {
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Box sx={{
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: 'center'
|
||||
}}>
|
||||
<Box
|
||||
sx={{
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
width: "322px",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
width: '322px',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
padding: '0px 20px',
|
||||
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: "13px",
|
||||
fontSize: '13px',
|
||||
fontWeight: 600,
|
||||
}}
|
||||
>
|
||||
New Thread Posts:
|
||||
</Typography>
|
||||
<Spacer height="10px" />
|
||||
<Spacer height="10px" />
|
||||
</Box>
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
width: "322px",
|
||||
height: isMobile ? "165px" : "250px",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
bgcolor: "background.paper",
|
||||
padding: "20px",
|
||||
borderRadius: '19px'
|
||||
bgcolor: 'background.paper',
|
||||
borderRadius: '19px',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
height: '250px',
|
||||
padding: '20px',
|
||||
width: '322px',
|
||||
}}
|
||||
>
|
||||
{loading && posts.length === 0 && (
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
>
|
||||
<CustomLoader />
|
||||
@ -114,19 +108,18 @@ export const ListOfThreadPostsWatched = () => {
|
||||
{!loading && posts.length === 0 && (
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
height: '100%',
|
||||
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: "11px",
|
||||
fontSize: '11px',
|
||||
fontWeight: 400,
|
||||
color: 'rgba(255, 255, 255, 0.2)'
|
||||
color: 'rgba(255, 255, 255, 0.2)',
|
||||
}}
|
||||
>
|
||||
Nothing to display
|
||||
@ -134,47 +127,46 @@ export const ListOfThreadPostsWatched = () => {
|
||||
</Box>
|
||||
)}
|
||||
{posts?.length > 0 && (
|
||||
<List
|
||||
className="scrollable-container"
|
||||
sx={{
|
||||
width: "100%",
|
||||
maxWidth: 360,
|
||||
bgcolor: "background.paper",
|
||||
maxHeight: "300px",
|
||||
overflow: "auto",
|
||||
}}
|
||||
>
|
||||
{posts?.map((post) => {
|
||||
return (
|
||||
<ListItem
|
||||
key={post?.thread?.threadId}
|
||||
onClick={() => {
|
||||
executeEvent("openThreadNewPost", {
|
||||
data: post,
|
||||
});
|
||||
}}
|
||||
disablePadding
|
||||
secondaryAction={
|
||||
<IconButton edge="end" aria-label="comments">
|
||||
<VisibilityIcon
|
||||
sx={{
|
||||
color: "red",
|
||||
}}
|
||||
/>
|
||||
</IconButton>
|
||||
}
|
||||
>
|
||||
<ListItemButton disableRipple role={undefined} dense>
|
||||
<ListItemText
|
||||
primary={`New post in ${post?.thread?.threadData?.title}`}
|
||||
/>
|
||||
</ListItemButton>
|
||||
</ListItem>
|
||||
);
|
||||
})}
|
||||
</List>
|
||||
<List
|
||||
className="scrollable-container"
|
||||
sx={{
|
||||
width: '100%',
|
||||
maxWidth: 360,
|
||||
bgcolor: 'background.paper',
|
||||
maxHeight: '300px',
|
||||
overflow: 'auto',
|
||||
}}
|
||||
>
|
||||
{posts?.map((post) => {
|
||||
return (
|
||||
<ListItem
|
||||
key={post?.thread?.threadId}
|
||||
onClick={() => {
|
||||
executeEvent('openThreadNewPost', {
|
||||
data: post,
|
||||
});
|
||||
}}
|
||||
disablePadding
|
||||
secondaryAction={
|
||||
<IconButton edge="end" aria-label="comments">
|
||||
<VisibilityIcon
|
||||
sx={{
|
||||
color: 'red',
|
||||
}}
|
||||
/>
|
||||
</IconButton>
|
||||
}
|
||||
>
|
||||
<ListItemButton disableRipple role={undefined} dense>
|
||||
<ListItemText
|
||||
primary={`New post in ${post?.thread?.threadData?.title}`}
|
||||
/>
|
||||
</ListItemButton>
|
||||
</ListItem>
|
||||
);
|
||||
})}
|
||||
</List>
|
||||
)}
|
||||
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
|
@ -1,10 +1,6 @@
|
||||
import * as React from 'react';
|
||||
import Button from '@mui/material/Button';
|
||||
import Dialog from '@mui/material/Dialog';
|
||||
import ListItemText from '@mui/material/ListItemText';
|
||||
import ListItemButton from '@mui/material/ListItemButton';
|
||||
import List from '@mui/material/List';
|
||||
import Divider from '@mui/material/Divider';
|
||||
import AppBar from '@mui/material/AppBar';
|
||||
import Toolbar from '@mui/material/Toolbar';
|
||||
import IconButton from '@mui/material/IconButton';
|
||||
@ -19,7 +15,7 @@ import { ListOfBans } from './ListOfBans';
|
||||
import { ListOfJoinRequests } from './ListOfJoinRequests';
|
||||
import { Box, ButtonBase, Card, Tab, Tabs } from '@mui/material';
|
||||
import { CustomizedSnackbars } from '../Snackbar/Snackbar';
|
||||
import { MyContext, getBaseApiReact, isMobile } from '../../App';
|
||||
import { MyContext, getBaseApiReact } from '../../App';
|
||||
import { getGroupMembers, getNames } from './Group';
|
||||
import { LoadingSnackbar } from '../Snackbar/LoadingSnackbar';
|
||||
import { getFee } from '../../background';
|
||||
@ -27,6 +23,7 @@ import { LoadingButton } from '@mui/lab';
|
||||
import { subscribeToEvent, unsubscribeFromEvent } from '../../utils/events';
|
||||
import { Spacer } from '../../common/Spacer';
|
||||
import InsertLinkIcon from '@mui/icons-material/InsertLink';
|
||||
|
||||
function a11yProps(index: number) {
|
||||
return {
|
||||
id: `simple-tab-${index}`,
|
||||
@ -193,9 +190,9 @@ export const ManageMembers = ({
|
||||
<Box
|
||||
sx={{
|
||||
bgcolor: '#27282c',
|
||||
color: 'white',
|
||||
flexGrow: 1,
|
||||
overflowY: 'auto',
|
||||
color: 'white',
|
||||
}}
|
||||
>
|
||||
<Box sx={{ borderBottom: 1, borderColor: 'divider' }}>
|
||||
@ -221,9 +218,10 @@ export const ManageMembers = ({
|
||||
'&.Mui-selected': {
|
||||
color: 'white',
|
||||
},
|
||||
fontSize: isMobile ? '0.75rem' : '1rem', // Adjust font size for mobile
|
||||
fontSize: '1rem',
|
||||
}}
|
||||
/>
|
||||
|
||||
<Tab
|
||||
label="Invite new member"
|
||||
{...a11yProps(1)}
|
||||
@ -231,9 +229,10 @@ export const ManageMembers = ({
|
||||
'&.Mui-selected': {
|
||||
color: 'white',
|
||||
},
|
||||
fontSize: isMobile ? '0.75rem' : '1rem',
|
||||
fontSize: '1rem',
|
||||
}}
|
||||
/>
|
||||
|
||||
<Tab
|
||||
label="List of invites"
|
||||
{...a11yProps(2)}
|
||||
@ -241,9 +240,10 @@ export const ManageMembers = ({
|
||||
'&.Mui-selected': {
|
||||
color: 'white',
|
||||
},
|
||||
fontSize: isMobile ? '0.75rem' : '1rem',
|
||||
fontSize: '1rem',
|
||||
}}
|
||||
/>
|
||||
|
||||
<Tab
|
||||
label="List of bans"
|
||||
{...a11yProps(3)}
|
||||
@ -251,9 +251,10 @@ export const ManageMembers = ({
|
||||
'&.Mui-selected': {
|
||||
color: 'white',
|
||||
},
|
||||
fontSize: isMobile ? '0.75rem' : '1rem',
|
||||
fontSize: '1rem',
|
||||
}}
|
||||
/>
|
||||
|
||||
<Tab
|
||||
label="Join requests"
|
||||
{...a11yProps(4)}
|
||||
@ -261,11 +262,12 @@ export const ManageMembers = ({
|
||||
'&.Mui-selected': {
|
||||
color: 'white',
|
||||
},
|
||||
fontSize: isMobile ? '0.75rem' : '1rem',
|
||||
fontSize: '1rem',
|
||||
}}
|
||||
/>
|
||||
</Tabs>
|
||||
</Box>
|
||||
|
||||
<Card
|
||||
sx={{
|
||||
padding: '10px',
|
||||
@ -274,10 +276,13 @@ export const ManageMembers = ({
|
||||
>
|
||||
<Box>
|
||||
<Typography>GroupId: {groupInfo?.groupId}</Typography>
|
||||
|
||||
<Typography>GroupName: {groupInfo?.groupName}</Typography>
|
||||
|
||||
<Typography>
|
||||
Number of members: {groupInfo?.memberCount}
|
||||
</Typography>
|
||||
|
||||
<ButtonBase
|
||||
sx={{
|
||||
gap: '10px',
|
||||
@ -290,7 +295,9 @@ export const ManageMembers = ({
|
||||
<InsertLinkIcon /> <Typography>Join Group Link</Typography>
|
||||
</ButtonBase>
|
||||
</Box>
|
||||
|
||||
<Spacer height="20px" />
|
||||
|
||||
{selectedGroup?.groupId && !isOwner && (
|
||||
<LoadingButton
|
||||
size="small"
|
||||
@ -317,7 +324,9 @@ export const ManageMembers = ({
|
||||
>
|
||||
Load members with names
|
||||
</Button>
|
||||
|
||||
<Spacer height="10px" />
|
||||
|
||||
<ListOfMembers
|
||||
members={membersWithNames || []}
|
||||
groupId={selectedGroup?.groupId}
|
||||
@ -397,12 +406,14 @@ export const ManageMembers = ({
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
<CustomizedSnackbars
|
||||
open={openSnack}
|
||||
setOpen={setOpenSnack}
|
||||
info={infoSnack}
|
||||
setInfo={setInfoSnack}
|
||||
/>
|
||||
|
||||
<LoadingSnackbar
|
||||
open={isLoadingMembers}
|
||||
info={{
|
||||
|
@ -6,7 +6,7 @@ import ListItemIcon from '@mui/material/ListItemIcon';
|
||||
import ListItemText from '@mui/material/ListItemText';
|
||||
import moment from 'moment';
|
||||
import { Box, ButtonBase, Collapse, Typography, useTheme } from '@mui/material';
|
||||
import { getBaseApiReact, isMobile } from '../../App';
|
||||
import { getBaseApiReact } from '../../App';
|
||||
import MailIcon from '@mui/icons-material/Mail';
|
||||
import MailOutlineIcon from '@mui/icons-material/MailOutline';
|
||||
import { executeEvent } from '../../utils/events';
|
||||
@ -180,7 +180,7 @@ export const QMailMessages = ({ userName, userAddress }) => {
|
||||
borderRadius: '19px',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
height: isMobile ? '165px' : '250px',
|
||||
height: '250px',
|
||||
overflow: 'auto',
|
||||
padding: '20px',
|
||||
width: '322px',
|
||||
|
@ -1,28 +1,23 @@
|
||||
import * as React from "react";
|
||||
import List from "@mui/material/List";
|
||||
import ListItem from "@mui/material/ListItem";
|
||||
import ListItemButton from "@mui/material/ListItemButton";
|
||||
import ListItemIcon from "@mui/material/ListItemIcon";
|
||||
import ListItemText from "@mui/material/ListItemText";
|
||||
import Checkbox from "@mui/material/Checkbox";
|
||||
import IconButton from "@mui/material/IconButton";
|
||||
import CommentIcon from "@mui/icons-material/Comment";
|
||||
import InfoIcon from "@mui/icons-material/Info";
|
||||
import { Box, Typography } from "@mui/material";
|
||||
import { Spacer } from "../../common/Spacer";
|
||||
import { isMobile } from "../../App";
|
||||
import { QMailMessages } from "./QMailMessages";
|
||||
import { executeEvent } from "../../utils/events";
|
||||
import * as React from 'react';
|
||||
import List from '@mui/material/List';
|
||||
import ListItem from '@mui/material/ListItem';
|
||||
import ListItemButton from '@mui/material/ListItemButton';
|
||||
import ListItemIcon from '@mui/material/ListItemIcon';
|
||||
import ListItemText from '@mui/material/ListItemText';
|
||||
import { Box, Typography } from '@mui/material';
|
||||
import { Spacer } from '../../common/Spacer';
|
||||
import { QMailMessages } from './QMailMessages';
|
||||
import { executeEvent } from '../../utils/events';
|
||||
|
||||
export const ThingsToDoInitial = ({ myAddress, name, hasGroups, balance, userInfo }) => {
|
||||
export const ThingsToDoInitial = ({
|
||||
myAddress,
|
||||
name,
|
||||
hasGroups,
|
||||
balance,
|
||||
userInfo,
|
||||
}) => {
|
||||
const [checked1, setChecked1] = React.useState(false);
|
||||
const [checked2, setChecked2] = React.useState(false);
|
||||
// const [checked3, setChecked3] = React.useState(false);
|
||||
|
||||
// React.useEffect(() => {
|
||||
// if (hasGroups) setChecked3(true);
|
||||
// }, [hasGroups]);
|
||||
|
||||
|
||||
React.useEffect(() => {
|
||||
if (balance && +balance >= 6) {
|
||||
@ -30,111 +25,114 @@ export const ThingsToDoInitial = ({ myAddress, name, hasGroups, balance, userInf
|
||||
}
|
||||
}, [balance]);
|
||||
|
||||
|
||||
React.useEffect(() => {
|
||||
if (name) setChecked2(true);
|
||||
}, [name]);
|
||||
|
||||
const isLoaded = React.useMemo(() => {
|
||||
if (userInfo !== null) return true;
|
||||
return false;
|
||||
}, [userInfo]);
|
||||
|
||||
const isLoaded = React.useMemo(()=> {
|
||||
if(userInfo !== null) return true
|
||||
return false
|
||||
}, [ userInfo])
|
||||
const hasDoneNameAndBalanceAndIsLoaded = React.useMemo(() => {
|
||||
if (isLoaded && checked1 && checked2) return true;
|
||||
return false;
|
||||
}, [checked1, isLoaded, checked2]);
|
||||
|
||||
const hasDoneNameAndBalanceAndIsLoaded = React.useMemo(()=> {
|
||||
if(isLoaded && checked1 && checked2) return true
|
||||
return false
|
||||
}, [checked1, isLoaded, checked2])
|
||||
|
||||
if(hasDoneNameAndBalanceAndIsLoaded){
|
||||
return (
|
||||
<QMailMessages userAddress={userInfo?.address} userName={userInfo?.name} />
|
||||
);
|
||||
}
|
||||
if(!isLoaded) return null
|
||||
if (hasDoneNameAndBalanceAndIsLoaded) {
|
||||
return (
|
||||
<QMailMessages
|
||||
userAddress={userInfo?.address}
|
||||
userName={userInfo?.name}
|
||||
/>
|
||||
);
|
||||
}
|
||||
if (!isLoaded) return null;
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
width: "322px",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
padding: "0px 20px",
|
||||
width: '322px',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
padding: '0px 20px',
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: "1rem",
|
||||
fontSize: '1rem',
|
||||
fontWeight: 600,
|
||||
}}
|
||||
>
|
||||
{!isLoaded ? 'Loading...' : 'Getting Started' }
|
||||
{!isLoaded ? 'Loading...' : 'Getting Started'}
|
||||
</Typography>
|
||||
|
||||
<Spacer height="10px" />
|
||||
</Box>
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
width: "322px",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
bgcolor: "background.paper",
|
||||
padding: "20px",
|
||||
borderRadius: "19px",
|
||||
width: '322px',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
bgcolor: 'background.paper',
|
||||
padding: '20px',
|
||||
borderRadius: '19px',
|
||||
}}
|
||||
>
|
||||
{isLoaded && (
|
||||
<List sx={{ width: "100%", maxWidth: 360 }}>
|
||||
<ListItem
|
||||
|
||||
disablePadding
|
||||
sx={{
|
||||
marginBottom: '20px'
|
||||
}}
|
||||
>
|
||||
<ListItemButton
|
||||
sx={{
|
||||
padding: "0px",
|
||||
}}
|
||||
disableRipple
|
||||
role={undefined}
|
||||
dense
|
||||
onClick={()=> {
|
||||
executeEvent("openBuyQortInfo", {})
|
||||
}}
|
||||
>
|
||||
<ListItemText
|
||||
sx={{
|
||||
"& .MuiTypography-root": {
|
||||
fontSize: "1rem",
|
||||
fontWeight: 400,
|
||||
},
|
||||
}}
|
||||
primary={`Have at least 6 QORT in your wallet`}
|
||||
/>
|
||||
<ListItemIcon
|
||||
sx={{
|
||||
justifyContent: "flex-end",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
height: "18px",
|
||||
width: "18px",
|
||||
borderRadius: "50%",
|
||||
backgroundColor: checked1 ? "rgba(9, 182, 232, 1)" : "transparent",
|
||||
outline: "1px solid rgba(9, 182, 232, 1)",
|
||||
}}
|
||||
/>
|
||||
{/* <Checkbox
|
||||
<List sx={{ width: '100%', maxWidth: 360 }}>
|
||||
<ListItem
|
||||
disablePadding
|
||||
sx={{
|
||||
marginBottom: '20px',
|
||||
}}
|
||||
>
|
||||
<ListItemButton
|
||||
sx={{
|
||||
padding: '0px',
|
||||
}}
|
||||
disableRipple
|
||||
role={undefined}
|
||||
dense
|
||||
onClick={() => {
|
||||
executeEvent('openBuyQortInfo', {});
|
||||
}}
|
||||
>
|
||||
<ListItemText
|
||||
sx={{
|
||||
'& .MuiTypography-root': {
|
||||
fontSize: '1rem',
|
||||
fontWeight: 400,
|
||||
},
|
||||
}}
|
||||
primary={`Have at least 6 QORT in your wallet`}
|
||||
/>
|
||||
<ListItemIcon
|
||||
sx={{
|
||||
justifyContent: 'flex-end',
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
height: '18px',
|
||||
width: '18px',
|
||||
borderRadius: '50%',
|
||||
backgroundColor: checked1
|
||||
? 'rgba(9, 182, 232, 1)'
|
||||
: 'transparent',
|
||||
outline: '1px solid rgba(9, 182, 232, 1)',
|
||||
}}
|
||||
/>
|
||||
{/* <Checkbox
|
||||
edge="start"
|
||||
checked={checked1}
|
||||
tabIndex={-1}
|
||||
@ -149,52 +147,64 @@ if(!isLoaded) return null
|
||||
},
|
||||
}}
|
||||
/> */}
|
||||
</ListItemIcon>
|
||||
</ListItemButton>
|
||||
</ListItem>
|
||||
<ListItem
|
||||
sx={{
|
||||
marginBottom: '20px'
|
||||
}}
|
||||
// secondaryAction={
|
||||
// <IconButton edge="end" aria-label="comments">
|
||||
// <InfoIcon
|
||||
// sx={{
|
||||
// color: "white",
|
||||
// }}
|
||||
// />
|
||||
// </IconButton>
|
||||
// }
|
||||
disablePadding
|
||||
>
|
||||
<ListItemButton sx={{
|
||||
padding: "0px",
|
||||
}} disableRipple role={undefined} dense>
|
||||
|
||||
<ListItemText onClick={() => {
|
||||
executeEvent('openRegisterName', {})
|
||||
}} sx={{
|
||||
"& .MuiTypography-root": {
|
||||
fontSize: "1rem",
|
||||
fontWeight: 400,
|
||||
},
|
||||
}} primary={`Register a name`} />
|
||||
<ListItemIcon sx={{
|
||||
justifyContent: "flex-end",
|
||||
}}>
|
||||
<Box
|
||||
sx={{
|
||||
height: "18px",
|
||||
width: "18px",
|
||||
borderRadius: "50%",
|
||||
backgroundColor: checked2 ? "rgba(9, 182, 232, 1)" : "transparent",
|
||||
outline: "1px solid rgba(9, 182, 232, 1)",
|
||||
}}
|
||||
/>
|
||||
</ListItemIcon>
|
||||
</ListItemButton>
|
||||
</ListItem>
|
||||
{/* <ListItem
|
||||
</ListItemIcon>
|
||||
</ListItemButton>
|
||||
</ListItem>
|
||||
<ListItem
|
||||
sx={{
|
||||
marginBottom: '20px',
|
||||
}}
|
||||
// secondaryAction={
|
||||
// <IconButton edge="end" aria-label="comments">
|
||||
// <InfoIcon
|
||||
// sx={{
|
||||
// color: "white",
|
||||
// }}
|
||||
// />
|
||||
// </IconButton>
|
||||
// }
|
||||
disablePadding
|
||||
>
|
||||
<ListItemButton
|
||||
sx={{
|
||||
padding: '0px',
|
||||
}}
|
||||
disableRipple
|
||||
role={undefined}
|
||||
dense
|
||||
>
|
||||
<ListItemText
|
||||
onClick={() => {
|
||||
executeEvent('openRegisterName', {});
|
||||
}}
|
||||
sx={{
|
||||
'& .MuiTypography-root': {
|
||||
fontSize: '1rem',
|
||||
fontWeight: 400,
|
||||
},
|
||||
}}
|
||||
primary={`Register a name`}
|
||||
/>
|
||||
<ListItemIcon
|
||||
sx={{
|
||||
justifyContent: 'flex-end',
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
height: '18px',
|
||||
width: '18px',
|
||||
borderRadius: '50%',
|
||||
backgroundColor: checked2
|
||||
? 'rgba(9, 182, 232, 1)'
|
||||
: 'transparent',
|
||||
outline: '1px solid rgba(9, 182, 232, 1)',
|
||||
}}
|
||||
/>
|
||||
</ListItemIcon>
|
||||
</ListItemButton>
|
||||
</ListItem>
|
||||
{/* <ListItem
|
||||
disablePadding
|
||||
>
|
||||
<ListItemButton sx={{
|
||||
@ -222,9 +232,8 @@ if(!isLoaded) return null
|
||||
</ListItemIcon>
|
||||
</ListItemButton>
|
||||
</ListItem> */}
|
||||
</List>
|
||||
</List>
|
||||
)}
|
||||
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
|
@ -1,9 +1,8 @@
|
||||
import React, { useState, useRef, useEffect } from 'react';
|
||||
import { useState, useRef, useEffect } from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import Picker, { EmojiStyle, Theme } from 'emoji-picker-react';
|
||||
import './ReactionPicker.css';
|
||||
import { ButtonBase } from '@mui/material';
|
||||
import { isMobile } from '../App';
|
||||
|
||||
export const ReactionPicker = ({ onReaction }) => {
|
||||
const [showPicker, setShowPicker] = useState(false);
|
||||
@ -30,7 +29,7 @@ export const ReactionPicker = ({ onReaction }) => {
|
||||
} else {
|
||||
// Get the button's position
|
||||
const buttonRect = buttonRef.current.getBoundingClientRect();
|
||||
const pickerWidth = isMobile ? 300 : 350; // Adjust based on picker width
|
||||
const pickerWidth = 350;
|
||||
|
||||
// Calculate position to align the right edge of the picker with the button's right edge
|
||||
setPickerPosition({
|
||||
@ -90,15 +89,15 @@ export const ReactionPicker = ({ onReaction }) => {
|
||||
}}
|
||||
>
|
||||
<Picker
|
||||
height={isMobile ? 350 : 450}
|
||||
width={isMobile ? 300 : 350}
|
||||
reactionsDefaultOpen={true}
|
||||
onReactionClick={handleReaction}
|
||||
onEmojiClick={handlePicker}
|
||||
allowExpandReactions={true}
|
||||
autoFocusSearch={false}
|
||||
theme={Theme.DARK}
|
||||
emojiStyle={EmojiStyle.NATIVE}
|
||||
height="450"
|
||||
onEmojiClick={handlePicker}
|
||||
onReactionClick={handleReaction}
|
||||
reactionsDefaultOpen={true}
|
||||
theme={Theme.DARK}
|
||||
width="350"
|
||||
/>
|
||||
</div>,
|
||||
document.body
|
||||
|
@ -12,7 +12,7 @@ import PendingIcon from '@mui/icons-material/Pending';
|
||||
import TaskAltIcon from '@mui/icons-material/TaskAlt';
|
||||
import ExpandLess from '@mui/icons-material/ExpandLess';
|
||||
import ExpandMore from '@mui/icons-material/ExpandMore';
|
||||
import { MyContext, getBaseApiReact, isMobile } from '../../App';
|
||||
import { MyContext, getBaseApiReact } from '../../App';
|
||||
import { executeEvent } from '../../utils/events';
|
||||
|
||||
export const TaskManager = ({ getUserInfo }) => {
|
||||
@ -141,8 +141,7 @@ export const TaskManager = ({ getUserInfo }) => {
|
||||
});
|
||||
}, [txList]);
|
||||
|
||||
if (isMobile || txList?.length === 0 || txList.every((item) => item?.done))
|
||||
return null;
|
||||
if (txList?.length === 0 || txList.every((item) => item?.done)) return null;
|
||||
|
||||
return (
|
||||
<>
|
||||
|
@ -1,72 +1,103 @@
|
||||
import React, { useContext, useState } from 'react'
|
||||
import { GlobalContext, MyContext } from '../../App';
|
||||
import { Button, Dialog, DialogActions, DialogContent, DialogTitle, IconButton, Tab, Tabs, Typography } from '@mui/material';
|
||||
import { useContext, useState } from 'react';
|
||||
import { GlobalContext } from '../../App';
|
||||
import {
|
||||
Button,
|
||||
Dialog,
|
||||
DialogActions,
|
||||
DialogContent,
|
||||
DialogTitle,
|
||||
IconButton,
|
||||
Tab,
|
||||
Tabs,
|
||||
useTheme,
|
||||
} from '@mui/material';
|
||||
import CloseIcon from '@mui/icons-material/Close';
|
||||
import { VideoPlayer } from '../Embeds/VideoPlayer';
|
||||
|
||||
export const Tutorials = () => {
|
||||
const { openTutorialModal, setOpenTutorialModal } = useContext(GlobalContext);
|
||||
const [multiNumber, setMultiNumber] = useState(0)
|
||||
const handleClose = ()=> {
|
||||
setOpenTutorialModal(null)
|
||||
setMultiNumber(0)
|
||||
}
|
||||
if(!openTutorialModal) return null
|
||||
if(openTutorialModal?.multi){
|
||||
const selectedTutorial = openTutorialModal?.multi[multiNumber]
|
||||
return (
|
||||
<Dialog
|
||||
const { openTutorialModal, setOpenTutorialModal } = useContext(GlobalContext);
|
||||
const [multiNumber, setMultiNumber] = useState(0);
|
||||
const theme = useTheme();
|
||||
|
||||
const handleClose = () => {
|
||||
setOpenTutorialModal(null);
|
||||
setMultiNumber(0);
|
||||
};
|
||||
|
||||
if (!openTutorialModal) return null;
|
||||
|
||||
if (openTutorialModal?.multi) {
|
||||
const selectedTutorial = openTutorialModal?.multi[multiNumber];
|
||||
return (
|
||||
<Dialog
|
||||
onClose={handleClose}
|
||||
aria-labelledby="customized-dialog-title"
|
||||
open={!!openTutorialModal}
|
||||
fullWidth={true}
|
||||
maxWidth="xl"
|
||||
>
|
||||
<Tabs sx={{
|
||||
"& .MuiTabs-indicator": {
|
||||
backgroundColor: "white",
|
||||
},
|
||||
}} value={multiNumber} onChange={(e, value)=> setMultiNumber(value)} aria-label="basic tabs example">
|
||||
{openTutorialModal?.multi?.map((item, index)=> {
|
||||
return (
|
||||
<Tab sx={{
|
||||
"&.Mui-selected": {
|
||||
color: "white",
|
||||
},
|
||||
}} label={item?.title} value={index} />
|
||||
|
||||
)
|
||||
})}
|
||||
</Tabs>
|
||||
<DialogTitle sx={{ m: 0, p: 2 }} >
|
||||
<Tabs
|
||||
sx={{
|
||||
'& .MuiTabs-indicator': {
|
||||
backgroundColor: theme.palette.background.default,
|
||||
},
|
||||
}}
|
||||
value={multiNumber}
|
||||
onChange={(e, value) => setMultiNumber(value)}
|
||||
aria-label="basic tabs example"
|
||||
>
|
||||
{openTutorialModal?.multi?.map((item, index) => {
|
||||
return (
|
||||
<Tab
|
||||
sx={{
|
||||
'&.Mui-selected': {
|
||||
color: theme.palette.text.primary,
|
||||
},
|
||||
}}
|
||||
label={item?.title}
|
||||
value={index}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</Tabs>
|
||||
|
||||
<DialogTitle sx={{ m: 0, p: 2 }}>
|
||||
{selectedTutorial?.title} {` Tutorial`}
|
||||
</DialogTitle>
|
||||
|
||||
<IconButton
|
||||
aria-label="close"
|
||||
onClick={handleClose}
|
||||
sx={(theme) => ({
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
right: 8,
|
||||
top: 8,
|
||||
color: theme.palette.grey[500],
|
||||
})}
|
||||
color: theme.palette.text.primary,
|
||||
}}
|
||||
>
|
||||
<CloseIcon />
|
||||
</IconButton>
|
||||
<DialogContent dividers sx={{
|
||||
height: '85vh'
|
||||
}}>
|
||||
|
||||
<VideoPlayer node="https://ext-node.qortal.link" {...selectedTutorial?.resource || {}} />
|
||||
<DialogContent
|
||||
dividers
|
||||
sx={{
|
||||
height: '85vh',
|
||||
}}
|
||||
>
|
||||
<VideoPlayer
|
||||
node="https://ext-node.qortal.link"
|
||||
{...(selectedTutorial?.resource || {})}
|
||||
/>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button variant="contained" onClick={handleClose}>
|
||||
Close
|
||||
Close
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
)
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Dialog
|
||||
@ -76,33 +107,41 @@ export const Tutorials = () => {
|
||||
fullWidth={true}
|
||||
maxWidth="xl"
|
||||
>
|
||||
<DialogTitle sx={{ m: 0, p: 2 }} >
|
||||
<DialogTitle sx={{ m: 0, p: 2 }}>
|
||||
{openTutorialModal?.title} {` Tutorial`}
|
||||
</DialogTitle>
|
||||
|
||||
<IconButton
|
||||
aria-label="close"
|
||||
onClick={handleClose}
|
||||
sx={(theme) => ({
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
right: 8,
|
||||
top: 8,
|
||||
color: theme.palette.grey[500],
|
||||
})}
|
||||
color: theme.palette.text.primary,
|
||||
}}
|
||||
>
|
||||
<CloseIcon />
|
||||
</IconButton>
|
||||
<DialogContent dividers sx={{
|
||||
height: '85vh'
|
||||
}}>
|
||||
|
||||
<VideoPlayer node="https://ext-node.qortal.link" {...openTutorialModal?.resource || {}} />
|
||||
<DialogContent
|
||||
dividers
|
||||
sx={{
|
||||
height: '85vh',
|
||||
}}
|
||||
>
|
||||
<VideoPlayer
|
||||
node="https://ext-node.qortal.link"
|
||||
{...(openTutorialModal?.resource || {})}
|
||||
/>
|
||||
</DialogContent>
|
||||
|
||||
<DialogActions>
|
||||
<Button variant="contained" onClick={handleClose}>
|
||||
Close
|
||||
Close
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
</>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
@ -1,67 +1,85 @@
|
||||
import { useCallback, useEffect } from 'react';
|
||||
import { isMobile } from './App';
|
||||
|
||||
export const useAppFullScreen = (setFullScreen) => {
|
||||
const enterFullScreen = useCallback(() => {
|
||||
const element = document.documentElement; // Target the entire HTML document
|
||||
if (element.requestFullscreen) {
|
||||
element.requestFullscreen();
|
||||
} else if (element.mozRequestFullScreen) { // Firefox
|
||||
element.mozRequestFullScreen();
|
||||
} else if (element.webkitRequestFullscreen) { // Chrome, Safari and Opera
|
||||
element.webkitRequestFullscreen();
|
||||
} else if (element.msRequestFullscreen) { // IE/Edge
|
||||
element.msRequestFullscreen();
|
||||
}
|
||||
}, []);
|
||||
const enterFullScreen = useCallback(() => {
|
||||
const element = document.documentElement; // Target the entire HTML document
|
||||
if (element.requestFullscreen) {
|
||||
element.requestFullscreen();
|
||||
} else if (element.mozRequestFullScreen) {
|
||||
// Firefox
|
||||
element.mozRequestFullScreen();
|
||||
} else if (element.webkitRequestFullscreen) {
|
||||
// Chrome, Safari and Opera
|
||||
element.webkitRequestFullscreen();
|
||||
} else if (element.msRequestFullscreen) {
|
||||
// IE/Edge
|
||||
element.msRequestFullscreen();
|
||||
}
|
||||
}, []);
|
||||
|
||||
const exitFullScreen = useCallback(() => {
|
||||
if (document.fullscreenElement) {
|
||||
document.exitFullscreen();
|
||||
} else if (document.mozFullScreenElement) {
|
||||
document.mozCancelFullScreen();
|
||||
} else if (document.webkitFullscreenElement) {
|
||||
document.webkitExitFullscreen();
|
||||
} else if (document.msFullscreenElement) {
|
||||
document.msExitFullscreen();
|
||||
}
|
||||
}, []);
|
||||
const exitFullScreen = useCallback(() => {
|
||||
if (document.fullscreenElement) {
|
||||
document.exitFullscreen();
|
||||
} else if (document.mozFullScreenElement) {
|
||||
document.mozCancelFullScreen();
|
||||
} else if (document.webkitFullscreenElement) {
|
||||
document.webkitExitFullscreen();
|
||||
} else if (document.msFullscreenElement) {
|
||||
document.msExitFullscreen();
|
||||
}
|
||||
}, []);
|
||||
|
||||
const toggleFullScreen = useCallback(() => {
|
||||
if(!isMobile || isMobile) return
|
||||
if (document.fullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement || document.msFullscreenElement) {
|
||||
exitFullScreen();
|
||||
setFullScreen(false)
|
||||
} else {
|
||||
enterFullScreen();
|
||||
setFullScreen(true)
|
||||
}
|
||||
}, [enterFullScreen, exitFullScreen]);
|
||||
const toggleFullScreen = useCallback(() => {
|
||||
if (
|
||||
document.fullscreenElement ||
|
||||
document.mozFullScreenElement ||
|
||||
document.webkitFullscreenElement ||
|
||||
document.msFullscreenElement
|
||||
) {
|
||||
exitFullScreen();
|
||||
setFullScreen(false);
|
||||
} else {
|
||||
enterFullScreen();
|
||||
setFullScreen(true);
|
||||
}
|
||||
}, [enterFullScreen, exitFullScreen]);
|
||||
|
||||
// Listen for changes to fullscreen state
|
||||
useEffect(() => {
|
||||
const handleFullScreenChange = () => {
|
||||
if (document.fullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement || document.msFullscreenElement) {
|
||||
|
||||
} else {
|
||||
setFullScreen(false);
|
||||
}
|
||||
};
|
||||
// Listen for changes to fullscreen state
|
||||
useEffect(() => {
|
||||
const handleFullScreenChange = () => {
|
||||
if (
|
||||
document.fullscreenElement ||
|
||||
document.mozFullScreenElement ||
|
||||
document.webkitFullscreenElement ||
|
||||
document.msFullscreenElement
|
||||
) {
|
||||
// TODO check empty block
|
||||
} else {
|
||||
setFullScreen(false);
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener('fullscreenchange', handleFullScreenChange);
|
||||
document.addEventListener('webkitfullscreenchange', handleFullScreenChange); // Safari
|
||||
document.addEventListener('mozfullscreenchange', handleFullScreenChange); // Firefox
|
||||
document.addEventListener('MSFullscreenChange', handleFullScreenChange); // IE/Edge
|
||||
document.addEventListener('fullscreenchange', handleFullScreenChange);
|
||||
document.addEventListener('webkitfullscreenchange', handleFullScreenChange); // Safari
|
||||
document.addEventListener('mozfullscreenchange', handleFullScreenChange); // Firefox
|
||||
document.addEventListener('MSFullscreenChange', handleFullScreenChange); // IE/Edge
|
||||
|
||||
return () => {
|
||||
document.removeEventListener('fullscreenchange', handleFullScreenChange);
|
||||
document.removeEventListener('webkitfullscreenchange', handleFullScreenChange);
|
||||
document.removeEventListener('mozfullscreenchange', handleFullScreenChange);
|
||||
document.removeEventListener('MSFullscreenChange', handleFullScreenChange);
|
||||
};
|
||||
}, []);
|
||||
return () => {
|
||||
document.removeEventListener('fullscreenchange', handleFullScreenChange);
|
||||
document.removeEventListener(
|
||||
'webkitfullscreenchange',
|
||||
handleFullScreenChange
|
||||
);
|
||||
document.removeEventListener(
|
||||
'mozfullscreenchange',
|
||||
handleFullScreenChange
|
||||
);
|
||||
document.removeEventListener(
|
||||
'MSFullscreenChange',
|
||||
handleFullScreenChange
|
||||
);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return { enterFullScreen, exitFullScreen, toggleFullScreen };
|
||||
return { enterFullScreen, exitFullScreen, toggleFullScreen };
|
||||
};
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user