mirror of
https://github.com/Qortal/Qortal-Hub.git
synced 2025-06-13 19:41:22 +00:00
Remove isMobile check
This commit is contained in:
parent
c71853f754
commit
de9285a280
@ -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;
|
||||
|
@ -1,8 +1,6 @@
|
||||
import React, { useEffect, useMemo, useState } from "react";
|
||||
import {
|
||||
AppCircle,
|
||||
AppCircleContainer,
|
||||
AppCircleLabel,
|
||||
AppDownloadButton,
|
||||
AppDownloadButtonText,
|
||||
AppInfoAppName,
|
||||
@ -17,193 +15,204 @@ 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 LogoSelected from "../../assets/svgs/LogoSelected.svg";
|
||||
|
||||
import { Spacer } from "../../common/Spacer";
|
||||
import { executeEvent } from "../../utils/events";
|
||||
import { AppRating } from "./AppRating";
|
||||
import { settingsLocalLastUpdatedAtom, sortablePinnedAppsAtom } from "../../atoms/global";
|
||||
import { saveToLocalStorage } from "./AppsNavBar";
|
||||
import { useRecoilState, useSetRecoilState } from "recoil";
|
||||
} from './Apps-styles';
|
||||
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';
|
||||
import {
|
||||
settingsLocalLastUpdatedAtom,
|
||||
sortablePinnedAppsAtom,
|
||||
} from '../../atoms/global';
|
||||
import { saveToLocalStorage } from './AppsNavBar';
|
||||
import { useRecoilState, useSetRecoilState } from 'recoil';
|
||||
|
||||
export const AppInfo = ({ app, myName }) => {
|
||||
const isInstalled = app?.status?.status === "READY";
|
||||
const [sortablePinnedApps, setSortablePinnedApps] = useRecoilState(sortablePinnedAppsAtom);
|
||||
const isInstalled = app?.status?.status === 'READY';
|
||||
const [sortablePinnedApps, setSortablePinnedApps] = useRecoilState(
|
||||
sortablePinnedAppsAtom
|
||||
);
|
||||
|
||||
const isSelectedAppPinned = !!sortablePinnedApps?.find((item)=> item?.name === app?.name && item?.service === app?.service)
|
||||
const setSettingsLocalLastUpdated = useSetRecoilState(settingsLocalLastUpdatedAtom);
|
||||
const isSelectedAppPinned = !!sortablePinnedApps?.find(
|
||||
(item) => item?.name === app?.name && item?.service === app?.service
|
||||
);
|
||||
const setSettingsLocalLastUpdated = useSetRecoilState(
|
||||
settingsLocalLastUpdatedAtom
|
||||
);
|
||||
|
||||
return (
|
||||
<AppsLibraryContainer
|
||||
sx={{
|
||||
height: !isMobile && "100%",
|
||||
justifyContent: !isMobile && "flex-start",
|
||||
alignItems: isMobile && 'center'
|
||||
height: '100%',
|
||||
justifyContent: 'flex-start',
|
||||
}}
|
||||
>
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
maxWidth: "500px",
|
||||
width: '90%'
|
||||
}}>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
maxWidth: '500px',
|
||||
width: '90%',
|
||||
}}
|
||||
>
|
||||
<Spacer height="30px" />
|
||||
|
||||
|
||||
{!isMobile && <Spacer height="30px" />}
|
||||
<AppsWidthLimiter>
|
||||
<AppInfoSnippetContainer>
|
||||
<AppInfoSnippetLeft
|
||||
sx={{
|
||||
flexGrow: 1,
|
||||
gap: "18px",
|
||||
}}
|
||||
>
|
||||
<AppCircleContainer
|
||||
<AppsWidthLimiter>
|
||||
<AppInfoSnippetContainer>
|
||||
<AppInfoSnippetLeft
|
||||
sx={{
|
||||
width: "auto",
|
||||
flexGrow: 1,
|
||||
gap: '18px',
|
||||
}}
|
||||
>
|
||||
<AppCircle
|
||||
<AppCircleContainer
|
||||
sx={{
|
||||
border: "none",
|
||||
height: "100px",
|
||||
width: "100px",
|
||||
width: 'auto',
|
||||
}}
|
||||
>
|
||||
<Avatar
|
||||
<AppCircle
|
||||
sx={{
|
||||
height: "43px",
|
||||
width: "43px",
|
||||
"& img": {
|
||||
objectFit: "fill",
|
||||
},
|
||||
border: 'none',
|
||||
height: '100px',
|
||||
width: '100px',
|
||||
}}
|
||||
alt={app?.name}
|
||||
src={`${getBaseApiReact()}/arbitrary/THUMBNAIL/${
|
||||
app?.name
|
||||
}/qortal_avatar?async=true`}
|
||||
>
|
||||
<img
|
||||
style={{
|
||||
width: "43px",
|
||||
height: "auto",
|
||||
<Avatar
|
||||
sx={{
|
||||
height: '43px',
|
||||
width: '43px',
|
||||
'& img': {
|
||||
objectFit: 'fill',
|
||||
},
|
||||
}}
|
||||
src={LogoSelected}
|
||||
alt="center-icon"
|
||||
/>
|
||||
</Avatar>
|
||||
</AppCircle>
|
||||
</AppCircleContainer>
|
||||
<AppInfoSnippetMiddle>
|
||||
<AppInfoAppName>
|
||||
{app?.metadata?.title || app?.name}
|
||||
</AppInfoAppName>
|
||||
<Spacer height="6px" />
|
||||
<AppInfoUserName>{app?.name}</AppInfoUserName>
|
||||
<Spacer height="3px" />
|
||||
</AppInfoSnippetMiddle>
|
||||
</AppInfoSnippetLeft>
|
||||
<AppInfoSnippetRight></AppInfoSnippetRight>
|
||||
</AppInfoSnippetContainer>
|
||||
<Spacer height="11px" />
|
||||
<Box sx={{
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '20px'
|
||||
}}>
|
||||
<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)
|
||||
alt={app?.name}
|
||||
src={`${getBaseApiReact()}/arbitrary/THUMBNAIL/${
|
||||
app?.name
|
||||
}/qortal_avatar?async=true`}
|
||||
>
|
||||
<img
|
||||
style={{
|
||||
width: '43px',
|
||||
height: 'auto',
|
||||
}}
|
||||
src={LogoSelected}
|
||||
alt="center-icon"
|
||||
/>
|
||||
</Avatar>
|
||||
</AppCircle>
|
||||
</AppCircleContainer>
|
||||
<AppInfoSnippetMiddle>
|
||||
<AppInfoAppName>
|
||||
{app?.metadata?.title || app?.name}
|
||||
</AppInfoAppName>
|
||||
<Spacer height="6px" />
|
||||
<AppInfoUserName>{app?.name}</AppInfoUserName>
|
||||
<Spacer height="3px" />
|
||||
</AppInfoSnippetMiddle>
|
||||
</AppInfoSnippetLeft>
|
||||
<AppInfoSnippetRight></AppInfoSnippetRight>
|
||||
</AppInfoSnippetContainer>
|
||||
<Spacer height="11px" />
|
||||
<Box
|
||||
sx={{
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '20px',
|
||||
}}
|
||||
>
|
||||
<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
|
||||
);
|
||||
} 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",
|
||||
width: "100%",
|
||||
maxWidth: "320px",
|
||||
height: "29px",
|
||||
opacity: isSelectedAppPinned ? 0.6 : 1
|
||||
}}
|
||||
>
|
||||
<AppDownloadButtonText>
|
||||
{!isMobile ? (
|
||||
<>
|
||||
{isSelectedAppPinned ? 'Unpin from dashboard' : 'Pin to dashboard'}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{isSelectedAppPinned ? 'Unpin' : 'Pin'}
|
||||
</>
|
||||
)}
|
||||
|
||||
</AppDownloadButtonText>
|
||||
</AppDownloadButton>
|
||||
<AppDownloadButton
|
||||
onClick={() => {
|
||||
executeEvent("addTab", {
|
||||
data: app,
|
||||
});
|
||||
}}
|
||||
sx={{
|
||||
backgroundColor: isInstalled ? "#0091E1" : "#247C0E",
|
||||
width: "100%",
|
||||
maxWidth: "320px",
|
||||
height: "29px",
|
||||
}}
|
||||
>
|
||||
<AppDownloadButtonText>
|
||||
{isInstalled ? "Open" : "Download"}
|
||||
</AppDownloadButtonText>
|
||||
</AppDownloadButton>
|
||||
</Box>
|
||||
|
||||
</AppsWidthLimiter>
|
||||
<Spacer height="20px" />
|
||||
<AppsWidthLimiter>
|
||||
<AppsCategoryInfo>
|
||||
<AppRating ratingCountPosition="top" myName={myName} app={app} />
|
||||
<Spacer width="16px" />
|
||||
<Spacer height="40px" width="1px" backgroundColor="white" />
|
||||
<Spacer width="16px" />
|
||||
<AppsCategoryInfoSub>
|
||||
<AppsCategoryInfoLabel>Category:</AppsCategoryInfoLabel>
|
||||
<Spacer height="4px" />
|
||||
<AppsCategoryInfoValue>
|
||||
{app?.metadata?.categoryName || "none"}
|
||||
</AppsCategoryInfoValue>
|
||||
</AppsCategoryInfoSub>
|
||||
</AppsCategoryInfo>
|
||||
<Spacer height="30px" />
|
||||
<AppInfoAppName>About this Q-App</AppInfoAppName>
|
||||
</AppsWidthLimiter>
|
||||
<Spacer height="20px" />
|
||||
<AppsInfoDescription>
|
||||
{app?.metadata?.description || "No description"}
|
||||
</AppsInfoDescription>
|
||||
return updatedApps;
|
||||
});
|
||||
setSettingsLocalLastUpdated(Date.now());
|
||||
}}
|
||||
sx={{
|
||||
backgroundColor: '#359ff7ff',
|
||||
width: '100%',
|
||||
maxWidth: '320px',
|
||||
height: '29px',
|
||||
opacity: isSelectedAppPinned ? 0.6 : 1,
|
||||
}}
|
||||
>
|
||||
<AppDownloadButtonText>
|
||||
{isSelectedAppPinned
|
||||
? 'Unpin from dashboard'
|
||||
: 'Pin to dashboard'}
|
||||
</AppDownloadButtonText>
|
||||
</AppDownloadButton>
|
||||
<AppDownloadButton
|
||||
onClick={() => {
|
||||
executeEvent('addTab', {
|
||||
data: app,
|
||||
});
|
||||
}}
|
||||
sx={{
|
||||
backgroundColor: isInstalled ? '#0091E1' : '#247C0E',
|
||||
width: '100%',
|
||||
maxWidth: '320px',
|
||||
height: '29px',
|
||||
}}
|
||||
>
|
||||
<AppDownloadButtonText>
|
||||
{isInstalled ? 'Open' : 'Download'}
|
||||
</AppDownloadButtonText>
|
||||
</AppDownloadButton>
|
||||
</Box>
|
||||
</AppsWidthLimiter>
|
||||
<Spacer height="20px" />
|
||||
<AppsWidthLimiter>
|
||||
<AppsCategoryInfo>
|
||||
<AppRating ratingCountPosition="top" myName={myName} app={app} />
|
||||
<Spacer width="16px" />
|
||||
<Spacer height="40px" width="1px" backgroundColor="white" />
|
||||
<Spacer width="16px" />
|
||||
<AppsCategoryInfoSub>
|
||||
<AppsCategoryInfoLabel>Category:</AppsCategoryInfoLabel>
|
||||
<Spacer height="4px" />
|
||||
<AppsCategoryInfoValue>
|
||||
{app?.metadata?.categoryName || 'none'}
|
||||
</AppsCategoryInfoValue>
|
||||
</AppsCategoryInfoSub>
|
||||
</AppsCategoryInfo>
|
||||
<Spacer height="30px" />
|
||||
<AppInfoAppName>About this Q-App</AppInfoAppName>
|
||||
</AppsWidthLimiter>
|
||||
<Spacer height="20px" />
|
||||
<AppsInfoDescription>
|
||||
{app?.metadata?.description || 'No description'}
|
||||
</AppsInfoDescription>
|
||||
</Box>
|
||||
</AppsLibraryContainer>
|
||||
);
|
||||
|
@ -1,4 +1,3 @@
|
||||
import React from "react";
|
||||
import {
|
||||
AppCircle,
|
||||
AppCircleContainer,
|
||||
@ -10,148 +9,176 @@ import {
|
||||
AppInfoSnippetMiddle,
|
||||
AppInfoSnippetRight,
|
||||
AppInfoUserName,
|
||||
} from "./Apps-styles";
|
||||
import { Avatar, ButtonBase } from "@mui/material";
|
||||
import { getBaseApiReact, isMobile } from "../../App";
|
||||
import LogoSelected from "../../assets/svgs/LogoSelected.svg";
|
||||
} from './Apps-styles';
|
||||
import { Avatar, ButtonBase } 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';
|
||||
import { useRecoilState, useSetRecoilState } from 'recoil';
|
||||
import {
|
||||
settingsLocalLastUpdatedAtom,
|
||||
sortablePinnedAppsAtom,
|
||||
} from '../../atoms/global';
|
||||
import { saveToLocalStorage } from './AppsNavBar';
|
||||
|
||||
import { Spacer } from "../../common/Spacer";
|
||||
import { executeEvent } from "../../utils/events";
|
||||
import { AppRating } from "./AppRating";
|
||||
import { useRecoilState, useSetRecoilState } from "recoil";
|
||||
import { settingsLocalLastUpdatedAtom, sortablePinnedAppsAtom } from "../../atoms/global";
|
||||
import { saveToLocalStorage } from "./AppsNavBar";
|
||||
export const AppInfoSnippet = ({
|
||||
app,
|
||||
myName,
|
||||
isFromCategory,
|
||||
parentStyles = {},
|
||||
}) => {
|
||||
const isInstalled = app?.status?.status === 'READY';
|
||||
const [sortablePinnedApps, setSortablePinnedApps] = useRecoilState(
|
||||
sortablePinnedAppsAtom
|
||||
);
|
||||
|
||||
export const AppInfoSnippet = ({ app, myName, isFromCategory, parentStyles = {} }) => {
|
||||
|
||||
const isInstalled = app?.status?.status === 'READY'
|
||||
const [sortablePinnedApps, setSortablePinnedApps] = useRecoilState(sortablePinnedAppsAtom);
|
||||
|
||||
const isSelectedAppPinned = !!sortablePinnedApps?.find((item)=> item?.name === app?.name && item?.service === app?.service)
|
||||
const setSettingsLocalLastUpdated = useSetRecoilState(settingsLocalLastUpdatedAtom);
|
||||
const isSelectedAppPinned = !!sortablePinnedApps?.find(
|
||||
(item) => item?.name === app?.name && item?.service === app?.service
|
||||
);
|
||||
const setSettingsLocalLastUpdated = useSetRecoilState(
|
||||
settingsLocalLastUpdatedAtom
|
||||
);
|
||||
return (
|
||||
<AppInfoSnippetContainer sx={{
|
||||
...parentStyles
|
||||
}}>
|
||||
<AppInfoSnippetContainer
|
||||
sx={{
|
||||
...parentStyles,
|
||||
}}
|
||||
>
|
||||
<AppInfoSnippetLeft>
|
||||
<ButtonBase
|
||||
sx={{
|
||||
height: "80px",
|
||||
width: "60px",
|
||||
}}
|
||||
onClick={()=> {
|
||||
if(isFromCategory){
|
||||
executeEvent("selectedAppInfoCategory", {
|
||||
<ButtonBase
|
||||
sx={{
|
||||
height: '80px',
|
||||
width: '60px',
|
||||
}}
|
||||
onClick={() => {
|
||||
if (isFromCategory) {
|
||||
executeEvent('selectedAppInfoCategory', {
|
||||
data: app,
|
||||
});
|
||||
return;
|
||||
}
|
||||
executeEvent('selectedAppInfo', {
|
||||
data: app,
|
||||
});
|
||||
return
|
||||
}
|
||||
executeEvent("selectedAppInfo", {
|
||||
data: app,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<AppCircleContainer>
|
||||
<AppCircle
|
||||
sx={{
|
||||
border: "none",
|
||||
}}
|
||||
>
|
||||
<AppCircleContainer>
|
||||
<AppCircle
|
||||
sx={{
|
||||
border: 'none',
|
||||
}}
|
||||
>
|
||||
<Avatar
|
||||
sx={{
|
||||
height: '42px',
|
||||
width: '42px',
|
||||
'& img': {
|
||||
objectFit: 'fill',
|
||||
},
|
||||
}}
|
||||
alt={app?.name}
|
||||
src={`${getBaseApiReact()}/arbitrary/THUMBNAIL/${
|
||||
app?.name
|
||||
}/qortal_avatar?async=true`}
|
||||
>
|
||||
<img
|
||||
style={{
|
||||
width: '31px',
|
||||
height: 'auto',
|
||||
}}
|
||||
src={LogoSelected}
|
||||
alt="center-icon"
|
||||
/>
|
||||
</Avatar>
|
||||
</AppCircle>
|
||||
</AppCircleContainer>
|
||||
</ButtonBase>
|
||||
<AppInfoSnippetMiddle>
|
||||
<ButtonBase
|
||||
onClick={() => {
|
||||
if (isFromCategory) {
|
||||
executeEvent('selectedAppInfoCategory', {
|
||||
data: app,
|
||||
});
|
||||
return;
|
||||
}
|
||||
executeEvent('selectedAppInfo', {
|
||||
data: app,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<Avatar
|
||||
sx={{
|
||||
height: "42px",
|
||||
width: "42px",
|
||||
'& img': {
|
||||
objectFit: 'fill',
|
||||
}
|
||||
}}
|
||||
alt={app?.name}
|
||||
src={`${getBaseApiReact()}/arbitrary/THUMBNAIL/${
|
||||
app?.name
|
||||
}/qortal_avatar?async=true`}
|
||||
>
|
||||
<img
|
||||
style={{
|
||||
width: "31px",
|
||||
height: "auto",
|
||||
}}
|
||||
src={LogoSelected}
|
||||
alt="center-icon"
|
||||
/>
|
||||
</Avatar>
|
||||
</AppCircle>
|
||||
</AppCircleContainer>
|
||||
</ButtonBase>
|
||||
<AppInfoSnippetMiddle>
|
||||
|
||||
<ButtonBase onClick={()=> {
|
||||
if(isFromCategory){
|
||||
executeEvent("selectedAppInfoCategory", {
|
||||
data: app,
|
||||
});
|
||||
return
|
||||
}
|
||||
executeEvent("selectedAppInfo", {
|
||||
data: app,
|
||||
});
|
||||
}}>
|
||||
<AppInfoAppName >
|
||||
{app?.metadata?.title || app?.name}
|
||||
</AppInfoAppName>
|
||||
</ButtonBase>
|
||||
<Spacer height="6px" />
|
||||
<AppInfoUserName>
|
||||
{ app?.name}
|
||||
</AppInfoUserName>
|
||||
<AppInfoAppName>{app?.metadata?.title || app?.name}</AppInfoAppName>
|
||||
</ButtonBase>
|
||||
<Spacer height="6px" />
|
||||
<AppInfoUserName>{app?.name}</AppInfoUserName>
|
||||
<Spacer height="3px" />
|
||||
<AppRating app={app} myName={myName} />
|
||||
</AppInfoSnippetMiddle>
|
||||
</AppInfoSnippetMiddle>
|
||||
</AppInfoSnippetLeft>
|
||||
<AppInfoSnippetRight sx={{
|
||||
gap: '10px'
|
||||
}}>
|
||||
{!isMobile && (
|
||||
<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)
|
||||
return updatedApps;
|
||||
});
|
||||
setSettingsLocalLastUpdated(Date.now())
|
||||
}} sx={{
|
||||
backgroundColor: '#359ff7ff',
|
||||
opacity: isSelectedAppPinned ? 0.6 : 1
|
||||
|
||||
}}>
|
||||
<AppDownloadButtonText> {isSelectedAppPinned ? 'Unpin' : 'Pin'}</AppDownloadButtonText>
|
||||
<AppInfoSnippetRight
|
||||
sx={{
|
||||
gap: '10px',
|
||||
}}
|
||||
>
|
||||
<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
|
||||
);
|
||||
return updatedApps;
|
||||
});
|
||||
setSettingsLocalLastUpdated(Date.now());
|
||||
}}
|
||||
sx={{
|
||||
backgroundColor: '#359ff7ff',
|
||||
opacity: isSelectedAppPinned ? 0.6 : 1,
|
||||
}}
|
||||
>
|
||||
<AppDownloadButtonText>
|
||||
{' '}
|
||||
{isSelectedAppPinned ? 'Unpin' : 'Pin'}
|
||||
</AppDownloadButtonText>
|
||||
</AppDownloadButton>
|
||||
)}
|
||||
|
||||
<AppDownloadButton onClick={()=> {
|
||||
|
||||
executeEvent("addTab", {
|
||||
data: app
|
||||
})
|
||||
}} sx={{
|
||||
backgroundColor: isInstalled ? '#0091E1' : '#247C0E',
|
||||
|
||||
}}>
|
||||
<AppDownloadButtonText>{isInstalled ? 'Open' : 'Download'}</AppDownloadButtonText>
|
||||
|
||||
<AppDownloadButton
|
||||
onClick={() => {
|
||||
executeEvent('addTab', {
|
||||
data: app,
|
||||
});
|
||||
}}
|
||||
sx={{
|
||||
backgroundColor: isInstalled ? '#0091E1' : '#247C0E',
|
||||
}}
|
||||
>
|
||||
<AppDownloadButtonText>
|
||||
{isInstalled ? 'Open' : 'Download'}
|
||||
</AppDownloadButtonText>
|
||||
</AppDownloadButton>
|
||||
</AppInfoSnippetRight>
|
||||
</AppInfoSnippetContainer>
|
||||
|
@ -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,17 +1,12 @@
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import {
|
||||
AppCircle,
|
||||
AppCircleContainer,
|
||||
AppCircleLabel,
|
||||
AppLibrarySubTitle,
|
||||
AppsContainer,
|
||||
AppsParent,
|
||||
} from './Apps-styles';
|
||||
import { Avatar, ButtonBase } from '@mui/material';
|
||||
import { ButtonBase } from '@mui/material';
|
||||
import { Add } from '@mui/icons-material';
|
||||
import { getBaseApiReact, isMobile } from '../../App';
|
||||
import LogoSelected from '../../assets/svgs/LogoSelected.svg';
|
||||
import { executeEvent } from '../../utils/events';
|
||||
import { SortablePinnedApps } from './SortablePinnedApps';
|
||||
import { Spacer } from '../../common/Spacer';
|
||||
|
||||
@ -35,7 +30,7 @@ export const AppsHome = ({ setMode, myApp, myWebsite, availableQapps }) => {
|
||||
>
|
||||
<AppCircleContainer
|
||||
sx={{
|
||||
gap: !isMobile ? '10px' : '5px',
|
||||
gap: '10px',
|
||||
}}
|
||||
>
|
||||
<AppCircle>
|
||||
|
@ -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,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