mirror of
https://github.com/Qortal/Qortal-Hub.git
synced 2025-06-14 12:01:21 +00:00
Remove isMobile check
This commit is contained in:
parent
c71853f754
commit
de9285a280
@ -1,5 +1,4 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
|
|
||||||
import './qortalRequests';
|
import './qortalRequests';
|
||||||
import { isArray } from 'lodash';
|
import { isArray } from 'lodash';
|
||||||
import {
|
import {
|
||||||
@ -30,7 +29,6 @@ import { RequestQueueWithPromise } from './utils/queue/queue';
|
|||||||
import { validateAddress } from './utils/validateAddress';
|
import { validateAddress } from './utils/validateAddress';
|
||||||
import { Sha256 } from 'asmcrypto.js';
|
import { Sha256 } from 'asmcrypto.js';
|
||||||
import { TradeBotRespondMultipleRequest } from './transactions/TradeBotRespondMultipleRequest';
|
import { TradeBotRespondMultipleRequest } from './transactions/TradeBotRespondMultipleRequest';
|
||||||
|
|
||||||
import { RESOURCE_TYPE_NUMBER_GROUP_CHAT_REACTIONS } from './constants/resourceTypes';
|
import { RESOURCE_TYPE_NUMBER_GROUP_CHAT_REACTIONS } from './constants/resourceTypes';
|
||||||
import {
|
import {
|
||||||
addDataPublishesCase,
|
addDataPublishesCase,
|
||||||
@ -111,6 +109,7 @@ export let groupSecretkeys = {};
|
|||||||
export function cleanUrl(url) {
|
export function cleanUrl(url) {
|
||||||
return url?.replace(/^(https?:\/\/)?(www\.)?/, '');
|
return url?.replace(/^(https?:\/\/)?(www\.)?/, '');
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getProtocol(url) {
|
export function getProtocol(url) {
|
||||||
if (url?.startsWith('https://')) {
|
if (url?.startsWith('https://')) {
|
||||||
return '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';
|
export const groupApiSocketLocal = 'ws://127.0.0.1:12391';
|
||||||
const timeDifferenceForNotificationChatsBackground = 86400000;
|
const timeDifferenceForNotificationChatsBackground = 86400000;
|
||||||
const requestQueueAnnouncements = new RequestQueueWithPromise(1);
|
const requestQueueAnnouncements = new RequestQueueWithPromise(1);
|
||||||
let isMobile = true;
|
|
||||||
|
|
||||||
function handleNotificationClick(notificationId) {
|
function handleNotificationClick(notificationId) {
|
||||||
// Decode the notificationId if it was encoded
|
// 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 = {
|
const allQueues = {
|
||||||
requestQueueAnnouncements: requestQueueAnnouncements,
|
requestQueueAnnouncements: requestQueueAnnouncements,
|
||||||
};
|
};
|
||||||
@ -264,7 +242,9 @@ export const getForeignKey = async (foreignBlockchain) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const pauseAllQueues = () => controlAllQueues('pause');
|
export const pauseAllQueues = () => controlAllQueues('pause');
|
||||||
|
|
||||||
export const resumeAllQueues = () => controlAllQueues('resume');
|
export const resumeAllQueues = () => controlAllQueues('resume');
|
||||||
|
|
||||||
export const checkDifference = (
|
export const checkDifference = (
|
||||||
createdTimestamp,
|
createdTimestamp,
|
||||||
diff = timeDifferenceForNotificationChatsBackground
|
diff = timeDifferenceForNotificationChatsBackground
|
||||||
@ -302,6 +282,7 @@ export const getBaseApi = async (customApi?: string) => {
|
|||||||
return groupApi;
|
return groupApi;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const isUsingLocal = async () => {
|
export const isUsingLocal = async () => {
|
||||||
const apiKey = await getApiKeyFromStorage(); // Retrieve apiKey asynchronously
|
const apiKey = await getApiKeyFromStorage(); // Retrieve apiKey asynchronously
|
||||||
if (apiKey?.url) {
|
if (apiKey?.url) {
|
||||||
@ -345,13 +326,13 @@ const proxyAccountAddress = 'QXPejUe5Za1KD3zCMViWCX35AreMQ9H7ku';
|
|||||||
const proxyAccountPublicKey = '5hP6stDWybojoDw5t8z9D51nV945oMPX7qBd29rhX1G7';
|
const proxyAccountPublicKey = '5hP6stDWybojoDw5t8z9D51nV945oMPX7qBd29rhX1G7';
|
||||||
const pendingResponses = new Map();
|
const pendingResponses = new Map();
|
||||||
let groups = null;
|
let groups = null;
|
||||||
|
|
||||||
let socket;
|
let socket;
|
||||||
let timeoutId;
|
let timeoutId;
|
||||||
let groupSocketTimeout;
|
let groupSocketTimeout;
|
||||||
let socketTimeout: any;
|
let socketTimeout: any;
|
||||||
let interval;
|
let interval;
|
||||||
let intervalThreads;
|
let intervalThreads;
|
||||||
|
|
||||||
// Function to check each API endpoint
|
// Function to check each API endpoint
|
||||||
export async function findUsableApi() {
|
export async function findUsableApi() {
|
||||||
for (const endpoint of apiEndpoints) {
|
for (const endpoint of apiEndpoints) {
|
||||||
@ -435,6 +416,7 @@ async function checkWebviewFocus() {
|
|||||||
window.addEventListener('message', handleMessage);
|
window.addEventListener('message', handleMessage);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const worker = new ChatComputePowWorker();
|
const worker = new ChatComputePowWorker();
|
||||||
|
|
||||||
export async function performPowTask(chatBytes, difficulty) {
|
export async function performPowTask(chatBytes, difficulty) {
|
||||||
@ -584,6 +566,7 @@ const handleNotificationDirect = async (directs) => {
|
|||||||
setChatHeadsDirect(dataDirects);
|
setChatHeadsDirect(dataDirects);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
async function getThreadActivity(): Promise<any | null> {
|
async function getThreadActivity(): Promise<any | null> {
|
||||||
const wallet = await getSaveWallet();
|
const wallet = await getSaveWallet();
|
||||||
const address = wallet.address0;
|
const address = wallet.address0;
|
||||||
@ -852,6 +835,7 @@ export async function getNameInfoForOthers(address) {
|
|||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getAddressInfo(address) {
|
export async function getAddressInfo(address) {
|
||||||
const validApi = await getBaseApi();
|
const validApi = await getBaseApi();
|
||||||
const response = await fetch(validApi + '/addresses/' + address);
|
const response = await fetch(validApi + '/addresses/' + address);
|
||||||
@ -932,6 +916,7 @@ async function getTradeInfo(qortalAtAddress) {
|
|||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getTradesInfo(qortalAtAddresses) {
|
async function getTradesInfo(qortalAtAddresses) {
|
||||||
// Use Promise.all to fetch data for all addresses concurrently
|
// Use Promise.all to fetch data for all addresses concurrently
|
||||||
const trades = await Promise.all(
|
const trades = await Promise.all(
|
||||||
@ -951,6 +936,7 @@ export async function getBalanceInfo() {
|
|||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getLTCBalance() {
|
export async function getLTCBalance() {
|
||||||
const wallet = await getSaveWallet();
|
const wallet = await getSaveWallet();
|
||||||
let _url = `${buyTradeNodeBaseUrl}/crosschain/ltc/walletbalance`;
|
let _url = `${buyTradeNodeBaseUrl}/crosschain/ltc/walletbalance`;
|
||||||
@ -1075,6 +1061,7 @@ const transaction = async (
|
|||||||
data: res,
|
data: res,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const makeTransactionRequest = async (
|
const makeTransactionRequest = async (
|
||||||
receiver,
|
receiver,
|
||||||
lastRef,
|
lastRef,
|
||||||
@ -1113,6 +1100,7 @@ export const getLastRef = async () => {
|
|||||||
const data = await response.text();
|
const data = await response.text();
|
||||||
return data;
|
return data;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const sendQortFee = async (): Promise<number> => {
|
export const sendQortFee = async (): Promise<number> => {
|
||||||
const validApi = await getBaseApi();
|
const validApi = await getBaseApi();
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
@ -1386,6 +1374,7 @@ export async function signChatFunc(
|
|||||||
}
|
}
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
function sbrk(size, heap) {
|
function sbrk(size, heap) {
|
||||||
let brk = 512 * 1024; // stack top
|
let brk = 512 * 1024; // stack top
|
||||||
let old = brk;
|
let old = brk;
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
import React, { useEffect, useMemo, useState } from "react";
|
|
||||||
import {
|
import {
|
||||||
AppCircle,
|
AppCircle,
|
||||||
AppCircleContainer,
|
AppCircleContainer,
|
||||||
AppCircleLabel,
|
|
||||||
AppDownloadButton,
|
AppDownloadButton,
|
||||||
AppDownloadButtonText,
|
AppDownloadButtonText,
|
||||||
AppInfoAppName,
|
AppInfoAppName,
|
||||||
@ -17,193 +15,204 @@ import {
|
|||||||
AppsCategoryInfoValue,
|
AppsCategoryInfoValue,
|
||||||
AppsInfoDescription,
|
AppsInfoDescription,
|
||||||
AppsLibraryContainer,
|
AppsLibraryContainer,
|
||||||
AppsParent,
|
|
||||||
AppsWidthLimiter,
|
AppsWidthLimiter,
|
||||||
} from "./Apps-styles";
|
} from './Apps-styles';
|
||||||
import { Avatar, Box, ButtonBase, InputBase } from "@mui/material";
|
import { Avatar, Box } from '@mui/material';
|
||||||
import { Add } from "@mui/icons-material";
|
import { getBaseApiReact } from '../../App';
|
||||||
import { getBaseApiReact, isMobile } from "../../App";
|
import LogoSelected from '../../assets/svgs/LogoSelected.svg';
|
||||||
import LogoSelected from "../../assets/svgs/LogoSelected.svg";
|
import { Spacer } from '../../common/Spacer';
|
||||||
|
import { executeEvent } from '../../utils/events';
|
||||||
import { Spacer } from "../../common/Spacer";
|
import { AppRating } from './AppRating';
|
||||||
import { executeEvent } from "../../utils/events";
|
import {
|
||||||
import { AppRating } from "./AppRating";
|
settingsLocalLastUpdatedAtom,
|
||||||
import { settingsLocalLastUpdatedAtom, sortablePinnedAppsAtom } from "../../atoms/global";
|
sortablePinnedAppsAtom,
|
||||||
import { saveToLocalStorage } from "./AppsNavBar";
|
} from '../../atoms/global';
|
||||||
import { useRecoilState, useSetRecoilState } from "recoil";
|
import { saveToLocalStorage } from './AppsNavBar';
|
||||||
|
import { useRecoilState, useSetRecoilState } from 'recoil';
|
||||||
|
|
||||||
export const AppInfo = ({ app, myName }) => {
|
export const AppInfo = ({ app, myName }) => {
|
||||||
const isInstalled = app?.status?.status === "READY";
|
const isInstalled = app?.status?.status === 'READY';
|
||||||
const [sortablePinnedApps, setSortablePinnedApps] = useRecoilState(sortablePinnedAppsAtom);
|
const [sortablePinnedApps, setSortablePinnedApps] = useRecoilState(
|
||||||
|
sortablePinnedAppsAtom
|
||||||
|
);
|
||||||
|
|
||||||
const isSelectedAppPinned = !!sortablePinnedApps?.find((item)=> item?.name === app?.name && item?.service === app?.service)
|
const isSelectedAppPinned = !!sortablePinnedApps?.find(
|
||||||
const setSettingsLocalLastUpdated = useSetRecoilState(settingsLocalLastUpdatedAtom);
|
(item) => item?.name === app?.name && item?.service === app?.service
|
||||||
|
);
|
||||||
|
const setSettingsLocalLastUpdated = useSetRecoilState(
|
||||||
|
settingsLocalLastUpdatedAtom
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AppsLibraryContainer
|
<AppsLibraryContainer
|
||||||
sx={{
|
sx={{
|
||||||
height: !isMobile && "100%",
|
height: '100%',
|
||||||
justifyContent: !isMobile && "flex-start",
|
justifyContent: 'flex-start',
|
||||||
alignItems: isMobile && 'center'
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box sx={{
|
<Box
|
||||||
display: 'flex',
|
sx={{
|
||||||
flexDirection: 'column',
|
display: 'flex',
|
||||||
maxWidth: "500px",
|
flexDirection: 'column',
|
||||||
width: '90%'
|
maxWidth: '500px',
|
||||||
}}>
|
width: '90%',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Spacer height="30px" />
|
||||||
|
|
||||||
|
<AppsWidthLimiter>
|
||||||
{!isMobile && <Spacer height="30px" />}
|
<AppInfoSnippetContainer>
|
||||||
<AppsWidthLimiter>
|
<AppInfoSnippetLeft
|
||||||
<AppInfoSnippetContainer>
|
|
||||||
<AppInfoSnippetLeft
|
|
||||||
sx={{
|
|
||||||
flexGrow: 1,
|
|
||||||
gap: "18px",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<AppCircleContainer
|
|
||||||
sx={{
|
sx={{
|
||||||
width: "auto",
|
flexGrow: 1,
|
||||||
|
gap: '18px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<AppCircle
|
<AppCircleContainer
|
||||||
sx={{
|
sx={{
|
||||||
border: "none",
|
width: 'auto',
|
||||||
height: "100px",
|
|
||||||
width: "100px",
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Avatar
|
<AppCircle
|
||||||
sx={{
|
sx={{
|
||||||
height: "43px",
|
border: 'none',
|
||||||
width: "43px",
|
height: '100px',
|
||||||
"& img": {
|
width: '100px',
|
||||||
objectFit: "fill",
|
|
||||||
},
|
|
||||||
}}
|
}}
|
||||||
alt={app?.name}
|
|
||||||
src={`${getBaseApiReact()}/arbitrary/THUMBNAIL/${
|
|
||||||
app?.name
|
|
||||||
}/qortal_avatar?async=true`}
|
|
||||||
>
|
>
|
||||||
<img
|
<Avatar
|
||||||
style={{
|
sx={{
|
||||||
width: "43px",
|
height: '43px',
|
||||||
height: "auto",
|
width: '43px',
|
||||||
|
'& img': {
|
||||||
|
objectFit: 'fill',
|
||||||
|
},
|
||||||
}}
|
}}
|
||||||
src={LogoSelected}
|
alt={app?.name}
|
||||||
alt="center-icon"
|
src={`${getBaseApiReact()}/arbitrary/THUMBNAIL/${
|
||||||
/>
|
app?.name
|
||||||
</Avatar>
|
}/qortal_avatar?async=true`}
|
||||||
</AppCircle>
|
>
|
||||||
</AppCircleContainer>
|
<img
|
||||||
<AppInfoSnippetMiddle>
|
style={{
|
||||||
<AppInfoAppName>
|
width: '43px',
|
||||||
{app?.metadata?.title || app?.name}
|
height: 'auto',
|
||||||
</AppInfoAppName>
|
}}
|
||||||
<Spacer height="6px" />
|
src={LogoSelected}
|
||||||
<AppInfoUserName>{app?.name}</AppInfoUserName>
|
alt="center-icon"
|
||||||
<Spacer height="3px" />
|
/>
|
||||||
</AppInfoSnippetMiddle>
|
</Avatar>
|
||||||
</AppInfoSnippetLeft>
|
</AppCircle>
|
||||||
<AppInfoSnippetRight></AppInfoSnippetRight>
|
</AppCircleContainer>
|
||||||
</AppInfoSnippetContainer>
|
<AppInfoSnippetMiddle>
|
||||||
<Spacer height="11px" />
|
<AppInfoAppName>
|
||||||
<Box sx={{
|
{app?.metadata?.title || app?.name}
|
||||||
width: '100%',
|
</AppInfoAppName>
|
||||||
display: 'flex',
|
<Spacer height="6px" />
|
||||||
alignItems: 'center',
|
<AppInfoUserName>{app?.name}</AppInfoUserName>
|
||||||
gap: '20px'
|
<Spacer height="3px" />
|
||||||
}}>
|
</AppInfoSnippetMiddle>
|
||||||
<AppDownloadButton
|
</AppInfoSnippetLeft>
|
||||||
onClick={() => {
|
<AppInfoSnippetRight></AppInfoSnippetRight>
|
||||||
setSortablePinnedApps((prev) => {
|
</AppInfoSnippetContainer>
|
||||||
let updatedApps;
|
<Spacer height="11px" />
|
||||||
|
<Box
|
||||||
if (isSelectedAppPinned) {
|
sx={{
|
||||||
// Remove the selected app if it is pinned
|
width: '100%',
|
||||||
updatedApps = prev.filter(
|
display: 'flex',
|
||||||
(item) => !(item?.name === app?.name && item?.service === app?.service)
|
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 {
|
return updatedApps;
|
||||||
// Add the selected app if it is not pinned
|
});
|
||||||
updatedApps = [...prev, {
|
setSettingsLocalLastUpdated(Date.now());
|
||||||
name: app?.name,
|
}}
|
||||||
service: app?.service,
|
sx={{
|
||||||
}];
|
backgroundColor: '#359ff7ff',
|
||||||
}
|
width: '100%',
|
||||||
|
maxWidth: '320px',
|
||||||
saveToLocalStorage('ext_saved_settings', 'sortablePinnedApps', updatedApps)
|
height: '29px',
|
||||||
return updatedApps;
|
opacity: isSelectedAppPinned ? 0.6 : 1,
|
||||||
});
|
}}
|
||||||
setSettingsLocalLastUpdated(Date.now())
|
>
|
||||||
}}
|
<AppDownloadButtonText>
|
||||||
sx={{
|
{isSelectedAppPinned
|
||||||
backgroundColor: "#359ff7ff",
|
? 'Unpin from dashboard'
|
||||||
width: "100%",
|
: 'Pin to dashboard'}
|
||||||
maxWidth: "320px",
|
</AppDownloadButtonText>
|
||||||
height: "29px",
|
</AppDownloadButton>
|
||||||
opacity: isSelectedAppPinned ? 0.6 : 1
|
<AppDownloadButton
|
||||||
}}
|
onClick={() => {
|
||||||
>
|
executeEvent('addTab', {
|
||||||
<AppDownloadButtonText>
|
data: app,
|
||||||
{!isMobile ? (
|
});
|
||||||
<>
|
}}
|
||||||
{isSelectedAppPinned ? 'Unpin from dashboard' : 'Pin to dashboard'}
|
sx={{
|
||||||
</>
|
backgroundColor: isInstalled ? '#0091E1' : '#247C0E',
|
||||||
) : (
|
width: '100%',
|
||||||
<>
|
maxWidth: '320px',
|
||||||
{isSelectedAppPinned ? 'Unpin' : 'Pin'}
|
height: '29px',
|
||||||
</>
|
}}
|
||||||
)}
|
>
|
||||||
|
<AppDownloadButtonText>
|
||||||
</AppDownloadButtonText>
|
{isInstalled ? 'Open' : 'Download'}
|
||||||
</AppDownloadButton>
|
</AppDownloadButtonText>
|
||||||
<AppDownloadButton
|
</AppDownloadButton>
|
||||||
onClick={() => {
|
</Box>
|
||||||
executeEvent("addTab", {
|
</AppsWidthLimiter>
|
||||||
data: app,
|
<Spacer height="20px" />
|
||||||
});
|
<AppsWidthLimiter>
|
||||||
}}
|
<AppsCategoryInfo>
|
||||||
sx={{
|
<AppRating ratingCountPosition="top" myName={myName} app={app} />
|
||||||
backgroundColor: isInstalled ? "#0091E1" : "#247C0E",
|
<Spacer width="16px" />
|
||||||
width: "100%",
|
<Spacer height="40px" width="1px" backgroundColor="white" />
|
||||||
maxWidth: "320px",
|
<Spacer width="16px" />
|
||||||
height: "29px",
|
<AppsCategoryInfoSub>
|
||||||
}}
|
<AppsCategoryInfoLabel>Category:</AppsCategoryInfoLabel>
|
||||||
>
|
<Spacer height="4px" />
|
||||||
<AppDownloadButtonText>
|
<AppsCategoryInfoValue>
|
||||||
{isInstalled ? "Open" : "Download"}
|
{app?.metadata?.categoryName || 'none'}
|
||||||
</AppDownloadButtonText>
|
</AppsCategoryInfoValue>
|
||||||
</AppDownloadButton>
|
</AppsCategoryInfoSub>
|
||||||
</Box>
|
</AppsCategoryInfo>
|
||||||
|
<Spacer height="30px" />
|
||||||
</AppsWidthLimiter>
|
<AppInfoAppName>About this Q-App</AppInfoAppName>
|
||||||
<Spacer height="20px" />
|
</AppsWidthLimiter>
|
||||||
<AppsWidthLimiter>
|
<Spacer height="20px" />
|
||||||
<AppsCategoryInfo>
|
<AppsInfoDescription>
|
||||||
<AppRating ratingCountPosition="top" myName={myName} app={app} />
|
{app?.metadata?.description || 'No description'}
|
||||||
<Spacer width="16px" />
|
</AppsInfoDescription>
|
||||||
<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>
|
</Box>
|
||||||
</AppsLibraryContainer>
|
</AppsLibraryContainer>
|
||||||
);
|
);
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import React from "react";
|
|
||||||
import {
|
import {
|
||||||
AppCircle,
|
AppCircle,
|
||||||
AppCircleContainer,
|
AppCircleContainer,
|
||||||
@ -10,148 +9,176 @@ import {
|
|||||||
AppInfoSnippetMiddle,
|
AppInfoSnippetMiddle,
|
||||||
AppInfoSnippetRight,
|
AppInfoSnippetRight,
|
||||||
AppInfoUserName,
|
AppInfoUserName,
|
||||||
} from "./Apps-styles";
|
} from './Apps-styles';
|
||||||
import { Avatar, ButtonBase } from "@mui/material";
|
import { Avatar, ButtonBase } from '@mui/material';
|
||||||
import { getBaseApiReact, isMobile } from "../../App";
|
import { getBaseApiReact } from '../../App';
|
||||||
import LogoSelected from "../../assets/svgs/LogoSelected.svg";
|
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";
|
export const AppInfoSnippet = ({
|
||||||
import { executeEvent } from "../../utils/events";
|
app,
|
||||||
import { AppRating } from "./AppRating";
|
myName,
|
||||||
import { useRecoilState, useSetRecoilState } from "recoil";
|
isFromCategory,
|
||||||
import { settingsLocalLastUpdatedAtom, sortablePinnedAppsAtom } from "../../atoms/global";
|
parentStyles = {},
|
||||||
import { saveToLocalStorage } from "./AppsNavBar";
|
}) => {
|
||||||
|
const isInstalled = app?.status?.status === 'READY';
|
||||||
|
const [sortablePinnedApps, setSortablePinnedApps] = useRecoilState(
|
||||||
|
sortablePinnedAppsAtom
|
||||||
|
);
|
||||||
|
|
||||||
export const AppInfoSnippet = ({ app, myName, isFromCategory, parentStyles = {} }) => {
|
const isSelectedAppPinned = !!sortablePinnedApps?.find(
|
||||||
|
(item) => item?.name === app?.name && item?.service === app?.service
|
||||||
const isInstalled = app?.status?.status === 'READY'
|
);
|
||||||
const [sortablePinnedApps, setSortablePinnedApps] = useRecoilState(sortablePinnedAppsAtom);
|
const setSettingsLocalLastUpdated = useSetRecoilState(
|
||||||
|
settingsLocalLastUpdatedAtom
|
||||||
const isSelectedAppPinned = !!sortablePinnedApps?.find((item)=> item?.name === app?.name && item?.service === app?.service)
|
);
|
||||||
const setSettingsLocalLastUpdated = useSetRecoilState(settingsLocalLastUpdatedAtom);
|
|
||||||
return (
|
return (
|
||||||
<AppInfoSnippetContainer sx={{
|
<AppInfoSnippetContainer
|
||||||
...parentStyles
|
sx={{
|
||||||
}}>
|
...parentStyles,
|
||||||
|
}}
|
||||||
|
>
|
||||||
<AppInfoSnippetLeft>
|
<AppInfoSnippetLeft>
|
||||||
<ButtonBase
|
<ButtonBase
|
||||||
sx={{
|
sx={{
|
||||||
height: "80px",
|
height: '80px',
|
||||||
width: "60px",
|
width: '60px',
|
||||||
}}
|
}}
|
||||||
onClick={()=> {
|
onClick={() => {
|
||||||
if(isFromCategory){
|
if (isFromCategory) {
|
||||||
executeEvent("selectedAppInfoCategory", {
|
executeEvent('selectedAppInfoCategory', {
|
||||||
|
data: app,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
executeEvent('selectedAppInfo', {
|
||||||
data: app,
|
data: app,
|
||||||
});
|
});
|
||||||
return
|
}}
|
||||||
}
|
>
|
||||||
executeEvent("selectedAppInfo", {
|
<AppCircleContainer>
|
||||||
data: app,
|
<AppCircle
|
||||||
});
|
sx={{
|
||||||
}}
|
border: 'none',
|
||||||
>
|
}}
|
||||||
<AppCircleContainer>
|
>
|
||||||
<AppCircle
|
<Avatar
|
||||||
sx={{
|
sx={{
|
||||||
border: "none",
|
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
|
<AppInfoAppName>{app?.metadata?.title || app?.name}</AppInfoAppName>
|
||||||
sx={{
|
</ButtonBase>
|
||||||
height: "42px",
|
<Spacer height="6px" />
|
||||||
width: "42px",
|
<AppInfoUserName>{app?.name}</AppInfoUserName>
|
||||||
'& 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>
|
|
||||||
<Spacer height="3px" />
|
<Spacer height="3px" />
|
||||||
<AppRating app={app} myName={myName} />
|
<AppRating app={app} myName={myName} />
|
||||||
</AppInfoSnippetMiddle>
|
</AppInfoSnippetMiddle>
|
||||||
</AppInfoSnippetLeft>
|
</AppInfoSnippetLeft>
|
||||||
<AppInfoSnippetRight sx={{
|
<AppInfoSnippetRight
|
||||||
gap: '10px'
|
sx={{
|
||||||
}}>
|
gap: '10px',
|
||||||
{!isMobile && (
|
}}
|
||||||
<AppDownloadButton onClick={()=> {
|
>
|
||||||
|
<AppDownloadButton
|
||||||
setSortablePinnedApps((prev) => {
|
onClick={() => {
|
||||||
let updatedApps;
|
setSortablePinnedApps((prev) => {
|
||||||
|
let updatedApps;
|
||||||
if (isSelectedAppPinned) {
|
|
||||||
// Remove the selected app if it is pinned
|
if (isSelectedAppPinned) {
|
||||||
updatedApps = prev.filter(
|
// Remove the selected app if it is pinned
|
||||||
(item) => !(item?.name === app?.name && item?.service === app?.service)
|
updatedApps = prev.filter(
|
||||||
);
|
(item) =>
|
||||||
} else {
|
!(
|
||||||
// Add the selected app if it is not pinned
|
item?.name === app?.name && item?.service === app?.service
|
||||||
updatedApps = [...prev, {
|
)
|
||||||
name: app?.name,
|
);
|
||||||
service: app?.service,
|
} else {
|
||||||
}];
|
// Add the selected app if it is not pinned
|
||||||
}
|
updatedApps = [
|
||||||
|
...prev,
|
||||||
saveToLocalStorage('ext_saved_settings', 'sortablePinnedApps', updatedApps)
|
{
|
||||||
return updatedApps;
|
name: app?.name,
|
||||||
});
|
service: app?.service,
|
||||||
setSettingsLocalLastUpdated(Date.now())
|
},
|
||||||
}} sx={{
|
];
|
||||||
backgroundColor: '#359ff7ff',
|
}
|
||||||
opacity: isSelectedAppPinned ? 0.6 : 1
|
|
||||||
|
saveToLocalStorage(
|
||||||
}}>
|
'ext_saved_settings',
|
||||||
<AppDownloadButtonText> {isSelectedAppPinned ? 'Unpin' : 'Pin'}</AppDownloadButtonText>
|
'sortablePinnedApps',
|
||||||
|
updatedApps
|
||||||
|
);
|
||||||
|
return updatedApps;
|
||||||
|
});
|
||||||
|
setSettingsLocalLastUpdated(Date.now());
|
||||||
|
}}
|
||||||
|
sx={{
|
||||||
|
backgroundColor: '#359ff7ff',
|
||||||
|
opacity: isSelectedAppPinned ? 0.6 : 1,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<AppDownloadButtonText>
|
||||||
|
{' '}
|
||||||
|
{isSelectedAppPinned ? 'Unpin' : 'Pin'}
|
||||||
|
</AppDownloadButtonText>
|
||||||
</AppDownloadButton>
|
</AppDownloadButton>
|
||||||
)}
|
|
||||||
|
<AppDownloadButton
|
||||||
<AppDownloadButton onClick={()=> {
|
onClick={() => {
|
||||||
|
executeEvent('addTab', {
|
||||||
executeEvent("addTab", {
|
data: app,
|
||||||
data: app
|
});
|
||||||
})
|
}}
|
||||||
}} sx={{
|
sx={{
|
||||||
backgroundColor: isInstalled ? '#0091E1' : '#247C0E',
|
backgroundColor: isInstalled ? '#0091E1' : '#247C0E',
|
||||||
|
}}
|
||||||
}}>
|
>
|
||||||
<AppDownloadButtonText>{isInstalled ? 'Open' : 'Download'}</AppDownloadButtonText>
|
<AppDownloadButtonText>
|
||||||
|
{isInstalled ? 'Open' : 'Download'}
|
||||||
|
</AppDownloadButtonText>
|
||||||
</AppDownloadButton>
|
</AppDownloadButton>
|
||||||
</AppInfoSnippetRight>
|
</AppInfoSnippetRight>
|
||||||
</AppInfoSnippetContainer>
|
</AppInfoSnippetContainer>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { useContext, useEffect, useMemo, useState } from "react";
|
import React, { useContext, useEffect, useState } from 'react';
|
||||||
import {
|
import {
|
||||||
AppCircle,
|
AppCircle,
|
||||||
AppCircleContainer,
|
AppCircleContainer,
|
||||||
@ -19,90 +19,74 @@ import {
|
|||||||
PublishQAppCTAButton,
|
PublishQAppCTAButton,
|
||||||
PublishQAppChoseFile,
|
PublishQAppChoseFile,
|
||||||
PublishQAppInfo,
|
PublishQAppInfo,
|
||||||
} from "./Apps-styles";
|
} from './Apps-styles';
|
||||||
import {
|
import { InputBase, InputLabel, MenuItem, Select } from '@mui/material';
|
||||||
Avatar,
|
import { styled } from '@mui/system';
|
||||||
Box,
|
import UnfoldMoreRoundedIcon from '@mui/icons-material/UnfoldMoreRounded';
|
||||||
ButtonBase,
|
import { Add } from '@mui/icons-material';
|
||||||
InputBase,
|
import { MyContext, getBaseApiReact } from '../../App';
|
||||||
InputLabel,
|
import LogoSelected from '../../assets/svgs/LogoSelected.svg';
|
||||||
MenuItem,
|
import { Spacer } from '../../common/Spacer';
|
||||||
Select,
|
import { executeEvent } from '../../utils/events';
|
||||||
} from "@mui/material";
|
import { useDropzone } from 'react-dropzone';
|
||||||
import {
|
import { LoadingSnackbar } from '../Snackbar/LoadingSnackbar';
|
||||||
Select as BaseSelect,
|
import { CustomizedSnackbars } from '../Snackbar/Snackbar';
|
||||||
SelectProps,
|
import { getFee } from '../../background';
|
||||||
selectClasses,
|
import { fileToBase64 } from '../../utils/fileReading';
|
||||||
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";
|
|
||||||
|
|
||||||
const CustomSelect = styled(Select)({
|
const CustomSelect = styled(Select)({
|
||||||
border: "0.5px solid var(--50-white, #FFFFFF80)",
|
border: '0.5px solid var(--50-white, #FFFFFF80)',
|
||||||
padding: "0px 15px",
|
padding: '0px 15px',
|
||||||
borderRadius: "5px",
|
borderRadius: '5px',
|
||||||
height: "36px",
|
height: '36px',
|
||||||
width: "100%",
|
width: '100%',
|
||||||
maxWidth: "450px",
|
maxWidth: '450px',
|
||||||
"& .MuiSelect-select": {
|
'& .MuiSelect-select': {
|
||||||
padding: "0px",
|
padding: '0px',
|
||||||
},
|
},
|
||||||
"&:hover": {
|
'&:hover': {
|
||||||
borderColor: "none", // Border color on hover
|
borderColor: 'none', // Border color on hover
|
||||||
},
|
},
|
||||||
"&.Mui-focused .MuiOutlinedInput-notchedOutline": {
|
'&.Mui-focused .MuiOutlinedInput-notchedOutline': {
|
||||||
borderColor: "none", // Border color when focused
|
borderColor: 'none', // Border color when focused
|
||||||
},
|
},
|
||||||
"&.Mui-disabled": {
|
'&.Mui-disabled': {
|
||||||
opacity: 0.5, // Lower opacity when disabled
|
opacity: 0.5, // Lower opacity when disabled
|
||||||
},
|
},
|
||||||
"& .MuiSvgIcon-root": {
|
'& .MuiSvgIcon-root': {
|
||||||
color: "var(--50-white, #FFFFFF80)",
|
color: 'var(--50-white, #FFFFFF80)',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const CustomMenuItem = styled(MenuItem)({
|
const CustomMenuItem = styled(MenuItem)({
|
||||||
backgroundColor: "#1f1f1f", // Background for dropdown items
|
backgroundColor: '#1f1f1f', // Background for dropdown items
|
||||||
color: "#ccc",
|
color: '#ccc',
|
||||||
"&:hover": {
|
'&:hover': {
|
||||||
backgroundColor: "#333", // Darker background on hover
|
backgroundColor: '#333', // Darker background on hover
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export const AppPublish = ({ names, categories }) => {
|
export const AppPublish = ({ names, categories }) => {
|
||||||
const [name, setName] = useState("");
|
const [name, setName] = useState('');
|
||||||
const [title, setTitle] = useState("");
|
const [title, setTitle] = useState('');
|
||||||
const [description, setDescription] = useState("");
|
const [description, setDescription] = useState('');
|
||||||
const [category, setCategory] = useState("");
|
const [category, setCategory] = useState('');
|
||||||
const [appType, setAppType] = useState("APP");
|
const [appType, setAppType] = useState('APP');
|
||||||
const [file, setFile] = useState(null);
|
const [file, setFile] = useState(null);
|
||||||
const { show } = useContext(MyContext);
|
const { show } = useContext(MyContext);
|
||||||
|
|
||||||
const [tag1, setTag1] = useState("");
|
const [tag1, setTag1] = useState('');
|
||||||
const [tag2, setTag2] = useState("");
|
const [tag2, setTag2] = useState('');
|
||||||
const [tag3, setTag3] = useState("");
|
const [tag3, setTag3] = useState('');
|
||||||
const [tag4, setTag4] = useState("");
|
const [tag4, setTag4] = useState('');
|
||||||
const [tag5, setTag5] = useState("");
|
const [tag5, setTag5] = useState('');
|
||||||
const [openSnack, setOpenSnack] = useState(false);
|
const [openSnack, setOpenSnack] = useState(false);
|
||||||
const [infoSnack, setInfoSnack] = useState(null);
|
const [infoSnack, setInfoSnack] = useState(null);
|
||||||
const [isLoading, setIsLoading] = useState("");
|
const [isLoading, setIsLoading] = useState('');
|
||||||
const maxFileSize = appType === "APP" ? 50 * 1024 * 1024 : 400 * 1024 * 1024; // 50MB or 400MB
|
const maxFileSize = appType === 'APP' ? 50 * 1024 * 1024 : 400 * 1024 * 1024; // 50MB or 400MB
|
||||||
const { getRootProps, getInputProps } = useDropzone({
|
const { getRootProps, getInputProps } = useDropzone({
|
||||||
accept: {
|
accept: {
|
||||||
"application/zip": [".zip"], // Only accept zip files
|
'application/zip': ['.zip'], // Only accept zip files
|
||||||
},
|
},
|
||||||
maxSize: maxFileSize, // Set the max size based on appType
|
maxSize: maxFileSize, // Set the max size based on appType
|
||||||
multiple: false, // Disable multiple file uploads
|
multiple: false, // Disable multiple file uploads
|
||||||
@ -114,7 +98,7 @@ export const AppPublish = ({ names, categories }) => {
|
|||||||
onDropRejected: (fileRejections) => {
|
onDropRejected: (fileRejections) => {
|
||||||
fileRejections.forEach(({ file, errors }) => {
|
fileRejections.forEach(({ file, errors }) => {
|
||||||
errors.forEach((error) => {
|
errors.forEach((error) => {
|
||||||
if (error.code === "file-too-large") {
|
if (error.code === 'file-too-large') {
|
||||||
console.error(
|
console.error(
|
||||||
`File ${file.name} is too large. Max size allowed is ${
|
`File ${file.name} is too large. Max size allowed is ${
|
||||||
maxFileSize / (1024 * 1024)
|
maxFileSize / (1024 * 1024)
|
||||||
@ -128,13 +112,13 @@ export const AppPublish = ({ names, categories }) => {
|
|||||||
|
|
||||||
const getQapp = React.useCallback(async (name, appType) => {
|
const getQapp = React.useCallback(async (name, appType) => {
|
||||||
try {
|
try {
|
||||||
setIsLoading("Loading app information");
|
setIsLoading('Loading app information');
|
||||||
const url = `${getBaseApiReact()}/arbitrary/resources/search?service=${appType}&mode=ALL&name=${name}&includemetadata=true`;
|
const url = `${getBaseApiReact()}/arbitrary/resources/search?service=${appType}&mode=ALL&name=${name}&includemetadata=true`;
|
||||||
|
|
||||||
const response = await fetch(url, {
|
const response = await fetch(url, {
|
||||||
method: "GET",
|
method: 'GET',
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
'Content-Type': 'application/json',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
if (!response?.ok) return;
|
if (!response?.ok) return;
|
||||||
@ -142,18 +126,18 @@ export const AppPublish = ({ names, categories }) => {
|
|||||||
|
|
||||||
if (responseData?.length > 0) {
|
if (responseData?.length > 0) {
|
||||||
const myApp = responseData[0];
|
const myApp = responseData[0];
|
||||||
setTitle(myApp?.metadata?.title || "");
|
setTitle(myApp?.metadata?.title || '');
|
||||||
setDescription(myApp?.metadata?.description || "");
|
setDescription(myApp?.metadata?.description || '');
|
||||||
setCategory(myApp?.metadata?.category || "");
|
setCategory(myApp?.metadata?.category || '');
|
||||||
setTag1(myApp?.metadata?.tags[0] || "");
|
setTag1(myApp?.metadata?.tags[0] || '');
|
||||||
setTag2(myApp?.metadata?.tags[1] || "");
|
setTag2(myApp?.metadata?.tags[1] || '');
|
||||||
setTag3(myApp?.metadata?.tags[2] || "");
|
setTag3(myApp?.metadata?.tags[2] || '');
|
||||||
setTag4(myApp?.metadata?.tags[3] || "");
|
setTag4(myApp?.metadata?.tags[3] || '');
|
||||||
setTag5(myApp?.metadata?.tags[4] || "");
|
setTag5(myApp?.metadata?.tags[4] || '');
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
} finally {
|
} finally {
|
||||||
setIsLoading("");
|
setIsLoading('');
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
@ -173,12 +157,12 @@ export const AppPublish = ({ names, categories }) => {
|
|||||||
file,
|
file,
|
||||||
};
|
};
|
||||||
const requiredFields = [
|
const requiredFields = [
|
||||||
"name",
|
'name',
|
||||||
"title",
|
'title',
|
||||||
"description",
|
'description',
|
||||||
"category",
|
'category',
|
||||||
"appType",
|
'appType',
|
||||||
"file",
|
'file',
|
||||||
];
|
];
|
||||||
|
|
||||||
const missingFields: string[] = [];
|
const missingFields: string[] = [];
|
||||||
@ -188,32 +172,33 @@ export const AppPublish = ({ names, categories }) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (missingFields.length > 0) {
|
if (missingFields.length > 0) {
|
||||||
const missingFieldsString = missingFields.join(", ");
|
const missingFieldsString = missingFields.join(', ');
|
||||||
const errorMsg = `Missing fields: ${missingFieldsString}`;
|
const errorMsg = `Missing fields: ${missingFieldsString}`;
|
||||||
throw new Error(errorMsg);
|
throw new Error(errorMsg);
|
||||||
}
|
}
|
||||||
const fee = await getFee("ARBITRARY");
|
const fee = await getFee('ARBITRARY');
|
||||||
|
|
||||||
await show({
|
await show({
|
||||||
message: "Would you like to publish this app?",
|
message: 'Would you like to publish this app?',
|
||||||
publishFee: fee.fee + " QORT",
|
publishFee: fee.fee + ' QORT',
|
||||||
});
|
});
|
||||||
setIsLoading("Publishing... Please wait.");
|
setIsLoading('Publishing... Please wait.');
|
||||||
const fileBase64 = await fileToBase64(file);
|
const fileBase64 = await fileToBase64(file);
|
||||||
await new Promise((res, rej) => {
|
await new Promise((res, rej) => {
|
||||||
window.sendMessage("publishOnQDN", {
|
window
|
||||||
data: fileBase64,
|
.sendMessage('publishOnQDN', {
|
||||||
service: appType,
|
data: fileBase64,
|
||||||
title,
|
service: appType,
|
||||||
description,
|
title,
|
||||||
category,
|
description,
|
||||||
tag1,
|
category,
|
||||||
tag2,
|
tag1,
|
||||||
tag3,
|
tag2,
|
||||||
tag4,
|
tag3,
|
||||||
tag5,
|
tag4,
|
||||||
uploadType: "zip",
|
tag5,
|
||||||
})
|
uploadType: 'zip',
|
||||||
|
})
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
if (!response?.error) {
|
if (!response?.error) {
|
||||||
res(response);
|
res(response);
|
||||||
@ -222,14 +207,13 @@ export const AppPublish = ({ names, categories }) => {
|
|||||||
rej(response.error);
|
rej(response.error);
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
rej(error.message || "An error occurred");
|
rej(error.message || 'An error occurred');
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
setInfoSnack({
|
setInfoSnack({
|
||||||
type: "success",
|
type: 'success',
|
||||||
message:
|
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);
|
setOpenSnack(true);
|
||||||
const dataObj = {
|
const dataObj = {
|
||||||
@ -242,35 +226,49 @@ export const AppPublish = ({ names, categories }) => {
|
|||||||
},
|
},
|
||||||
created: Date.now(),
|
created: Date.now(),
|
||||||
};
|
};
|
||||||
executeEvent("addTab", {
|
executeEvent('addTab', {
|
||||||
data: dataObj,
|
data: dataObj,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setInfoSnack({
|
setInfoSnack({
|
||||||
type: "error",
|
type: 'error',
|
||||||
message: error?.message || "Unable to publish app",
|
message: error?.message || 'Unable to publish app',
|
||||||
});
|
});
|
||||||
setOpenSnack(true);
|
setOpenSnack(true);
|
||||||
} finally {
|
} finally {
|
||||||
setIsLoading("");
|
setIsLoading('');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AppsLibraryContainer sx={{
|
<AppsLibraryContainer
|
||||||
height: !isMobile ? '100%' : 'auto',
|
sx={{
|
||||||
paddingTop: !isMobile && '30px',
|
alignItems: 'center',
|
||||||
alignItems: !isMobile && 'center'
|
height: '100%',
|
||||||
}}>
|
paddingTop: '30px',
|
||||||
<AppsWidthLimiter sx={{
|
}}
|
||||||
width: !isMobile ? 'auto' : '90%'
|
>
|
||||||
}}>
|
<AppsWidthLimiter
|
||||||
|
sx={{
|
||||||
|
width: 'auto',
|
||||||
|
}}
|
||||||
|
>
|
||||||
<AppLibrarySubTitle>Create Apps!</AppLibrarySubTitle>
|
<AppLibrarySubTitle>Create Apps!</AppLibrarySubTitle>
|
||||||
|
|
||||||
<Spacer height="18px" />
|
<Spacer height="18px" />
|
||||||
|
|
||||||
<PublishQAppInfo>
|
<PublishQAppInfo>
|
||||||
Note: Currently, only one App and Website is allowed per Name.
|
Note: Currently, only one App and Website is allowed per Name.
|
||||||
</PublishQAppInfo>
|
</PublishQAppInfo>
|
||||||
|
|
||||||
<Spacer height="18px" />
|
<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
|
<CustomSelect
|
||||||
placeholder="Select Name/App"
|
placeholder="Select Name/App"
|
||||||
displayEmpty
|
displayEmpty
|
||||||
@ -280,19 +278,26 @@ export const AppPublish = ({ names, categories }) => {
|
|||||||
<CustomMenuItem value="">
|
<CustomMenuItem value="">
|
||||||
<em
|
<em
|
||||||
style={{
|
style={{
|
||||||
color: "var(--50-white, #FFFFFF80)",
|
color: 'var(--50-white, #FFFFFF80)',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Select Name/App
|
Select Name/App
|
||||||
</em>{" "}
|
</em>{' '}
|
||||||
{/* This is the placeholder item */}
|
{/* This is the placeholder item */}
|
||||||
</CustomMenuItem>
|
</CustomMenuItem>
|
||||||
{names.map((name) => {
|
{names.map((name) => {
|
||||||
return <CustomMenuItem value={name}>{name}</CustomMenuItem>;
|
return <CustomMenuItem value={name}>{name}</CustomMenuItem>;
|
||||||
})}
|
})}
|
||||||
</CustomSelect>
|
</CustomSelect>
|
||||||
|
|
||||||
<Spacer height="15px" />
|
<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
|
<CustomSelect
|
||||||
placeholder="SERVICE TYPE"
|
placeholder="SERVICE TYPE"
|
||||||
displayEmpty
|
displayEmpty
|
||||||
@ -302,59 +307,79 @@ export const AppPublish = ({ names, categories }) => {
|
|||||||
<CustomMenuItem value="">
|
<CustomMenuItem value="">
|
||||||
<em
|
<em
|
||||||
style={{
|
style={{
|
||||||
color: "var(--50-white, #FFFFFF80)",
|
color: 'var(--50-white, #FFFFFF80)',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Select App Type
|
Select App Type
|
||||||
</em>{" "}
|
</em>{' '}
|
||||||
{/* This is the placeholder item */}
|
{/* This is the placeholder item */}
|
||||||
</CustomMenuItem>
|
</CustomMenuItem>
|
||||||
<CustomMenuItem value={"APP"}>App</CustomMenuItem>
|
<CustomMenuItem value={'APP'}>App</CustomMenuItem>
|
||||||
<CustomMenuItem value={"WEBSITE"}>Website</CustomMenuItem>
|
<CustomMenuItem value={'WEBSITE'}>Website</CustomMenuItem>
|
||||||
</CustomSelect>
|
</CustomSelect>
|
||||||
|
|
||||||
<Spacer height="15px" />
|
<Spacer height="15px" />
|
||||||
<InputLabel sx={{ color: '#888', fontSize: '14px', marginBottom: '2px' }}>Title</InputLabel>
|
|
||||||
|
<InputLabel
|
||||||
|
sx={{ color: '#888', fontSize: '14px', marginBottom: '2px' }}
|
||||||
|
>
|
||||||
|
Title
|
||||||
|
</InputLabel>
|
||||||
|
|
||||||
<InputBase
|
<InputBase
|
||||||
value={title}
|
value={title}
|
||||||
onChange={(e) => setTitle(e.target.value)}
|
onChange={(e) => setTitle(e.target.value)}
|
||||||
sx={{
|
sx={{
|
||||||
border: "0.5px solid var(--50-white, #FFFFFF80)",
|
border: '0.5px solid var(--50-white, #FFFFFF80)',
|
||||||
padding: "0px 15px",
|
padding: '0px 15px',
|
||||||
borderRadius: "5px",
|
borderRadius: '5px',
|
||||||
height: "36px",
|
height: '36px',
|
||||||
width: "100%",
|
width: '100%',
|
||||||
maxWidth: "450px",
|
maxWidth: '450px',
|
||||||
}}
|
}}
|
||||||
placeholder="Title"
|
placeholder="Title"
|
||||||
inputProps={{
|
inputProps={{
|
||||||
"aria-label": "Title",
|
'aria-label': 'Title',
|
||||||
fontSize: "14px",
|
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",
|
|
||||||
fontWeight: 400,
|
fontWeight: 400,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Spacer height="15px" />
|
<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
|
<CustomSelect
|
||||||
displayEmpty
|
displayEmpty
|
||||||
placeholder="Select Category"
|
placeholder="Select Category"
|
||||||
@ -364,11 +389,11 @@ export const AppPublish = ({ names, categories }) => {
|
|||||||
<CustomMenuItem value="">
|
<CustomMenuItem value="">
|
||||||
<em
|
<em
|
||||||
style={{
|
style={{
|
||||||
color: "var(--50-white, #FFFFFF80)",
|
color: 'var(--50-white, #FFFFFF80)',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Select Category
|
Select Category
|
||||||
</em>{" "}
|
</em>{' '}
|
||||||
{/* This is the placeholder item */}
|
{/* This is the placeholder item */}
|
||||||
</CustomMenuItem>
|
</CustomMenuItem>
|
||||||
{categories?.map((category) => {
|
{categories?.map((category) => {
|
||||||
@ -379,23 +404,30 @@ export const AppPublish = ({ names, categories }) => {
|
|||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</CustomSelect>
|
</CustomSelect>
|
||||||
|
|
||||||
<Spacer height="15px" />
|
<Spacer height="15px" />
|
||||||
<InputLabel sx={{ color: '#888', fontSize: '14px', marginBottom: '2px' }}>Tags</InputLabel>
|
|
||||||
|
<InputLabel
|
||||||
|
sx={{ color: '#888', fontSize: '14px', marginBottom: '2px' }}
|
||||||
|
>
|
||||||
|
Tags
|
||||||
|
</InputLabel>
|
||||||
|
|
||||||
<AppPublishTagsContainer>
|
<AppPublishTagsContainer>
|
||||||
<InputBase
|
<InputBase
|
||||||
value={tag1}
|
value={tag1}
|
||||||
onChange={(e) => setTag1(e.target.value)}
|
onChange={(e) => setTag1(e.target.value)}
|
||||||
sx={{
|
sx={{
|
||||||
border: "0.5px solid var(--50-white, #FFFFFF80)",
|
border: '0.5px solid var(--50-white, #FFFFFF80)',
|
||||||
padding: "0px 15px",
|
padding: '0px 15px',
|
||||||
borderRadius: "5px",
|
borderRadius: '5px',
|
||||||
height: "36px",
|
height: '36px',
|
||||||
width: "100px",
|
width: '100px',
|
||||||
}}
|
}}
|
||||||
placeholder="Tag 1"
|
placeholder="Tag 1"
|
||||||
inputProps={{
|
inputProps={{
|
||||||
"aria-label": "Tag 1",
|
'aria-label': 'Tag 1',
|
||||||
fontSize: "14px",
|
fontSize: '14px',
|
||||||
fontWeight: 400,
|
fontWeight: 400,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@ -403,16 +435,16 @@ export const AppPublish = ({ names, categories }) => {
|
|||||||
value={tag2}
|
value={tag2}
|
||||||
onChange={(e) => setTag2(e.target.value)}
|
onChange={(e) => setTag2(e.target.value)}
|
||||||
sx={{
|
sx={{
|
||||||
border: "0.5px solid var(--50-white, #FFFFFF80)",
|
border: '0.5px solid var(--50-white, #FFFFFF80)',
|
||||||
padding: "0px 15px",
|
padding: '0px 15px',
|
||||||
borderRadius: "5px",
|
borderRadius: '5px',
|
||||||
height: "36px",
|
height: '36px',
|
||||||
width: "100px",
|
width: '100px',
|
||||||
}}
|
}}
|
||||||
placeholder="Tag 2"
|
placeholder="Tag 2"
|
||||||
inputProps={{
|
inputProps={{
|
||||||
"aria-label": "Tag 2",
|
'aria-label': 'Tag 2',
|
||||||
fontSize: "14px",
|
fontSize: '14px',
|
||||||
fontWeight: 400,
|
fontWeight: 400,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@ -420,16 +452,16 @@ export const AppPublish = ({ names, categories }) => {
|
|||||||
value={tag3}
|
value={tag3}
|
||||||
onChange={(e) => setTag3(e.target.value)}
|
onChange={(e) => setTag3(e.target.value)}
|
||||||
sx={{
|
sx={{
|
||||||
border: "0.5px solid var(--50-white, #FFFFFF80)",
|
border: '0.5px solid var(--50-white, #FFFFFF80)',
|
||||||
padding: "0px 15px",
|
padding: '0px 15px',
|
||||||
borderRadius: "5px",
|
borderRadius: '5px',
|
||||||
height: "36px",
|
height: '36px',
|
||||||
width: "100px",
|
width: '100px',
|
||||||
}}
|
}}
|
||||||
placeholder="Tag 3"
|
placeholder="Tag 3"
|
||||||
inputProps={{
|
inputProps={{
|
||||||
"aria-label": "Tag 3",
|
'aria-label': 'Tag 3',
|
||||||
fontSize: "14px",
|
fontSize: '14px',
|
||||||
fontWeight: 400,
|
fontWeight: 400,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@ -437,16 +469,16 @@ export const AppPublish = ({ names, categories }) => {
|
|||||||
value={tag4}
|
value={tag4}
|
||||||
onChange={(e) => setTag4(e.target.value)}
|
onChange={(e) => setTag4(e.target.value)}
|
||||||
sx={{
|
sx={{
|
||||||
border: "0.5px solid var(--50-white, #FFFFFF80)",
|
border: '0.5px solid var(--50-white, #FFFFFF80)',
|
||||||
padding: "0px 15px",
|
padding: '0px 15px',
|
||||||
borderRadius: "5px",
|
borderRadius: '5px',
|
||||||
height: "36px",
|
height: '36px',
|
||||||
width: "100px",
|
width: '100px',
|
||||||
}}
|
}}
|
||||||
placeholder="Tag 4"
|
placeholder="Tag 4"
|
||||||
inputProps={{
|
inputProps={{
|
||||||
"aria-label": "Tag 4",
|
'aria-label': 'Tag 4',
|
||||||
fontSize: "14px",
|
fontSize: '14px',
|
||||||
fontWeight: 400,
|
fontWeight: 400,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@ -454,27 +486,31 @@ export const AppPublish = ({ names, categories }) => {
|
|||||||
value={tag5}
|
value={tag5}
|
||||||
onChange={(e) => setTag5(e.target.value)}
|
onChange={(e) => setTag5(e.target.value)}
|
||||||
sx={{
|
sx={{
|
||||||
border: "0.5px solid var(--50-white, #FFFFFF80)",
|
border: '0.5px solid var(--50-white, #FFFFFF80)',
|
||||||
padding: "0px 15px",
|
padding: '0px 15px',
|
||||||
borderRadius: "5px",
|
borderRadius: '5px',
|
||||||
height: "36px",
|
height: '36px',
|
||||||
width: "100px",
|
width: '100px',
|
||||||
}}
|
}}
|
||||||
placeholder="Tag 5"
|
placeholder="Tag 5"
|
||||||
inputProps={{
|
inputProps={{
|
||||||
"aria-label": "Tag 5",
|
'aria-label': 'Tag 5',
|
||||||
fontSize: "14px",
|
fontSize: '14px',
|
||||||
fontWeight: 400,
|
fontWeight: 400,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</AppPublishTagsContainer>
|
</AppPublishTagsContainer>
|
||||||
|
|
||||||
<Spacer height="30px" />
|
<Spacer height="30px" />
|
||||||
|
|
||||||
<PublishQAppInfo>
|
<PublishQAppInfo>
|
||||||
Select .zip file containing static content:{" "}
|
Select .zip file containing static content:{' '}
|
||||||
</PublishQAppInfo>
|
</PublishQAppInfo>
|
||||||
|
|
||||||
<Spacer height="10px" />
|
<Spacer height="10px" />
|
||||||
|
|
||||||
<PublishQAppInfo>{`(${
|
<PublishQAppInfo>{`(${
|
||||||
appType === "APP" ? "50mb" : "400mb"
|
appType === 'APP' ? '50mb' : '400mb'
|
||||||
} MB maximum)`}</PublishQAppInfo>
|
} MB maximum)`}</PublishQAppInfo>
|
||||||
{file && (
|
{file && (
|
||||||
<>
|
<>
|
||||||
@ -484,21 +520,25 @@ export const AppPublish = ({ names, categories }) => {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
<Spacer height="18px" />
|
<Spacer height="18px" />
|
||||||
|
|
||||||
<PublishQAppChoseFile {...getRootProps()}>
|
<PublishQAppChoseFile {...getRootProps()}>
|
||||||
{" "}
|
{' '}
|
||||||
<input {...getInputProps()} />
|
<input {...getInputProps()} />
|
||||||
Choose File
|
Choose File
|
||||||
</PublishQAppChoseFile>
|
</PublishQAppChoseFile>
|
||||||
|
|
||||||
<Spacer height="35px" />
|
<Spacer height="35px" />
|
||||||
|
|
||||||
<PublishQAppCTAButton
|
<PublishQAppCTAButton
|
||||||
sx={{
|
sx={{
|
||||||
alignSelf: "center",
|
alignSelf: 'center',
|
||||||
}}
|
}}
|
||||||
onClick={publishApp}
|
onClick={publishApp}
|
||||||
>
|
>
|
||||||
Publish
|
Publish
|
||||||
</PublishQAppCTAButton>
|
</PublishQAppCTAButton>
|
||||||
</AppsWidthLimiter>
|
</AppsWidthLimiter>
|
||||||
|
|
||||||
<LoadingSnackbar
|
<LoadingSnackbar
|
||||||
open={!!isLoading}
|
open={!!isLoading}
|
||||||
info={{
|
info={{
|
||||||
@ -512,7 +552,6 @@ export const AppPublish = ({ names, categories }) => {
|
|||||||
info={infoSnack}
|
info={infoSnack}
|
||||||
setInfo={setInfoSnack}
|
setInfo={setInfoSnack}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
</AppsLibraryContainer>
|
</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";
|
export const AppViewer = React.forwardRef(
|
||||||
import { MyContext, getBaseApiReact, isMobile } from "../../App";
|
({ 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";
|
useEffect(() => {
|
||||||
import { useFrame } from "react-frame-component";
|
if (app?.isPreview) return;
|
||||||
import { useQortalMessageListener } from "./useQortalMessageListener";
|
if (isDevMode) {
|
||||||
import { useThemeContext } from "../Theme/ThemeContext";
|
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) => {
|
||||||
export const AppViewer = React.forwardRef(({ app , hide, isDevMode, skipAuth}, iframeRef) => {
|
const { tabId } = e.detail;
|
||||||
const { rootHeight } = useContext(MyContext);
|
if (tabId === app?.tabId) {
|
||||||
// const iframeRef = useRef(null);
|
if (isDevMode) {
|
||||||
const { window: frameWindow } = useFrame();
|
resetHistory();
|
||||||
const {path, history, changeCurrentIndex, resetHistory} = useQortalMessageListener(frameWindow, iframeRef, app?.tabId, isDevMode, app?.name, app?.service, skipAuth)
|
if (!app?.isPreview || app?.isPrivate) {
|
||||||
const [url, setUrl] = useState('')
|
setUrl(app?.url + `?time=${Date.now()}`);
|
||||||
const { themeMode } = useThemeContext();
|
}
|
||||||
|
return;
|
||||||
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()}`)
|
|
||||||
}
|
}
|
||||||
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(()=> {
|
useEffect(() => {
|
||||||
if(!iframeRef?.current) return
|
subscribeToEvent('refreshApp', refreshAppFunc);
|
||||||
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])
|
|
||||||
|
|
||||||
const removeTrailingSlash = (str) => str.replace(/\/$/, '');
|
return () => {
|
||||||
const copyLinkFunc = (e) => {
|
unsubscribeFromEvent('refreshApp', refreshAppFunc);
|
||||||
const {tabId} = e.detail
|
};
|
||||||
if(tabId === app?.tabId){
|
}, [app, path, isDevMode]);
|
||||||
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);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
subscribeToEvent("copyLink", copyLinkFunc);
|
if (!iframeRef?.current) return;
|
||||||
|
const targetOrigin = iframeRef.current
|
||||||
return () => {
|
? new URL(iframeRef.current.src).origin
|
||||||
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 : "*";
|
|
||||||
// Send the navigation command after setting up the listener and timeout
|
// Send the navigation command after setting up the listener and timeout
|
||||||
iframeRef.current.contentWindow.postMessage(
|
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
|
const removeTrailingSlash = (str) => str.replace(/\/$/, '');
|
||||||
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) => {
|
const copyLinkFunc = (e) => {
|
||||||
|
const { tabId } = e.detail;
|
||||||
navigateBackInIframe()
|
if (tabId === app?.tabId) {
|
||||||
};
|
let link = 'qortal://' + app?.service + '/' + app?.name;
|
||||||
|
if (path && path.startsWith('/')) {
|
||||||
useEffect(() => {
|
link = link + removeTrailingSlash(path);
|
||||||
if(!app?.tabId) return
|
}
|
||||||
subscribeToEvent(`navigateBackApp-${app?.tabId}`, navigateBackAppFunc);
|
if (path && !path.startsWith('/')) {
|
||||||
|
link = link + '/' + removeTrailingSlash(path);
|
||||||
return () => {
|
}
|
||||||
unsubscribeFromEvent(`navigateBackApp-${app?.tabId}`, navigateBackAppFunc);
|
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
|
return () => {
|
||||||
const navigateForwardInIframe = async () => {
|
unsubscribeFromEvent('copyLink', copyLinkFunc);
|
||||||
|
};
|
||||||
|
}, [app, path]);
|
||||||
|
|
||||||
|
// Function to navigate back in iframe
|
||||||
if (iframeRef.current && iframeRef.current.contentWindow) {
|
const navigateBackInIframe = async () => {
|
||||||
const targetOrigin = iframeRef.current ? new URL(iframeRef.current.src).origin : "*";
|
if (
|
||||||
iframeRef.current.contentWindow.postMessage(
|
iframeRef.current &&
|
||||||
{ action: 'NAVIGATE_FORWARD'},
|
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
|
targetOrigin
|
||||||
);
|
);
|
||||||
} else {
|
// Update the current index locally
|
||||||
console.log('Iframe not accessible or does not have a content window.');
|
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 { AppViewer } from './AppViewer';
|
||||||
import Frame from 'react-frame-component';
|
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 AppViewerContainer = React.forwardRef(
|
||||||
const { rootHeight } = useContext(MyContext);
|
({ app, isSelected, hide, isDevMode, customHeight, skipAuth }, ref) => {
|
||||||
|
const { rootHeight } = useContext(MyContext);
|
||||||
|
|
||||||
|
return (
|
||||||
return (
|
<Frame
|
||||||
<Frame
|
id={`browser-iframe-${app?.tabId}`}
|
||||||
id={`browser-iframe-${app?.tabId}`}
|
head={
|
||||||
|
<>
|
||||||
head={
|
<style>
|
||||||
<>
|
{`
|
||||||
<style>
|
|
||||||
{`
|
|
||||||
body {
|
body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
@ -28,24 +27,31 @@ const AppViewerContainer = React.forwardRef(({ app, isSelected, hide, isDevMode,
|
|||||||
}
|
}
|
||||||
.frame-content {
|
.frame-content {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
height: ${!isMobile ? '100vh' : `calc(${rootHeight} - 60px - 45px)`};
|
height: '100vh';
|
||||||
}
|
}
|
||||||
`}
|
`}
|
||||||
</style>
|
</style>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
style={{
|
style={{
|
||||||
position: (!isSelected || hide) && 'fixed',
|
border: 'none',
|
||||||
left: (!isSelected || hide) && '-200vw',
|
height: '100vh',
|
||||||
height: customHeight ? customHeight : !isMobile ? '100vh' : `calc(${rootHeight} - 60px - 45px)`,
|
left: (!isSelected || hide) && '-200vw',
|
||||||
border: 'none',
|
overflow: 'hidden',
|
||||||
width: '100%',
|
position: (!isSelected || hide) && 'fixed',
|
||||||
overflow: 'hidden',
|
width: '100%',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<AppViewer skipAuth={skipAuth} app={app} ref={ref} hide={!isSelected || hide} isDevMode={isDevMode} />
|
<AppViewer
|
||||||
</Frame>
|
skipAuth={skipAuth}
|
||||||
);
|
app={app}
|
||||||
});
|
ref={ref}
|
||||||
|
hide={!isSelected || hide}
|
||||||
|
isDevMode={isDevMode}
|
||||||
|
/>
|
||||||
|
</Frame>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
export default AppViewerContainer;
|
export default AppViewerContainer;
|
||||||
|
@ -137,9 +137,9 @@ export const AppCircle = styled(Box)(({ theme }) => ({
|
|||||||
theme.palette.mode === 'dark'
|
theme.palette.mode === 'dark'
|
||||||
? 'rgb(209, 209, 209)'
|
? 'rgb(209, 209, 209)'
|
||||||
: 'rgba(41, 41, 43, 1)',
|
: 'rgba(41, 41, 43, 1)',
|
||||||
borderWidth: '1px',
|
|
||||||
borderRadius: '50%',
|
borderRadius: '50%',
|
||||||
borderStyle: 'solid',
|
borderStyle: 'solid',
|
||||||
|
borderWidth: '1px',
|
||||||
color: theme.palette.text.primary,
|
color: theme.palette.text.primary,
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
|
@ -22,7 +22,7 @@ import {
|
|||||||
Input,
|
Input,
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import { Add } from '@mui/icons-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 LogoSelected from '../../assets/svgs/LogoSelected.svg';
|
||||||
import { executeEvent } from '../../utils/events';
|
import { executeEvent } from '../../utils/events';
|
||||||
import { Spacer } from '../../common/Spacer';
|
import { Spacer } from '../../common/Spacer';
|
||||||
@ -279,7 +279,9 @@ export const AppsDevModeHome = ({
|
|||||||
Dev Mode Apps
|
Dev Mode Apps
|
||||||
</AppLibrarySubTitle>
|
</AppLibrarySubTitle>
|
||||||
</AppsContainer>
|
</AppsContainer>
|
||||||
|
|
||||||
<Spacer height="45px" />
|
<Spacer height="45px" />
|
||||||
|
|
||||||
<AppsContainer
|
<AppsContainer
|
||||||
sx={{
|
sx={{
|
||||||
gap: '75px',
|
gap: '75px',
|
||||||
@ -293,7 +295,7 @@ export const AppsDevModeHome = ({
|
|||||||
>
|
>
|
||||||
<AppCircleContainer
|
<AppCircleContainer
|
||||||
sx={{
|
sx={{
|
||||||
gap: !isMobile ? '10px' : '5px',
|
gap: '10px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<AppCircle>
|
<AppCircle>
|
||||||
@ -302,6 +304,7 @@ export const AppsDevModeHome = ({
|
|||||||
<AppCircleLabel>Server</AppCircleLabel>
|
<AppCircleLabel>Server</AppCircleLabel>
|
||||||
</AppCircleContainer>
|
</AppCircleContainer>
|
||||||
</ButtonBase>
|
</ButtonBase>
|
||||||
|
|
||||||
<ButtonBase
|
<ButtonBase
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
addPreviewApp();
|
addPreviewApp();
|
||||||
@ -309,15 +312,17 @@ export const AppsDevModeHome = ({
|
|||||||
>
|
>
|
||||||
<AppCircleContainer
|
<AppCircleContainer
|
||||||
sx={{
|
sx={{
|
||||||
gap: !isMobile ? '10px' : '5px',
|
gap: '10px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<AppCircle>
|
<AppCircle>
|
||||||
<Add>+</Add>
|
<Add>+</Add>
|
||||||
</AppCircle>
|
</AppCircle>
|
||||||
|
|
||||||
<AppCircleLabel>Zip</AppCircleLabel>
|
<AppCircleLabel>Zip</AppCircleLabel>
|
||||||
</AppCircleContainer>
|
</AppCircleContainer>
|
||||||
</ButtonBase>
|
</ButtonBase>
|
||||||
|
|
||||||
<ButtonBase
|
<ButtonBase
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
addPreviewAppWithDirectory();
|
addPreviewAppWithDirectory();
|
||||||
@ -325,7 +330,7 @@ export const AppsDevModeHome = ({
|
|||||||
>
|
>
|
||||||
<AppCircleContainer
|
<AppCircleContainer
|
||||||
sx={{
|
sx={{
|
||||||
gap: !isMobile ? '10px' : '5px',
|
gap: '10px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<AppCircle>
|
<AppCircle>
|
||||||
@ -334,6 +339,7 @@ export const AppsDevModeHome = ({
|
|||||||
<AppCircleLabel>Directory</AppCircleLabel>
|
<AppCircleLabel>Directory</AppCircleLabel>
|
||||||
</AppCircleContainer>
|
</AppCircleContainer>
|
||||||
</ButtonBase>
|
</ButtonBase>
|
||||||
|
|
||||||
<ButtonBase
|
<ButtonBase
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
executeEvent('appsDevModeAddTab', {
|
executeEvent('appsDevModeAddTab', {
|
||||||
@ -347,7 +353,7 @@ export const AppsDevModeHome = ({
|
|||||||
>
|
>
|
||||||
<AppCircleContainer
|
<AppCircleContainer
|
||||||
sx={{
|
sx={{
|
||||||
gap: !isMobile ? '10px' : '5px',
|
gap: '10px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<AppCircle>
|
<AppCircle>
|
||||||
@ -371,9 +377,11 @@ export const AppsDevModeHome = ({
|
|||||||
/>
|
/>
|
||||||
</Avatar>
|
</Avatar>
|
||||||
</AppCircle>
|
</AppCircle>
|
||||||
|
|
||||||
<AppCircleLabel>Q-Sandbox</AppCircleLabel>
|
<AppCircleLabel>Q-Sandbox</AppCircleLabel>
|
||||||
</AppCircleContainer>
|
</AppCircleContainer>
|
||||||
</ButtonBase>
|
</ButtonBase>
|
||||||
|
|
||||||
<ButtonBase
|
<ButtonBase
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
executeEvent('appsDevModeAddTab', {
|
executeEvent('appsDevModeAddTab', {
|
||||||
@ -387,7 +395,7 @@ export const AppsDevModeHome = ({
|
|||||||
>
|
>
|
||||||
<AppCircleContainer
|
<AppCircleContainer
|
||||||
sx={{
|
sx={{
|
||||||
gap: !isMobile ? '10px' : '5px',
|
gap: '10px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<AppCircle>
|
<AppCircle>
|
||||||
@ -411,10 +419,12 @@ export const AppsDevModeHome = ({
|
|||||||
/>
|
/>
|
||||||
</Avatar>
|
</Avatar>
|
||||||
</AppCircle>
|
</AppCircle>
|
||||||
|
|
||||||
<AppCircleLabel>API</AppCircleLabel>
|
<AppCircleLabel>API</AppCircleLabel>
|
||||||
</AppCircleContainer>
|
</AppCircleContainer>
|
||||||
</ButtonBase>
|
</ButtonBase>
|
||||||
</AppsContainer>
|
</AppsContainer>
|
||||||
|
|
||||||
{isShow && (
|
{isShow && (
|
||||||
<Dialog
|
<Dialog
|
||||||
open={isShow}
|
open={isShow}
|
||||||
@ -429,6 +439,7 @@ export const AppsDevModeHome = ({
|
|||||||
<DialogTitle id="alert-dialog-title">
|
<DialogTitle id="alert-dialog-title">
|
||||||
{'Add custom framework'}
|
{'Add custom framework'}
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
|
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
@ -460,6 +471,7 @@ export const AppsDevModeHome = ({
|
|||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
|
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
<Button variant="contained" onClick={onCancel}>
|
<Button variant="contained" onClick={onCancel}>
|
||||||
Close
|
Close
|
||||||
|
@ -1,17 +1,12 @@
|
|||||||
import React, { useMemo, useState } from 'react';
|
|
||||||
import {
|
import {
|
||||||
AppCircle,
|
AppCircle,
|
||||||
AppCircleContainer,
|
AppCircleContainer,
|
||||||
AppCircleLabel,
|
AppCircleLabel,
|
||||||
AppLibrarySubTitle,
|
AppLibrarySubTitle,
|
||||||
AppsContainer,
|
AppsContainer,
|
||||||
AppsParent,
|
|
||||||
} from './Apps-styles';
|
} from './Apps-styles';
|
||||||
import { Avatar, ButtonBase } from '@mui/material';
|
import { ButtonBase } from '@mui/material';
|
||||||
import { Add } from '@mui/icons-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 { SortablePinnedApps } from './SortablePinnedApps';
|
||||||
import { Spacer } from '../../common/Spacer';
|
import { Spacer } from '../../common/Spacer';
|
||||||
|
|
||||||
@ -35,7 +30,7 @@ export const AppsHome = ({ setMode, myApp, myWebsite, availableQapps }) => {
|
|||||||
>
|
>
|
||||||
<AppCircleContainer
|
<AppCircleContainer
|
||||||
sx={{
|
sx={{
|
||||||
gap: !isMobile ? '10px' : '5px',
|
gap: '10px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<AppCircle>
|
<AppCircle>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import {
|
import {
|
||||||
AppCircle,
|
AppCircle,
|
||||||
AppCircleContainer,
|
AppCircleContainer,
|
||||||
@ -8,7 +8,6 @@ import {
|
|||||||
} from './Apps-styles';
|
} from './Apps-styles';
|
||||||
import { Box, ButtonBase, Input, useTheme } from '@mui/material';
|
import { Box, ButtonBase, Input, useTheme } from '@mui/material';
|
||||||
import { Add } from '@mui/icons-material';
|
import { Add } from '@mui/icons-material';
|
||||||
import { isMobile } from '../../App';
|
|
||||||
import { executeEvent } from '../../utils/events';
|
import { executeEvent } from '../../utils/events';
|
||||||
import { Spacer } from '../../common/Spacer';
|
import { Spacer } from '../../common/Spacer';
|
||||||
import { SortablePinnedApps } from './SortablePinnedApps';
|
import { SortablePinnedApps } from './SortablePinnedApps';
|
||||||
@ -137,12 +136,13 @@ export const AppsHomeDesktop = ({
|
|||||||
>
|
>
|
||||||
<AppCircleContainer
|
<AppCircleContainer
|
||||||
sx={{
|
sx={{
|
||||||
gap: !isMobile ? '10px' : '5px',
|
gap: '10px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<AppCircle>
|
<AppCircle>
|
||||||
<Add>+</Add>
|
<Add>+</Add>
|
||||||
</AppCircle>
|
</AppCircle>
|
||||||
|
|
||||||
<AppCircleLabel>Library</AppCircleLabel>
|
<AppCircleLabel>Library</AppCircleLabel>
|
||||||
</AppCircleContainer>
|
</AppCircleContainer>
|
||||||
</ButtonBase>
|
</ButtonBase>
|
||||||
|
@ -1,11 +1,4 @@
|
|||||||
import React, {
|
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||||
useCallback,
|
|
||||||
useContext,
|
|
||||||
useEffect,
|
|
||||||
useMemo,
|
|
||||||
useRef,
|
|
||||||
useState,
|
|
||||||
} from 'react';
|
|
||||||
import {
|
import {
|
||||||
AppCircle,
|
AppCircle,
|
||||||
AppCircleContainer,
|
AppCircleContainer,
|
||||||
@ -32,17 +25,14 @@ import {
|
|||||||
Typography,
|
Typography,
|
||||||
styled,
|
styled,
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import { Add } from '@mui/icons-material';
|
import { getBaseApiReact } from '../../App';
|
||||||
import { MyContext, getBaseApiReact } from '../../App';
|
|
||||||
import LogoSelected from '../../assets/svgs/LogoSelected.svg';
|
import LogoSelected from '../../assets/svgs/LogoSelected.svg';
|
||||||
import IconSearch from '../../assets/svgs/Search.svg';
|
import IconSearch from '../../assets/svgs/Search.svg';
|
||||||
import IconClearInput from '../../assets/svgs/ClearInput.svg';
|
import IconClearInput from '../../assets/svgs/ClearInput.svg';
|
||||||
import qappDevelopText from '../../assets/svgs/qappDevelopText.svg';
|
import qappDevelopText from '../../assets/svgs/qappDevelopText.svg';
|
||||||
import qappLibraryText from '../../assets/svgs/qappLibraryText.svg';
|
import qappLibraryText from '../../assets/svgs/qappLibraryText.svg';
|
||||||
import RefreshIcon from '@mui/icons-material/Refresh';
|
import RefreshIcon from '@mui/icons-material/Refresh';
|
||||||
|
|
||||||
import qappDots from '../../assets/svgs/qappDots.svg';
|
import qappDots from '../../assets/svgs/qappDots.svg';
|
||||||
|
|
||||||
import { Spacer } from '../../common/Spacer';
|
import { Spacer } from '../../common/Spacer';
|
||||||
import { AppInfoSnippet } from './AppInfoSnippet';
|
import { AppInfoSnippet } from './AppInfoSnippet';
|
||||||
import { Virtuoso } from 'react-virtuoso';
|
import { Virtuoso } from 'react-virtuoso';
|
||||||
@ -57,6 +47,7 @@ import {
|
|||||||
MailIconImg,
|
MailIconImg,
|
||||||
ShowMessageReturnButton,
|
ShowMessageReturnButton,
|
||||||
} from '../Group/Forum/Mail-styles';
|
} from '../Group/Forum/Mail-styles';
|
||||||
|
|
||||||
const officialAppList = [
|
const officialAppList = [
|
||||||
'q-tube',
|
'q-tube',
|
||||||
'q-blog',
|
'q-blog',
|
||||||
|
@ -6,7 +6,6 @@ import {
|
|||||||
Dialog,
|
Dialog,
|
||||||
DialogActions,
|
DialogActions,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
DialogTitle,
|
|
||||||
Input,
|
Input,
|
||||||
MenuItem,
|
MenuItem,
|
||||||
Select,
|
Select,
|
||||||
@ -32,7 +31,7 @@ import {
|
|||||||
PublishQAppInfo,
|
PublishQAppInfo,
|
||||||
} from './Apps-styles';
|
} from './Apps-styles';
|
||||||
import ImageUploader from '../../common/ImageUploader';
|
import ImageUploader from '../../common/ImageUploader';
|
||||||
import { isMobile, MyContext } from '../../App';
|
import { MyContext } from '../../App';
|
||||||
import { fileToBase64 } from '../../utils/fileReading';
|
import { fileToBase64 } from '../../utils/fileReading';
|
||||||
import { objectToBase64 } from '../../qdn/encryption/group-encryption';
|
import { objectToBase64 } from '../../qdn/encryption/group-encryption';
|
||||||
import { getFee } from '../../background';
|
import { getFee } from '../../background';
|
||||||
@ -67,6 +66,7 @@ export const AppsPrivate = ({ myName }) => {
|
|||||||
(group) => groupsProperties[group?.groupId]?.isOpen === false
|
(group) => groupsProperties[group?.groupId]?.isOpen === false
|
||||||
);
|
);
|
||||||
}, [memberGroups, groupsProperties]);
|
}, [memberGroups, groupsProperties]);
|
||||||
|
|
||||||
const [privateAppValues, setPrivateAppValues] = useState({
|
const [privateAppValues, setPrivateAppValues] = useState({
|
||||||
name: '',
|
name: '',
|
||||||
service: 'DOCUMENT',
|
service: 'DOCUMENT',
|
||||||
@ -230,7 +230,7 @@ export const AppsPrivate = ({ myName }) => {
|
|||||||
>
|
>
|
||||||
<AppCircleContainer
|
<AppCircleContainer
|
||||||
sx={{
|
sx={{
|
||||||
gap: !isMobile ? '10px' : '5px',
|
gap: '10px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<AppCircle>
|
<AppCircle>
|
||||||
@ -267,9 +267,8 @@ export const AppsPrivate = ({ myName }) => {
|
|||||||
value={valueTabPrivateApp}
|
value={valueTabPrivateApp}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
aria-label="basic tabs example"
|
aria-label="basic tabs example"
|
||||||
variant={isMobile ? 'scrollable' : 'fullWidth'} // Scrollable on mobile, full width on desktop
|
variant={'fullWidth'}
|
||||||
scrollButtons="auto"
|
scrollButtons="auto"
|
||||||
allowScrollButtonsMobile
|
|
||||||
sx={{
|
sx={{
|
||||||
'& .MuiTabs-indicator': {
|
'& .MuiTabs-indicator': {
|
||||||
backgroundColor: theme.palette.background.default,
|
backgroundColor: theme.palette.background.default,
|
||||||
@ -283,7 +282,7 @@ export const AppsPrivate = ({ myName }) => {
|
|||||||
'&.Mui-selected': {
|
'&.Mui-selected': {
|
||||||
color: theme.palette.text.primary,
|
color: theme.palette.text.primary,
|
||||||
},
|
},
|
||||||
fontSize: isMobile ? '0.75rem' : '1rem', // Adjust font size for mobile
|
fontSize: '1rem',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Tab
|
<Tab
|
||||||
@ -293,7 +292,7 @@ export const AppsPrivate = ({ myName }) => {
|
|||||||
'&.Mui-selected': {
|
'&.Mui-selected': {
|
||||||
color: theme.palette.text.primary,
|
color: theme.palette.text.primary,
|
||||||
},
|
},
|
||||||
fontSize: isMobile ? '0.75rem' : '1rem', // Adjust font size for mobile
|
fontSize: '1rem',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { useContext, useEffect, useState } from 'react';
|
import { useContext, useEffect, useState } from 'react';
|
||||||
import { MyContext, isMobile } from '../../App';
|
import { MyContext } from '../../App';
|
||||||
import { Box, Typography } from '@mui/material';
|
import { Box, Typography } from '@mui/material';
|
||||||
import { AdminSpaceInner } from './AdminSpaceInner';
|
import { AdminSpaceInner } from './AdminSpaceInner';
|
||||||
|
|
||||||
@ -29,10 +29,9 @@ export const AdminSpace = ({
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
// reference to change height
|
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
height: isMobile ? `calc(${rootHeight} - 127px` : 'calc(100vh - 70px)',
|
height: 'calc(100vh - 70px)',
|
||||||
left: hide && '-1000px',
|
left: hide && '-1000px',
|
||||||
opacity: hide ? 0 : 1,
|
opacity: hide ? 0 : 1,
|
||||||
position: hide ? 'fixed' : 'relative',
|
position: hide ? 'fixed' : 'relative',
|
||||||
|
@ -21,7 +21,6 @@ import ArrowBackIcon from '@mui/icons-material/ArrowBack';
|
|||||||
import {
|
import {
|
||||||
getArbitraryEndpointReact,
|
getArbitraryEndpointReact,
|
||||||
getBaseApiReact,
|
getBaseApiReact,
|
||||||
isMobile,
|
|
||||||
pauseAllQueues,
|
pauseAllQueues,
|
||||||
resumeAllQueues,
|
resumeAllQueues,
|
||||||
} from '../../App';
|
} from '../../App';
|
||||||
@ -56,12 +55,6 @@ export const AnnouncementDiscussion = ({
|
|||||||
const clearEditorContent = () => {
|
const clearEditorContent = () => {
|
||||||
if (editorRef.current) {
|
if (editorRef.current) {
|
||||||
editorRef.current.chain().focus().clearContent().run();
|
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={{
|
style={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
height: isMobile ? '100%' : '100%',
|
height: '100%',
|
||||||
width: '100%',
|
width: '100%',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
@ -20,7 +20,6 @@ import { CustomizedSnackbars } from '../Snackbar/Snackbar';
|
|||||||
import {
|
import {
|
||||||
getBaseApiReact,
|
getBaseApiReact,
|
||||||
getBaseApiReactSocket,
|
getBaseApiReactSocket,
|
||||||
isMobile,
|
|
||||||
pauseAllQueues,
|
pauseAllQueues,
|
||||||
resumeAllQueues,
|
resumeAllQueues,
|
||||||
} from '../../App';
|
} from '../../App';
|
||||||
@ -410,16 +409,6 @@ export const ChatDirect = ({
|
|||||||
if (editorRef.current) {
|
if (editorRef.current) {
|
||||||
setMessageSize(0);
|
setMessageSize(0);
|
||||||
editorRef.current.chain().focus().clearContent().run();
|
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(() => {
|
useEffect(() => {
|
||||||
@ -547,108 +536,41 @@ export const ChatDirect = ({
|
|||||||
background: theme.palette.background.default,
|
background: theme.palette.background.default,
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
height: isMobile ? '100%' : '100vh',
|
height: '100vh',
|
||||||
width: '100%',
|
width: '100%',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{!isMobile && (
|
<Box
|
||||||
<Box
|
onClick={close}
|
||||||
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={{
|
sx={{
|
||||||
alignItems: 'center',
|
color: theme.palette.text.primary,
|
||||||
alignSelf: 'center',
|
fontSize: '20px',
|
||||||
background: theme.palette.background.default,
|
}}
|
||||||
borderRadius: '3px',
|
/>
|
||||||
cursor: 'pointer',
|
<Typography
|
||||||
display: 'flex',
|
sx={{
|
||||||
gap: '5px',
|
color: theme.palette.text.primary,
|
||||||
margin: '10px 0px',
|
fontSize: '14px',
|
||||||
padding: '4px 6px',
|
|
||||||
width: 'fit-content',
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ArrowBackIcon
|
Close Direct Chat
|
||||||
sx={{
|
</Typography>
|
||||||
color: theme.palette.text.primary,
|
</Box>
|
||||||
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>
|
|
||||||
)}
|
|
||||||
{isNewChat && (
|
{isNewChat && (
|
||||||
<>
|
<>
|
||||||
<Spacer height="30px" />
|
<Spacer height="30px" />
|
||||||
@ -685,7 +607,7 @@ export const ChatDirect = ({
|
|||||||
flexShrink: 0,
|
flexShrink: 0,
|
||||||
minHeight: '150px',
|
minHeight: '150px',
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
padding: isMobile ? '10px' : '20px',
|
padding: '20px',
|
||||||
position: isFocusedParent ? 'fixed' : 'relative',
|
position: isFocusedParent ? 'fixed' : 'relative',
|
||||||
top: isFocusedParent ? '0px' : 'unset',
|
top: isFocusedParent ? '0px' : 'unset',
|
||||||
width: '100%',
|
width: '100%',
|
||||||
@ -753,7 +675,7 @@ export const ChatDirect = ({
|
|||||||
setEditorRef={setEditorRef}
|
setEditorRef={setEditorRef}
|
||||||
onEnter={sendMessage}
|
onEnter={sendMessage}
|
||||||
isChat
|
isChat
|
||||||
disableEnter={isMobile ? true : false}
|
disableEnter={false}
|
||||||
setIsFocusedParent={setIsFocusedParent}
|
setIsFocusedParent={setIsFocusedParent}
|
||||||
/>
|
/>
|
||||||
{messageSize > 750 && (
|
{messageSize > 750 && (
|
||||||
|
@ -20,7 +20,6 @@ import { LoadingSnackbar } from '../Snackbar/LoadingSnackbar';
|
|||||||
import {
|
import {
|
||||||
getBaseApiReact,
|
getBaseApiReact,
|
||||||
getBaseApiReactSocket,
|
getBaseApiReactSocket,
|
||||||
isMobile,
|
|
||||||
MyContext,
|
MyContext,
|
||||||
pauseAllQueues,
|
pauseAllQueues,
|
||||||
resumeAllQueues,
|
resumeAllQueues,
|
||||||
@ -746,16 +745,6 @@ export const ChatGroup = ({
|
|||||||
if (editorRef.current) {
|
if (editorRef.current) {
|
||||||
setMessageSize(0);
|
setMessageSize(0);
|
||||||
editorRef.current.chain().focus().clearContent().run();
|
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}
|
setEditorRef={setEditorRef}
|
||||||
onEnter={sendMessage}
|
onEnter={sendMessage}
|
||||||
isChat
|
isChat
|
||||||
disableEnter={isMobile ? true : false}
|
disableEnter={false}
|
||||||
isFocusedParent={isFocusedParent}
|
isFocusedParent={isFocusedParent}
|
||||||
setIsFocusedParent={setIsFocusedParent}
|
setIsFocusedParent={setIsFocusedParent}
|
||||||
membersWithNames={members}
|
membersWithNames={members}
|
||||||
|
@ -26,7 +26,6 @@ import {
|
|||||||
MyContext,
|
MyContext,
|
||||||
getArbitraryEndpointReact,
|
getArbitraryEndpointReact,
|
||||||
getBaseApiReact,
|
getBaseApiReact,
|
||||||
isMobile,
|
|
||||||
pauseAllQueues,
|
pauseAllQueues,
|
||||||
resumeAllQueues,
|
resumeAllQueues,
|
||||||
} from '../../App';
|
} from '../../App';
|
||||||
@ -262,15 +261,6 @@ export const GroupAnnouncements = ({
|
|||||||
const clearEditorContent = () => {
|
const clearEditorContent = () => {
|
||||||
if (editorRef.current) {
|
if (editorRef.current) {
|
||||||
editorRef.current.chain().focus().clearContent().run();
|
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 (
|
return (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
// reference to change height
|
|
||||||
height: isMobile
|
|
||||||
? `calc(${rootHeight} - 127px`
|
|
||||||
: 'calc(100vh - 70px)',
|
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
|
height: 'calc(100vh - 70px)',
|
||||||
left: hide && '-1000px',
|
left: hide && '-1000px',
|
||||||
position: hide && 'fixed',
|
position: hide && 'fixed',
|
||||||
visibility: hide && 'hidden',
|
visibility: hide && 'hidden',
|
||||||
@ -576,26 +563,24 @@ export const GroupAnnouncements = ({
|
|||||||
width: '100%',
|
width: '100%',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{!isMobile && (
|
<Box
|
||||||
<Box
|
sx={{
|
||||||
|
alignItems: 'center',
|
||||||
|
display: 'flex',
|
||||||
|
fontSize: '20px',
|
||||||
|
gap: '20px',
|
||||||
|
justifyContent: 'center',
|
||||||
|
padding: '25px',
|
||||||
|
width: '100%',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CampaignIcon
|
||||||
sx={{
|
sx={{
|
||||||
alignItems: 'center',
|
fontSize: '30px',
|
||||||
display: 'flex',
|
|
||||||
fontSize: '20px',
|
|
||||||
gap: '20px',
|
|
||||||
justifyContent: 'center',
|
|
||||||
padding: '25px',
|
|
||||||
width: '100%',
|
|
||||||
}}
|
}}
|
||||||
>
|
/>
|
||||||
<CampaignIcon
|
Group Announcements
|
||||||
sx={{
|
</Box>
|
||||||
fontSize: '30px',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
Group Announcements
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Spacer height={'25px'} />
|
<Spacer height={'25px'} />
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { useContext, useEffect, useState } from 'react';
|
import { useContext, useEffect, useState } from 'react';
|
||||||
import { GroupMail } from '../Group/Forum/GroupMail';
|
import { GroupMail } from '../Group/Forum/GroupMail';
|
||||||
import { MyContext, isMobile } from '../../App';
|
import { MyContext } from '../../App';
|
||||||
|
|
||||||
export const GroupForum = ({
|
export const GroupForum = ({
|
||||||
selectedGroup,
|
selectedGroup,
|
||||||
|
@ -23,7 +23,6 @@ import DeveloperModeIcon from '@mui/icons-material/DeveloperMode';
|
|||||||
import Compressor from 'compressorjs';
|
import Compressor from 'compressorjs';
|
||||||
import Mention from '@tiptap/extension-mention';
|
import Mention from '@tiptap/extension-mention';
|
||||||
import ImageResize from 'tiptap-extension-resize-image'; // Import the ResizeImage extension
|
import ImageResize from 'tiptap-extension-resize-image'; // Import the ResizeImage extension
|
||||||
import { isMobile } from '../../App';
|
|
||||||
import tippy from 'tippy.js';
|
import tippy from 'tippy.js';
|
||||||
import 'tippy.js/dist/tippy.css';
|
import 'tippy.js/dist/tippy.css';
|
||||||
import { ReactRenderer } from '@tiptap/react';
|
import { ReactRenderer } from '@tiptap/react';
|
||||||
@ -137,7 +136,7 @@ const MenuBar = ({
|
|||||||
color: editor.isActive('bold')
|
color: editor.isActive('bold')
|
||||||
? theme.palette.text.primary
|
? theme.palette.text.primary
|
||||||
: theme.palette.text.secondary,
|
: theme.palette.text.secondary,
|
||||||
padding: isMobile ? '5px' : 'revert',
|
padding: 'revert',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<FormatBoldIcon />
|
<FormatBoldIcon />
|
||||||
@ -149,7 +148,7 @@ const MenuBar = ({
|
|||||||
color: editor.isActive('italic')
|
color: editor.isActive('italic')
|
||||||
? theme.palette.text.primary
|
? theme.palette.text.primary
|
||||||
: theme.palette.text.secondary,
|
: theme.palette.text.secondary,
|
||||||
padding: isMobile ? '5px' : 'revert',
|
padding: 'revert',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<FormatItalicIcon />
|
<FormatItalicIcon />
|
||||||
@ -161,7 +160,7 @@ const MenuBar = ({
|
|||||||
color: editor.isActive('strike')
|
color: editor.isActive('strike')
|
||||||
? theme.palette.text.primary
|
? theme.palette.text.primary
|
||||||
: theme.palette.text.secondary,
|
: theme.palette.text.secondary,
|
||||||
padding: isMobile ? '5px' : 'revert',
|
padding: 'revert',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<StrikethroughSIcon />
|
<StrikethroughSIcon />
|
||||||
@ -173,7 +172,7 @@ const MenuBar = ({
|
|||||||
color: editor.isActive('code')
|
color: editor.isActive('code')
|
||||||
? theme.palette.text.primary
|
? theme.palette.text.primary
|
||||||
: theme.palette.text.secondary,
|
: theme.palette.text.secondary,
|
||||||
padding: isMobile ? '5px' : 'revert',
|
padding: 'revert',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<CodeIcon />
|
<CodeIcon />
|
||||||
@ -188,7 +187,7 @@ const MenuBar = ({
|
|||||||
editor.isActive('code')
|
editor.isActive('code')
|
||||||
? theme.palette.text.primary
|
? theme.palette.text.primary
|
||||||
: theme.palette.text.secondary,
|
: theme.palette.text.secondary,
|
||||||
padding: isMobile ? '5px' : 'revert',
|
padding: 'revert',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<FormatClearIcon />
|
<FormatClearIcon />
|
||||||
@ -199,7 +198,7 @@ const MenuBar = ({
|
|||||||
color: editor.isActive('bulletList')
|
color: editor.isActive('bulletList')
|
||||||
? theme.palette.text.primary
|
? theme.palette.text.primary
|
||||||
: theme.palette.text.secondary,
|
: theme.palette.text.secondary,
|
||||||
padding: isMobile ? '5px' : 'revert',
|
padding: 'revert',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<FormatListBulletedIcon />
|
<FormatListBulletedIcon />
|
||||||
@ -210,7 +209,7 @@ const MenuBar = ({
|
|||||||
color: editor.isActive('orderedList')
|
color: editor.isActive('orderedList')
|
||||||
? theme.palette.text.primary
|
? theme.palette.text.primary
|
||||||
: theme.palette.text.secondary,
|
: theme.palette.text.secondary,
|
||||||
padding: isMobile ? '5px' : 'revert',
|
padding: 'revert',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<FormatListNumberedIcon />
|
<FormatListNumberedIcon />
|
||||||
@ -221,7 +220,7 @@ const MenuBar = ({
|
|||||||
color: editor.isActive('codeBlock')
|
color: editor.isActive('codeBlock')
|
||||||
? theme.palette.text.primary
|
? theme.palette.text.primary
|
||||||
: theme.palette.text.secondary,
|
: theme.palette.text.secondary,
|
||||||
padding: isMobile ? '5px' : 'revert',
|
padding: 'revert',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<DeveloperModeIcon />
|
<DeveloperModeIcon />
|
||||||
@ -232,7 +231,7 @@ const MenuBar = ({
|
|||||||
color: editor.isActive('blockquote')
|
color: editor.isActive('blockquote')
|
||||||
? theme.palette.text.primary
|
? theme.palette.text.primary
|
||||||
: theme.palette.text.secondary,
|
: theme.palette.text.secondary,
|
||||||
padding: isMobile ? '5px' : 'revert',
|
padding: 'revert',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<FormatQuoteIcon />
|
<FormatQuoteIcon />
|
||||||
@ -240,7 +239,7 @@ const MenuBar = ({
|
|||||||
<IconButton
|
<IconButton
|
||||||
onClick={() => editor.chain().focus().setHorizontalRule().run()}
|
onClick={() => editor.chain().focus().setHorizontalRule().run()}
|
||||||
disabled={!editor.can().chain().focus().setHorizontalRule().run()}
|
disabled={!editor.can().chain().focus().setHorizontalRule().run()}
|
||||||
sx={{ color: 'gray', padding: isMobile ? '5px' : 'revert' }}
|
sx={{ color: 'gray', padding: 'revert' }}
|
||||||
>
|
>
|
||||||
<HorizontalRuleIcon />
|
<HorizontalRuleIcon />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
@ -252,7 +251,7 @@ const MenuBar = ({
|
|||||||
color: editor.isActive('heading', { level: 1 })
|
color: editor.isActive('heading', { level: 1 })
|
||||||
? theme.palette.text.primary
|
? theme.palette.text.primary
|
||||||
: theme.palette.text.secondary,
|
: theme.palette.text.secondary,
|
||||||
padding: isMobile ? '5px' : 'revert',
|
padding: 'revert',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<FormatHeadingIcon fontSize="small" />
|
<FormatHeadingIcon fontSize="small" />
|
||||||
@ -260,7 +259,7 @@ const MenuBar = ({
|
|||||||
<IconButton
|
<IconButton
|
||||||
onClick={() => editor.chain().focus().undo().run()}
|
onClick={() => editor.chain().focus().undo().run()}
|
||||||
disabled={!editor.can().chain().focus().undo().run()}
|
disabled={!editor.can().chain().focus().undo().run()}
|
||||||
sx={{ color: 'gray', padding: isMobile ? '5px' : 'revert' }}
|
sx={{ color: 'gray', padding: 'revert' }}
|
||||||
>
|
>
|
||||||
<UndoIcon />
|
<UndoIcon />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
@ -313,7 +312,7 @@ const MenuBar = ({
|
|||||||
onClick={triggerImageUpload}
|
onClick={triggerImageUpload}
|
||||||
sx={{
|
sx={{
|
||||||
color: theme.palette.text.secondary,
|
color: theme.palette.text.secondary,
|
||||||
padding: isMobile ? '5px' : 'revert',
|
padding: 'revert',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ImageIcon />
|
<ImageIcon />
|
||||||
@ -398,11 +397,6 @@ export default ({
|
|||||||
usersRef.current = users; // Keep users up-to-date
|
usersRef.current = users; // Keep users up-to-date
|
||||||
}, [users]);
|
}, [users]);
|
||||||
|
|
||||||
const handleFocus = () => {
|
|
||||||
if (!isMobile) return;
|
|
||||||
setIsFocusedParent(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleBlur = () => {
|
const handleBlur = () => {
|
||||||
const htmlContent = editorRef.current.getHTML();
|
const htmlContent = editorRef.current.getHTML();
|
||||||
if (!htmlContent?.trim() || htmlContent?.trim() === '<p></p>') {
|
if (!htmlContent?.trim() || htmlContent?.trim() === '<p></p>') {
|
||||||
@ -499,7 +493,7 @@ export default ({
|
|||||||
>
|
>
|
||||||
<EditorProvider
|
<EditorProvider
|
||||||
slotBefore={
|
slotBefore={
|
||||||
(isFocusedParent || !isMobile || overrideMobile) && (
|
(isFocusedParent || overrideMobile) && (
|
||||||
<MenuBar
|
<MenuBar
|
||||||
setEditorRef={setEditorRefFunc}
|
setEditorRef={setEditorRefFunc}
|
||||||
isChat={isChat}
|
isChat={isChat}
|
||||||
@ -511,21 +505,15 @@ export default ({
|
|||||||
extensions={[...extensionsFiltered, ...additionalExtensions]}
|
extensions={[...extensionsFiltered, ...additionalExtensions]}
|
||||||
content={content}
|
content={content}
|
||||||
onCreate={({ editor }) => {
|
onCreate={({ editor }) => {
|
||||||
editor.on('focus', handleFocus); // Listen for focus event
|
|
||||||
editor.on('blur', handleBlur); // Listen for blur event
|
editor.on('blur', handleBlur); // Listen for blur event
|
||||||
}}
|
}}
|
||||||
onUpdate={({ editor }) => {
|
onUpdate={({ editor }) => {
|
||||||
editor.on('focus', handleFocus); // Ensure focus is updated
|
|
||||||
editor.on('blur', handleBlur); // Ensure blur is updated
|
editor.on('blur', handleBlur); // Ensure blur is updated
|
||||||
}}
|
}}
|
||||||
editorProps={{
|
editorProps={{
|
||||||
attributes: {
|
attributes: {
|
||||||
class: 'tiptap-prosemirror',
|
class: 'tiptap-prosemirror',
|
||||||
style: isMobile
|
style: `overflow: auto; max-height: 250px`,
|
||||||
? `overflow: auto; min-height: ${
|
|
||||||
customEditorHeight ? '200px' : '0px'
|
|
||||||
}; max-height:calc(100svh - ${customEditorHeight || '140px'})`
|
|
||||||
: `overflow: auto; max-height: 250px`,
|
|
||||||
},
|
},
|
||||||
handleKeyDown(view, event) {
|
handleKeyDown(view, event) {
|
||||||
if (
|
if (
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import Box from '@mui/material/Box';
|
import Box from '@mui/material/Box';
|
||||||
import Drawer from '@mui/material/Drawer';
|
import Drawer from '@mui/material/Drawer';
|
||||||
import { isMobile } from '../../App';
|
|
||||||
export const DrawerComponent = ({ open, setOpen, children }) => {
|
export const DrawerComponent = ({ open, setOpen, children }) => {
|
||||||
const toggleDrawer = (newOpen: boolean) => () => {
|
const toggleDrawer = (newOpen: boolean) => () => {
|
||||||
setOpen(newOpen);
|
setOpen(newOpen);
|
||||||
@ -9,10 +8,7 @@ export const DrawerComponent = ({ open, setOpen, children }) => {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Drawer open={open} onClose={toggleDrawer(false)}>
|
<Drawer open={open} onClose={toggleDrawer(false)}>
|
||||||
<Box
|
<Box sx={{ width: '400px', height: '100%' }} role="presentation">
|
||||||
sx={{ width: isMobile ? '100vw' : '400px', height: '100%' }}
|
|
||||||
role="presentation"
|
|
||||||
>
|
|
||||||
{children}
|
{children}
|
||||||
</Box>
|
</Box>
|
||||||
</Drawer>
|
</Drawer>
|
||||||
|
@ -1,23 +1,25 @@
|
|||||||
import React, { useContext, useEffect, useMemo, useRef, useState } from 'react'
|
import React, { useContext, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import ReactDOM from 'react-dom'
|
import ReactDOM from 'react-dom';
|
||||||
import { Box, IconButton, Slider } from '@mui/material'
|
import { Box, IconButton, Slider } from '@mui/material';
|
||||||
import { CircularProgress, Typography } from '@mui/material'
|
import { CircularProgress, Typography } from '@mui/material';
|
||||||
import { Key } from 'ts-key-enum'
|
import { Key } from 'ts-key-enum';
|
||||||
import {
|
import {
|
||||||
PlayArrow,
|
PlayArrow,
|
||||||
Pause,
|
Pause,
|
||||||
VolumeUp,
|
VolumeUp,
|
||||||
Fullscreen,
|
Fullscreen,
|
||||||
PictureInPicture, VolumeOff, Calculate
|
PictureInPicture,
|
||||||
} from '@mui/icons-material'
|
VolumeOff,
|
||||||
import { styled } from '@mui/system'
|
Calculate,
|
||||||
import { Refresh } from '@mui/icons-material'
|
} from '@mui/icons-material';
|
||||||
|
import { styled } from '@mui/system';
|
||||||
|
import { Refresh } from '@mui/icons-material';
|
||||||
|
|
||||||
import { Menu, MenuItem } from '@mui/material'
|
import { Menu, MenuItem } from '@mui/material';
|
||||||
import { MoreVert as MoreIcon } from '@mui/icons-material'
|
import { MoreVert as MoreIcon } from '@mui/icons-material';
|
||||||
import { GlobalContext, getBaseApiReact } from '../../App'
|
import { GlobalContext, getBaseApiReact } from '../../App';
|
||||||
import { resourceKeySelector } from '../../atoms/global'
|
import { resourceKeySelector } from '../../atoms/global';
|
||||||
import { useRecoilValue } from 'recoil'
|
import { useRecoilValue } from 'recoil';
|
||||||
const VideoContainer = styled(Box)`
|
const VideoContainer = styled(Box)`
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -28,14 +30,14 @@ const VideoContainer = styled(Box)`
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
margin: 0px;
|
margin: 0px;
|
||||||
padding: 0px;
|
padding: 0px;
|
||||||
`
|
`;
|
||||||
|
|
||||||
const VideoElement = styled('video')`
|
const VideoElement = styled('video')`
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: auto;
|
height: auto;
|
||||||
max-height: calc(100vh - 150px);
|
max-height: calc(100vh - 150px);
|
||||||
background: rgb(33, 33, 33);
|
background: rgb(33, 33, 33);
|
||||||
`
|
`;
|
||||||
|
|
||||||
const ControlsContainer = styled(Box)`
|
const ControlsContainer = styled(Box)`
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -47,18 +49,18 @@ const ControlsContainer = styled(Box)`
|
|||||||
right: 0;
|
right: 0;
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
background-color: rgba(0, 0, 0, 0.6);
|
background-color: rgba(0, 0, 0, 0.6);
|
||||||
`
|
`;
|
||||||
|
|
||||||
interface VideoPlayerProps {
|
interface VideoPlayerProps {
|
||||||
src?: string
|
src?: string;
|
||||||
poster?: string
|
poster?: string;
|
||||||
name?: string
|
name?: string;
|
||||||
identifier?: string
|
identifier?: string;
|
||||||
service?: string
|
service?: string;
|
||||||
autoplay?: boolean
|
autoplay?: boolean;
|
||||||
from?: string | null
|
from?: string | null;
|
||||||
customStyle?: any
|
customStyle?: any;
|
||||||
user?: string
|
user?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const VideoPlayer: React.FC<VideoPlayerProps> = ({
|
export const VideoPlayer: React.FC<VideoPlayerProps> = ({
|
||||||
@ -69,33 +71,30 @@ export const VideoPlayer: React.FC<VideoPlayerProps> = ({
|
|||||||
autoplay = true,
|
autoplay = true,
|
||||||
from = null,
|
from = null,
|
||||||
customStyle = {},
|
customStyle = {},
|
||||||
node
|
node,
|
||||||
}) => {
|
}) => {
|
||||||
|
const keyIdentifier = useMemo(() => {
|
||||||
const keyIdentifier = useMemo(()=> {
|
if (name && identifier && service) {
|
||||||
|
return `${service}-${name}-${identifier}`;
|
||||||
if(name && identifier && service){
|
|
||||||
return `${service}-${name}-${identifier}`
|
|
||||||
} else {
|
} else {
|
||||||
return undefined
|
return undefined;
|
||||||
}
|
}
|
||||||
}, [service, name, identifier])
|
}, [service, name, identifier]);
|
||||||
const download = useRecoilValue(resourceKeySelector(keyIdentifier));
|
const download = useRecoilValue(resourceKeySelector(keyIdentifier));
|
||||||
const { downloadResource } = useContext(GlobalContext);
|
const { downloadResource } = useContext(GlobalContext);
|
||||||
|
|
||||||
const videoRef = useRef<HTMLVideoElement | null>(null)
|
const videoRef = useRef<HTMLVideoElement | null>(null);
|
||||||
const [playing, setPlaying] = useState(false)
|
const [playing, setPlaying] = useState(false);
|
||||||
const [volume, setVolume] = useState(1)
|
const [volume, setVolume] = useState(1);
|
||||||
const [mutedVolume, setMutedVolume] = useState(1)
|
const [mutedVolume, setMutedVolume] = useState(1);
|
||||||
const [isMuted, setIsMuted] = useState(false)
|
const [isMuted, setIsMuted] = useState(false);
|
||||||
const [progress, setProgress] = useState(0)
|
const [progress, setProgress] = useState(0);
|
||||||
const [isLoading, setIsLoading] = useState(false)
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const [canPlay, setCanPlay] = useState(false)
|
const [canPlay, setCanPlay] = useState(false);
|
||||||
const [startPlay, setStartPlay] = useState(false)
|
const [startPlay, setStartPlay] = useState(false);
|
||||||
const [isMobileView, setIsMobileView] = useState(false)
|
const [playbackRate, setPlaybackRate] = useState(1);
|
||||||
const [playbackRate, setPlaybackRate] = useState(1)
|
const [anchorEl, setAnchorEl] = useState(null);
|
||||||
const [anchorEl, setAnchorEl] = useState(null)
|
const reDownload = useRef<boolean>(false);
|
||||||
const reDownload = useRef<boolean>(false)
|
|
||||||
|
|
||||||
const resetVideoState = () => {
|
const resetVideoState = () => {
|
||||||
// Reset all states to their initial values
|
// Reset all states to their initial values
|
||||||
@ -107,10 +106,9 @@ export const VideoPlayer: React.FC<VideoPlayerProps> = ({
|
|||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
setCanPlay(false);
|
setCanPlay(false);
|
||||||
setStartPlay(false);
|
setStartPlay(false);
|
||||||
setIsMobileView(false);
|
|
||||||
setPlaybackRate(1);
|
setPlaybackRate(1);
|
||||||
setAnchorEl(null);
|
setAnchorEl(null);
|
||||||
|
|
||||||
// Reset refs to their initial values
|
// Reset refs to their initial values
|
||||||
if (videoRef.current) {
|
if (videoRef.current) {
|
||||||
videoRef.current.pause(); // Ensure the video is paused
|
videoRef.current.pause(); // Ensure the video is paused
|
||||||
@ -120,18 +118,19 @@ export const VideoPlayer: React.FC<VideoPlayerProps> = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const src = useMemo(() => {
|
const src = useMemo(() => {
|
||||||
if(name && identifier && service){
|
if (name && identifier && service) {
|
||||||
return `${node || getBaseApiReact()}/arbitrary/${service}/${name}/${identifier}`
|
return `${node || getBaseApiReact()}/arbitrary/${service}/${name}/${identifier}`;
|
||||||
}
|
}
|
||||||
return ''
|
return '';
|
||||||
}, [service, name, identifier])
|
}, [service, name, identifier]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
resetVideoState();
|
||||||
|
}, [keyIdentifier]);
|
||||||
|
|
||||||
useEffect(()=> {
|
|
||||||
resetVideoState()
|
|
||||||
}, [keyIdentifier])
|
|
||||||
const resourceStatus = useMemo(() => {
|
const resourceStatus = useMemo(() => {
|
||||||
return download?.status || {}
|
return download?.status || {};
|
||||||
}, [download])
|
}, [download]);
|
||||||
|
|
||||||
const minSpeed = 0.25;
|
const minSpeed = 0.25;
|
||||||
const maxSpeed = 4.0;
|
const maxSpeed = 4.0;
|
||||||
@ -139,306 +138,339 @@ export const VideoPlayer: React.FC<VideoPlayerProps> = ({
|
|||||||
|
|
||||||
const updatePlaybackRate = (newSpeed: number) => {
|
const updatePlaybackRate = (newSpeed: number) => {
|
||||||
if (videoRef.current) {
|
if (videoRef.current) {
|
||||||
if (newSpeed > maxSpeed || newSpeed < minSpeed)
|
if (newSpeed > maxSpeed || newSpeed < minSpeed) newSpeed = minSpeed;
|
||||||
newSpeed = minSpeed
|
videoRef.current.playbackRate = newSpeed;
|
||||||
videoRef.current.playbackRate = newSpeed
|
setPlaybackRate(newSpeed);
|
||||||
setPlaybackRate(newSpeed)
|
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
const increaseSpeed = (wrapOverflow = true) => {
|
const increaseSpeed = (wrapOverflow = true) => {
|
||||||
const changedSpeed = playbackRate + speedChange
|
const changedSpeed = playbackRate + speedChange;
|
||||||
let newSpeed = wrapOverflow ? changedSpeed : Math.min(changedSpeed, maxSpeed)
|
let newSpeed = wrapOverflow
|
||||||
|
? changedSpeed
|
||||||
|
: Math.min(changedSpeed, maxSpeed);
|
||||||
|
|
||||||
if (videoRef.current) {
|
if (videoRef.current) {
|
||||||
updatePlaybackRate(newSpeed);
|
updatePlaybackRate(newSpeed);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
const decreaseSpeed = () => {
|
const decreaseSpeed = () => {
|
||||||
if (videoRef.current) {
|
if (videoRef.current) {
|
||||||
updatePlaybackRate(playbackRate - speedChange);
|
updatePlaybackRate(playbackRate - speedChange);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
||||||
const togglePlay = async () => {
|
const togglePlay = async () => {
|
||||||
if (!videoRef.current) return
|
if (!videoRef.current) return;
|
||||||
setStartPlay(true)
|
setStartPlay(true);
|
||||||
if (!src || resourceStatus?.status !== 'READY') {
|
if (!src || resourceStatus?.status !== 'READY') {
|
||||||
ReactDOM.flushSync(() => {
|
ReactDOM.flushSync(() => {
|
||||||
setIsLoading(true)
|
setIsLoading(true);
|
||||||
})
|
});
|
||||||
getSrc()
|
getSrc();
|
||||||
}
|
}
|
||||||
if (playing) {
|
if (playing) {
|
||||||
videoRef.current.pause()
|
videoRef.current.pause();
|
||||||
} else {
|
} else {
|
||||||
videoRef.current.play()
|
videoRef.current.play();
|
||||||
}
|
}
|
||||||
setPlaying(!playing)
|
setPlaying(!playing);
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
||||||
const onVolumeChange = (_: any, value: number | number[]) => {
|
const onVolumeChange = (_: any, value: number | number[]) => {
|
||||||
if (!videoRef.current) return
|
if (!videoRef.current) return;
|
||||||
videoRef.current.volume = value as number
|
videoRef.current.volume = value as number;
|
||||||
setVolume(value as number)
|
setVolume(value as number);
|
||||||
setIsMuted(false)
|
setIsMuted(false);
|
||||||
}
|
};
|
||||||
|
|
||||||
const onProgressChange = (_: any, value: number | number[]) => {
|
const onProgressChange = (_: any, value: number | number[]) => {
|
||||||
if (!videoRef.current) return
|
if (!videoRef.current) return;
|
||||||
videoRef.current.currentTime = value as number
|
videoRef.current.currentTime = value as number;
|
||||||
setProgress(value as number)
|
setProgress(value as number);
|
||||||
if (!playing) {
|
if (!playing) {
|
||||||
videoRef.current.play()
|
videoRef.current.play();
|
||||||
setPlaying(true)
|
setPlaying(true);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
const handleEnded = () => {
|
const handleEnded = () => {
|
||||||
setPlaying(false)
|
setPlaying(false);
|
||||||
}
|
};
|
||||||
|
|
||||||
const updateProgress = () => {
|
const updateProgress = () => {
|
||||||
if (!videoRef.current) return
|
if (!videoRef.current) return;
|
||||||
setProgress(videoRef.current.currentTime)
|
setProgress(videoRef.current.currentTime);
|
||||||
}
|
};
|
||||||
|
|
||||||
const [isFullscreen, setIsFullscreen] = useState(false)
|
const [isFullscreen, setIsFullscreen] = useState(false);
|
||||||
|
|
||||||
const enterFullscreen = () => {
|
const enterFullscreen = () => {
|
||||||
if (!videoRef.current) return
|
if (!videoRef.current) return;
|
||||||
if (videoRef.current.requestFullscreen) {
|
if (videoRef.current.requestFullscreen) {
|
||||||
videoRef.current.requestFullscreen()
|
videoRef.current.requestFullscreen();
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
const exitFullscreen = () => {
|
const exitFullscreen = () => {
|
||||||
if (document.exitFullscreen) {
|
if (document.exitFullscreen) {
|
||||||
document.exitFullscreen()
|
document.exitFullscreen();
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
const toggleFullscreen = () => {
|
const toggleFullscreen = () => {
|
||||||
isFullscreen ? exitFullscreen() : enterFullscreen()
|
isFullscreen ? exitFullscreen() : enterFullscreen();
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleFullscreenChange = () => {
|
const handleFullscreenChange = () => {
|
||||||
setIsFullscreen(!!document.fullscreenElement)
|
setIsFullscreen(!!document.fullscreenElement);
|
||||||
}
|
};
|
||||||
|
|
||||||
document.addEventListener('fullscreenchange', handleFullscreenChange)
|
document.addEventListener('fullscreenchange', handleFullscreenChange);
|
||||||
return () => {
|
return () => {
|
||||||
document.removeEventListener('fullscreenchange', handleFullscreenChange)
|
document.removeEventListener('fullscreenchange', handleFullscreenChange);
|
||||||
}
|
};
|
||||||
}, [])
|
}, []);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const handleCanPlay = () => {
|
const handleCanPlay = () => {
|
||||||
setIsLoading(false)
|
setIsLoading(false);
|
||||||
setCanPlay(true)
|
setCanPlay(true);
|
||||||
}
|
};
|
||||||
|
|
||||||
const getSrc = React.useCallback(async () => {
|
const getSrc = React.useCallback(async () => {
|
||||||
if (!name || !identifier || !service) return
|
if (!name || !identifier || !service) return;
|
||||||
try {
|
try {
|
||||||
downloadResource({
|
downloadResource({
|
||||||
name,
|
name,
|
||||||
service,
|
service,
|
||||||
identifier
|
identifier,
|
||||||
})
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error)
|
console.error(error);
|
||||||
}
|
}
|
||||||
}, [identifier, name, service])
|
}, [identifier, name, service]);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function formatTime(seconds: number): string {
|
function formatTime(seconds: number): string {
|
||||||
seconds = Math.floor(seconds)
|
seconds = Math.floor(seconds);
|
||||||
let minutes: number | string = Math.floor(seconds / 60)
|
let minutes: number | string = Math.floor(seconds / 60);
|
||||||
let hours: number | string = Math.floor(minutes / 60)
|
let hours: number | string = Math.floor(minutes / 60);
|
||||||
|
|
||||||
let remainingSeconds: number | string = seconds % 60
|
let remainingSeconds: number | string = seconds % 60;
|
||||||
let remainingMinutes: number | string = minutes % 60
|
let remainingMinutes: number | string = minutes % 60;
|
||||||
|
|
||||||
if (remainingSeconds < 10) {
|
if (remainingSeconds < 10) {
|
||||||
remainingSeconds = '0' + remainingSeconds
|
remainingSeconds = '0' + remainingSeconds;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (remainingMinutes < 10) {
|
if (remainingMinutes < 10) {
|
||||||
remainingMinutes = '0' + remainingMinutes
|
remainingMinutes = '0' + remainingMinutes;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hours === 0) {
|
if (hours === 0) {
|
||||||
hours = ''
|
hours = '';
|
||||||
}
|
} else {
|
||||||
else {
|
hours = hours + ':';
|
||||||
hours = hours + ':'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return hours + remainingMinutes + ':' + remainingSeconds
|
return hours + remainingMinutes + ':' + remainingSeconds;
|
||||||
}
|
}
|
||||||
|
|
||||||
const reloadVideo = () => {
|
const reloadVideo = () => {
|
||||||
if (!videoRef.current) return
|
if (!videoRef.current) return;
|
||||||
const currentTime = videoRef.current.currentTime
|
const currentTime = videoRef.current.currentTime;
|
||||||
videoRef.current.src = src
|
videoRef.current.src = src;
|
||||||
videoRef.current.load()
|
videoRef.current.load();
|
||||||
videoRef.current.currentTime = currentTime
|
videoRef.current.currentTime = currentTime;
|
||||||
if (playing) {
|
if (playing) {
|
||||||
videoRef.current.play()
|
videoRef.current.play();
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (
|
if (
|
||||||
resourceStatus?.status === 'DOWNLOADED' &&
|
resourceStatus?.status === 'DOWNLOADED' &&
|
||||||
reDownload?.current === false
|
reDownload?.current === false
|
||||||
) {
|
) {
|
||||||
getSrc()
|
getSrc();
|
||||||
reDownload.current = true
|
reDownload.current = true;
|
||||||
}
|
}
|
||||||
}, [getSrc, resourceStatus])
|
}, [getSrc, resourceStatus]);
|
||||||
|
|
||||||
const handleMenuOpen = (event: any) => {
|
const handleMenuOpen = (event: any) => {
|
||||||
setAnchorEl(event.currentTarget)
|
setAnchorEl(event.currentTarget);
|
||||||
}
|
};
|
||||||
|
|
||||||
const handleMenuClose = () => {
|
const handleMenuClose = () => {
|
||||||
setAnchorEl(null)
|
setAnchorEl(null);
|
||||||
}
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const videoWidth = videoRef?.current?.offsetWidth
|
const videoWidth = videoRef?.current?.offsetWidth;
|
||||||
if (videoWidth && videoWidth <= 600) {
|
}, [canPlay]);
|
||||||
setIsMobileView(true)
|
|
||||||
}
|
|
||||||
}, [canPlay])
|
|
||||||
|
|
||||||
const getDownloadProgress = (current: number, total: number) => {
|
const getDownloadProgress = (current: number, total: number) => {
|
||||||
const progress = current / total * 100;
|
const progress = (current / total) * 100;
|
||||||
return Number.isNaN(progress) ? '' : progress.toFixed(0) + '%'
|
return Number.isNaN(progress) ? '' : progress.toFixed(0) + '%';
|
||||||
}
|
};
|
||||||
const mute = () => {
|
const mute = () => {
|
||||||
setIsMuted(true)
|
setIsMuted(true);
|
||||||
setMutedVolume(volume)
|
setMutedVolume(volume);
|
||||||
setVolume(0)
|
setVolume(0);
|
||||||
if (videoRef.current) videoRef.current.volume = 0
|
if (videoRef.current) videoRef.current.volume = 0;
|
||||||
}
|
};
|
||||||
const unMute = () => {
|
const unMute = () => {
|
||||||
setIsMuted(false)
|
setIsMuted(false);
|
||||||
setVolume(mutedVolume)
|
setVolume(mutedVolume);
|
||||||
if (videoRef.current) videoRef.current.volume = mutedVolume
|
if (videoRef.current) videoRef.current.volume = mutedVolume;
|
||||||
}
|
};
|
||||||
|
|
||||||
const toggleMute = () => {
|
const toggleMute = () => {
|
||||||
isMuted ? unMute() : mute();
|
isMuted ? unMute() : mute();
|
||||||
}
|
};
|
||||||
|
|
||||||
const changeVolume = (volumeChange: number) => {
|
const changeVolume = (volumeChange: number) => {
|
||||||
if (videoRef.current) {
|
if (videoRef.current) {
|
||||||
const minVolume = 0;
|
const minVolume = 0;
|
||||||
const maxVolume = 1;
|
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)
|
setIsMuted(false);
|
||||||
newVolume = Math.min(newVolume, maxVolume)
|
setMutedVolume(newVolume);
|
||||||
|
videoRef.current.volume = newVolume;
|
||||||
setIsMuted(false)
|
|
||||||
setMutedVolume(newVolume)
|
|
||||||
videoRef.current.volume = newVolume
|
|
||||||
setVolume(newVolume);
|
setVolume(newVolume);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
}
|
|
||||||
const setProgressRelative = (secondsChange: number) => {
|
const setProgressRelative = (secondsChange: number) => {
|
||||||
if (videoRef.current) {
|
if (videoRef.current) {
|
||||||
const currentTime = videoRef.current?.currentTime
|
const currentTime = videoRef.current?.currentTime;
|
||||||
const minTime = 0
|
const minTime = 0;
|
||||||
const maxTime = videoRef.current?.duration || 100
|
const maxTime = videoRef.current?.duration || 100;
|
||||||
|
|
||||||
let newTime = currentTime + secondsChange;
|
let newTime = currentTime + secondsChange;
|
||||||
newTime = Math.max(newTime, minTime)
|
newTime = Math.max(newTime, minTime);
|
||||||
newTime = Math.min(newTime, maxTime)
|
newTime = Math.min(newTime, maxTime);
|
||||||
videoRef.current.currentTime = newTime;
|
videoRef.current.currentTime = newTime;
|
||||||
setProgress(newTime);
|
setProgress(newTime);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
const setProgressAbsolute = (videoPercent: number) => {
|
const setProgressAbsolute = (videoPercent: number) => {
|
||||||
if (videoRef.current) {
|
if (videoRef.current) {
|
||||||
videoPercent = Math.min(videoPercent, 100)
|
videoPercent = Math.min(videoPercent, 100);
|
||||||
videoPercent = Math.max(videoPercent, 0)
|
videoPercent = Math.max(videoPercent, 0);
|
||||||
const finalTime = videoRef.current?.duration * videoPercent / 100
|
const finalTime = (videoRef.current?.duration * videoPercent) / 100;
|
||||||
videoRef.current.currentTime = finalTime
|
videoRef.current.currentTime = finalTime;
|
||||||
setProgress(finalTime);
|
setProgress(finalTime);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
||||||
const keyboardShortcutsDown = (e: React.KeyboardEvent<HTMLDivElement>) => {
|
const keyboardShortcutsDown = (e: React.KeyboardEvent<HTMLDivElement>) => {
|
||||||
e.preventDefault()
|
e.preventDefault();
|
||||||
|
|
||||||
switch (e.key) {
|
switch (e.key) {
|
||||||
case Key.Add: increaseSpeed(false); break;
|
case Key.Add:
|
||||||
case '+': increaseSpeed(false); break;
|
increaseSpeed(false);
|
||||||
case '>': increaseSpeed(false); break;
|
break;
|
||||||
|
case '+':
|
||||||
|
increaseSpeed(false);
|
||||||
|
break;
|
||||||
|
case '>':
|
||||||
|
increaseSpeed(false);
|
||||||
|
break;
|
||||||
|
|
||||||
case Key.Subtract: decreaseSpeed(); break;
|
case Key.Subtract:
|
||||||
case '-': decreaseSpeed(); break;
|
decreaseSpeed();
|
||||||
case '<': decreaseSpeed(); break;
|
break;
|
||||||
|
case '-':
|
||||||
|
decreaseSpeed();
|
||||||
|
break;
|
||||||
|
case '<':
|
||||||
|
decreaseSpeed();
|
||||||
|
break;
|
||||||
|
|
||||||
case Key.ArrowLeft: {
|
case Key.ArrowLeft:
|
||||||
if (e.shiftKey) setProgressRelative(-300);
|
{
|
||||||
else if (e.ctrlKey) setProgressRelative(-60);
|
if (e.shiftKey) setProgressRelative(-300);
|
||||||
else if (e.altKey) setProgressRelative(-10);
|
else if (e.ctrlKey) setProgressRelative(-60);
|
||||||
else setProgressRelative(-5);
|
else if (e.altKey) setProgressRelative(-10);
|
||||||
} break;
|
else setProgressRelative(-5);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case Key.ArrowRight: {
|
case Key.ArrowRight:
|
||||||
if (e.shiftKey) setProgressRelative(300);
|
{
|
||||||
else if (e.ctrlKey) setProgressRelative(60);
|
if (e.shiftKey) setProgressRelative(300);
|
||||||
else if (e.altKey) setProgressRelative(10);
|
else if (e.ctrlKey) setProgressRelative(60);
|
||||||
else setProgressRelative(5);
|
else if (e.altKey) setProgressRelative(10);
|
||||||
} break;
|
else setProgressRelative(5);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case Key.ArrowDown: changeVolume(-0.05); break;
|
case Key.ArrowDown:
|
||||||
case Key.ArrowUp: changeVolume(0.05); break;
|
changeVolume(-0.05);
|
||||||
|
break;
|
||||||
|
case Key.ArrowUp:
|
||||||
|
changeVolume(0.05);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
const keyboardShortcutsUp = (e: React.KeyboardEvent<HTMLDivElement>) => {
|
const keyboardShortcutsUp = (e: React.KeyboardEvent<HTMLDivElement>) => {
|
||||||
e.preventDefault()
|
e.preventDefault();
|
||||||
|
|
||||||
switch (e.key) {
|
switch (e.key) {
|
||||||
case ' ': togglePlay(); break;
|
case ' ':
|
||||||
case 'm': toggleMute(); break;
|
togglePlay();
|
||||||
|
break;
|
||||||
|
case 'm':
|
||||||
|
toggleMute();
|
||||||
|
break;
|
||||||
|
|
||||||
case 'f': enterFullscreen(); break;
|
case 'f':
|
||||||
case Key.Escape: exitFullscreen(); break;
|
enterFullscreen();
|
||||||
|
break;
|
||||||
|
case Key.Escape:
|
||||||
|
exitFullscreen();
|
||||||
|
break;
|
||||||
|
|
||||||
case '0': setProgressAbsolute(0); break;
|
case '0':
|
||||||
case '1': setProgressAbsolute(10); break;
|
setProgressAbsolute(0);
|
||||||
case '2': setProgressAbsolute(20); break;
|
break;
|
||||||
case '3': setProgressAbsolute(30); break;
|
case '1':
|
||||||
case '4': setProgressAbsolute(40); break;
|
setProgressAbsolute(10);
|
||||||
case '5': setProgressAbsolute(50); break;
|
break;
|
||||||
case '6': setProgressAbsolute(60); break;
|
case '2':
|
||||||
case '7': setProgressAbsolute(70); break;
|
setProgressAbsolute(20);
|
||||||
case '8': setProgressAbsolute(80); break;
|
break;
|
||||||
case '9': setProgressAbsolute(90); 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 (
|
return (
|
||||||
<VideoContainer
|
<VideoContainer
|
||||||
@ -451,7 +483,6 @@ export const VideoPlayer: React.FC<VideoPlayerProps> = ({
|
|||||||
height: '100%',
|
height: '100%',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|
||||||
{isLoading && (
|
{isLoading && (
|
||||||
<Box
|
<Box
|
||||||
position="absolute"
|
position="absolute"
|
||||||
@ -467,40 +498,44 @@ export const VideoPlayer: React.FC<VideoPlayerProps> = ({
|
|||||||
sx={{
|
sx={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
gap: '10px'
|
gap: '10px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<CircularProgress color="secondary" />
|
<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</>
|
<Typography
|
||||||
</>
|
variant="subtitle2"
|
||||||
) : resourceStatus?.status === 'DOWNLOADED' ? (
|
component="div"
|
||||||
<>Download Completed: building tutorial video...</>
|
sx={{
|
||||||
) : resourceStatus?.status !== 'READY' ? (
|
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...</>
|
<> Refetching data in 25 seconds</>
|
||||||
)}
|
</>
|
||||||
</Typography>
|
) : 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>
|
</Box>
|
||||||
)}
|
)}
|
||||||
{((!src && !isLoading) || !startPlay) && (
|
{((!src && !isLoading) || !startPlay) && (
|
||||||
@ -516,59 +551,61 @@ export const VideoPlayer: React.FC<VideoPlayerProps> = ({
|
|||||||
zIndex={500}
|
zIndex={500}
|
||||||
bgcolor="rgba(0, 0, 0, 0.6)"
|
bgcolor="rgba(0, 0, 0, 0.6)"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
togglePlay()
|
togglePlay();
|
||||||
}}
|
}}
|
||||||
sx={{
|
sx={{
|
||||||
cursor: 'pointer'
|
cursor: 'pointer',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<PlayArrow
|
<PlayArrow
|
||||||
sx={{
|
sx={{
|
||||||
width: '50px',
|
width: '50px',
|
||||||
height: '50px',
|
height: '50px',
|
||||||
color: 'white'
|
color: 'white',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
<Box sx={{
|
<Box
|
||||||
display: 'flex',
|
sx={{
|
||||||
flexGrow: 1,
|
display: 'flex',
|
||||||
width: '100%',
|
flexGrow: 1,
|
||||||
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%',
|
width: '100%',
|
||||||
height: '100%',
|
height: 'calc(100% - 60px)',
|
||||||
...customStyle
|
|
||||||
}}
|
}}
|
||||||
/>
|
>
|
||||||
|
<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>
|
</Box>
|
||||||
<ControlsContainer
|
<ControlsContainer
|
||||||
sx={{
|
sx={{
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
background: 'var(--bg-primary)',
|
background: 'var(--bg-primary)',
|
||||||
width: '100%',
|
width: '100%',
|
||||||
flexShrink: 0
|
flexShrink: 0,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{isMobileView && canPlay ? (
|
{canPlay ? (
|
||||||
<>
|
<>
|
||||||
<IconButton
|
<IconButton
|
||||||
sx={{
|
sx={{
|
||||||
color: 'rgba(255, 255, 255, 0.7)'
|
color: 'rgba(255, 255, 255, 0.7)',
|
||||||
}}
|
}}
|
||||||
onClick={togglePlay}
|
onClick={togglePlay}
|
||||||
>
|
>
|
||||||
@ -577,77 +614,7 @@ export const VideoPlayer: React.FC<VideoPlayerProps> = ({
|
|||||||
<IconButton
|
<IconButton
|
||||||
sx={{
|
sx={{
|
||||||
color: 'rgba(255, 255, 255, 0.7)',
|
color: 'rgba(255, 255, 255, 0.7)',
|
||||||
marginLeft: '15px'
|
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'
|
|
||||||
}}
|
}}
|
||||||
onClick={reloadVideo}
|
onClick={reloadVideo}
|
||||||
>
|
>
|
||||||
@ -669,7 +636,7 @@ export const VideoPlayer: React.FC<VideoPlayerProps> = ({
|
|||||||
!videoRef.current?.duration || !progress
|
!videoRef.current?.duration || !progress
|
||||||
? 'hidden'
|
? 'hidden'
|
||||||
: 'visible',
|
: 'visible',
|
||||||
flexShrink: 0
|
flexShrink: 0,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{progress && videoRef.current?.duration && formatTime(progress)}/
|
{progress && videoRef.current?.duration && formatTime(progress)}/
|
||||||
@ -680,7 +647,7 @@ export const VideoPlayer: React.FC<VideoPlayerProps> = ({
|
|||||||
<IconButton
|
<IconButton
|
||||||
sx={{
|
sx={{
|
||||||
color: 'rgba(255, 255, 255, 0.7)',
|
color: 'rgba(255, 255, 255, 0.7)',
|
||||||
marginRight: '10px'
|
marginRight: '10px',
|
||||||
}}
|
}}
|
||||||
onClick={toggleMute}
|
onClick={toggleMute}
|
||||||
>
|
>
|
||||||
@ -694,14 +661,14 @@ export const VideoPlayer: React.FC<VideoPlayerProps> = ({
|
|||||||
step={0.01}
|
step={0.01}
|
||||||
sx={{
|
sx={{
|
||||||
maxWidth: '100px',
|
maxWidth: '100px',
|
||||||
color: 'var(--Mail-Background)'
|
color: 'var(--Mail-Background)',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<IconButton
|
<IconButton
|
||||||
sx={{
|
sx={{
|
||||||
color: 'rgba(255, 255, 255, 0.7)',
|
color: 'rgba(255, 255, 255, 0.7)',
|
||||||
fontSize: '14px',
|
fontSize: '14px',
|
||||||
marginLeft: '5px'
|
marginLeft: '5px',
|
||||||
}}
|
}}
|
||||||
onClick={(e) => increaseSpeed()}
|
onClick={(e) => increaseSpeed()}
|
||||||
>
|
>
|
||||||
@ -709,7 +676,7 @@ export const VideoPlayer: React.FC<VideoPlayerProps> = ({
|
|||||||
</IconButton>
|
</IconButton>
|
||||||
<IconButton
|
<IconButton
|
||||||
sx={{
|
sx={{
|
||||||
color: 'rgba(255, 255, 255, 0.7)'
|
color: 'rgba(255, 255, 255, 0.7)',
|
||||||
}}
|
}}
|
||||||
onClick={toggleFullscreen}
|
onClick={toggleFullscreen}
|
||||||
>
|
>
|
||||||
@ -719,5 +686,5 @@ export const VideoPlayer: React.FC<VideoPlayerProps> = ({
|
|||||||
) : null}
|
) : null}
|
||||||
</ControlsContainer>
|
</ControlsContainer>
|
||||||
</VideoContainer>
|
</VideoContainer>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
|
@ -26,7 +26,7 @@ import { AddGroupList } from './AddGroupList';
|
|||||||
import { UserListOfInvites } from './UserListOfInvites';
|
import { UserListOfInvites } from './UserListOfInvites';
|
||||||
import { CustomizedSnackbars } from '../Snackbar/Snackbar';
|
import { CustomizedSnackbars } from '../Snackbar/Snackbar';
|
||||||
import { getFee } from '../../background';
|
import { getFee } from '../../background';
|
||||||
import { MyContext, isMobile } from '../../App';
|
import { MyContext } from '../../App';
|
||||||
import { subscribeToEvent, unsubscribeFromEvent } from '../../utils/events';
|
import { subscribeToEvent, unsubscribeFromEvent } from '../../utils/events';
|
||||||
|
|
||||||
export const Label = styled('label')`
|
export const Label = styled('label')`
|
||||||
@ -231,7 +231,7 @@ export const AddGroup = ({ address, open, setOpen }) => {
|
|||||||
value={value}
|
value={value}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
aria-label="basic tabs example"
|
aria-label="basic tabs example"
|
||||||
variant={isMobile ? 'scrollable' : 'fullWidth'} // Scrollable on mobile, full width on desktop
|
variant={'fullWidth'}
|
||||||
scrollButtons="auto"
|
scrollButtons="auto"
|
||||||
allowScrollButtonsMobile
|
allowScrollButtonsMobile
|
||||||
sx={{
|
sx={{
|
||||||
@ -247,7 +247,7 @@ export const AddGroup = ({ address, open, setOpen }) => {
|
|||||||
'&.Mui-selected': {
|
'&.Mui-selected': {
|
||||||
color: theme.palette.text.primary,
|
color: theme.palette.text.primary,
|
||||||
},
|
},
|
||||||
fontSize: isMobile ? '0.75rem' : '1rem', // Adjust font size for mobile
|
fontSize: '1rem',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Tab
|
<Tab
|
||||||
@ -257,7 +257,7 @@ export const AddGroup = ({ address, open, setOpen }) => {
|
|||||||
'&.Mui-selected': {
|
'&.Mui-selected': {
|
||||||
color: theme.palette.text.primary,
|
color: theme.palette.text.primary,
|
||||||
},
|
},
|
||||||
fontSize: isMobile ? '0.75rem' : '1rem', // Adjust font size for mobile
|
fontSize: '1rem',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Tab
|
<Tab
|
||||||
@ -267,7 +267,7 @@ export const AddGroup = ({ address, open, setOpen }) => {
|
|||||||
'&.Mui-selected': {
|
'&.Mui-selected': {
|
||||||
color: theme.palette.text.primary,
|
color: theme.palette.text.primary,
|
||||||
},
|
},
|
||||||
fontSize: isMobile ? '0.75rem' : '1rem', // Adjust font size for mobile
|
fontSize: '1rem',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
@ -61,11 +61,7 @@ import {
|
|||||||
unsubscribeFromEvent,
|
unsubscribeFromEvent,
|
||||||
} from '../../../utils/events';
|
} from '../../../utils/events';
|
||||||
import RefreshIcon from '@mui/icons-material/Refresh';
|
import RefreshIcon from '@mui/icons-material/Refresh';
|
||||||
import {
|
import { getArbitraryEndpointReact, getBaseApiReact } from '../../../App';
|
||||||
getArbitraryEndpointReact,
|
|
||||||
getBaseApiReact,
|
|
||||||
isMobile,
|
|
||||||
} from '../../../App';
|
|
||||||
import { WrapperUserAction } from '../../WrapperUserAction';
|
import { WrapperUserAction } from '../../WrapperUserAction';
|
||||||
import { addDataPublishesFunc, getDataPublishesFunc } from '../Group';
|
import { addDataPublishesFunc, getDataPublishesFunc } from '../Group';
|
||||||
const filterOptions = ['Recently active', 'Newest', 'Oldest'];
|
const filterOptions = ['Recently active', 'Newest', 'Oldest'];
|
||||||
@ -754,7 +750,6 @@ export const GroupMail = ({
|
|||||||
<ThreadSingleTitle
|
<ThreadSingleTitle
|
||||||
sx={{
|
sx={{
|
||||||
fontWeight: shouldAppearLighter && 300,
|
fontWeight: shouldAppearLighter && 300,
|
||||||
fontSize: isMobile && '18px',
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{thread?.threadData?.title}
|
{thread?.threadData?.title}
|
||||||
|
@ -1,18 +1,9 @@
|
|||||||
import React, { useEffect, useRef, useState } from 'react';
|
import React, { useEffect, useRef, useState } from 'react';
|
||||||
import {
|
import { Box, CircularProgress, Input } from '@mui/material';
|
||||||
Box,
|
|
||||||
Button,
|
|
||||||
CircularProgress,
|
|
||||||
Input,
|
|
||||||
Typography,
|
|
||||||
} from '@mui/material';
|
|
||||||
import ShortUniqueId from 'short-unique-id';
|
import ShortUniqueId from 'short-unique-id';
|
||||||
import CloseIcon from '@mui/icons-material/Close';
|
import CloseIcon from '@mui/icons-material/Close';
|
||||||
|
|
||||||
import ModalCloseSVG from '../../../assets/svgs/ModalClose.svg';
|
import ModalCloseSVG from '../../../assets/svgs/ModalClose.svg';
|
||||||
|
|
||||||
import ComposeIconSVG from '../../../assets/svgs/ComposeIcon.svg';
|
import ComposeIconSVG from '../../../assets/svgs/ComposeIcon.svg';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
AttachmentContainer,
|
AttachmentContainer,
|
||||||
CloseContainer,
|
CloseContainer,
|
||||||
@ -22,7 +13,6 @@ import {
|
|||||||
InstanceFooter,
|
InstanceFooter,
|
||||||
InstanceListContainer,
|
InstanceListContainer,
|
||||||
InstanceListHeader,
|
InstanceListHeader,
|
||||||
NewMessageAttachmentImg,
|
|
||||||
NewMessageCloseImg,
|
NewMessageCloseImg,
|
||||||
NewMessageHeaderP,
|
NewMessageHeaderP,
|
||||||
NewMessageInputRow,
|
NewMessageInputRow,
|
||||||
@ -32,16 +22,9 @@ import {
|
|||||||
|
|
||||||
import { ReusableModal } from './ReusableModal';
|
import { ReusableModal } from './ReusableModal';
|
||||||
import { Spacer } from '../../../common/Spacer';
|
import { Spacer } from '../../../common/Spacer';
|
||||||
import { formatBytes } from '../../../utils/Size';
|
|
||||||
import { CreateThreadIcon } from '../../../assets/Icons/CreateThreadIcon';
|
import { CreateThreadIcon } from '../../../assets/Icons/CreateThreadIcon';
|
||||||
import { SendNewMessage } from '../../../assets/Icons/SendNewMessage';
|
import { SendNewMessage } from '../../../assets/Icons/SendNewMessage';
|
||||||
import { TextEditor } from './TextEditor';
|
import { MyContext, pauseAllQueues, resumeAllQueues } from '../../../App';
|
||||||
import {
|
|
||||||
MyContext,
|
|
||||||
isMobile,
|
|
||||||
pauseAllQueues,
|
|
||||||
resumeAllQueues,
|
|
||||||
} from '../../../App';
|
|
||||||
import { getFee } from '../../../background';
|
import { getFee } from '../../../background';
|
||||||
import TipTap from '../../Chat/TipTap';
|
import TipTap from '../../Chat/TipTap';
|
||||||
import { MessageDisplay } from '../../Chat/MessageDisplay';
|
import { MessageDisplay } from '../../Chat/MessageDisplay';
|
||||||
@ -411,8 +394,8 @@ export const NewThread = ({
|
|||||||
>
|
>
|
||||||
<ComposeContainer
|
<ComposeContainer
|
||||||
sx={{
|
sx={{
|
||||||
padding: isMobile ? '5px' : '15px',
|
padding: '15px',
|
||||||
justifyContent: isMobile ? 'flex-start' : 'revert',
|
justifyContent: 'revert',
|
||||||
}}
|
}}
|
||||||
onClick={() => setIsOpen(true)}
|
onClick={() => setIsOpen(true)}
|
||||||
>
|
>
|
||||||
@ -423,7 +406,7 @@ export const NewThread = ({
|
|||||||
<ReusableModal
|
<ReusableModal
|
||||||
open={isOpen}
|
open={isOpen}
|
||||||
customStyles={{
|
customStyles={{
|
||||||
maxHeight: isMobile ? '95svh' : '95vh',
|
maxHeight: '95vh',
|
||||||
maxWidth: '950px',
|
maxWidth: '950px',
|
||||||
height: '700px',
|
height: '700px',
|
||||||
borderRadius: '12px 12px 0px 0px',
|
borderRadius: '12px 12px 0px 0px',
|
||||||
@ -434,8 +417,8 @@ export const NewThread = ({
|
|||||||
>
|
>
|
||||||
<InstanceListHeader
|
<InstanceListHeader
|
||||||
sx={{
|
sx={{
|
||||||
height: isMobile ? 'auto' : '50px',
|
height: '50px',
|
||||||
padding: isMobile ? '5px' : '20px 42px',
|
padding: '20px 42px',
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'space-between',
|
justifyContent: 'space-between',
|
||||||
@ -457,7 +440,7 @@ export const NewThread = ({
|
|||||||
<InstanceListContainer
|
<InstanceListContainer
|
||||||
sx={{
|
sx={{
|
||||||
backgroundColor: '#434448',
|
backgroundColor: '#434448',
|
||||||
padding: isMobile ? '5px' : '20px 42px',
|
padding: '20px 42px',
|
||||||
height: 'calc(100% - 165px)',
|
height: 'calc(100% - 165px)',
|
||||||
flexShrink: 0,
|
flexShrink: 0,
|
||||||
}}
|
}}
|
||||||
@ -481,7 +464,7 @@ export const NewThread = ({
|
|||||||
color: 'white',
|
color: 'white',
|
||||||
'& .MuiInput-input::placeholder': {
|
'& .MuiInput-input::placeholder': {
|
||||||
color: 'rgba(255,255,255, 0.70) !important',
|
color: 'rgba(255,255,255, 0.70) !important',
|
||||||
fontSize: isMobile ? '14px' : '20px',
|
fontSize: '20px',
|
||||||
fontStyle: 'normal',
|
fontStyle: 'normal',
|
||||||
fontWeight: 400,
|
fontWeight: 400,
|
||||||
lineHeight: '120%', // 24px
|
lineHeight: '120%', // 24px
|
||||||
@ -509,7 +492,9 @@ export const NewThread = ({
|
|||||||
<MessageDisplay htmlContent={postReply?.textContentV2} />
|
<MessageDisplay htmlContent={postReply?.textContentV2} />
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
{!isMobile && <Spacer height="30px" />}
|
|
||||||
|
<Spacer height="30px" />
|
||||||
|
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
maxHeight: '40vh',
|
maxHeight: '40vh',
|
||||||
@ -530,12 +515,13 @@ export const NewThread = ({
|
|||||||
/> */}
|
/> */}
|
||||||
</Box>
|
</Box>
|
||||||
</InstanceListContainer>
|
</InstanceListContainer>
|
||||||
|
|
||||||
<InstanceFooter
|
<InstanceFooter
|
||||||
sx={{
|
sx={{
|
||||||
backgroundColor: '#434448',
|
backgroundColor: '#434448',
|
||||||
padding: isMobile ? '5px' : '20px 42px',
|
padding: '20px 42px',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
height: isMobile ? 'auto' : '90px',
|
height: '90px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<NewMessageSendButton onClick={sendMail}>
|
<NewMessageSendButton onClick={sendMail}>
|
||||||
@ -553,9 +539,11 @@ export const NewThread = ({
|
|||||||
<CircularProgress sx={{}} size={'12px'} />
|
<CircularProgress sx={{}} size={'12px'} />
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<NewMessageSendP>
|
<NewMessageSendP>
|
||||||
{isMessage ? 'Post' : 'Create Thread'}
|
{isMessage ? 'Post' : 'Create Thread'}
|
||||||
</NewMessageSendP>
|
</NewMessageSendP>
|
||||||
|
|
||||||
{isMessage ? (
|
{isMessage ? (
|
||||||
<SendNewMessage opacity={1} height="25px" width="25px" />
|
<SendNewMessage opacity={1} height="25px" width="25px" />
|
||||||
) : (
|
) : (
|
||||||
@ -564,6 +552,7 @@ export const NewThread = ({
|
|||||||
</NewMessageSendButton>
|
</NewMessageSendButton>
|
||||||
</InstanceFooter>
|
</InstanceFooter>
|
||||||
</ReusableModal>
|
</ReusableModal>
|
||||||
|
|
||||||
<CustomizedSnackbars
|
<CustomizedSnackbars
|
||||||
open={openSnack}
|
open={openSnack}
|
||||||
setOpen={setOpenSnack}
|
setOpen={setOpenSnack}
|
||||||
|
@ -1,13 +1,12 @@
|
|||||||
import React from 'react'
|
import React from 'react';
|
||||||
import { Box, Modal, useTheme } from '@mui/material'
|
import { Box, Modal, useTheme } from '@mui/material';
|
||||||
import { isMobile } from '../../../App'
|
|
||||||
|
|
||||||
interface MyModalProps {
|
interface MyModalProps {
|
||||||
open: boolean
|
open: boolean;
|
||||||
onClose?: () => void
|
onClose?: () => void;
|
||||||
onSubmit?: (obj: any) => Promise<void>
|
onSubmit?: (obj: any) => Promise<void>;
|
||||||
children: any
|
children: any;
|
||||||
customStyles?: any
|
customStyles?: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ReusableModal: React.FC<MyModalProps> = ({
|
export const ReusableModal: React.FC<MyModalProps> = ({
|
||||||
@ -15,9 +14,10 @@ export const ReusableModal: React.FC<MyModalProps> = ({
|
|||||||
onClose,
|
onClose,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
children,
|
children,
|
||||||
customStyles = {}
|
customStyles = {},
|
||||||
}) => {
|
}) => {
|
||||||
const theme = useTheme()
|
const theme = useTheme();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
open={open}
|
open={open}
|
||||||
@ -32,27 +32,27 @@ export const ReusableModal: React.FC<MyModalProps> = ({
|
|||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
disableAutoFocus
|
disableAutoFocus
|
||||||
disableEnforceFocus
|
disableEnforceFocus
|
||||||
disableRestoreFocus
|
disableRestoreFocus
|
||||||
>
|
>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
position: 'absolute',
|
|
||||||
top: '50%',
|
|
||||||
left: '50%',
|
|
||||||
transform: 'translate(-50%, -50%)',
|
|
||||||
width: isMobile ? '95%' : '75%',
|
|
||||||
bgcolor: theme.palette.primary.main,
|
bgcolor: theme.palette.primary.main,
|
||||||
boxShadow: 24,
|
boxShadow: 24,
|
||||||
p: 4,
|
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
gap: 2,
|
gap: 2,
|
||||||
...customStyles
|
left: '50%',
|
||||||
|
p: 4,
|
||||||
|
position: 'absolute',
|
||||||
|
top: '50%',
|
||||||
|
transform: 'translate(-50%, -50%)',
|
||||||
|
width: '75%',
|
||||||
|
...customStyles,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</Box>
|
</Box>
|
||||||
</Modal>
|
</Modal>
|
||||||
)
|
);
|
||||||
}
|
};
|
||||||
|
@ -41,11 +41,7 @@ import {
|
|||||||
import { LoadingSnackbar } from '../../Snackbar/LoadingSnackbar';
|
import { LoadingSnackbar } from '../../Snackbar/LoadingSnackbar';
|
||||||
import { subscribeToEvent, unsubscribeFromEvent } from '../../../utils/events';
|
import { subscribeToEvent, unsubscribeFromEvent } from '../../../utils/events';
|
||||||
import RefreshIcon from '@mui/icons-material/Refresh';
|
import RefreshIcon from '@mui/icons-material/Refresh';
|
||||||
import {
|
import { getArbitraryEndpointReact, getBaseApiReact } from '../../../App';
|
||||||
getArbitraryEndpointReact,
|
|
||||||
getBaseApiReact,
|
|
||||||
isMobile,
|
|
||||||
} from '../../../App';
|
|
||||||
import {
|
import {
|
||||||
ArrowDownward as ArrowDownwardIcon,
|
ArrowDownward as ArrowDownwardIcon,
|
||||||
ArrowUpward as ArrowUpwardIcon,
|
ArrowUpward as ArrowUpwardIcon,
|
||||||
@ -602,23 +598,18 @@ export const Thread = ({
|
|||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
gap: isMobile ? '45px' : '35px',
|
gap: '35px',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
padding: isMobile && '5px',
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ShowMessageReturnButton
|
<ShowMessageReturnButton
|
||||||
sx={{
|
|
||||||
padding: isMobile && '5px',
|
|
||||||
minWidth: isMobile && '50px',
|
|
||||||
}}
|
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setMessages([]);
|
setMessages([]);
|
||||||
closeThread();
|
closeThread();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<MailIconImg src={ReturnSVG} />
|
<MailIconImg src={ReturnSVG} />
|
||||||
{!isMobile && <ComposeP>Return to Threads</ComposeP>}
|
<ComposeP>Return to Threads</ComposeP>
|
||||||
</ShowMessageReturnButton>
|
</ShowMessageReturnButton>
|
||||||
{/* Conditionally render the scroll buttons */}
|
{/* Conditionally render the scroll buttons */}
|
||||||
{showScrollButton &&
|
{showScrollButton &&
|
||||||
@ -628,7 +619,7 @@ export const Thread = ({
|
|||||||
sx={{
|
sx={{
|
||||||
color: 'white',
|
color: 'white',
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
fontSize: isMobile ? '28px' : '36px',
|
fontSize: '36px',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</ButtonBase>
|
</ButtonBase>
|
||||||
@ -638,7 +629,7 @@ export const Thread = ({
|
|||||||
sx={{
|
sx={{
|
||||||
color: 'white',
|
color: 'white',
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
fontSize: isMobile ? '28px' : '36px',
|
fontSize: '36px',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</ButtonBase>
|
</ButtonBase>
|
||||||
@ -655,38 +646,30 @@ export const Thread = ({
|
|||||||
>
|
>
|
||||||
<div ref={threadBeginningRef} />
|
<div ref={threadBeginningRef} />
|
||||||
<ThreadContainer>
|
<ThreadContainer>
|
||||||
<Spacer height={isMobile ? '10px' : '30px'} />
|
<Spacer height={'30px'} />
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
width: '100%',
|
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
justifyContent: 'space-between',
|
justifyContent: 'space-between',
|
||||||
|
width: '100%',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<GroupNameP
|
<GroupNameP>{currentThread?.threadData?.title}</GroupNameP>
|
||||||
sx={{
|
|
||||||
fontSize: isMobile && '18px',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{currentThread?.threadData?.title}
|
|
||||||
</GroupNameP>
|
|
||||||
</Box>
|
</Box>
|
||||||
<Spacer height={'15px'} />
|
<Spacer height={'15px'} />
|
||||||
|
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
width: '100%',
|
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
justifyContent: 'center',
|
|
||||||
gap: '5px',
|
gap: '5px',
|
||||||
|
justifyContent: 'center',
|
||||||
|
width: '100%',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
sx={{
|
sx={{
|
||||||
padding: isMobile && '5px',
|
|
||||||
fontSize: isMobile && '14px',
|
|
||||||
textTransformation: 'capitalize',
|
textTransformation: 'capitalize',
|
||||||
}}
|
}}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@ -705,8 +688,6 @@ export const Thread = ({
|
|||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
sx={{
|
sx={{
|
||||||
padding: isMobile && '5px',
|
|
||||||
fontSize: isMobile && '14px',
|
|
||||||
textTransformation: 'capitalize',
|
textTransformation: 'capitalize',
|
||||||
}}
|
}}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@ -725,8 +706,6 @@ export const Thread = ({
|
|||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
sx={{
|
sx={{
|
||||||
padding: isMobile && '5px',
|
|
||||||
fontSize: isMobile && '14px',
|
|
||||||
textTransformation: 'capitalize',
|
textTransformation: 'capitalize',
|
||||||
}}
|
}}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@ -745,8 +724,6 @@ export const Thread = ({
|
|||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
sx={{
|
sx={{
|
||||||
padding: isMobile && '5px',
|
|
||||||
fontSize: isMobile && '14px',
|
|
||||||
textTransformation: 'capitalize',
|
textTransformation: 'capitalize',
|
||||||
}}
|
}}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@ -764,7 +741,9 @@ export const Thread = ({
|
|||||||
Last
|
Last
|
||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
<Spacer height={isMobile ? '10px' : '30px'} />
|
|
||||||
|
<Spacer height={'30px'} />
|
||||||
|
|
||||||
{combinedListTempAndReal.map((message, index, list) => {
|
{combinedListTempAndReal.map((message, index, list) => {
|
||||||
let fullMessage = message;
|
let fullMessage = message;
|
||||||
|
|
||||||
@ -780,17 +759,17 @@ export const Thread = ({
|
|||||||
>
|
>
|
||||||
<Box
|
<Box
|
||||||
style={{
|
style={{
|
||||||
width: '100%',
|
|
||||||
borderRadius: '8px',
|
borderRadius: '8px',
|
||||||
|
flexDirection: 'column',
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
flexDirection: 'column',
|
width: '100%',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'flex-start',
|
alignItems: 'flex-start',
|
||||||
|
display: 'flex',
|
||||||
gap: '10px',
|
gap: '10px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@ -812,6 +791,7 @@ export const Thread = ({
|
|||||||
{message?.name?.charAt(0)}
|
{message?.name?.charAt(0)}
|
||||||
</Avatar>
|
</Avatar>
|
||||||
</WrapperUserAction>
|
</WrapperUserAction>
|
||||||
|
|
||||||
<ThreadInfoColumn>
|
<ThreadInfoColumn>
|
||||||
<WrapperUserAction
|
<WrapperUserAction
|
||||||
disabled={userInfo?.name === message?.name}
|
disabled={userInfo?.name === message?.name}
|
||||||
@ -822,18 +802,20 @@ export const Thread = ({
|
|||||||
{message?.name}
|
{message?.name}
|
||||||
</ThreadInfoColumnNameP>
|
</ThreadInfoColumnNameP>
|
||||||
</WrapperUserAction>
|
</WrapperUserAction>
|
||||||
|
|
||||||
<ThreadInfoColumnTime>
|
<ThreadInfoColumnTime>
|
||||||
{formatTimestampForum(message?.created)}
|
{formatTimestampForum(message?.created)}
|
||||||
</ThreadInfoColumnTime>
|
</ThreadInfoColumnTime>
|
||||||
</ThreadInfoColumn>
|
</ThreadInfoColumn>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
width: '100%',
|
|
||||||
display: 'flex',
|
|
||||||
justifyContent: 'center',
|
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
|
justifyContent: 'center',
|
||||||
|
width: '100%',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography
|
<Typography
|
||||||
@ -876,17 +858,17 @@ export const Thread = ({
|
|||||||
>
|
>
|
||||||
<Box
|
<Box
|
||||||
style={{
|
style={{
|
||||||
width: '100%',
|
|
||||||
borderRadius: '8px',
|
borderRadius: '8px',
|
||||||
|
flexDirection: 'column',
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
flexDirection: 'column',
|
width: '100%',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'flex-start',
|
alignItems: 'flex-start',
|
||||||
|
display: 'flex',
|
||||||
gap: '10px',
|
gap: '10px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@ -908,6 +890,7 @@ export const Thread = ({
|
|||||||
{message?.name?.charAt(0)}
|
{message?.name?.charAt(0)}
|
||||||
</Avatar>
|
</Avatar>
|
||||||
</WrapperUserAction>
|
</WrapperUserAction>
|
||||||
|
|
||||||
<ThreadInfoColumn>
|
<ThreadInfoColumn>
|
||||||
<WrapperUserAction
|
<WrapperUserAction
|
||||||
disabled={userInfo?.name === message?.name}
|
disabled={userInfo?.name === message?.name}
|
||||||
@ -918,18 +901,20 @@ export const Thread = ({
|
|||||||
{message?.name}
|
{message?.name}
|
||||||
</ThreadInfoColumnNameP>
|
</ThreadInfoColumnNameP>
|
||||||
</WrapperUserAction>
|
</WrapperUserAction>
|
||||||
|
|
||||||
<ThreadInfoColumnTime>
|
<ThreadInfoColumnTime>
|
||||||
{formatTimestampForum(message?.created)}
|
{formatTimestampForum(message?.created)}
|
||||||
</ThreadInfoColumnTime>
|
</ThreadInfoColumnTime>
|
||||||
</ThreadInfoColumn>
|
</ThreadInfoColumn>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
width: '100%',
|
|
||||||
display: 'flex',
|
|
||||||
justifyContent: 'center',
|
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
|
justifyContent: 'center',
|
||||||
|
width: '100%',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<CustomLoader />
|
<CustomLoader />
|
||||||
@ -952,9 +937,9 @@ export const Thread = ({
|
|||||||
<Spacer height="20px" />
|
<Spacer height="20px" />
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
width: '100%',
|
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
justifyContent: 'flex-end',
|
justifyContent: 'flex-end',
|
||||||
|
width: '100%',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
@ -997,8 +982,6 @@ export const Thread = ({
|
|||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
sx={{
|
sx={{
|
||||||
padding: isMobile && '5px',
|
|
||||||
fontSize: isMobile && '14px',
|
|
||||||
textTransformation: 'capitalize',
|
textTransformation: 'capitalize',
|
||||||
}}
|
}}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@ -1017,8 +1000,6 @@ export const Thread = ({
|
|||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
sx={{
|
sx={{
|
||||||
padding: isMobile && '5px',
|
|
||||||
fontSize: isMobile && '14px',
|
|
||||||
textTransformation: 'capitalize',
|
textTransformation: 'capitalize',
|
||||||
}}
|
}}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@ -1037,8 +1018,6 @@ export const Thread = ({
|
|||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
sx={{
|
sx={{
|
||||||
padding: isMobile && '5px',
|
|
||||||
fontSize: isMobile && '14px',
|
|
||||||
textTransformation: 'capitalize',
|
textTransformation: 'capitalize',
|
||||||
}}
|
}}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@ -1057,8 +1036,6 @@ export const Thread = ({
|
|||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
sx={{
|
sx={{
|
||||||
padding: isMobile && '5px',
|
|
||||||
fontSize: isMobile && '14px',
|
|
||||||
textTransformation: 'capitalize',
|
textTransformation: 'capitalize',
|
||||||
}}
|
}}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
@ -37,7 +37,6 @@ import {
|
|||||||
clearAllQueues,
|
clearAllQueues,
|
||||||
getArbitraryEndpointReact,
|
getArbitraryEndpointReact,
|
||||||
getBaseApiReact,
|
getBaseApiReact,
|
||||||
isMobile,
|
|
||||||
pauseAllQueues,
|
pauseAllQueues,
|
||||||
resumeAllQueues,
|
resumeAllQueues,
|
||||||
} from '../../App';
|
} from '../../App';
|
||||||
@ -1096,12 +1095,8 @@ export const Group = ({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (findDirect) {
|
if (findDirect) {
|
||||||
if (!isMobile) {
|
setDesktopSideView('directs');
|
||||||
setDesktopSideView('directs');
|
setDesktopViewMode('home');
|
||||||
setDesktopViewMode('home');
|
|
||||||
} else {
|
|
||||||
setMobileViewModeKeepOpen('messaging');
|
|
||||||
}
|
|
||||||
setSelectedDirect(null);
|
setSelectedDirect(null);
|
||||||
|
|
||||||
setNewChat(false);
|
setNewChat(false);
|
||||||
@ -1136,11 +1131,7 @@ export const Group = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (findDirect) {
|
if (findDirect) {
|
||||||
if (!isMobile) {
|
setDesktopSideView('directs');
|
||||||
setDesktopSideView('directs');
|
|
||||||
} else {
|
|
||||||
setMobileViewModeKeepOpen('messaging');
|
|
||||||
}
|
|
||||||
setSelectedDirect(null);
|
setSelectedDirect(null);
|
||||||
|
|
||||||
setNewChat(false);
|
setNewChat(false);
|
||||||
@ -1162,11 +1153,7 @@ export const Group = ({
|
|||||||
getTimestampEnterChat();
|
getTimestampEnterChat();
|
||||||
}, 200);
|
}, 200);
|
||||||
} else {
|
} else {
|
||||||
if (!isMobile) {
|
setDesktopSideView('directs');
|
||||||
setDesktopSideView('directs');
|
|
||||||
} else {
|
|
||||||
setMobileViewModeKeepOpen('messaging');
|
|
||||||
}
|
|
||||||
setNewChat(true);
|
setNewChat(true);
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
executeEvent('setDirectToValueNewChat', {
|
executeEvent('setDirectToValueNewChat', {
|
||||||
@ -1284,9 +1271,7 @@ export const Group = ({
|
|||||||
setupGroupWebsocketInterval.current = null;
|
setupGroupWebsocketInterval.current = null;
|
||||||
settimeoutForRefetchSecretKey.current = null;
|
settimeoutForRefetchSecretKey.current = null;
|
||||||
initiatedGetMembers.current = false;
|
initiatedGetMembers.current = false;
|
||||||
if (!isMobile) {
|
setDesktopViewMode('home');
|
||||||
setDesktopViewMode('home');
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const logoutEventFunc = () => {
|
const logoutEventFunc = () => {
|
||||||
@ -1303,34 +1288,7 @@ export const Group = ({
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const openAppsMode = () => {
|
const openAppsMode = () => {
|
||||||
if (isMobile) {
|
setDesktopViewMode('apps');
|
||||||
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);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -1372,9 +1330,7 @@ export const Group = ({
|
|||||||
setTriedToFetchSecretKey(false);
|
setTriedToFetchSecretKey(false);
|
||||||
setFirstSecretKeyInCreation(false);
|
setFirstSecretKeyInCreation(false);
|
||||||
setGroupSection('chat');
|
setGroupSection('chat');
|
||||||
if (!isMobile) {
|
setDesktopViewMode('chat');
|
||||||
setDesktopViewMode('chat');
|
|
||||||
}
|
|
||||||
|
|
||||||
window
|
window
|
||||||
.sendMessage('addTimestampEnterChat', {
|
.sendMessage('addTimestampEnterChat', {
|
||||||
@ -1430,9 +1386,7 @@ export const Group = ({
|
|||||||
setTriedToFetchSecretKey(false);
|
setTriedToFetchSecretKey(false);
|
||||||
setFirstSecretKeyInCreation(false);
|
setFirstSecretKeyInCreation(false);
|
||||||
setGroupSection('announcement');
|
setGroupSection('announcement');
|
||||||
if (!isMobile) {
|
setDesktopViewMode('chat');
|
||||||
setDesktopViewMode('chat');
|
|
||||||
}
|
|
||||||
window
|
window
|
||||||
.sendMessage('addGroupNotificationTimestamp', {
|
.sendMessage('addGroupNotificationTimestamp', {
|
||||||
timestamp: Date.now(),
|
timestamp: Date.now(),
|
||||||
@ -1496,9 +1450,7 @@ export const Group = ({
|
|||||||
setFirstSecretKeyInCreation(false);
|
setFirstSecretKeyInCreation(false);
|
||||||
setGroupSection('forum');
|
setGroupSection('forum');
|
||||||
setDefaultThread(data);
|
setDefaultThread(data);
|
||||||
if (!isMobile) {
|
setDesktopViewMode('chat');
|
||||||
setDesktopViewMode('chat');
|
|
||||||
}
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
setSelectedGroup(findGroup);
|
setSelectedGroup(findGroup);
|
||||||
setMobileViewMode('group');
|
setMobileViewMode('group');
|
||||||
@ -1521,12 +1473,6 @@ export const Group = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const goToHome = async () => {
|
const goToHome = async () => {
|
||||||
if (isMobile) {
|
|
||||||
setMobileViewMode('home');
|
|
||||||
}
|
|
||||||
if (!isMobile) {
|
|
||||||
// TODO: empty block. Check it!
|
|
||||||
}
|
|
||||||
setDesktopViewMode('home');
|
setDesktopViewMode('home');
|
||||||
|
|
||||||
await new Promise((res) => {
|
await new Promise((res) => {
|
||||||
@ -1614,26 +1560,38 @@ export const Group = ({
|
|||||||
borderRadius: '0px 15px 15px 0px',
|
borderRadius: '0px 15px 15px 0px',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
height: isMobile ? `calc(${rootHeight} - 45px)` : '100%',
|
height: '100%',
|
||||||
width: isMobile ? '100%' : '380px',
|
width: '380px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{!isMobile && (
|
<Box
|
||||||
<Box
|
sx={{
|
||||||
sx={{
|
alignItems: 'center',
|
||||||
alignItems: 'center',
|
display: 'flex',
|
||||||
display: 'flex',
|
gap: '10px',
|
||||||
gap: '10px',
|
justifyContent: 'center',
|
||||||
justifyContent: 'center',
|
width: '100%',
|
||||||
width: '100%',
|
}}
|
||||||
|
>
|
||||||
|
<ButtonBase
|
||||||
|
onClick={() => {
|
||||||
|
setDesktopSideView('groups');
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ButtonBase
|
<IconWrapper
|
||||||
onClick={() => {
|
color={
|
||||||
setDesktopSideView('groups');
|
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={
|
color={
|
||||||
groupChatHasUnread || groupsAnnHasUnread
|
groupChatHasUnread || groupsAnnHasUnread
|
||||||
? 'var(--unread)'
|
? 'var(--unread)'
|
||||||
@ -1641,29 +1599,28 @@ export const Group = ({
|
|||||||
? theme.palette.text.primary
|
? theme.palette.text.primary
|
||||||
: theme.palette.text.secondary
|
: theme.palette.text.secondary
|
||||||
}
|
}
|
||||||
label="Groups"
|
/>
|
||||||
selected={desktopSideView === 'groups'}
|
</IconWrapper>
|
||||||
customWidth="75px"
|
</ButtonBase>
|
||||||
>
|
<ButtonBase
|
||||||
<HubsIcon
|
onClick={() => {
|
||||||
height={24}
|
setDesktopSideView('directs');
|
||||||
color={
|
}}
|
||||||
groupChatHasUnread || groupsAnnHasUnread
|
>
|
||||||
? 'var(--unread)'
|
<IconWrapper
|
||||||
: desktopSideView === 'groups'
|
customWidth="75px"
|
||||||
? theme.palette.text.primary
|
color={
|
||||||
: theme.palette.text.secondary
|
directChatHasUnread
|
||||||
}
|
? 'var(--unread)'
|
||||||
/>
|
: desktopSideView === 'directs'
|
||||||
</IconWrapper>
|
? theme.palette.text.primary
|
||||||
</ButtonBase>
|
: theme.palette.text.secondary
|
||||||
<ButtonBase
|
}
|
||||||
onClick={() => {
|
label="Messaging"
|
||||||
setDesktopSideView('directs');
|
selected={desktopSideView === 'directs'}
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<IconWrapper
|
<MessagingIcon
|
||||||
customWidth="75px"
|
height={24}
|
||||||
color={
|
color={
|
||||||
directChatHasUnread
|
directChatHasUnread
|
||||||
? 'var(--unread)'
|
? 'var(--unread)'
|
||||||
@ -1671,23 +1628,10 @@ export const Group = ({
|
|||||||
? theme.palette.text.primary
|
? theme.palette.text.primary
|
||||||
: theme.palette.text.secondary
|
: theme.palette.text.secondary
|
||||||
}
|
}
|
||||||
label="Messaging"
|
/>
|
||||||
selected={desktopSideView === 'directs'}
|
</IconWrapper>
|
||||||
>
|
</ButtonBase>
|
||||||
<MessagingIcon
|
</Box>
|
||||||
height={24}
|
|
||||||
color={
|
|
||||||
directChatHasUnread
|
|
||||||
? 'var(--unread)'
|
|
||||||
: desktopSideView === 'directs'
|
|
||||||
? theme.palette.text.primary
|
|
||||||
: theme.palette.text.secondary
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</IconWrapper>
|
|
||||||
</ButtonBase>
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
@ -1843,30 +1787,42 @@ export const Group = ({
|
|||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
width: isMobile ? '100%' : '380px',
|
width: '380px',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
alignItems: 'flex-start',
|
alignItems: 'flex-start',
|
||||||
height: isMobile ? `calc(${rootHeight} - 45px)` : '100%',
|
height: '100%',
|
||||||
background: !isMobile && theme.palette.background.default,
|
background: theme.palette.background.default,
|
||||||
borderRadius: !isMobile && '0px 15px 15px 0px',
|
borderRadius: '0px 15px 15px 0px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{!isMobile && (
|
<Box
|
||||||
<Box
|
sx={{
|
||||||
sx={{
|
width: '100%',
|
||||||
width: '100%',
|
alignItems: 'center',
|
||||||
alignItems: 'center',
|
justifyContent: 'center',
|
||||||
justifyContent: 'center',
|
display: 'flex',
|
||||||
display: 'flex',
|
gap: '10px',
|
||||||
gap: '10px',
|
}}
|
||||||
|
>
|
||||||
|
<ButtonBase
|
||||||
|
onClick={() => {
|
||||||
|
setDesktopSideView('groups');
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ButtonBase
|
<IconWrapper
|
||||||
onClick={() => {
|
color={
|
||||||
setDesktopSideView('groups');
|
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={
|
color={
|
||||||
groupChatHasUnread || groupsAnnHasUnread
|
groupChatHasUnread || groupsAnnHasUnread
|
||||||
? 'var(--unread)'
|
? 'var(--unread)'
|
||||||
@ -1874,29 +1830,28 @@ export const Group = ({
|
|||||||
? theme.palette.text.primary
|
? theme.palette.text.primary
|
||||||
: theme.palette.text.secondary
|
: theme.palette.text.secondary
|
||||||
}
|
}
|
||||||
label="Groups"
|
/>
|
||||||
selected={desktopSideView === 'groups'}
|
</IconWrapper>
|
||||||
customWidth="75px"
|
</ButtonBase>
|
||||||
>
|
<ButtonBase
|
||||||
<HubsIcon
|
onClick={() => {
|
||||||
height={24}
|
setDesktopSideView('directs');
|
||||||
color={
|
}}
|
||||||
groupChatHasUnread || groupsAnnHasUnread
|
>
|
||||||
? 'var(--unread)'
|
<IconWrapper
|
||||||
: desktopSideView === 'groups'
|
customWidth="75px"
|
||||||
? theme.palette.text.primary
|
color={
|
||||||
: theme.palette.text.secondary
|
directChatHasUnread
|
||||||
}
|
? 'var(--unread)'
|
||||||
/>
|
: desktopSideView === 'directs'
|
||||||
</IconWrapper>
|
? theme.palette.text.primary
|
||||||
</ButtonBase>
|
: theme.palette.text.secondary
|
||||||
<ButtonBase
|
}
|
||||||
onClick={() => {
|
label="Messaging"
|
||||||
setDesktopSideView('directs');
|
selected={desktopSideView === 'directs'}
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<IconWrapper
|
<MessagingIcon
|
||||||
customWidth="75px"
|
height={24}
|
||||||
color={
|
color={
|
||||||
directChatHasUnread
|
directChatHasUnread
|
||||||
? 'var(--unread)'
|
? 'var(--unread)'
|
||||||
@ -1904,23 +1859,10 @@ export const Group = ({
|
|||||||
? theme.palette.text.primary
|
? theme.palette.text.primary
|
||||||
: theme.palette.text.secondary
|
: theme.palette.text.secondary
|
||||||
}
|
}
|
||||||
label="Messaging"
|
/>
|
||||||
selected={desktopSideView === 'directs'}
|
</IconWrapper>
|
||||||
>
|
</ButtonBase>
|
||||||
<MessagingIcon
|
</Box>
|
||||||
height={24}
|
|
||||||
color={
|
|
||||||
directChatHasUnread
|
|
||||||
? 'var(--unread)'
|
|
||||||
: desktopSideView === 'directs'
|
|
||||||
? theme.palette.text.primary
|
|
||||||
: theme.palette.text.secondary
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</IconWrapper>
|
|
||||||
</ButtonBase>
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
@ -2181,38 +2123,35 @@ export const Group = ({
|
|||||||
alignItems: 'flex-start',
|
alignItems: 'flex-start',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
height: isMobile ? '100%' : '100%',
|
height: '100%',
|
||||||
width: '100%',
|
width: '100%',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{!isMobile &&
|
{((desktopViewMode !== 'apps' && desktopViewMode !== 'dev') ||
|
||||||
((desktopViewMode !== 'apps' && desktopViewMode !== 'dev') ||
|
isOpenSideViewGroups) && (
|
||||||
isOpenSideViewGroups) && (
|
<DesktopSideBar
|
||||||
<DesktopSideBar
|
desktopViewMode={desktopViewMode}
|
||||||
desktopViewMode={desktopViewMode}
|
toggleSideViewGroups={toggleSideViewGroups}
|
||||||
toggleSideViewGroups={toggleSideViewGroups}
|
toggleSideViewDirects={toggleSideViewDirects}
|
||||||
toggleSideViewDirects={toggleSideViewDirects}
|
goToHome={goToHome}
|
||||||
goToHome={goToHome}
|
mode={appsMode}
|
||||||
mode={appsMode}
|
setMode={setAppsMode}
|
||||||
setMode={setAppsMode}
|
setDesktopSideView={setDesktopSideView}
|
||||||
setDesktopSideView={setDesktopSideView}
|
hasUnreadDirects={directChatHasUnread}
|
||||||
hasUnreadDirects={directChatHasUnread}
|
isApps={desktopViewMode === 'apps'}
|
||||||
isApps={desktopViewMode === 'apps'}
|
myName={userInfo?.name}
|
||||||
myName={userInfo?.name}
|
isGroups={isOpenSideViewGroups}
|
||||||
isGroups={isOpenSideViewGroups}
|
isDirects={isOpenSideViewDirects}
|
||||||
isDirects={isOpenSideViewDirects}
|
hasUnreadGroups={groupChatHasUnread || groupsAnnHasUnread}
|
||||||
hasUnreadGroups={groupChatHasUnread || groupsAnnHasUnread}
|
setDesktopViewMode={setDesktopViewMode}
|
||||||
setDesktopViewMode={setDesktopViewMode}
|
/>
|
||||||
/>
|
)}
|
||||||
)}
|
|
||||||
|
|
||||||
{!isMobile &&
|
{desktopViewMode === 'chat' &&
|
||||||
desktopViewMode === 'chat' &&
|
|
||||||
desktopSideView !== 'directs' &&
|
desktopSideView !== 'directs' &&
|
||||||
renderGroups()}
|
renderGroups()}
|
||||||
|
|
||||||
{!isMobile &&
|
{desktopViewMode === 'chat' &&
|
||||||
desktopViewMode === 'chat' &&
|
|
||||||
desktopSideView === 'directs' &&
|
desktopSideView === 'directs' &&
|
||||||
renderDirects()}
|
renderDirects()}
|
||||||
|
|
||||||
@ -2231,66 +2170,10 @@ export const Group = ({
|
|||||||
|
|
||||||
{newChat && (
|
{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
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
background: theme.palette.background.default,
|
background: theme.palette.background.default,
|
||||||
bottom: !(desktopViewMode === 'chat') ? 'unset' : '0px',
|
bottom: !(desktopViewMode === 'chat') ? 'unset' : '0px',
|
||||||
height: isMobile && `calc(${rootHeight} - 45px)`,
|
|
||||||
left: !(desktopViewMode === 'chat') ? '-100000px' : '0px',
|
left: !(desktopViewMode === 'chat') ? '-100000px' : '0px',
|
||||||
opacity: !(desktopViewMode === 'chat') ? 0 : 1,
|
opacity: !(desktopViewMode === 'chat') ? 0 : 1,
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
@ -2352,7 +2235,7 @@ export const Group = ({
|
|||||||
: '0px',
|
: '0px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{!isMobile && (
|
{
|
||||||
<DesktopHeader
|
<DesktopHeader
|
||||||
isPrivate={isPrivate}
|
isPrivate={isPrivate}
|
||||||
selectedGroup={selectedGroup}
|
selectedGroup={selectedGroup}
|
||||||
@ -2387,15 +2270,14 @@ export const Group = ({
|
|||||||
setGroupSection={setGroupSection}
|
setGroupSection={setGroupSection}
|
||||||
isForum={groupSection === 'forum'}
|
isForum={groupSection === 'forum'}
|
||||||
/>
|
/>
|
||||||
)}
|
}
|
||||||
|
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
position: 'relative',
|
|
||||||
flexGrow: 1,
|
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
// reference to change height
|
flexGrow: 1,
|
||||||
height: isMobile ? 'calc(100% - 82px)' : 'calc(100vh - 70px)',
|
height: 'calc(100vh - 70px)',
|
||||||
|
position: 'relative',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{triedToFetchSecretKey && (
|
{triedToFetchSecretKey && (
|
||||||
@ -2449,15 +2331,13 @@ export const Group = ({
|
|||||||
(!secretKeyPublishDate && !firstSecretKeyInCreation) ? (
|
(!secretKeyPublishDate && !firstSecretKeyInCreation) ? (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
display: 'flex',
|
|
||||||
width: '100%',
|
|
||||||
height: isMobile
|
|
||||||
? `calc(${rootHeight} - 113px)`
|
|
||||||
: 'calc(100vh - 70px)',
|
|
||||||
flexDirection: 'column',
|
|
||||||
alignItems: 'flex-start',
|
alignItems: 'flex-start',
|
||||||
padding: '20px',
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
height: 'calc(100vh - 70px)',
|
||||||
overflow: 'auto',
|
overflow: 'auto',
|
||||||
|
padding: '20px',
|
||||||
|
width: '100%',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{' '}
|
{' '}
|
||||||
@ -2605,7 +2485,6 @@ export const Group = ({
|
|||||||
sx={{
|
sx={{
|
||||||
background: theme.palette.background.default,
|
background: theme.palette.background.default,
|
||||||
bottom: !(desktopViewMode === 'chat') ? 'unset' : '0px',
|
bottom: !(desktopViewMode === 'chat') ? 'unset' : '0px',
|
||||||
height: isMobile && `calc(${rootHeight} - 45px)`,
|
|
||||||
left: !(desktopViewMode === 'chat') ? '-100000px' : '0px',
|
left: !(desktopViewMode === 'chat') ? '-100000px' : '0px',
|
||||||
opacity: !(desktopViewMode === 'chat') ? 0 : 1,
|
opacity: !(desktopViewMode === 'chat') ? 0 : 1,
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
@ -2642,7 +2521,7 @@ export const Group = ({
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{!isMobile && (
|
{
|
||||||
<AppsDesktop
|
<AppsDesktop
|
||||||
toggleSideViewGroups={toggleSideViewGroups}
|
toggleSideViewGroups={toggleSideViewGroups}
|
||||||
toggleSideViewDirects={toggleSideViewDirects}
|
toggleSideViewDirects={toggleSideViewDirects}
|
||||||
@ -2660,8 +2539,8 @@ export const Group = ({
|
|||||||
isApps={desktopViewMode === 'apps'}
|
isApps={desktopViewMode === 'apps'}
|
||||||
desktopViewMode={desktopViewMode}
|
desktopViewMode={desktopViewMode}
|
||||||
/>
|
/>
|
||||||
)}
|
}
|
||||||
{!isMobile && (
|
{
|
||||||
<AppsDevMode
|
<AppsDevMode
|
||||||
toggleSideViewGroups={toggleSideViewGroups}
|
toggleSideViewGroups={toggleSideViewGroups}
|
||||||
toggleSideViewDirects={toggleSideViewDirects}
|
toggleSideViewDirects={toggleSideViewDirects}
|
||||||
@ -2679,9 +2558,9 @@ export const Group = ({
|
|||||||
desktopViewMode={desktopViewMode}
|
desktopViewMode={desktopViewMode}
|
||||||
isApps={desktopViewMode === 'apps'}
|
isApps={desktopViewMode === 'apps'}
|
||||||
/>
|
/>
|
||||||
)}
|
}
|
||||||
|
|
||||||
{!isMobile && (
|
{
|
||||||
<HomeDesktop
|
<HomeDesktop
|
||||||
name={userInfo?.name}
|
name={userInfo?.name}
|
||||||
refreshHomeDataFunc={refreshHomeDataFunc}
|
refreshHomeDataFunc={refreshHomeDataFunc}
|
||||||
@ -2699,7 +2578,7 @@ export const Group = ({
|
|||||||
setDesktopViewMode={setDesktopViewMode}
|
setDesktopViewMode={setDesktopViewMode}
|
||||||
desktopViewMode={desktopViewMode}
|
desktopViewMode={desktopViewMode}
|
||||||
/>
|
/>
|
||||||
)}
|
}
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<AuthenticatedContainerInnerRight
|
<AuthenticatedContainerInnerRight
|
||||||
@ -2708,7 +2587,6 @@ export const Group = ({
|
|||||||
width: '31px',
|
width: '31px',
|
||||||
padding: '5px',
|
padding: '5px',
|
||||||
display:
|
display:
|
||||||
isMobile ||
|
|
||||||
desktopViewMode === 'apps' ||
|
desktopViewMode === 'apps' ||
|
||||||
desktopViewMode === 'dev' ||
|
desktopViewMode === 'dev' ||
|
||||||
desktopViewMode === 'chat'
|
desktopViewMode === 'chat'
|
||||||
|
@ -9,7 +9,7 @@ import { executeEvent } from '../../utils/events';
|
|||||||
import { Box, ButtonBase, Collapse, Typography, useTheme } from '@mui/material';
|
import { Box, ButtonBase, Collapse, Typography, useTheme } from '@mui/material';
|
||||||
import { getGroupNames } from './UserListOfInvites';
|
import { getGroupNames } from './UserListOfInvites';
|
||||||
import { CustomLoader } from '../../common/CustomLoader';
|
import { CustomLoader } from '../../common/CustomLoader';
|
||||||
import { getBaseApiReact, isMobile } from '../../App';
|
import { getBaseApiReact } from '../../App';
|
||||||
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
|
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
|
||||||
import ExpandLessIcon from '@mui/icons-material/ExpandLess';
|
import ExpandLessIcon from '@mui/icons-material/ExpandLess';
|
||||||
|
|
||||||
@ -97,7 +97,7 @@ export const GroupInvites = ({ myAddress, setOpenAddGroup }) => {
|
|||||||
borderRadius: '19px',
|
borderRadius: '19px',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
height: isMobile ? '165px' : '250px',
|
height: '250px',
|
||||||
padding: '20px',
|
padding: '20px',
|
||||||
width: '322px',
|
width: '322px',
|
||||||
}}
|
}}
|
||||||
|
@ -1,244 +1,277 @@
|
|||||||
import * as React from "react";
|
import * as React from 'react';
|
||||||
import List from "@mui/material/List";
|
import List from '@mui/material/List';
|
||||||
import ListItem from "@mui/material/ListItem";
|
import ListItem from '@mui/material/ListItem';
|
||||||
import ListItemButton from "@mui/material/ListItemButton";
|
import ListItemButton from '@mui/material/ListItemButton';
|
||||||
import ListItemIcon from "@mui/material/ListItemIcon";
|
import ListItemText from '@mui/material/ListItemText';
|
||||||
import ListItemText from "@mui/material/ListItemText";
|
import IconButton from '@mui/material/IconButton';
|
||||||
import Checkbox from "@mui/material/Checkbox";
|
import { RequestQueueWithPromise } from '../../utils/queue/queue';
|
||||||
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 GroupAddIcon from '@mui/icons-material/GroupAdd';
|
import GroupAddIcon from '@mui/icons-material/GroupAdd';
|
||||||
import { executeEvent } from "../../utils/events";
|
import { executeEvent } from '../../utils/events';
|
||||||
import { Box, ButtonBase, Collapse, Typography } from "@mui/material";
|
import { Box, ButtonBase, Collapse, Typography } from '@mui/material';
|
||||||
import { Spacer } from "../../common/Spacer";
|
import { CustomLoader } from '../../common/CustomLoader';
|
||||||
import { CustomLoader } from "../../common/CustomLoader";
|
import { MyContext, getBaseApiReact } from '../../App';
|
||||||
import { getBaseApi } from "../../background";
|
import { myGroupsWhereIAmAdminAtom } from '../../atoms/global';
|
||||||
import { MyContext, getBaseApiReact, isMobile } from "../../App";
|
import { useSetRecoilState } from 'recoil';
|
||||||
import { myGroupsWhereIAmAdminAtom } from "../../atoms/global";
|
|
||||||
import { useSetRecoilState } from "recoil";
|
|
||||||
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
|
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
|
||||||
import ExpandLessIcon from '@mui/icons-material/ExpandLess';
|
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 }) => {
|
export const GroupJoinRequests = ({
|
||||||
const [isExpanded, setIsExpanded] = React.useState(false)
|
myAddress,
|
||||||
|
groups,
|
||||||
const [groupsWithJoinRequests, setGroupsWithJoinRequests] = React.useState([])
|
setOpenManageMembers,
|
||||||
const [loading, setLoading] = React.useState(true)
|
getTimestampEnterChat,
|
||||||
const {txList, setTxList} = React.useContext(MyContext)
|
setSelectedGroup,
|
||||||
const setMyGroupsWhereIAmAdmin = useSetRecoilState(
|
setGroupSection,
|
||||||
myGroupsWhereIAmAdminAtom
|
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 {
|
try {
|
||||||
setLoading(true)
|
setLoading(true);
|
||||||
|
|
||||||
let groupsAsAdmin = []
|
let groupsAsAdmin = [];
|
||||||
const getAllGroupsAsAdmin = groups.filter((item)=> item.groupId !== '0').map(async (group)=> {
|
const getAllGroupsAsAdmin = groups
|
||||||
|
.filter((item) => item.groupId !== '0')
|
||||||
const isAdminResponse = await requestQueueGroupJoinRequests.enqueue(()=> {
|
.map(async (group) => {
|
||||||
return fetch(
|
const isAdminResponse = await requestQueueGroupJoinRequests.enqueue(
|
||||||
`${getBaseApiReact()}/groups/members/${group.groupId}?limit=0&onlyAdmins=true`
|
() => {
|
||||||
|
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)
|
const findMyself = isAdminData?.members?.find(
|
||||||
|
(member) => member.member === myAddress
|
||||||
if(findMyself){
|
);
|
||||||
groupsAsAdmin.push(group)
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
|
|
||||||
|
if (findMyself) {
|
||||||
await Promise.all(getAllGroupsAsAdmin)
|
groupsAsAdmin.push(group);
|
||||||
setMyGroupsWhereIAmAdmin(groupsAsAdmin)
|
}
|
||||||
const res = await Promise.all(groupsAsAdmin.map(async (group)=> {
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
const joinRequestResponse = await requestQueueGroupJoinRequests.enqueue(()=> {
|
await Promise.all(getAllGroupsAsAdmin);
|
||||||
return fetch(
|
setMyGroupsWhereIAmAdmin(groupsAsAdmin);
|
||||||
`${getBaseApiReact()}/groups/joinrequests/${group.groupId}`
|
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()
|
const joinRequestData = await joinRequestResponse.json();
|
||||||
return {
|
return {
|
||||||
group,
|
group,
|
||||||
data: joinRequestData
|
data: joinRequestData,
|
||||||
}
|
};
|
||||||
}))
|
})
|
||||||
setGroupsWithJoinRequests(res)
|
);
|
||||||
|
setGroupsWithJoinRequests(res);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false)
|
setLoading(false);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (myAddress && groups.length > 0) {
|
if (myAddress && groups.length > 0) {
|
||||||
getJoinRequests()
|
getJoinRequests();
|
||||||
} else {
|
} else {
|
||||||
setLoading(false)
|
setLoading(false);
|
||||||
}
|
}
|
||||||
}, [myAddress, groups]);
|
}, [myAddress, groups]);
|
||||||
|
|
||||||
const filteredJoinRequests = React.useMemo(()=> {
|
const filteredJoinRequests = React.useMemo(() => {
|
||||||
return groupsWithJoinRequests.map((group)=> {
|
return groupsWithJoinRequests.map((group) => {
|
||||||
const filteredGroupRequests = group?.data?.filter((gd)=> {
|
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 findJoinRequsetInTxList = txList?.find(
|
||||||
|
(tx) =>
|
||||||
|
tx?.groupId === group?.group?.groupId &&
|
||||||
|
tx?.qortalAddress === gd?.joiner &&
|
||||||
|
tx?.type === 'join-request-accept'
|
||||||
|
);
|
||||||
|
|
||||||
if(findJoinRequsetInTxList) return false
|
if (findJoinRequsetInTxList) return false;
|
||||||
return true
|
return true;
|
||||||
})
|
});
|
||||||
return {
|
return {
|
||||||
...group,
|
...group,
|
||||||
data: filteredGroupRequests
|
data: filteredGroupRequests,
|
||||||
}
|
};
|
||||||
})
|
});
|
||||||
}, [groupsWithJoinRequests, txList])
|
}, [groupsWithJoinRequests, txList]);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box sx={{
|
<Box
|
||||||
width: "100%",
|
sx={{
|
||||||
display: "flex",
|
width: '100%',
|
||||||
flexDirection: "column",
|
display: 'flex',
|
||||||
alignItems: 'center'
|
flexDirection: 'column',
|
||||||
}}>
|
alignItems: 'center',
|
||||||
|
}}
|
||||||
|
>
|
||||||
<ButtonBase
|
<ButtonBase
|
||||||
sx={{
|
sx={{
|
||||||
width: "322px",
|
width: '322px',
|
||||||
display: "flex",
|
display: 'flex',
|
||||||
flexDirection: "row",
|
flexDirection: 'row',
|
||||||
padding: '0px 20px',
|
padding: '0px 20px',
|
||||||
gap: '10px',
|
gap: '10px',
|
||||||
justifyContent: 'flex-start'
|
justifyContent: 'flex-start',
|
||||||
}}
|
}}
|
||||||
onClick={()=> setIsExpanded((prev)=> !prev)}
|
onClick={() => setIsExpanded((prev) => !prev)}
|
||||||
>
|
>
|
||||||
<Typography
|
<Typography
|
||||||
sx={{
|
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>
|
</Typography>
|
||||||
{isExpanded ? <ExpandLessIcon sx={{
|
{isExpanded ? (
|
||||||
marginLeft: 'auto'
|
<ExpandLessIcon
|
||||||
}} /> : (
|
sx={{
|
||||||
<ExpandMoreIcon sx={{
|
marginLeft: 'auto',
|
||||||
marginLeft: 'auto'
|
}}
|
||||||
}}/>
|
/>
|
||||||
)}
|
) : (
|
||||||
|
<ExpandMoreIcon
|
||||||
|
sx={{
|
||||||
|
marginLeft: 'auto',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</ButtonBase>
|
</ButtonBase>
|
||||||
<Collapse in={isExpanded} timeout="auto" unmountOnExit>
|
<Collapse in={isExpanded} timeout="auto" unmountOnExit>
|
||||||
<Box
|
<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);
|
|
||||||
}}
|
|
||||||
sx={{
|
sx={{
|
||||||
marginBottom: '20px'
|
width: '322px',
|
||||||
}}
|
height: '250px',
|
||||||
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>
|
|
||||||
)
|
|
||||||
|
|
||||||
})}
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
bgcolor: 'background.paper',
|
||||||
|
padding: '20px',
|
||||||
</List>
|
borderRadius: '19px',
|
||||||
</Box>
|
}}
|
||||||
</Collapse>
|
>
|
||||||
|
{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>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -16,19 +16,12 @@ import {
|
|||||||
DialogContent,
|
DialogContent,
|
||||||
DialogContentText,
|
DialogContentText,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
ListItem,
|
|
||||||
ListItemAvatar,
|
|
||||||
ListItemButton,
|
|
||||||
ListItemText,
|
|
||||||
MenuItem,
|
MenuItem,
|
||||||
Popover,
|
Popover,
|
||||||
Select,
|
Select,
|
||||||
TextField,
|
TextField,
|
||||||
Typography,
|
Typography,
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
|
|
||||||
import { getNameInfo } from './Group';
|
|
||||||
import { getBaseApi, getFee } from '../../background';
|
|
||||||
import { LoadingButton } from '@mui/lab';
|
import { LoadingButton } from '@mui/lab';
|
||||||
import LockIcon from '@mui/icons-material/Lock';
|
import LockIcon from '@mui/icons-material/Lock';
|
||||||
import NoEncryptionGmailerrorredIcon from '@mui/icons-material/NoEncryptionGmailerrorred';
|
import NoEncryptionGmailerrorredIcon from '@mui/icons-material/NoEncryptionGmailerrorred';
|
||||||
@ -36,7 +29,6 @@ import {
|
|||||||
MyContext,
|
MyContext,
|
||||||
getArbitraryEndpointReact,
|
getArbitraryEndpointReact,
|
||||||
getBaseApiReact,
|
getBaseApiReact,
|
||||||
isMobile,
|
|
||||||
} from '../../App';
|
} from '../../App';
|
||||||
import { Spacer } from '../../common/Spacer';
|
import { Spacer } from '../../common/Spacer';
|
||||||
import { CustomLoader } from '../../common/CustomLoader';
|
import { CustomLoader } from '../../common/CustomLoader';
|
||||||
@ -51,7 +43,6 @@ import { Label } from './AddGroup';
|
|||||||
import ShortUniqueId from 'short-unique-id';
|
import ShortUniqueId from 'short-unique-id';
|
||||||
import { CustomizedSnackbars } from '../Snackbar/Snackbar';
|
import { CustomizedSnackbars } from '../Snackbar/Snackbar';
|
||||||
import { getGroupNames } from './UserListOfInvites';
|
import { getGroupNames } from './UserListOfInvites';
|
||||||
import { WrapperUserAction } from '../WrapperUserAction';
|
|
||||||
import { useVirtualizer } from '@tanstack/react-virtual';
|
import { useVirtualizer } from '@tanstack/react-virtual';
|
||||||
import ErrorBoundary from '../../common/ErrorBoundary';
|
import ErrorBoundary from '../../common/ErrorBoundary';
|
||||||
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
|
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
|
||||||
@ -390,6 +381,7 @@ export const ListOfGroupPromotions = () => {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</ButtonBase>
|
</ButtonBase>
|
||||||
|
|
||||||
<Box
|
<Box
|
||||||
style={{
|
style={{
|
||||||
width: '330px',
|
width: '330px',
|
||||||
@ -401,7 +393,7 @@ export const ListOfGroupPromotions = () => {
|
|||||||
<>
|
<>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
width: isMobile ? '320px' : '750px',
|
width: '750px',
|
||||||
maxWidth: '90%',
|
maxWidth: '90%',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
@ -422,6 +414,7 @@ export const ListOfGroupPromotions = () => {
|
|||||||
fontWeight: 600,
|
fontWeight: 600,
|
||||||
}}
|
}}
|
||||||
></Typography>
|
></Typography>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
variant="contained"
|
variant="contained"
|
||||||
onClick={() => setIsShowModal(true)}
|
onClick={() => setIsShowModal(true)}
|
||||||
@ -432,18 +425,19 @@ export const ListOfGroupPromotions = () => {
|
|||||||
Add Promotion
|
Add Promotion
|
||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Spacer height="10px" />
|
<Spacer height="10px" />
|
||||||
</Box>
|
</Box>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
width: isMobile ? '320px' : '750px',
|
bgcolor: 'background.paper',
|
||||||
maxWidth: '90%',
|
borderRadius: '19px',
|
||||||
maxHeight: '700px',
|
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
bgcolor: 'background.paper',
|
maxHeight: '700px',
|
||||||
|
maxWidth: '90%',
|
||||||
padding: '20px 0px',
|
padding: '20px 0px',
|
||||||
borderRadius: '19px',
|
width: '750px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{loading && promotions.length === 0 && (
|
{loading && promotions.length === 0 && (
|
||||||
@ -589,6 +583,7 @@ export const ListOfGroupPromotions = () => {
|
|||||||
>
|
>
|
||||||
Group name: {` ${promotion?.groupName}`}
|
Group name: {` ${promotion?.groupName}`}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
<Typography
|
<Typography
|
||||||
sx={{
|
sx={{
|
||||||
fontSize: '13px',
|
fontSize: '13px',
|
||||||
@ -598,6 +593,7 @@ export const ListOfGroupPromotions = () => {
|
|||||||
Number of members:{' '}
|
Number of members:{' '}
|
||||||
{` ${promotion?.memberCount}`}
|
{` ${promotion?.memberCount}`}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
{promotion?.description && (
|
{promotion?.description && (
|
||||||
<Typography
|
<Typography
|
||||||
sx={{
|
sx={{
|
||||||
@ -608,6 +604,7 @@ export const ListOfGroupPromotions = () => {
|
|||||||
{promotion?.description}
|
{promotion?.description}
|
||||||
</Typography>
|
</Typography>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{promotion?.isOpen === false && (
|
{promotion?.isOpen === false && (
|
||||||
<Typography
|
<Typography
|
||||||
sx={{
|
sx={{
|
||||||
@ -620,7 +617,9 @@ export const ListOfGroupPromotions = () => {
|
|||||||
your request
|
your request
|
||||||
</Typography>
|
</Typography>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Spacer height="5px" />
|
<Spacer height="5px" />
|
||||||
|
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
@ -638,6 +637,7 @@ export const ListOfGroupPromotions = () => {
|
|||||||
>
|
>
|
||||||
Close
|
Close
|
||||||
</LoadingButton>
|
</LoadingButton>
|
||||||
|
|
||||||
<LoadingButton
|
<LoadingButton
|
||||||
loading={isLoadingJoinGroup}
|
loading={isLoadingJoinGroup}
|
||||||
loadingPosition="start"
|
loadingPosition="start"
|
||||||
@ -682,6 +682,7 @@ export const ListOfGroupPromotions = () => {
|
|||||||
>
|
>
|
||||||
{promotion?.name?.charAt(0)}
|
{promotion?.name?.charAt(0)}
|
||||||
</Avatar>
|
</Avatar>
|
||||||
|
|
||||||
<Typography
|
<Typography
|
||||||
sx={{
|
sx={{
|
||||||
fontWight: 600,
|
fontWight: 600,
|
||||||
@ -692,6 +693,7 @@ export const ListOfGroupPromotions = () => {
|
|||||||
{promotion?.name}
|
{promotion?.name}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Typography
|
<Typography
|
||||||
sx={{
|
sx={{
|
||||||
fontWight: 600,
|
fontWight: 600,
|
||||||
@ -702,7 +704,9 @@ export const ListOfGroupPromotions = () => {
|
|||||||
{promotion?.groupName}
|
{promotion?.groupName}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Spacer height="20px" />
|
<Spacer height="20px" />
|
||||||
|
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
@ -735,7 +739,9 @@ export const ListOfGroupPromotions = () => {
|
|||||||
: 'Private group'}
|
: 'Private group'}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Spacer height="20px" />
|
<Spacer height="20px" />
|
||||||
|
|
||||||
<Typography
|
<Typography
|
||||||
sx={{
|
sx={{
|
||||||
fontWight: 600,
|
fontWight: 600,
|
||||||
@ -745,7 +751,9 @@ export const ListOfGroupPromotions = () => {
|
|||||||
>
|
>
|
||||||
{promotion?.data}
|
{promotion?.data}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
<Spacer height="20px" />
|
<Spacer height="20px" />
|
||||||
|
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
@ -767,6 +775,7 @@ export const ListOfGroupPromotions = () => {
|
|||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Spacer height="50px" />
|
<Spacer height="50px" />
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
</div>
|
</div>
|
||||||
@ -779,6 +788,7 @@ export const ListOfGroupPromotions = () => {
|
|||||||
</Box>
|
</Box>
|
||||||
</>
|
</>
|
||||||
</Collapse>
|
</Collapse>
|
||||||
|
|
||||||
<Spacer height="20px" />
|
<Spacer height="20px" />
|
||||||
|
|
||||||
{isShowModal && (
|
{isShowModal && (
|
||||||
|
@ -1,21 +1,14 @@
|
|||||||
import * as React from "react";
|
import * as React from 'react';
|
||||||
import List from "@mui/material/List";
|
import List from '@mui/material/List';
|
||||||
import ListItem from "@mui/material/ListItem";
|
import ListItem from '@mui/material/ListItem';
|
||||||
import ListItemButton from "@mui/material/ListItemButton";
|
import ListItemButton from '@mui/material/ListItemButton';
|
||||||
import ListItemIcon from "@mui/material/ListItemIcon";
|
import ListItemText from '@mui/material/ListItemText';
|
||||||
import ListItemText from "@mui/material/ListItemText";
|
import IconButton from '@mui/material/IconButton';
|
||||||
import Checkbox from "@mui/material/Checkbox";
|
import { executeEvent } from '../../utils/events';
|
||||||
import IconButton from "@mui/material/IconButton";
|
import { Box, Typography } from '@mui/material';
|
||||||
import CommentIcon from "@mui/icons-material/Comment";
|
import { Spacer } from '../../common/Spacer';
|
||||||
import InfoIcon from "@mui/icons-material/Info";
|
import { CustomLoader } from '../../common/CustomLoader';
|
||||||
import GroupAddIcon from "@mui/icons-material/GroupAdd";
|
import VisibilityIcon from '@mui/icons-material/Visibility';
|
||||||
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";
|
|
||||||
|
|
||||||
export const ListOfThreadPostsWatched = () => {
|
export const ListOfThreadPostsWatched = () => {
|
||||||
const [posts, setPosts] = React.useState([]);
|
const [posts, setPosts] = React.useState([]);
|
||||||
@ -24,33 +17,33 @@ export const ListOfThreadPostsWatched = () => {
|
|||||||
const getPosts = async () => {
|
const getPosts = async () => {
|
||||||
try {
|
try {
|
||||||
await new Promise((res, rej) => {
|
await new Promise((res, rej) => {
|
||||||
window.sendMessage("getThreadActivity", {})
|
window
|
||||||
.then((response) => {
|
.sendMessage('getThreadActivity', {})
|
||||||
if (!response?.error) {
|
.then((response) => {
|
||||||
if (!response) {
|
if (!response?.error) {
|
||||||
res(null);
|
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;
|
return;
|
||||||
}
|
}
|
||||||
const uniquePosts = response.reduce((acc, current) => {
|
rej(response.error);
|
||||||
const x = acc.find(
|
})
|
||||||
(item) => item?.thread?.threadId === current?.thread?.threadId
|
.catch((error) => {
|
||||||
);
|
rej(error.message || 'An error occurred');
|
||||||
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");
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
} finally {
|
} finally {
|
||||||
@ -63,49 +56,50 @@ export const ListOfThreadPostsWatched = () => {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box sx={{
|
<Box
|
||||||
width: "100%",
|
sx={{
|
||||||
display: "flex",
|
width: '100%',
|
||||||
flexDirection: "column",
|
display: 'flex',
|
||||||
alignItems: 'center'
|
flexDirection: 'column',
|
||||||
}}>
|
alignItems: 'center',
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
width: "322px",
|
width: '322px',
|
||||||
display: "flex",
|
display: 'flex',
|
||||||
flexDirection: "column",
|
flexDirection: 'column',
|
||||||
padding: '0px 20px',
|
padding: '0px 20px',
|
||||||
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography
|
<Typography
|
||||||
sx={{
|
sx={{
|
||||||
fontSize: "13px",
|
fontSize: '13px',
|
||||||
fontWeight: 600,
|
fontWeight: 600,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
New Thread Posts:
|
New Thread Posts:
|
||||||
</Typography>
|
</Typography>
|
||||||
<Spacer height="10px" />
|
<Spacer height="10px" />
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
width: "322px",
|
bgcolor: 'background.paper',
|
||||||
height: isMobile ? "165px" : "250px",
|
borderRadius: '19px',
|
||||||
display: "flex",
|
display: 'flex',
|
||||||
flexDirection: "column",
|
flexDirection: 'column',
|
||||||
bgcolor: "background.paper",
|
height: '250px',
|
||||||
padding: "20px",
|
padding: '20px',
|
||||||
borderRadius: '19px'
|
width: '322px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{loading && posts.length === 0 && (
|
{loading && posts.length === 0 && (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
width: "100%",
|
width: '100%',
|
||||||
display: "flex",
|
display: 'flex',
|
||||||
justifyContent: "center",
|
justifyContent: 'center',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<CustomLoader />
|
<CustomLoader />
|
||||||
@ -114,19 +108,18 @@ export const ListOfThreadPostsWatched = () => {
|
|||||||
{!loading && posts.length === 0 && (
|
{!loading && posts.length === 0 && (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
width: "100%",
|
width: '100%',
|
||||||
display: "flex",
|
display: 'flex',
|
||||||
justifyContent: "center",
|
justifyContent: 'center',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
height: '100%',
|
height: '100%',
|
||||||
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography
|
<Typography
|
||||||
sx={{
|
sx={{
|
||||||
fontSize: "11px",
|
fontSize: '11px',
|
||||||
fontWeight: 400,
|
fontWeight: 400,
|
||||||
color: 'rgba(255, 255, 255, 0.2)'
|
color: 'rgba(255, 255, 255, 0.2)',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Nothing to display
|
Nothing to display
|
||||||
@ -134,47 +127,46 @@ export const ListOfThreadPostsWatched = () => {
|
|||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
{posts?.length > 0 && (
|
{posts?.length > 0 && (
|
||||||
<List
|
<List
|
||||||
className="scrollable-container"
|
className="scrollable-container"
|
||||||
sx={{
|
sx={{
|
||||||
width: "100%",
|
width: '100%',
|
||||||
maxWidth: 360,
|
maxWidth: 360,
|
||||||
bgcolor: "background.paper",
|
bgcolor: 'background.paper',
|
||||||
maxHeight: "300px",
|
maxHeight: '300px',
|
||||||
overflow: "auto",
|
overflow: 'auto',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{posts?.map((post) => {
|
{posts?.map((post) => {
|
||||||
return (
|
return (
|
||||||
<ListItem
|
<ListItem
|
||||||
key={post?.thread?.threadId}
|
key={post?.thread?.threadId}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
executeEvent("openThreadNewPost", {
|
executeEvent('openThreadNewPost', {
|
||||||
data: post,
|
data: post,
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
disablePadding
|
disablePadding
|
||||||
secondaryAction={
|
secondaryAction={
|
||||||
<IconButton edge="end" aria-label="comments">
|
<IconButton edge="end" aria-label="comments">
|
||||||
<VisibilityIcon
|
<VisibilityIcon
|
||||||
sx={{
|
sx={{
|
||||||
color: "red",
|
color: 'red',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</IconButton>
|
</IconButton>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<ListItemButton disableRipple role={undefined} dense>
|
<ListItemButton disableRipple role={undefined} dense>
|
||||||
<ListItemText
|
<ListItemText
|
||||||
primary={`New post in ${post?.thread?.threadData?.title}`}
|
primary={`New post in ${post?.thread?.threadData?.title}`}
|
||||||
/>
|
/>
|
||||||
</ListItemButton>
|
</ListItemButton>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</List>
|
</List>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
@ -1,10 +1,6 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import Button from '@mui/material/Button';
|
import Button from '@mui/material/Button';
|
||||||
import Dialog from '@mui/material/Dialog';
|
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 AppBar from '@mui/material/AppBar';
|
||||||
import Toolbar from '@mui/material/Toolbar';
|
import Toolbar from '@mui/material/Toolbar';
|
||||||
import IconButton from '@mui/material/IconButton';
|
import IconButton from '@mui/material/IconButton';
|
||||||
@ -19,7 +15,7 @@ import { ListOfBans } from './ListOfBans';
|
|||||||
import { ListOfJoinRequests } from './ListOfJoinRequests';
|
import { ListOfJoinRequests } from './ListOfJoinRequests';
|
||||||
import { Box, ButtonBase, Card, Tab, Tabs } from '@mui/material';
|
import { Box, ButtonBase, Card, Tab, Tabs } from '@mui/material';
|
||||||
import { CustomizedSnackbars } from '../Snackbar/Snackbar';
|
import { CustomizedSnackbars } from '../Snackbar/Snackbar';
|
||||||
import { MyContext, getBaseApiReact, isMobile } from '../../App';
|
import { MyContext, getBaseApiReact } from '../../App';
|
||||||
import { getGroupMembers, getNames } from './Group';
|
import { getGroupMembers, getNames } from './Group';
|
||||||
import { LoadingSnackbar } from '../Snackbar/LoadingSnackbar';
|
import { LoadingSnackbar } from '../Snackbar/LoadingSnackbar';
|
||||||
import { getFee } from '../../background';
|
import { getFee } from '../../background';
|
||||||
@ -27,6 +23,7 @@ import { LoadingButton } from '@mui/lab';
|
|||||||
import { subscribeToEvent, unsubscribeFromEvent } from '../../utils/events';
|
import { subscribeToEvent, unsubscribeFromEvent } from '../../utils/events';
|
||||||
import { Spacer } from '../../common/Spacer';
|
import { Spacer } from '../../common/Spacer';
|
||||||
import InsertLinkIcon from '@mui/icons-material/InsertLink';
|
import InsertLinkIcon from '@mui/icons-material/InsertLink';
|
||||||
|
|
||||||
function a11yProps(index: number) {
|
function a11yProps(index: number) {
|
||||||
return {
|
return {
|
||||||
id: `simple-tab-${index}`,
|
id: `simple-tab-${index}`,
|
||||||
@ -193,9 +190,9 @@ export const ManageMembers = ({
|
|||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
bgcolor: '#27282c',
|
bgcolor: '#27282c',
|
||||||
|
color: 'white',
|
||||||
flexGrow: 1,
|
flexGrow: 1,
|
||||||
overflowY: 'auto',
|
overflowY: 'auto',
|
||||||
color: 'white',
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box sx={{ borderBottom: 1, borderColor: 'divider' }}>
|
<Box sx={{ borderBottom: 1, borderColor: 'divider' }}>
|
||||||
@ -221,9 +218,10 @@ export const ManageMembers = ({
|
|||||||
'&.Mui-selected': {
|
'&.Mui-selected': {
|
||||||
color: 'white',
|
color: 'white',
|
||||||
},
|
},
|
||||||
fontSize: isMobile ? '0.75rem' : '1rem', // Adjust font size for mobile
|
fontSize: '1rem',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Tab
|
<Tab
|
||||||
label="Invite new member"
|
label="Invite new member"
|
||||||
{...a11yProps(1)}
|
{...a11yProps(1)}
|
||||||
@ -231,9 +229,10 @@ export const ManageMembers = ({
|
|||||||
'&.Mui-selected': {
|
'&.Mui-selected': {
|
||||||
color: 'white',
|
color: 'white',
|
||||||
},
|
},
|
||||||
fontSize: isMobile ? '0.75rem' : '1rem',
|
fontSize: '1rem',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Tab
|
<Tab
|
||||||
label="List of invites"
|
label="List of invites"
|
||||||
{...a11yProps(2)}
|
{...a11yProps(2)}
|
||||||
@ -241,9 +240,10 @@ export const ManageMembers = ({
|
|||||||
'&.Mui-selected': {
|
'&.Mui-selected': {
|
||||||
color: 'white',
|
color: 'white',
|
||||||
},
|
},
|
||||||
fontSize: isMobile ? '0.75rem' : '1rem',
|
fontSize: '1rem',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Tab
|
<Tab
|
||||||
label="List of bans"
|
label="List of bans"
|
||||||
{...a11yProps(3)}
|
{...a11yProps(3)}
|
||||||
@ -251,9 +251,10 @@ export const ManageMembers = ({
|
|||||||
'&.Mui-selected': {
|
'&.Mui-selected': {
|
||||||
color: 'white',
|
color: 'white',
|
||||||
},
|
},
|
||||||
fontSize: isMobile ? '0.75rem' : '1rem',
|
fontSize: '1rem',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Tab
|
<Tab
|
||||||
label="Join requests"
|
label="Join requests"
|
||||||
{...a11yProps(4)}
|
{...a11yProps(4)}
|
||||||
@ -261,11 +262,12 @@ export const ManageMembers = ({
|
|||||||
'&.Mui-selected': {
|
'&.Mui-selected': {
|
||||||
color: 'white',
|
color: 'white',
|
||||||
},
|
},
|
||||||
fontSize: isMobile ? '0.75rem' : '1rem',
|
fontSize: '1rem',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Card
|
<Card
|
||||||
sx={{
|
sx={{
|
||||||
padding: '10px',
|
padding: '10px',
|
||||||
@ -274,10 +276,13 @@ export const ManageMembers = ({
|
|||||||
>
|
>
|
||||||
<Box>
|
<Box>
|
||||||
<Typography>GroupId: {groupInfo?.groupId}</Typography>
|
<Typography>GroupId: {groupInfo?.groupId}</Typography>
|
||||||
|
|
||||||
<Typography>GroupName: {groupInfo?.groupName}</Typography>
|
<Typography>GroupName: {groupInfo?.groupName}</Typography>
|
||||||
|
|
||||||
<Typography>
|
<Typography>
|
||||||
Number of members: {groupInfo?.memberCount}
|
Number of members: {groupInfo?.memberCount}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
<ButtonBase
|
<ButtonBase
|
||||||
sx={{
|
sx={{
|
||||||
gap: '10px',
|
gap: '10px',
|
||||||
@ -290,7 +295,9 @@ export const ManageMembers = ({
|
|||||||
<InsertLinkIcon /> <Typography>Join Group Link</Typography>
|
<InsertLinkIcon /> <Typography>Join Group Link</Typography>
|
||||||
</ButtonBase>
|
</ButtonBase>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Spacer height="20px" />
|
<Spacer height="20px" />
|
||||||
|
|
||||||
{selectedGroup?.groupId && !isOwner && (
|
{selectedGroup?.groupId && !isOwner && (
|
||||||
<LoadingButton
|
<LoadingButton
|
||||||
size="small"
|
size="small"
|
||||||
@ -317,7 +324,9 @@ export const ManageMembers = ({
|
|||||||
>
|
>
|
||||||
Load members with names
|
Load members with names
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Spacer height="10px" />
|
<Spacer height="10px" />
|
||||||
|
|
||||||
<ListOfMembers
|
<ListOfMembers
|
||||||
members={membersWithNames || []}
|
members={membersWithNames || []}
|
||||||
groupId={selectedGroup?.groupId}
|
groupId={selectedGroup?.groupId}
|
||||||
@ -397,12 +406,14 @@ export const ManageMembers = ({
|
|||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<CustomizedSnackbars
|
<CustomizedSnackbars
|
||||||
open={openSnack}
|
open={openSnack}
|
||||||
setOpen={setOpenSnack}
|
setOpen={setOpenSnack}
|
||||||
info={infoSnack}
|
info={infoSnack}
|
||||||
setInfo={setInfoSnack}
|
setInfo={setInfoSnack}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<LoadingSnackbar
|
<LoadingSnackbar
|
||||||
open={isLoadingMembers}
|
open={isLoadingMembers}
|
||||||
info={{
|
info={{
|
||||||
|
@ -6,7 +6,7 @@ import ListItemIcon from '@mui/material/ListItemIcon';
|
|||||||
import ListItemText from '@mui/material/ListItemText';
|
import ListItemText from '@mui/material/ListItemText';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { Box, ButtonBase, Collapse, Typography, useTheme } from '@mui/material';
|
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 MailIcon from '@mui/icons-material/Mail';
|
||||||
import MailOutlineIcon from '@mui/icons-material/MailOutline';
|
import MailOutlineIcon from '@mui/icons-material/MailOutline';
|
||||||
import { executeEvent } from '../../utils/events';
|
import { executeEvent } from '../../utils/events';
|
||||||
@ -180,7 +180,7 @@ export const QMailMessages = ({ userName, userAddress }) => {
|
|||||||
borderRadius: '19px',
|
borderRadius: '19px',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
height: isMobile ? '165px' : '250px',
|
height: '250px',
|
||||||
overflow: 'auto',
|
overflow: 'auto',
|
||||||
padding: '20px',
|
padding: '20px',
|
||||||
width: '322px',
|
width: '322px',
|
||||||
|
@ -1,28 +1,23 @@
|
|||||||
import * as React from "react";
|
import * as React from 'react';
|
||||||
import List from "@mui/material/List";
|
import List from '@mui/material/List';
|
||||||
import ListItem from "@mui/material/ListItem";
|
import ListItem from '@mui/material/ListItem';
|
||||||
import ListItemButton from "@mui/material/ListItemButton";
|
import ListItemButton from '@mui/material/ListItemButton';
|
||||||
import ListItemIcon from "@mui/material/ListItemIcon";
|
import ListItemIcon from '@mui/material/ListItemIcon';
|
||||||
import ListItemText from "@mui/material/ListItemText";
|
import ListItemText from '@mui/material/ListItemText';
|
||||||
import Checkbox from "@mui/material/Checkbox";
|
import { Box, Typography } from '@mui/material';
|
||||||
import IconButton from "@mui/material/IconButton";
|
import { Spacer } from '../../common/Spacer';
|
||||||
import CommentIcon from "@mui/icons-material/Comment";
|
import { QMailMessages } from './QMailMessages';
|
||||||
import InfoIcon from "@mui/icons-material/Info";
|
import { executeEvent } from '../../utils/events';
|
||||||
import { Box, Typography } from "@mui/material";
|
|
||||||
import { Spacer } from "../../common/Spacer";
|
|
||||||
import { isMobile } from "../../App";
|
|
||||||
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 [checked1, setChecked1] = React.useState(false);
|
||||||
const [checked2, setChecked2] = React.useState(false);
|
const [checked2, setChecked2] = React.useState(false);
|
||||||
// const [checked3, setChecked3] = React.useState(false);
|
|
||||||
|
|
||||||
// React.useEffect(() => {
|
|
||||||
// if (hasGroups) setChecked3(true);
|
|
||||||
// }, [hasGroups]);
|
|
||||||
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (balance && +balance >= 6) {
|
if (balance && +balance >= 6) {
|
||||||
@ -30,111 +25,114 @@ export const ThingsToDoInitial = ({ myAddress, name, hasGroups, balance, userInf
|
|||||||
}
|
}
|
||||||
}, [balance]);
|
}, [balance]);
|
||||||
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (name) setChecked2(true);
|
if (name) setChecked2(true);
|
||||||
}, [name]);
|
}, [name]);
|
||||||
|
|
||||||
|
const isLoaded = React.useMemo(() => {
|
||||||
|
if (userInfo !== null) return true;
|
||||||
|
return false;
|
||||||
|
}, [userInfo]);
|
||||||
|
|
||||||
const isLoaded = React.useMemo(()=> {
|
const hasDoneNameAndBalanceAndIsLoaded = React.useMemo(() => {
|
||||||
if(userInfo !== null) return true
|
if (isLoaded && checked1 && checked2) return true;
|
||||||
return false
|
return false;
|
||||||
}, [ userInfo])
|
}, [checked1, isLoaded, checked2]);
|
||||||
|
|
||||||
const hasDoneNameAndBalanceAndIsLoaded = React.useMemo(()=> {
|
if (hasDoneNameAndBalanceAndIsLoaded) {
|
||||||
if(isLoaded && checked1 && checked2) return true
|
return (
|
||||||
return false
|
<QMailMessages
|
||||||
}, [checked1, isLoaded, checked2])
|
userAddress={userInfo?.address}
|
||||||
|
userName={userInfo?.name}
|
||||||
if(hasDoneNameAndBalanceAndIsLoaded){
|
/>
|
||||||
return (
|
);
|
||||||
<QMailMessages userAddress={userInfo?.address} userName={userInfo?.name} />
|
}
|
||||||
);
|
if (!isLoaded) return null;
|
||||||
}
|
|
||||||
if(!isLoaded) return null
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
width: "100%",
|
width: '100%',
|
||||||
display: "flex",
|
display: 'flex',
|
||||||
flexDirection: "column",
|
flexDirection: 'column',
|
||||||
alignItems: "center",
|
alignItems: 'center',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
width: "322px",
|
width: '322px',
|
||||||
display: "flex",
|
display: 'flex',
|
||||||
flexDirection: "column",
|
flexDirection: 'column',
|
||||||
padding: "0px 20px",
|
padding: '0px 20px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography
|
<Typography
|
||||||
sx={{
|
sx={{
|
||||||
fontSize: "1rem",
|
fontSize: '1rem',
|
||||||
fontWeight: 600,
|
fontWeight: 600,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{!isLoaded ? 'Loading...' : 'Getting Started' }
|
{!isLoaded ? 'Loading...' : 'Getting Started'}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
<Spacer height="10px" />
|
<Spacer height="10px" />
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
width: "322px",
|
width: '322px',
|
||||||
display: "flex",
|
display: 'flex',
|
||||||
flexDirection: "column",
|
flexDirection: 'column',
|
||||||
bgcolor: "background.paper",
|
bgcolor: 'background.paper',
|
||||||
padding: "20px",
|
padding: '20px',
|
||||||
borderRadius: "19px",
|
borderRadius: '19px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{isLoaded && (
|
{isLoaded && (
|
||||||
<List sx={{ width: "100%", maxWidth: 360 }}>
|
<List sx={{ width: '100%', maxWidth: 360 }}>
|
||||||
<ListItem
|
<ListItem
|
||||||
|
disablePadding
|
||||||
disablePadding
|
sx={{
|
||||||
sx={{
|
marginBottom: '20px',
|
||||||
marginBottom: '20px'
|
}}
|
||||||
}}
|
>
|
||||||
>
|
<ListItemButton
|
||||||
<ListItemButton
|
sx={{
|
||||||
sx={{
|
padding: '0px',
|
||||||
padding: "0px",
|
}}
|
||||||
}}
|
disableRipple
|
||||||
disableRipple
|
role={undefined}
|
||||||
role={undefined}
|
dense
|
||||||
dense
|
onClick={() => {
|
||||||
onClick={()=> {
|
executeEvent('openBuyQortInfo', {});
|
||||||
executeEvent("openBuyQortInfo", {})
|
}}
|
||||||
}}
|
>
|
||||||
>
|
<ListItemText
|
||||||
<ListItemText
|
sx={{
|
||||||
sx={{
|
'& .MuiTypography-root': {
|
||||||
"& .MuiTypography-root": {
|
fontSize: '1rem',
|
||||||
fontSize: "1rem",
|
fontWeight: 400,
|
||||||
fontWeight: 400,
|
},
|
||||||
},
|
}}
|
||||||
}}
|
primary={`Have at least 6 QORT in your wallet`}
|
||||||
primary={`Have at least 6 QORT in your wallet`}
|
/>
|
||||||
/>
|
<ListItemIcon
|
||||||
<ListItemIcon
|
sx={{
|
||||||
sx={{
|
justifyContent: 'flex-end',
|
||||||
justifyContent: "flex-end",
|
}}
|
||||||
}}
|
>
|
||||||
>
|
<Box
|
||||||
<Box
|
sx={{
|
||||||
sx={{
|
height: '18px',
|
||||||
height: "18px",
|
width: '18px',
|
||||||
width: "18px",
|
borderRadius: '50%',
|
||||||
borderRadius: "50%",
|
backgroundColor: checked1
|
||||||
backgroundColor: checked1 ? "rgba(9, 182, 232, 1)" : "transparent",
|
? 'rgba(9, 182, 232, 1)'
|
||||||
outline: "1px solid rgba(9, 182, 232, 1)",
|
: 'transparent',
|
||||||
}}
|
outline: '1px solid rgba(9, 182, 232, 1)',
|
||||||
/>
|
}}
|
||||||
{/* <Checkbox
|
/>
|
||||||
|
{/* <Checkbox
|
||||||
edge="start"
|
edge="start"
|
||||||
checked={checked1}
|
checked={checked1}
|
||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
@ -149,52 +147,64 @@ if(!isLoaded) return null
|
|||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
/> */}
|
/> */}
|
||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
</ListItemButton>
|
</ListItemButton>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
<ListItem
|
<ListItem
|
||||||
sx={{
|
sx={{
|
||||||
marginBottom: '20px'
|
marginBottom: '20px',
|
||||||
}}
|
}}
|
||||||
// secondaryAction={
|
// secondaryAction={
|
||||||
// <IconButton edge="end" aria-label="comments">
|
// <IconButton edge="end" aria-label="comments">
|
||||||
// <InfoIcon
|
// <InfoIcon
|
||||||
// sx={{
|
// sx={{
|
||||||
// color: "white",
|
// color: "white",
|
||||||
// }}
|
// }}
|
||||||
// />
|
// />
|
||||||
// </IconButton>
|
// </IconButton>
|
||||||
// }
|
// }
|
||||||
disablePadding
|
disablePadding
|
||||||
>
|
>
|
||||||
<ListItemButton sx={{
|
<ListItemButton
|
||||||
padding: "0px",
|
sx={{
|
||||||
}} disableRipple role={undefined} dense>
|
padding: '0px',
|
||||||
|
}}
|
||||||
<ListItemText onClick={() => {
|
disableRipple
|
||||||
executeEvent('openRegisterName', {})
|
role={undefined}
|
||||||
}} sx={{
|
dense
|
||||||
"& .MuiTypography-root": {
|
>
|
||||||
fontSize: "1rem",
|
<ListItemText
|
||||||
fontWeight: 400,
|
onClick={() => {
|
||||||
},
|
executeEvent('openRegisterName', {});
|
||||||
}} primary={`Register a name`} />
|
}}
|
||||||
<ListItemIcon sx={{
|
sx={{
|
||||||
justifyContent: "flex-end",
|
'& .MuiTypography-root': {
|
||||||
}}>
|
fontSize: '1rem',
|
||||||
<Box
|
fontWeight: 400,
|
||||||
sx={{
|
},
|
||||||
height: "18px",
|
}}
|
||||||
width: "18px",
|
primary={`Register a name`}
|
||||||
borderRadius: "50%",
|
/>
|
||||||
backgroundColor: checked2 ? "rgba(9, 182, 232, 1)" : "transparent",
|
<ListItemIcon
|
||||||
outline: "1px solid rgba(9, 182, 232, 1)",
|
sx={{
|
||||||
}}
|
justifyContent: 'flex-end',
|
||||||
/>
|
}}
|
||||||
</ListItemIcon>
|
>
|
||||||
</ListItemButton>
|
<Box
|
||||||
</ListItem>
|
sx={{
|
||||||
{/* <ListItem
|
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
|
disablePadding
|
||||||
>
|
>
|
||||||
<ListItemButton sx={{
|
<ListItemButton sx={{
|
||||||
@ -222,9 +232,8 @@ if(!isLoaded) return null
|
|||||||
</ListItemIcon>
|
</ListItemIcon>
|
||||||
</ListItemButton>
|
</ListItemButton>
|
||||||
</ListItem> */}
|
</ListItem> */}
|
||||||
</List>
|
</List>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
</Box>
|
</Box>
|
||||||
</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 ReactDOM from 'react-dom';
|
||||||
import Picker, { EmojiStyle, Theme } from 'emoji-picker-react';
|
import Picker, { EmojiStyle, Theme } from 'emoji-picker-react';
|
||||||
import './ReactionPicker.css';
|
import './ReactionPicker.css';
|
||||||
import { ButtonBase } from '@mui/material';
|
import { ButtonBase } from '@mui/material';
|
||||||
import { isMobile } from '../App';
|
|
||||||
|
|
||||||
export const ReactionPicker = ({ onReaction }) => {
|
export const ReactionPicker = ({ onReaction }) => {
|
||||||
const [showPicker, setShowPicker] = useState(false);
|
const [showPicker, setShowPicker] = useState(false);
|
||||||
@ -30,7 +29,7 @@ export const ReactionPicker = ({ onReaction }) => {
|
|||||||
} else {
|
} else {
|
||||||
// Get the button's position
|
// Get the button's position
|
||||||
const buttonRect = buttonRef.current.getBoundingClientRect();
|
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
|
// Calculate position to align the right edge of the picker with the button's right edge
|
||||||
setPickerPosition({
|
setPickerPosition({
|
||||||
@ -90,15 +89,15 @@ export const ReactionPicker = ({ onReaction }) => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Picker
|
<Picker
|
||||||
height={isMobile ? 350 : 450}
|
|
||||||
width={isMobile ? 300 : 350}
|
|
||||||
reactionsDefaultOpen={true}
|
|
||||||
onReactionClick={handleReaction}
|
|
||||||
onEmojiClick={handlePicker}
|
|
||||||
allowExpandReactions={true}
|
allowExpandReactions={true}
|
||||||
autoFocusSearch={false}
|
autoFocusSearch={false}
|
||||||
theme={Theme.DARK}
|
|
||||||
emojiStyle={EmojiStyle.NATIVE}
|
emojiStyle={EmojiStyle.NATIVE}
|
||||||
|
height="450"
|
||||||
|
onEmojiClick={handlePicker}
|
||||||
|
onReactionClick={handleReaction}
|
||||||
|
reactionsDefaultOpen={true}
|
||||||
|
theme={Theme.DARK}
|
||||||
|
width="350"
|
||||||
/>
|
/>
|
||||||
</div>,
|
</div>,
|
||||||
document.body
|
document.body
|
||||||
|
@ -12,7 +12,7 @@ import PendingIcon from '@mui/icons-material/Pending';
|
|||||||
import TaskAltIcon from '@mui/icons-material/TaskAlt';
|
import TaskAltIcon from '@mui/icons-material/TaskAlt';
|
||||||
import ExpandLess from '@mui/icons-material/ExpandLess';
|
import ExpandLess from '@mui/icons-material/ExpandLess';
|
||||||
import ExpandMore from '@mui/icons-material/ExpandMore';
|
import ExpandMore from '@mui/icons-material/ExpandMore';
|
||||||
import { MyContext, getBaseApiReact, isMobile } from '../../App';
|
import { MyContext, getBaseApiReact } from '../../App';
|
||||||
import { executeEvent } from '../../utils/events';
|
import { executeEvent } from '../../utils/events';
|
||||||
|
|
||||||
export const TaskManager = ({ getUserInfo }) => {
|
export const TaskManager = ({ getUserInfo }) => {
|
||||||
@ -141,8 +141,7 @@ export const TaskManager = ({ getUserInfo }) => {
|
|||||||
});
|
});
|
||||||
}, [txList]);
|
}, [txList]);
|
||||||
|
|
||||||
if (isMobile || txList?.length === 0 || txList.every((item) => item?.done))
|
if (txList?.length === 0 || txList.every((item) => item?.done)) return null;
|
||||||
return null;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -1,67 +1,85 @@
|
|||||||
import { useCallback, useEffect } from 'react';
|
import { useCallback, useEffect } from 'react';
|
||||||
import { isMobile } from './App';
|
|
||||||
|
|
||||||
export const useAppFullScreen = (setFullScreen) => {
|
export const useAppFullScreen = (setFullScreen) => {
|
||||||
const enterFullScreen = useCallback(() => {
|
const enterFullScreen = useCallback(() => {
|
||||||
const element = document.documentElement; // Target the entire HTML document
|
const element = document.documentElement; // Target the entire HTML document
|
||||||
if (element.requestFullscreen) {
|
if (element.requestFullscreen) {
|
||||||
element.requestFullscreen();
|
element.requestFullscreen();
|
||||||
} else if (element.mozRequestFullScreen) { // Firefox
|
} else if (element.mozRequestFullScreen) {
|
||||||
element.mozRequestFullScreen();
|
// Firefox
|
||||||
} else if (element.webkitRequestFullscreen) { // Chrome, Safari and Opera
|
element.mozRequestFullScreen();
|
||||||
element.webkitRequestFullscreen();
|
} else if (element.webkitRequestFullscreen) {
|
||||||
} else if (element.msRequestFullscreen) { // IE/Edge
|
// Chrome, Safari and Opera
|
||||||
element.msRequestFullscreen();
|
element.webkitRequestFullscreen();
|
||||||
}
|
} else if (element.msRequestFullscreen) {
|
||||||
}, []);
|
// IE/Edge
|
||||||
|
element.msRequestFullscreen();
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
const exitFullScreen = useCallback(() => {
|
const exitFullScreen = useCallback(() => {
|
||||||
if (document.fullscreenElement) {
|
if (document.fullscreenElement) {
|
||||||
document.exitFullscreen();
|
document.exitFullscreen();
|
||||||
} else if (document.mozFullScreenElement) {
|
} else if (document.mozFullScreenElement) {
|
||||||
document.mozCancelFullScreen();
|
document.mozCancelFullScreen();
|
||||||
} else if (document.webkitFullscreenElement) {
|
} else if (document.webkitFullscreenElement) {
|
||||||
document.webkitExitFullscreen();
|
document.webkitExitFullscreen();
|
||||||
} else if (document.msFullscreenElement) {
|
} else if (document.msFullscreenElement) {
|
||||||
document.msExitFullscreen();
|
document.msExitFullscreen();
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const toggleFullScreen = useCallback(() => {
|
const toggleFullScreen = useCallback(() => {
|
||||||
if(!isMobile || isMobile) return
|
if (
|
||||||
if (document.fullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement || document.msFullscreenElement) {
|
document.fullscreenElement ||
|
||||||
exitFullScreen();
|
document.mozFullScreenElement ||
|
||||||
setFullScreen(false)
|
document.webkitFullscreenElement ||
|
||||||
} else {
|
document.msFullscreenElement
|
||||||
enterFullScreen();
|
) {
|
||||||
setFullScreen(true)
|
exitFullScreen();
|
||||||
}
|
setFullScreen(false);
|
||||||
}, [enterFullScreen, exitFullScreen]);
|
} else {
|
||||||
|
enterFullScreen();
|
||||||
|
setFullScreen(true);
|
||||||
|
}
|
||||||
|
}, [enterFullScreen, exitFullScreen]);
|
||||||
|
|
||||||
// Listen for changes to fullscreen state
|
// Listen for changes to fullscreen state
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleFullScreenChange = () => {
|
const handleFullScreenChange = () => {
|
||||||
if (document.fullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement || document.msFullscreenElement) {
|
if (
|
||||||
|
document.fullscreenElement ||
|
||||||
} else {
|
document.mozFullScreenElement ||
|
||||||
setFullScreen(false);
|
document.webkitFullscreenElement ||
|
||||||
}
|
document.msFullscreenElement
|
||||||
};
|
) {
|
||||||
|
// TODO check empty block
|
||||||
|
} else {
|
||||||
|
setFullScreen(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
document.addEventListener('fullscreenchange', handleFullScreenChange);
|
document.addEventListener('fullscreenchange', handleFullScreenChange);
|
||||||
document.addEventListener('webkitfullscreenchange', handleFullScreenChange); // Safari
|
document.addEventListener('webkitfullscreenchange', handleFullScreenChange); // Safari
|
||||||
document.addEventListener('mozfullscreenchange', handleFullScreenChange); // Firefox
|
document.addEventListener('mozfullscreenchange', handleFullScreenChange); // Firefox
|
||||||
document.addEventListener('MSFullscreenChange', handleFullScreenChange); // IE/Edge
|
document.addEventListener('MSFullscreenChange', handleFullScreenChange); // IE/Edge
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
document.removeEventListener('fullscreenchange', handleFullScreenChange);
|
document.removeEventListener('fullscreenchange', handleFullScreenChange);
|
||||||
document.removeEventListener('webkitfullscreenchange', handleFullScreenChange);
|
document.removeEventListener(
|
||||||
document.removeEventListener('mozfullscreenchange', handleFullScreenChange);
|
'webkitfullscreenchange',
|
||||||
document.removeEventListener('MSFullscreenChange', handleFullScreenChange);
|
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