Remove isMobile check

This commit is contained in:
Nicola Benaglia 2025-04-20 15:41:45 +02:00
parent c71853f754
commit de9285a280
37 changed files with 2134 additions and 2279 deletions

View File

@ -1,5 +1,4 @@
// @ts-nocheck
import './qortalRequests';
import { isArray } from 'lodash';
import {
@ -30,7 +29,6 @@ import { RequestQueueWithPromise } from './utils/queue/queue';
import { validateAddress } from './utils/validateAddress';
import { Sha256 } from 'asmcrypto.js';
import { TradeBotRespondMultipleRequest } from './transactions/TradeBotRespondMultipleRequest';
import { RESOURCE_TYPE_NUMBER_GROUP_CHAT_REACTIONS } from './constants/resourceTypes';
import {
addDataPublishesCase,
@ -111,6 +109,7 @@ export let groupSecretkeys = {};
export function cleanUrl(url) {
return url?.replace(/^(https?:\/\/)?(www\.)?/, '');
}
export function getProtocol(url) {
if (url?.startsWith('https://')) {
return 'https';
@ -130,7 +129,6 @@ export const groupApiLocal = 'http://127.0.0.1:12391';
export const groupApiSocketLocal = 'ws://127.0.0.1:12391';
const timeDifferenceForNotificationChatsBackground = 86400000;
const requestQueueAnnouncements = new RequestQueueWithPromise(1);
let isMobile = true;
function handleNotificationClick(notificationId) {
// Decode the notificationId if it was encoded
@ -193,26 +191,6 @@ function handleNotificationClick(notificationId) {
}
}
const isMobileDevice = () => {
const userAgent = navigator.userAgent || navigator.vendor || window.opera;
if (/android/i.test(userAgent)) {
return true; // Android device
}
if (/iPad|iPhone|iPod/.test(userAgent) && !window.MSStream) {
return true; // iOS device
}
return false;
};
if (isMobileDevice()) {
isMobile = true;
console.log('Running on a mobile device');
} else {
console.log('Running on a desktop');
}
const allQueues = {
requestQueueAnnouncements: requestQueueAnnouncements,
};
@ -264,7 +242,9 @@ export const getForeignKey = async (foreignBlockchain) => {
};
export const pauseAllQueues = () => controlAllQueues('pause');
export const resumeAllQueues = () => controlAllQueues('resume');
export const checkDifference = (
createdTimestamp,
diff = timeDifferenceForNotificationChatsBackground
@ -302,6 +282,7 @@ export const getBaseApi = async (customApi?: string) => {
return groupApi;
}
};
export const isUsingLocal = async () => {
const apiKey = await getApiKeyFromStorage(); // Retrieve apiKey asynchronously
if (apiKey?.url) {
@ -345,13 +326,13 @@ const proxyAccountAddress = 'QXPejUe5Za1KD3zCMViWCX35AreMQ9H7ku';
const proxyAccountPublicKey = '5hP6stDWybojoDw5t8z9D51nV945oMPX7qBd29rhX1G7';
const pendingResponses = new Map();
let groups = null;
let socket;
let timeoutId;
let groupSocketTimeout;
let socketTimeout: any;
let interval;
let intervalThreads;
// Function to check each API endpoint
export async function findUsableApi() {
for (const endpoint of apiEndpoints) {
@ -435,6 +416,7 @@ async function checkWebviewFocus() {
window.addEventListener('message', handleMessage);
});
}
const worker = new ChatComputePowWorker();
export async function performPowTask(chatBytes, difficulty) {
@ -584,6 +566,7 @@ const handleNotificationDirect = async (directs) => {
setChatHeadsDirect(dataDirects);
}
};
async function getThreadActivity(): Promise<any | null> {
const wallet = await getSaveWallet();
const address = wallet.address0;
@ -852,6 +835,7 @@ export async function getNameInfoForOthers(address) {
return '';
}
}
export async function getAddressInfo(address) {
const validApi = await getBaseApi();
const response = await fetch(validApi + '/addresses/' + address);
@ -932,6 +916,7 @@ async function getTradeInfo(qortalAtAddress) {
const data = await response.json();
return data;
}
async function getTradesInfo(qortalAtAddresses) {
// Use Promise.all to fetch data for all addresses concurrently
const trades = await Promise.all(
@ -951,6 +936,7 @@ export async function getBalanceInfo() {
const data = await response.json();
return data;
}
export async function getLTCBalance() {
const wallet = await getSaveWallet();
let _url = `${buyTradeNodeBaseUrl}/crosschain/ltc/walletbalance`;
@ -1075,6 +1061,7 @@ const transaction = async (
data: res,
};
};
const makeTransactionRequest = async (
receiver,
lastRef,
@ -1113,6 +1100,7 @@ export const getLastRef = async () => {
const data = await response.text();
return data;
};
export const sendQortFee = async (): Promise<number> => {
const validApi = await getBaseApi();
const response = await fetch(
@ -1386,6 +1374,7 @@ export async function signChatFunc(
}
return response;
}
function sbrk(size, heap) {
let brk = 512 * 1024; // stack top
let old = brk;

View File

@ -1,8 +1,6 @@
import React, { useEffect, useMemo, useState } from "react";
import {
AppCircle,
AppCircleContainer,
AppCircleLabel,
AppDownloadButton,
AppDownloadButtonText,
AppInfoAppName,
@ -17,193 +15,204 @@ import {
AppsCategoryInfoValue,
AppsInfoDescription,
AppsLibraryContainer,
AppsParent,
AppsWidthLimiter,
} from "./Apps-styles";
import { Avatar, Box, ButtonBase, InputBase } from "@mui/material";
import { Add } from "@mui/icons-material";
import { getBaseApiReact, isMobile } from "../../App";
import LogoSelected from "../../assets/svgs/LogoSelected.svg";
import { Spacer } from "../../common/Spacer";
import { executeEvent } from "../../utils/events";
import { AppRating } from "./AppRating";
import { settingsLocalLastUpdatedAtom, sortablePinnedAppsAtom } from "../../atoms/global";
import { saveToLocalStorage } from "./AppsNavBar";
import { useRecoilState, useSetRecoilState } from "recoil";
} from './Apps-styles';
import { Avatar, Box } from '@mui/material';
import { getBaseApiReact } from '../../App';
import LogoSelected from '../../assets/svgs/LogoSelected.svg';
import { Spacer } from '../../common/Spacer';
import { executeEvent } from '../../utils/events';
import { AppRating } from './AppRating';
import {
settingsLocalLastUpdatedAtom,
sortablePinnedAppsAtom,
} from '../../atoms/global';
import { saveToLocalStorage } from './AppsNavBar';
import { useRecoilState, useSetRecoilState } from 'recoil';
export const AppInfo = ({ app, myName }) => {
const isInstalled = app?.status?.status === "READY";
const [sortablePinnedApps, setSortablePinnedApps] = useRecoilState(sortablePinnedAppsAtom);
const isInstalled = app?.status?.status === 'READY';
const [sortablePinnedApps, setSortablePinnedApps] = useRecoilState(
sortablePinnedAppsAtom
);
const isSelectedAppPinned = !!sortablePinnedApps?.find((item)=> item?.name === app?.name && item?.service === app?.service)
const setSettingsLocalLastUpdated = useSetRecoilState(settingsLocalLastUpdatedAtom);
const isSelectedAppPinned = !!sortablePinnedApps?.find(
(item) => item?.name === app?.name && item?.service === app?.service
);
const setSettingsLocalLastUpdated = useSetRecoilState(
settingsLocalLastUpdatedAtom
);
return (
<AppsLibraryContainer
sx={{
height: !isMobile && "100%",
justifyContent: !isMobile && "flex-start",
alignItems: isMobile && 'center'
height: '100%',
justifyContent: 'flex-start',
}}
>
<Box sx={{
display: 'flex',
flexDirection: 'column',
maxWidth: "500px",
width: '90%'
}}>
<Box
sx={{
display: 'flex',
flexDirection: 'column',
maxWidth: '500px',
width: '90%',
}}
>
<Spacer height="30px" />
{!isMobile && <Spacer height="30px" />}
<AppsWidthLimiter>
<AppInfoSnippetContainer>
<AppInfoSnippetLeft
sx={{
flexGrow: 1,
gap: "18px",
}}
>
<AppCircleContainer
<AppsWidthLimiter>
<AppInfoSnippetContainer>
<AppInfoSnippetLeft
sx={{
width: "auto",
flexGrow: 1,
gap: '18px',
}}
>
<AppCircle
<AppCircleContainer
sx={{
border: "none",
height: "100px",
width: "100px",
width: 'auto',
}}
>
<Avatar
<AppCircle
sx={{
height: "43px",
width: "43px",
"& img": {
objectFit: "fill",
},
border: 'none',
height: '100px',
width: '100px',
}}
alt={app?.name}
src={`${getBaseApiReact()}/arbitrary/THUMBNAIL/${
app?.name
}/qortal_avatar?async=true`}
>
<img
style={{
width: "43px",
height: "auto",
<Avatar
sx={{
height: '43px',
width: '43px',
'& img': {
objectFit: 'fill',
},
}}
src={LogoSelected}
alt="center-icon"
/>
</Avatar>
</AppCircle>
</AppCircleContainer>
<AppInfoSnippetMiddle>
<AppInfoAppName>
{app?.metadata?.title || app?.name}
</AppInfoAppName>
<Spacer height="6px" />
<AppInfoUserName>{app?.name}</AppInfoUserName>
<Spacer height="3px" />
</AppInfoSnippetMiddle>
</AppInfoSnippetLeft>
<AppInfoSnippetRight></AppInfoSnippetRight>
</AppInfoSnippetContainer>
<Spacer height="11px" />
<Box sx={{
width: '100%',
display: 'flex',
alignItems: 'center',
gap: '20px'
}}>
<AppDownloadButton
onClick={() => {
setSortablePinnedApps((prev) => {
let updatedApps;
if (isSelectedAppPinned) {
// Remove the selected app if it is pinned
updatedApps = prev.filter(
(item) => !(item?.name === app?.name && item?.service === app?.service)
alt={app?.name}
src={`${getBaseApiReact()}/arbitrary/THUMBNAIL/${
app?.name
}/qortal_avatar?async=true`}
>
<img
style={{
width: '43px',
height: 'auto',
}}
src={LogoSelected}
alt="center-icon"
/>
</Avatar>
</AppCircle>
</AppCircleContainer>
<AppInfoSnippetMiddle>
<AppInfoAppName>
{app?.metadata?.title || app?.name}
</AppInfoAppName>
<Spacer height="6px" />
<AppInfoUserName>{app?.name}</AppInfoUserName>
<Spacer height="3px" />
</AppInfoSnippetMiddle>
</AppInfoSnippetLeft>
<AppInfoSnippetRight></AppInfoSnippetRight>
</AppInfoSnippetContainer>
<Spacer height="11px" />
<Box
sx={{
width: '100%',
display: 'flex',
alignItems: 'center',
gap: '20px',
}}
>
<AppDownloadButton
onClick={() => {
setSortablePinnedApps((prev) => {
let updatedApps;
if (isSelectedAppPinned) {
// Remove the selected app if it is pinned
updatedApps = prev.filter(
(item) =>
!(
item?.name === app?.name &&
item?.service === app?.service
)
);
} else {
// Add the selected app if it is not pinned
updatedApps = [
...prev,
{
name: app?.name,
service: app?.service,
},
];
}
saveToLocalStorage(
'ext_saved_settings',
'sortablePinnedApps',
updatedApps
);
} else {
// Add the selected app if it is not pinned
updatedApps = [...prev, {
name: app?.name,
service: app?.service,
}];
}
saveToLocalStorage('ext_saved_settings', 'sortablePinnedApps', updatedApps)
return updatedApps;
});
setSettingsLocalLastUpdated(Date.now())
}}
sx={{
backgroundColor: "#359ff7ff",
width: "100%",
maxWidth: "320px",
height: "29px",
opacity: isSelectedAppPinned ? 0.6 : 1
}}
>
<AppDownloadButtonText>
{!isMobile ? (
<>
{isSelectedAppPinned ? 'Unpin from dashboard' : 'Pin to dashboard'}
</>
) : (
<>
{isSelectedAppPinned ? 'Unpin' : 'Pin'}
</>
)}
</AppDownloadButtonText>
</AppDownloadButton>
<AppDownloadButton
onClick={() => {
executeEvent("addTab", {
data: app,
});
}}
sx={{
backgroundColor: isInstalled ? "#0091E1" : "#247C0E",
width: "100%",
maxWidth: "320px",
height: "29px",
}}
>
<AppDownloadButtonText>
{isInstalled ? "Open" : "Download"}
</AppDownloadButtonText>
</AppDownloadButton>
</Box>
</AppsWidthLimiter>
<Spacer height="20px" />
<AppsWidthLimiter>
<AppsCategoryInfo>
<AppRating ratingCountPosition="top" myName={myName} app={app} />
<Spacer width="16px" />
<Spacer height="40px" width="1px" backgroundColor="white" />
<Spacer width="16px" />
<AppsCategoryInfoSub>
<AppsCategoryInfoLabel>Category:</AppsCategoryInfoLabel>
<Spacer height="4px" />
<AppsCategoryInfoValue>
{app?.metadata?.categoryName || "none"}
</AppsCategoryInfoValue>
</AppsCategoryInfoSub>
</AppsCategoryInfo>
<Spacer height="30px" />
<AppInfoAppName>About this Q-App</AppInfoAppName>
</AppsWidthLimiter>
<Spacer height="20px" />
<AppsInfoDescription>
{app?.metadata?.description || "No description"}
</AppsInfoDescription>
return updatedApps;
});
setSettingsLocalLastUpdated(Date.now());
}}
sx={{
backgroundColor: '#359ff7ff',
width: '100%',
maxWidth: '320px',
height: '29px',
opacity: isSelectedAppPinned ? 0.6 : 1,
}}
>
<AppDownloadButtonText>
{isSelectedAppPinned
? 'Unpin from dashboard'
: 'Pin to dashboard'}
</AppDownloadButtonText>
</AppDownloadButton>
<AppDownloadButton
onClick={() => {
executeEvent('addTab', {
data: app,
});
}}
sx={{
backgroundColor: isInstalled ? '#0091E1' : '#247C0E',
width: '100%',
maxWidth: '320px',
height: '29px',
}}
>
<AppDownloadButtonText>
{isInstalled ? 'Open' : 'Download'}
</AppDownloadButtonText>
</AppDownloadButton>
</Box>
</AppsWidthLimiter>
<Spacer height="20px" />
<AppsWidthLimiter>
<AppsCategoryInfo>
<AppRating ratingCountPosition="top" myName={myName} app={app} />
<Spacer width="16px" />
<Spacer height="40px" width="1px" backgroundColor="white" />
<Spacer width="16px" />
<AppsCategoryInfoSub>
<AppsCategoryInfoLabel>Category:</AppsCategoryInfoLabel>
<Spacer height="4px" />
<AppsCategoryInfoValue>
{app?.metadata?.categoryName || 'none'}
</AppsCategoryInfoValue>
</AppsCategoryInfoSub>
</AppsCategoryInfo>
<Spacer height="30px" />
<AppInfoAppName>About this Q-App</AppInfoAppName>
</AppsWidthLimiter>
<Spacer height="20px" />
<AppsInfoDescription>
{app?.metadata?.description || 'No description'}
</AppsInfoDescription>
</Box>
</AppsLibraryContainer>
);

View File

@ -1,4 +1,3 @@
import React from "react";
import {
AppCircle,
AppCircleContainer,
@ -10,148 +9,176 @@ import {
AppInfoSnippetMiddle,
AppInfoSnippetRight,
AppInfoUserName,
} from "./Apps-styles";
import { Avatar, ButtonBase } from "@mui/material";
import { getBaseApiReact, isMobile } from "../../App";
import LogoSelected from "../../assets/svgs/LogoSelected.svg";
} from './Apps-styles';
import { Avatar, ButtonBase } from '@mui/material';
import { getBaseApiReact } from '../../App';
import LogoSelected from '../../assets/svgs/LogoSelected.svg';
import { Spacer } from '../../common/Spacer';
import { executeEvent } from '../../utils/events';
import { AppRating } from './AppRating';
import { useRecoilState, useSetRecoilState } from 'recoil';
import {
settingsLocalLastUpdatedAtom,
sortablePinnedAppsAtom,
} from '../../atoms/global';
import { saveToLocalStorage } from './AppsNavBar';
import { Spacer } from "../../common/Spacer";
import { executeEvent } from "../../utils/events";
import { AppRating } from "./AppRating";
import { useRecoilState, useSetRecoilState } from "recoil";
import { settingsLocalLastUpdatedAtom, sortablePinnedAppsAtom } from "../../atoms/global";
import { saveToLocalStorage } from "./AppsNavBar";
export const AppInfoSnippet = ({
app,
myName,
isFromCategory,
parentStyles = {},
}) => {
const isInstalled = app?.status?.status === 'READY';
const [sortablePinnedApps, setSortablePinnedApps] = useRecoilState(
sortablePinnedAppsAtom
);
export const AppInfoSnippet = ({ app, myName, isFromCategory, parentStyles = {} }) => {
const isInstalled = app?.status?.status === 'READY'
const [sortablePinnedApps, setSortablePinnedApps] = useRecoilState(sortablePinnedAppsAtom);
const isSelectedAppPinned = !!sortablePinnedApps?.find((item)=> item?.name === app?.name && item?.service === app?.service)
const setSettingsLocalLastUpdated = useSetRecoilState(settingsLocalLastUpdatedAtom);
const isSelectedAppPinned = !!sortablePinnedApps?.find(
(item) => item?.name === app?.name && item?.service === app?.service
);
const setSettingsLocalLastUpdated = useSetRecoilState(
settingsLocalLastUpdatedAtom
);
return (
<AppInfoSnippetContainer sx={{
...parentStyles
}}>
<AppInfoSnippetContainer
sx={{
...parentStyles,
}}
>
<AppInfoSnippetLeft>
<ButtonBase
sx={{
height: "80px",
width: "60px",
}}
onClick={()=> {
if(isFromCategory){
executeEvent("selectedAppInfoCategory", {
<ButtonBase
sx={{
height: '80px',
width: '60px',
}}
onClick={() => {
if (isFromCategory) {
executeEvent('selectedAppInfoCategory', {
data: app,
});
return;
}
executeEvent('selectedAppInfo', {
data: app,
});
return
}
executeEvent("selectedAppInfo", {
data: app,
});
}}
>
<AppCircleContainer>
<AppCircle
sx={{
border: "none",
}}
>
<AppCircleContainer>
<AppCircle
sx={{
border: 'none',
}}
>
<Avatar
sx={{
height: '42px',
width: '42px',
'& img': {
objectFit: 'fill',
},
}}
alt={app?.name}
src={`${getBaseApiReact()}/arbitrary/THUMBNAIL/${
app?.name
}/qortal_avatar?async=true`}
>
<img
style={{
width: '31px',
height: 'auto',
}}
src={LogoSelected}
alt="center-icon"
/>
</Avatar>
</AppCircle>
</AppCircleContainer>
</ButtonBase>
<AppInfoSnippetMiddle>
<ButtonBase
onClick={() => {
if (isFromCategory) {
executeEvent('selectedAppInfoCategory', {
data: app,
});
return;
}
executeEvent('selectedAppInfo', {
data: app,
});
}}
>
<Avatar
sx={{
height: "42px",
width: "42px",
'& img': {
objectFit: 'fill',
}
}}
alt={app?.name}
src={`${getBaseApiReact()}/arbitrary/THUMBNAIL/${
app?.name
}/qortal_avatar?async=true`}
>
<img
style={{
width: "31px",
height: "auto",
}}
src={LogoSelected}
alt="center-icon"
/>
</Avatar>
</AppCircle>
</AppCircleContainer>
</ButtonBase>
<AppInfoSnippetMiddle>
<ButtonBase onClick={()=> {
if(isFromCategory){
executeEvent("selectedAppInfoCategory", {
data: app,
});
return
}
executeEvent("selectedAppInfo", {
data: app,
});
}}>
<AppInfoAppName >
{app?.metadata?.title || app?.name}
</AppInfoAppName>
</ButtonBase>
<Spacer height="6px" />
<AppInfoUserName>
{ app?.name}
</AppInfoUserName>
<AppInfoAppName>{app?.metadata?.title || app?.name}</AppInfoAppName>
</ButtonBase>
<Spacer height="6px" />
<AppInfoUserName>{app?.name}</AppInfoUserName>
<Spacer height="3px" />
<AppRating app={app} myName={myName} />
</AppInfoSnippetMiddle>
</AppInfoSnippetMiddle>
</AppInfoSnippetLeft>
<AppInfoSnippetRight sx={{
gap: '10px'
}}>
{!isMobile && (
<AppDownloadButton onClick={()=> {
setSortablePinnedApps((prev) => {
let updatedApps;
if (isSelectedAppPinned) {
// Remove the selected app if it is pinned
updatedApps = prev.filter(
(item) => !(item?.name === app?.name && item?.service === app?.service)
);
} else {
// Add the selected app if it is not pinned
updatedApps = [...prev, {
name: app?.name,
service: app?.service,
}];
}
saveToLocalStorage('ext_saved_settings', 'sortablePinnedApps', updatedApps)
return updatedApps;
});
setSettingsLocalLastUpdated(Date.now())
}} sx={{
backgroundColor: '#359ff7ff',
opacity: isSelectedAppPinned ? 0.6 : 1
}}>
<AppDownloadButtonText> {isSelectedAppPinned ? 'Unpin' : 'Pin'}</AppDownloadButtonText>
<AppInfoSnippetRight
sx={{
gap: '10px',
}}
>
<AppDownloadButton
onClick={() => {
setSortablePinnedApps((prev) => {
let updatedApps;
if (isSelectedAppPinned) {
// Remove the selected app if it is pinned
updatedApps = prev.filter(
(item) =>
!(
item?.name === app?.name && item?.service === app?.service
)
);
} else {
// Add the selected app if it is not pinned
updatedApps = [
...prev,
{
name: app?.name,
service: app?.service,
},
];
}
saveToLocalStorage(
'ext_saved_settings',
'sortablePinnedApps',
updatedApps
);
return updatedApps;
});
setSettingsLocalLastUpdated(Date.now());
}}
sx={{
backgroundColor: '#359ff7ff',
opacity: isSelectedAppPinned ? 0.6 : 1,
}}
>
<AppDownloadButtonText>
{' '}
{isSelectedAppPinned ? 'Unpin' : 'Pin'}
</AppDownloadButtonText>
</AppDownloadButton>
)}
<AppDownloadButton onClick={()=> {
executeEvent("addTab", {
data: app
})
}} sx={{
backgroundColor: isInstalled ? '#0091E1' : '#247C0E',
}}>
<AppDownloadButtonText>{isInstalled ? 'Open' : 'Download'}</AppDownloadButtonText>
<AppDownloadButton
onClick={() => {
executeEvent('addTab', {
data: app,
});
}}
sx={{
backgroundColor: isInstalled ? '#0091E1' : '#247C0E',
}}
>
<AppDownloadButtonText>
{isInstalled ? 'Open' : 'Download'}
</AppDownloadButtonText>
</AppDownloadButton>
</AppInfoSnippetRight>
</AppInfoSnippetContainer>

View File

@ -1,4 +1,4 @@
import React, { useContext, useEffect, useMemo, useState } from "react";
import React, { useContext, useEffect, useState } from 'react';
import {
AppCircle,
AppCircleContainer,
@ -19,90 +19,74 @@ import {
PublishQAppCTAButton,
PublishQAppChoseFile,
PublishQAppInfo,
} from "./Apps-styles";
import {
Avatar,
Box,
ButtonBase,
InputBase,
InputLabel,
MenuItem,
Select,
} from "@mui/material";
import {
Select as BaseSelect,
SelectProps,
selectClasses,
SelectRootSlotProps,
} from "@mui/base/Select";
import { Option as BaseOption, optionClasses } from "@mui/base/Option";
import { styled } from "@mui/system";
import UnfoldMoreRoundedIcon from "@mui/icons-material/UnfoldMoreRounded";
import { Add } from "@mui/icons-material";
import { MyContext, getBaseApiReact, isMobile } from "../../App";
import LogoSelected from "../../assets/svgs/LogoSelected.svg";
import { Spacer } from "../../common/Spacer";
import { executeEvent } from "../../utils/events";
import { useDropzone } from "react-dropzone";
import { LoadingSnackbar } from "../Snackbar/LoadingSnackbar";
import { CustomizedSnackbars } from "../Snackbar/Snackbar";
import { getFee } from "../../background";
import { fileToBase64 } from "../../utils/fileReading";
} from './Apps-styles';
import { InputBase, InputLabel, MenuItem, Select } from '@mui/material';
import { styled } from '@mui/system';
import UnfoldMoreRoundedIcon from '@mui/icons-material/UnfoldMoreRounded';
import { Add } from '@mui/icons-material';
import { MyContext, getBaseApiReact } from '../../App';
import LogoSelected from '../../assets/svgs/LogoSelected.svg';
import { Spacer } from '../../common/Spacer';
import { executeEvent } from '../../utils/events';
import { useDropzone } from 'react-dropzone';
import { LoadingSnackbar } from '../Snackbar/LoadingSnackbar';
import { CustomizedSnackbars } from '../Snackbar/Snackbar';
import { getFee } from '../../background';
import { fileToBase64 } from '../../utils/fileReading';
const CustomSelect = styled(Select)({
border: "0.5px solid var(--50-white, #FFFFFF80)",
padding: "0px 15px",
borderRadius: "5px",
height: "36px",
width: "100%",
maxWidth: "450px",
"& .MuiSelect-select": {
padding: "0px",
border: '0.5px solid var(--50-white, #FFFFFF80)',
padding: '0px 15px',
borderRadius: '5px',
height: '36px',
width: '100%',
maxWidth: '450px',
'& .MuiSelect-select': {
padding: '0px',
},
"&:hover": {
borderColor: "none", // Border color on hover
'&:hover': {
borderColor: 'none', // Border color on hover
},
"&.Mui-focused .MuiOutlinedInput-notchedOutline": {
borderColor: "none", // Border color when focused
'&.Mui-focused .MuiOutlinedInput-notchedOutline': {
borderColor: 'none', // Border color when focused
},
"&.Mui-disabled": {
'&.Mui-disabled': {
opacity: 0.5, // Lower opacity when disabled
},
"& .MuiSvgIcon-root": {
color: "var(--50-white, #FFFFFF80)",
'& .MuiSvgIcon-root': {
color: 'var(--50-white, #FFFFFF80)',
},
});
const CustomMenuItem = styled(MenuItem)({
backgroundColor: "#1f1f1f", // Background for dropdown items
color: "#ccc",
"&:hover": {
backgroundColor: "#333", // Darker background on hover
backgroundColor: '#1f1f1f', // Background for dropdown items
color: '#ccc',
'&:hover': {
backgroundColor: '#333', // Darker background on hover
},
});
export const AppPublish = ({ names, categories }) => {
const [name, setName] = useState("");
const [title, setTitle] = useState("");
const [description, setDescription] = useState("");
const [category, setCategory] = useState("");
const [appType, setAppType] = useState("APP");
const [name, setName] = useState('');
const [title, setTitle] = useState('');
const [description, setDescription] = useState('');
const [category, setCategory] = useState('');
const [appType, setAppType] = useState('APP');
const [file, setFile] = useState(null);
const { show } = useContext(MyContext);
const [tag1, setTag1] = useState("");
const [tag2, setTag2] = useState("");
const [tag3, setTag3] = useState("");
const [tag4, setTag4] = useState("");
const [tag5, setTag5] = useState("");
const [tag1, setTag1] = useState('');
const [tag2, setTag2] = useState('');
const [tag3, setTag3] = useState('');
const [tag4, setTag4] = useState('');
const [tag5, setTag5] = useState('');
const [openSnack, setOpenSnack] = useState(false);
const [infoSnack, setInfoSnack] = useState(null);
const [isLoading, setIsLoading] = useState("");
const maxFileSize = appType === "APP" ? 50 * 1024 * 1024 : 400 * 1024 * 1024; // 50MB or 400MB
const [isLoading, setIsLoading] = useState('');
const maxFileSize = appType === 'APP' ? 50 * 1024 * 1024 : 400 * 1024 * 1024; // 50MB or 400MB
const { getRootProps, getInputProps } = useDropzone({
accept: {
"application/zip": [".zip"], // Only accept zip files
'application/zip': ['.zip'], // Only accept zip files
},
maxSize: maxFileSize, // Set the max size based on appType
multiple: false, // Disable multiple file uploads
@ -114,7 +98,7 @@ export const AppPublish = ({ names, categories }) => {
onDropRejected: (fileRejections) => {
fileRejections.forEach(({ file, errors }) => {
errors.forEach((error) => {
if (error.code === "file-too-large") {
if (error.code === 'file-too-large') {
console.error(
`File ${file.name} is too large. Max size allowed is ${
maxFileSize / (1024 * 1024)
@ -128,13 +112,13 @@ export const AppPublish = ({ names, categories }) => {
const getQapp = React.useCallback(async (name, appType) => {
try {
setIsLoading("Loading app information");
setIsLoading('Loading app information');
const url = `${getBaseApiReact()}/arbitrary/resources/search?service=${appType}&mode=ALL&name=${name}&includemetadata=true`;
const response = await fetch(url, {
method: "GET",
method: 'GET',
headers: {
"Content-Type": "application/json",
'Content-Type': 'application/json',
},
});
if (!response?.ok) return;
@ -142,18 +126,18 @@ export const AppPublish = ({ names, categories }) => {
if (responseData?.length > 0) {
const myApp = responseData[0];
setTitle(myApp?.metadata?.title || "");
setDescription(myApp?.metadata?.description || "");
setCategory(myApp?.metadata?.category || "");
setTag1(myApp?.metadata?.tags[0] || "");
setTag2(myApp?.metadata?.tags[1] || "");
setTag3(myApp?.metadata?.tags[2] || "");
setTag4(myApp?.metadata?.tags[3] || "");
setTag5(myApp?.metadata?.tags[4] || "");
setTitle(myApp?.metadata?.title || '');
setDescription(myApp?.metadata?.description || '');
setCategory(myApp?.metadata?.category || '');
setTag1(myApp?.metadata?.tags[0] || '');
setTag2(myApp?.metadata?.tags[1] || '');
setTag3(myApp?.metadata?.tags[2] || '');
setTag4(myApp?.metadata?.tags[3] || '');
setTag5(myApp?.metadata?.tags[4] || '');
}
} catch (error) {
} finally {
setIsLoading("");
setIsLoading('');
}
}, []);
@ -173,12 +157,12 @@ export const AppPublish = ({ names, categories }) => {
file,
};
const requiredFields = [
"name",
"title",
"description",
"category",
"appType",
"file",
'name',
'title',
'description',
'category',
'appType',
'file',
];
const missingFields: string[] = [];
@ -188,32 +172,33 @@ export const AppPublish = ({ names, categories }) => {
}
});
if (missingFields.length > 0) {
const missingFieldsString = missingFields.join(", ");
const missingFieldsString = missingFields.join(', ');
const errorMsg = `Missing fields: ${missingFieldsString}`;
throw new Error(errorMsg);
}
const fee = await getFee("ARBITRARY");
const fee = await getFee('ARBITRARY');
await show({
message: "Would you like to publish this app?",
publishFee: fee.fee + " QORT",
message: 'Would you like to publish this app?',
publishFee: fee.fee + ' QORT',
});
setIsLoading("Publishing... Please wait.");
setIsLoading('Publishing... Please wait.');
const fileBase64 = await fileToBase64(file);
await new Promise((res, rej) => {
window.sendMessage("publishOnQDN", {
data: fileBase64,
service: appType,
title,
description,
category,
tag1,
tag2,
tag3,
tag4,
tag5,
uploadType: "zip",
})
window
.sendMessage('publishOnQDN', {
data: fileBase64,
service: appType,
title,
description,
category,
tag1,
tag2,
tag3,
tag4,
tag5,
uploadType: 'zip',
})
.then((response) => {
if (!response?.error) {
res(response);
@ -222,14 +207,13 @@ export const AppPublish = ({ names, categories }) => {
rej(response.error);
})
.catch((error) => {
rej(error.message || "An error occurred");
rej(error.message || 'An error occurred');
});
});
setInfoSnack({
type: "success",
type: 'success',
message:
"Successfully published. Please wait a couple minutes for the network to propogate the changes.",
'Successfully published. Please wait a couple minutes for the network to propogate the changes.',
});
setOpenSnack(true);
const dataObj = {
@ -242,35 +226,49 @@ export const AppPublish = ({ names, categories }) => {
},
created: Date.now(),
};
executeEvent("addTab", {
executeEvent('addTab', {
data: dataObj,
});
} catch (error) {
setInfoSnack({
type: "error",
message: error?.message || "Unable to publish app",
type: 'error',
message: error?.message || 'Unable to publish app',
});
setOpenSnack(true);
} finally {
setIsLoading("");
setIsLoading('');
}
};
return (
<AppsLibraryContainer sx={{
height: !isMobile ? '100%' : 'auto',
paddingTop: !isMobile && '30px',
alignItems: !isMobile && 'center'
}}>
<AppsWidthLimiter sx={{
width: !isMobile ? 'auto' : '90%'
}}>
<AppsLibraryContainer
sx={{
alignItems: 'center',
height: '100%',
paddingTop: '30px',
}}
>
<AppsWidthLimiter
sx={{
width: 'auto',
}}
>
<AppLibrarySubTitle>Create Apps!</AppLibrarySubTitle>
<Spacer height="18px" />
<PublishQAppInfo>
Note: Currently, only one App and Website is allowed per Name.
</PublishQAppInfo>
<Spacer height="18px" />
<InputLabel sx={{ color: '#888', fontSize: '14px', marginBottom: '2px' }}>Name/App</InputLabel>
<InputLabel
sx={{ color: '#888', fontSize: '14px', marginBottom: '2px' }}
>
Name/App
</InputLabel>
<CustomSelect
placeholder="Select Name/App"
displayEmpty
@ -280,19 +278,26 @@ export const AppPublish = ({ names, categories }) => {
<CustomMenuItem value="">
<em
style={{
color: "var(--50-white, #FFFFFF80)",
color: 'var(--50-white, #FFFFFF80)',
}}
>
Select Name/App
</em>{" "}
</em>{' '}
{/* This is the placeholder item */}
</CustomMenuItem>
{names.map((name) => {
return <CustomMenuItem value={name}>{name}</CustomMenuItem>;
})}
</CustomSelect>
<Spacer height="15px" />
<InputLabel sx={{ color: '#888', fontSize: '14px', marginBottom: '2px' }}>App service type</InputLabel>
<InputLabel
sx={{ color: '#888', fontSize: '14px', marginBottom: '2px' }}
>
App service type
</InputLabel>
<CustomSelect
placeholder="SERVICE TYPE"
displayEmpty
@ -302,59 +307,79 @@ export const AppPublish = ({ names, categories }) => {
<CustomMenuItem value="">
<em
style={{
color: "var(--50-white, #FFFFFF80)",
color: 'var(--50-white, #FFFFFF80)',
}}
>
Select App Type
</em>{" "}
</em>{' '}
{/* This is the placeholder item */}
</CustomMenuItem>
<CustomMenuItem value={"APP"}>App</CustomMenuItem>
<CustomMenuItem value={"WEBSITE"}>Website</CustomMenuItem>
<CustomMenuItem value={'APP'}>App</CustomMenuItem>
<CustomMenuItem value={'WEBSITE'}>Website</CustomMenuItem>
</CustomSelect>
<Spacer height="15px" />
<InputLabel sx={{ color: '#888', fontSize: '14px', marginBottom: '2px' }}>Title</InputLabel>
<InputLabel
sx={{ color: '#888', fontSize: '14px', marginBottom: '2px' }}
>
Title
</InputLabel>
<InputBase
value={title}
onChange={(e) => setTitle(e.target.value)}
sx={{
border: "0.5px solid var(--50-white, #FFFFFF80)",
padding: "0px 15px",
borderRadius: "5px",
height: "36px",
width: "100%",
maxWidth: "450px",
border: '0.5px solid var(--50-white, #FFFFFF80)',
padding: '0px 15px',
borderRadius: '5px',
height: '36px',
width: '100%',
maxWidth: '450px',
}}
placeholder="Title"
inputProps={{
"aria-label": "Title",
fontSize: "14px",
fontWeight: 400,
}}
/>
<Spacer height="15px" />
<InputLabel sx={{ color: '#888', fontSize: '14px', marginBottom: '2px' }}>Description</InputLabel>
<InputBase
value={description}
onChange={(e) => setDescription(e.target.value)}
sx={{
border: "0.5px solid var(--50-white, #FFFFFF80)",
padding: "0px 15px",
borderRadius: "5px",
height: "36px",
width: "100%",
maxWidth: "450px",
}}
placeholder="Description"
inputProps={{
"aria-label": "Description",
fontSize: "14px",
'aria-label': 'Title',
fontSize: '14px',
fontWeight: 400,
}}
/>
<Spacer height="15px" />
<InputLabel sx={{ color: '#888', fontSize: '14px', marginBottom: '2px' }}>Category</InputLabel>
<InputLabel
sx={{ color: '#888', fontSize: '14px', marginBottom: '2px' }}
>
Description
</InputLabel>
<InputBase
value={description}
onChange={(e) => setDescription(e.target.value)}
sx={{
border: '0.5px solid var(--50-white, #FFFFFF80)',
padding: '0px 15px',
borderRadius: '5px',
height: '36px',
width: '100%',
maxWidth: '450px',
}}
placeholder="Description"
inputProps={{
'aria-label': 'Description',
fontSize: '14px',
fontWeight: 400,
}}
/>
<Spacer height="15px" />
<InputLabel
sx={{ color: '#888', fontSize: '14px', marginBottom: '2px' }}
>
Category
</InputLabel>
<CustomSelect
displayEmpty
placeholder="Select Category"
@ -364,11 +389,11 @@ export const AppPublish = ({ names, categories }) => {
<CustomMenuItem value="">
<em
style={{
color: "var(--50-white, #FFFFFF80)",
color: 'var(--50-white, #FFFFFF80)',
}}
>
Select Category
</em>{" "}
</em>{' '}
{/* This is the placeholder item */}
</CustomMenuItem>
{categories?.map((category) => {
@ -379,23 +404,30 @@ export const AppPublish = ({ names, categories }) => {
);
})}
</CustomSelect>
<Spacer height="15px" />
<InputLabel sx={{ color: '#888', fontSize: '14px', marginBottom: '2px' }}>Tags</InputLabel>
<InputLabel
sx={{ color: '#888', fontSize: '14px', marginBottom: '2px' }}
>
Tags
</InputLabel>
<AppPublishTagsContainer>
<InputBase
value={tag1}
onChange={(e) => setTag1(e.target.value)}
sx={{
border: "0.5px solid var(--50-white, #FFFFFF80)",
padding: "0px 15px",
borderRadius: "5px",
height: "36px",
width: "100px",
border: '0.5px solid var(--50-white, #FFFFFF80)',
padding: '0px 15px',
borderRadius: '5px',
height: '36px',
width: '100px',
}}
placeholder="Tag 1"
inputProps={{
"aria-label": "Tag 1",
fontSize: "14px",
'aria-label': 'Tag 1',
fontSize: '14px',
fontWeight: 400,
}}
/>
@ -403,16 +435,16 @@ export const AppPublish = ({ names, categories }) => {
value={tag2}
onChange={(e) => setTag2(e.target.value)}
sx={{
border: "0.5px solid var(--50-white, #FFFFFF80)",
padding: "0px 15px",
borderRadius: "5px",
height: "36px",
width: "100px",
border: '0.5px solid var(--50-white, #FFFFFF80)',
padding: '0px 15px',
borderRadius: '5px',
height: '36px',
width: '100px',
}}
placeholder="Tag 2"
inputProps={{
"aria-label": "Tag 2",
fontSize: "14px",
'aria-label': 'Tag 2',
fontSize: '14px',
fontWeight: 400,
}}
/>
@ -420,16 +452,16 @@ export const AppPublish = ({ names, categories }) => {
value={tag3}
onChange={(e) => setTag3(e.target.value)}
sx={{
border: "0.5px solid var(--50-white, #FFFFFF80)",
padding: "0px 15px",
borderRadius: "5px",
height: "36px",
width: "100px",
border: '0.5px solid var(--50-white, #FFFFFF80)',
padding: '0px 15px',
borderRadius: '5px',
height: '36px',
width: '100px',
}}
placeholder="Tag 3"
inputProps={{
"aria-label": "Tag 3",
fontSize: "14px",
'aria-label': 'Tag 3',
fontSize: '14px',
fontWeight: 400,
}}
/>
@ -437,16 +469,16 @@ export const AppPublish = ({ names, categories }) => {
value={tag4}
onChange={(e) => setTag4(e.target.value)}
sx={{
border: "0.5px solid var(--50-white, #FFFFFF80)",
padding: "0px 15px",
borderRadius: "5px",
height: "36px",
width: "100px",
border: '0.5px solid var(--50-white, #FFFFFF80)',
padding: '0px 15px',
borderRadius: '5px',
height: '36px',
width: '100px',
}}
placeholder="Tag 4"
inputProps={{
"aria-label": "Tag 4",
fontSize: "14px",
'aria-label': 'Tag 4',
fontSize: '14px',
fontWeight: 400,
}}
/>
@ -454,27 +486,31 @@ export const AppPublish = ({ names, categories }) => {
value={tag5}
onChange={(e) => setTag5(e.target.value)}
sx={{
border: "0.5px solid var(--50-white, #FFFFFF80)",
padding: "0px 15px",
borderRadius: "5px",
height: "36px",
width: "100px",
border: '0.5px solid var(--50-white, #FFFFFF80)',
padding: '0px 15px',
borderRadius: '5px',
height: '36px',
width: '100px',
}}
placeholder="Tag 5"
inputProps={{
"aria-label": "Tag 5",
fontSize: "14px",
'aria-label': 'Tag 5',
fontSize: '14px',
fontWeight: 400,
}}
/>
</AppPublishTagsContainer>
<Spacer height="30px" />
<PublishQAppInfo>
Select .zip file containing static content:{" "}
Select .zip file containing static content:{' '}
</PublishQAppInfo>
<Spacer height="10px" />
<PublishQAppInfo>{`(${
appType === "APP" ? "50mb" : "400mb"
appType === 'APP' ? '50mb' : '400mb'
} MB maximum)`}</PublishQAppInfo>
{file && (
<>
@ -484,21 +520,25 @@ export const AppPublish = ({ names, categories }) => {
)}
<Spacer height="18px" />
<PublishQAppChoseFile {...getRootProps()}>
{" "}
{' '}
<input {...getInputProps()} />
Choose File
</PublishQAppChoseFile>
<Spacer height="35px" />
<PublishQAppCTAButton
sx={{
alignSelf: "center",
alignSelf: 'center',
}}
onClick={publishApp}
>
Publish
</PublishQAppCTAButton>
</AppsWidthLimiter>
<LoadingSnackbar
open={!!isLoading}
info={{
@ -512,7 +552,6 @@ export const AppPublish = ({ names, categories }) => {
info={infoSnack}
setInfo={setInfoSnack}
/>
</AppsLibraryContainer>
);
};

View File

@ -1,210 +1,249 @@
import React, { useContext, useEffect, useMemo, useState } from "react";
import React, { useContext, useEffect, useMemo, useState } from 'react';
import { Box } from '@mui/material';
import { MyContext, getBaseApiReact } from '../../App';
import { subscribeToEvent, unsubscribeFromEvent } from '../../utils/events';
import { useFrame } from 'react-frame-component';
import { useQortalMessageListener } from './useQortalMessageListener';
import { useThemeContext } from '../Theme/ThemeContext';
import { Box, } from "@mui/material";
import { MyContext, getBaseApiReact, isMobile } from "../../App";
export const AppViewer = React.forwardRef(
({ app, hide, isDevMode, skipAuth }, iframeRef) => {
const { rootHeight } = useContext(MyContext);
// const iframeRef = useRef(null);
const { window: frameWindow } = useFrame();
const { path, history, changeCurrentIndex, resetHistory } =
useQortalMessageListener(
frameWindow,
iframeRef,
app?.tabId,
isDevMode,
app?.name,
app?.service,
skipAuth
);
const [url, setUrl] = useState('');
const { themeMode } = useThemeContext();
import { subscribeToEvent, unsubscribeFromEvent } from "../../utils/events";
import { useFrame } from "react-frame-component";
import { useQortalMessageListener } from "./useQortalMessageListener";
import { useThemeContext } from "../Theme/ThemeContext";
useEffect(() => {
if (app?.isPreview) return;
if (isDevMode) {
setUrl(app?.url);
return;
}
let hasQueryParam = false;
if (app?.path && app.path.includes('?')) {
hasQueryParam = true;
}
setUrl(
`${getBaseApiReact()}/render/${app?.service}/${app?.name}${app?.path != null ? `/${app?.path}` : ''}${hasQueryParam ? '&' : '?'}theme=${themeMode}&identifier=${app?.identifier != null && app?.identifier != 'null' ? app?.identifier : ''}`
);
}, [app?.service, app?.name, app?.identifier, app?.path, app?.isPreview]);
useEffect(() => {
if (app?.isPreview && app?.url) {
resetHistory();
setUrl(app.url);
}
}, [app?.url, app?.isPreview]);
const defaultUrl = useMemo(() => {
return url;
}, [url, isDevMode]);
export const AppViewer = React.forwardRef(({ app , hide, isDevMode, skipAuth}, iframeRef) => {
const { rootHeight } = useContext(MyContext);
// const iframeRef = useRef(null);
const { window: frameWindow } = useFrame();
const {path, history, changeCurrentIndex, resetHistory} = useQortalMessageListener(frameWindow, iframeRef, app?.tabId, isDevMode, app?.name, app?.service, skipAuth)
const [url, setUrl] = useState('')
const { themeMode } = useThemeContext();
useEffect(()=> {
if(app?.isPreview) return
if(isDevMode){
setUrl(app?.url)
return
}
let hasQueryParam = false
if(app?.path && app.path.includes('?')){
hasQueryParam = true
}
setUrl(`${getBaseApiReact()}/render/${app?.service}/${app?.name}${app?.path != null ? `/${app?.path}` : ''}${hasQueryParam ? "&": "?" }theme=${themeMode}&identifier=${(app?.identifier != null && app?.identifier != 'null') ? app?.identifier : ''}`)
}, [app?.service, app?.name, app?.identifier, app?.path, app?.isPreview])
useEffect(()=> {
if(app?.isPreview && app?.url){
resetHistory()
setUrl(app.url)
}
}, [app?.url, app?.isPreview])
const defaultUrl = useMemo(()=> {
return url
}, [url, isDevMode])
const refreshAppFunc = (e) => {
const {tabId} = e.detail
if(tabId === app?.tabId){
if(isDevMode){
resetHistory()
if(!app?.isPreview || app?.isPrivate){
setUrl(app?.url + `?time=${Date.now()}`)
const refreshAppFunc = (e) => {
const { tabId } = e.detail;
if (tabId === app?.tabId) {
if (isDevMode) {
resetHistory();
if (!app?.isPreview || app?.isPrivate) {
setUrl(app?.url + `?time=${Date.now()}`);
}
return;
}
return
const constructUrl = `${getBaseApiReact()}/render/${app?.service}/${app?.name}${path != null ? path : ''}?theme=${themeMode}&identifier=${app?.identifier != null ? app?.identifier : ''}&time=${new Date().getMilliseconds()}`;
setUrl(constructUrl);
}
const constructUrl = `${getBaseApiReact()}/render/${app?.service}/${app?.name}${path != null ? path : ''}?theme=${themeMode}&identifier=${app?.identifier != null ? app?.identifier : ''}&time=${new Date().getMilliseconds()}`
setUrl(constructUrl)
}
};
useEffect(() => {
subscribeToEvent("refreshApp", refreshAppFunc);
return () => {
unsubscribeFromEvent("refreshApp", refreshAppFunc);
};
}, [app, path, isDevMode]);
useEffect(()=> {
if(!iframeRef?.current) return
const targetOrigin = iframeRef.current ? new URL(iframeRef.current.src).origin : "*";
// Send the navigation command after setting up the listener and timeout
iframeRef.current.contentWindow.postMessage(
{ action: 'THEME_CHANGED', theme: themeMode, requestedHandler: 'UI' }, targetOrigin
);
}, [themeMode])
useEffect(() => {
subscribeToEvent('refreshApp', refreshAppFunc);
const removeTrailingSlash = (str) => str.replace(/\/$/, '');
const copyLinkFunc = (e) => {
const {tabId} = e.detail
if(tabId === app?.tabId){
let link = 'qortal://' + app?.service + '/' + app?.name
if(path && path.startsWith('/')){
link = link + removeTrailingSlash(path)
}
if(path && !path.startsWith('/')){
link = link + '/' + removeTrailingSlash(path)
}
navigator.clipboard.writeText(link)
.then(() => {
console.log("Path copied to clipboard:", path);
})
.catch((error) => {
console.error("Failed to copy path:", error);
});
}
};
return () => {
unsubscribeFromEvent('refreshApp', refreshAppFunc);
};
}, [app, path, isDevMode]);
useEffect(() => {
subscribeToEvent("copyLink", copyLinkFunc);
return () => {
unsubscribeFromEvent("copyLink", copyLinkFunc);
};
}, [app, path]);
// Function to navigate back in iframe
const navigateBackInIframe = async () => {
if (iframeRef.current && iframeRef.current.contentWindow && history?.currentIndex > 0) {
// Calculate the previous index and path
const previousPageIndex = history.currentIndex - 1;
const previousPath = history.customQDNHistoryPaths[previousPageIndex];
const targetOrigin = iframeRef.current ? new URL(iframeRef.current.src).origin : "*";
// Signal non-manual navigation
iframeRef.current.contentWindow.postMessage(
{ action: 'PERFORMING_NON_MANUAL', currentIndex: previousPageIndex },targetOrigin
);
// Update the current index locally
changeCurrentIndex(previousPageIndex);
// Create a navigation promise with a 200ms timeout
const navigationPromise = new Promise((resolve, reject) => {
function handleNavigationSuccess(event) {
if (event.data?.action === 'NAVIGATION_SUCCESS' && event.data.path === previousPath) {
frameWindow.removeEventListener('message', handleNavigationSuccess);
resolve();
}
}
frameWindow.addEventListener('message', handleNavigationSuccess);
// Timeout after 200ms if no response
setTimeout(() => {
window.removeEventListener('message', handleNavigationSuccess);
reject(new Error("Navigation timeout"));
}, 200);
const targetOrigin = iframeRef.current ? new URL(iframeRef.current.src).origin : "*";
useEffect(() => {
if (!iframeRef?.current) return;
const targetOrigin = iframeRef.current
? new URL(iframeRef.current.src).origin
: '*';
// Send the navigation command after setting up the listener and timeout
iframeRef.current.contentWindow.postMessage(
{ action: 'NAVIGATE_TO_PATH', path: previousPath, requestedHandler: 'UI' }, targetOrigin
{ action: 'THEME_CHANGED', theme: themeMode, requestedHandler: 'UI' },
targetOrigin
);
});
}, [themeMode]);
// Execute navigation promise and handle timeout fallback
try {
await navigationPromise;
} catch (error) {
if(isDevMode){
setUrl(`${url}${previousPath != null ? previousPath : ''}?theme=${themeMode}&time=${new Date().getMilliseconds()}&isManualNavigation=false`)
return
}
setUrl(`${getBaseApiReact()}/render/${app?.service}/${app?.name}${previousPath != null ? previousPath : ''}?theme=${themeMode}&identifier=${(app?.identifier != null && app?.identifier != 'null') ? app?.identifier : ''}&time=${new Date().getMilliseconds()}&isManualNavigation=false`)
// iframeRef.current.contentWindow.location.href = previousPath; // Fallback URL update
}
} else {
console.log('Iframe not accessible or does not have a content window.');
}
};
const removeTrailingSlash = (str) => str.replace(/\/$/, '');
const navigateBackAppFunc = (e) => {
navigateBackInIframe()
};
useEffect(() => {
if(!app?.tabId) return
subscribeToEvent(`navigateBackApp-${app?.tabId}`, navigateBackAppFunc);
return () => {
unsubscribeFromEvent(`navigateBackApp-${app?.tabId}`, navigateBackAppFunc);
const copyLinkFunc = (e) => {
const { tabId } = e.detail;
if (tabId === app?.tabId) {
let link = 'qortal://' + app?.service + '/' + app?.name;
if (path && path.startsWith('/')) {
link = link + removeTrailingSlash(path);
}
if (path && !path.startsWith('/')) {
link = link + '/' + removeTrailingSlash(path);
}
navigator.clipboard
.writeText(link)
.then(() => {
console.log('Path copied to clipboard:', path);
})
.catch((error) => {
console.error('Failed to copy path:', error);
});
}
};
}, [app, history]);
useEffect(() => {
subscribeToEvent('copyLink', copyLinkFunc);
// Function to navigate back in iframe
const navigateForwardInIframe = async () => {
return () => {
unsubscribeFromEvent('copyLink', copyLinkFunc);
};
}, [app, path]);
if (iframeRef.current && iframeRef.current.contentWindow) {
const targetOrigin = iframeRef.current ? new URL(iframeRef.current.src).origin : "*";
iframeRef.current.contentWindow.postMessage(
{ action: 'NAVIGATE_FORWARD'},
// Function to navigate back in iframe
const navigateBackInIframe = async () => {
if (
iframeRef.current &&
iframeRef.current.contentWindow &&
history?.currentIndex > 0
) {
// Calculate the previous index and path
const previousPageIndex = history.currentIndex - 1;
const previousPath = history.customQDNHistoryPaths[previousPageIndex];
const targetOrigin = iframeRef.current
? new URL(iframeRef.current.src).origin
: '*';
// Signal non-manual navigation
iframeRef.current.contentWindow.postMessage(
{ action: 'PERFORMING_NON_MANUAL', currentIndex: previousPageIndex },
targetOrigin
);
} else {
console.log('Iframe not accessible or does not have a content window.');
);
// Update the current index locally
changeCurrentIndex(previousPageIndex);
// Create a navigation promise with a 200ms timeout
const navigationPromise = new Promise((resolve, reject) => {
function handleNavigationSuccess(event) {
if (
event.data?.action === 'NAVIGATION_SUCCESS' &&
event.data.path === previousPath
) {
frameWindow.removeEventListener(
'message',
handleNavigationSuccess
);
resolve();
}
}
frameWindow.addEventListener('message', handleNavigationSuccess);
// Timeout after 200ms if no response
setTimeout(() => {
window.removeEventListener('message', handleNavigationSuccess);
reject(new Error('Navigation timeout'));
}, 200);
const targetOrigin = iframeRef.current
? new URL(iframeRef.current.src).origin
: '*';
// Send the navigation command after setting up the listener and timeout
iframeRef.current.contentWindow.postMessage(
{
action: 'NAVIGATE_TO_PATH',
path: previousPath,
requestedHandler: 'UI',
},
targetOrigin
);
});
// Execute navigation promise and handle timeout fallback
try {
await navigationPromise;
} catch (error) {
if (isDevMode) {
setUrl(
`${url}${previousPath != null ? previousPath : ''}?theme=${themeMode}&time=${new Date().getMilliseconds()}&isManualNavigation=false`
);
return;
}
setUrl(
`${getBaseApiReact()}/render/${app?.service}/${app?.name}${previousPath != null ? previousPath : ''}?theme=${themeMode}&identifier=${app?.identifier != null && app?.identifier != 'null' ? app?.identifier : ''}&time=${new Date().getMilliseconds()}&isManualNavigation=false`
);
// iframeRef.current.contentWindow.location.href = previousPath; // Fallback URL update
}
} else {
console.log('Iframe not accessible or does not have a content window.');
}
};
const navigateBackAppFunc = (e) => {
navigateBackInIframe();
};
useEffect(() => {
if (!app?.tabId) return;
subscribeToEvent(`navigateBackApp-${app?.tabId}`, navigateBackAppFunc);
return () => {
unsubscribeFromEvent(
`navigateBackApp-${app?.tabId}`,
navigateBackAppFunc
);
};
}, [app, history]);
// Function to navigate back in iframe
const navigateForwardInIframe = async () => {
if (iframeRef.current && iframeRef.current.contentWindow) {
const targetOrigin = iframeRef.current
? new URL(iframeRef.current.src).origin
: '*';
iframeRef.current.contentWindow.postMessage(
{ action: 'NAVIGATE_FORWARD' },
targetOrigin
);
} else {
console.log('Iframe not accessible or does not have a content window.');
}
};
return (
<Box
sx={{
display: 'flex',
flexDirection: 'column',
}}
>
<iframe
ref={iframeRef}
style={{
height: '100vh',
border: 'none',
width: '100%',
}}
id="browser-iframe"
src={defaultUrl}
sandbox="allow-scripts allow-same-origin allow-forms allow-downloads allow-modals"
allow="fullscreen; clipboard-read; clipboard-write"
></iframe>
</Box>
);
}
};
return (
<Box sx={{
display: 'flex',
flexDirection: 'column',
}}>
<iframe ref={iframeRef} style={{
height: !isMobile ? '100vh' : `calc(${rootHeight} - 60px - 45px )`,
border: 'none',
width: '100%'
}} id="browser-iframe" src={defaultUrl} sandbox="allow-scripts allow-same-origin allow-forms allow-downloads allow-modals"
allow="fullscreen; clipboard-read; clipboard-write">
</iframe>
</Box>
);
});
);

View File

@ -1,20 +1,19 @@
import React, { useContext, } from 'react';
import React, { useContext } from 'react';
import { AppViewer } from './AppViewer';
import Frame from 'react-frame-component';
import { MyContext, isMobile } from '../../App';
import { MyContext } from '../../App';
const AppViewerContainer = React.forwardRef(({ app, isSelected, hide, isDevMode, customHeight, skipAuth }, ref) => {
const { rootHeight } = useContext(MyContext);
const AppViewerContainer = React.forwardRef(
({ app, isSelected, hide, isDevMode, customHeight, skipAuth }, ref) => {
const { rootHeight } = useContext(MyContext);
return (
<Frame
id={`browser-iframe-${app?.tabId}`}
head={
<>
<style>
{`
return (
<Frame
id={`browser-iframe-${app?.tabId}`}
head={
<>
<style>
{`
body {
margin: 0;
padding: 0;
@ -28,24 +27,31 @@ const AppViewerContainer = React.forwardRef(({ app, isSelected, hide, isDevMode,
}
.frame-content {
overflow: hidden;
height: ${!isMobile ? '100vh' : `calc(${rootHeight} - 60px - 45px)`};
height: '100vh';
}
`}
</style>
</>
}
style={{
position: (!isSelected || hide) && 'fixed',
left: (!isSelected || hide) && '-200vw',
height: customHeight ? customHeight : !isMobile ? '100vh' : `calc(${rootHeight} - 60px - 45px)`,
border: 'none',
width: '100%',
overflow: 'hidden',
}}
>
<AppViewer skipAuth={skipAuth} app={app} ref={ref} hide={!isSelected || hide} isDevMode={isDevMode} />
</Frame>
);
});
</style>
</>
}
style={{
border: 'none',
height: '100vh',
left: (!isSelected || hide) && '-200vw',
overflow: 'hidden',
position: (!isSelected || hide) && 'fixed',
width: '100%',
}}
>
<AppViewer
skipAuth={skipAuth}
app={app}
ref={ref}
hide={!isSelected || hide}
isDevMode={isDevMode}
/>
</Frame>
);
}
);
export default AppViewerContainer;

View File

@ -137,9 +137,9 @@ export const AppCircle = styled(Box)(({ theme }) => ({
theme.palette.mode === 'dark'
? 'rgb(209, 209, 209)'
: 'rgba(41, 41, 43, 1)',
borderWidth: '1px',
borderRadius: '50%',
borderStyle: 'solid',
borderWidth: '1px',
color: theme.palette.text.primary,
display: 'flex',
flexDirection: 'column',

View File

@ -22,7 +22,7 @@ import {
Input,
} from '@mui/material';
import { Add } from '@mui/icons-material';
import { MyContext, getBaseApiReact, isMobile } from '../../App';
import { MyContext, getBaseApiReact } from '../../App';
import LogoSelected from '../../assets/svgs/LogoSelected.svg';
import { executeEvent } from '../../utils/events';
import { Spacer } from '../../common/Spacer';
@ -279,7 +279,9 @@ export const AppsDevModeHome = ({
Dev Mode Apps
</AppLibrarySubTitle>
</AppsContainer>
<Spacer height="45px" />
<AppsContainer
sx={{
gap: '75px',
@ -293,7 +295,7 @@ export const AppsDevModeHome = ({
>
<AppCircleContainer
sx={{
gap: !isMobile ? '10px' : '5px',
gap: '10px',
}}
>
<AppCircle>
@ -302,6 +304,7 @@ export const AppsDevModeHome = ({
<AppCircleLabel>Server</AppCircleLabel>
</AppCircleContainer>
</ButtonBase>
<ButtonBase
onClick={() => {
addPreviewApp();
@ -309,15 +312,17 @@ export const AppsDevModeHome = ({
>
<AppCircleContainer
sx={{
gap: !isMobile ? '10px' : '5px',
gap: '10px',
}}
>
<AppCircle>
<Add>+</Add>
</AppCircle>
<AppCircleLabel>Zip</AppCircleLabel>
</AppCircleContainer>
</ButtonBase>
<ButtonBase
onClick={() => {
addPreviewAppWithDirectory();
@ -325,7 +330,7 @@ export const AppsDevModeHome = ({
>
<AppCircleContainer
sx={{
gap: !isMobile ? '10px' : '5px',
gap: '10px',
}}
>
<AppCircle>
@ -334,6 +339,7 @@ export const AppsDevModeHome = ({
<AppCircleLabel>Directory</AppCircleLabel>
</AppCircleContainer>
</ButtonBase>
<ButtonBase
onClick={() => {
executeEvent('appsDevModeAddTab', {
@ -347,7 +353,7 @@ export const AppsDevModeHome = ({
>
<AppCircleContainer
sx={{
gap: !isMobile ? '10px' : '5px',
gap: '10px',
}}
>
<AppCircle>
@ -371,9 +377,11 @@ export const AppsDevModeHome = ({
/>
</Avatar>
</AppCircle>
<AppCircleLabel>Q-Sandbox</AppCircleLabel>
</AppCircleContainer>
</ButtonBase>
<ButtonBase
onClick={() => {
executeEvent('appsDevModeAddTab', {
@ -387,7 +395,7 @@ export const AppsDevModeHome = ({
>
<AppCircleContainer
sx={{
gap: !isMobile ? '10px' : '5px',
gap: '10px',
}}
>
<AppCircle>
@ -411,10 +419,12 @@ export const AppsDevModeHome = ({
/>
</Avatar>
</AppCircle>
<AppCircleLabel>API</AppCircleLabel>
</AppCircleContainer>
</ButtonBase>
</AppsContainer>
{isShow && (
<Dialog
open={isShow}
@ -429,6 +439,7 @@ export const AppsDevModeHome = ({
<DialogTitle id="alert-dialog-title">
{'Add custom framework'}
</DialogTitle>
<DialogContent>
<Box
sx={{
@ -460,6 +471,7 @@ export const AppsDevModeHome = ({
/>
</Box>
</DialogContent>
<DialogActions>
<Button variant="contained" onClick={onCancel}>
Close

View File

@ -1,17 +1,12 @@
import React, { useMemo, useState } from 'react';
import {
AppCircle,
AppCircleContainer,
AppCircleLabel,
AppLibrarySubTitle,
AppsContainer,
AppsParent,
} from './Apps-styles';
import { Avatar, ButtonBase } from '@mui/material';
import { ButtonBase } from '@mui/material';
import { Add } from '@mui/icons-material';
import { getBaseApiReact, isMobile } from '../../App';
import LogoSelected from '../../assets/svgs/LogoSelected.svg';
import { executeEvent } from '../../utils/events';
import { SortablePinnedApps } from './SortablePinnedApps';
import { Spacer } from '../../common/Spacer';
@ -35,7 +30,7 @@ export const AppsHome = ({ setMode, myApp, myWebsite, availableQapps }) => {
>
<AppCircleContainer
sx={{
gap: !isMobile ? '10px' : '5px',
gap: '10px',
}}
>
<AppCircle>

View File

@ -1,4 +1,4 @@
import React, { useState } from 'react';
import { useState } from 'react';
import {
AppCircle,
AppCircleContainer,
@ -8,7 +8,6 @@ import {
} from './Apps-styles';
import { Box, ButtonBase, Input, useTheme } from '@mui/material';
import { Add } from '@mui/icons-material';
import { isMobile } from '../../App';
import { executeEvent } from '../../utils/events';
import { Spacer } from '../../common/Spacer';
import { SortablePinnedApps } from './SortablePinnedApps';
@ -137,12 +136,13 @@ export const AppsHomeDesktop = ({
>
<AppCircleContainer
sx={{
gap: !isMobile ? '10px' : '5px',
gap: '10px',
}}
>
<AppCircle>
<Add>+</Add>
</AppCircle>
<AppCircleLabel>Library</AppCircleLabel>
</AppCircleContainer>
</ButtonBase>

View File

@ -1,11 +1,4 @@
import React, {
useCallback,
useContext,
useEffect,
useMemo,
useRef,
useState,
} from 'react';
import { useEffect, useMemo, useRef, useState } from 'react';
import {
AppCircle,
AppCircleContainer,
@ -32,17 +25,14 @@ import {
Typography,
styled,
} from '@mui/material';
import { Add } from '@mui/icons-material';
import { MyContext, getBaseApiReact } from '../../App';
import { getBaseApiReact } from '../../App';
import LogoSelected from '../../assets/svgs/LogoSelected.svg';
import IconSearch from '../../assets/svgs/Search.svg';
import IconClearInput from '../../assets/svgs/ClearInput.svg';
import qappDevelopText from '../../assets/svgs/qappDevelopText.svg';
import qappLibraryText from '../../assets/svgs/qappLibraryText.svg';
import RefreshIcon from '@mui/icons-material/Refresh';
import qappDots from '../../assets/svgs/qappDots.svg';
import { Spacer } from '../../common/Spacer';
import { AppInfoSnippet } from './AppInfoSnippet';
import { Virtuoso } from 'react-virtuoso';
@ -57,6 +47,7 @@ import {
MailIconImg,
ShowMessageReturnButton,
} from '../Group/Forum/Mail-styles';
const officialAppList = [
'q-tube',
'q-blog',

View File

@ -6,7 +6,6 @@ import {
Dialog,
DialogActions,
DialogContent,
DialogTitle,
Input,
MenuItem,
Select,
@ -32,7 +31,7 @@ import {
PublishQAppInfo,
} from './Apps-styles';
import ImageUploader from '../../common/ImageUploader';
import { isMobile, MyContext } from '../../App';
import { MyContext } from '../../App';
import { fileToBase64 } from '../../utils/fileReading';
import { objectToBase64 } from '../../qdn/encryption/group-encryption';
import { getFee } from '../../background';
@ -67,6 +66,7 @@ export const AppsPrivate = ({ myName }) => {
(group) => groupsProperties[group?.groupId]?.isOpen === false
);
}, [memberGroups, groupsProperties]);
const [privateAppValues, setPrivateAppValues] = useState({
name: '',
service: 'DOCUMENT',
@ -230,7 +230,7 @@ export const AppsPrivate = ({ myName }) => {
>
<AppCircleContainer
sx={{
gap: !isMobile ? '10px' : '5px',
gap: '10px',
}}
>
<AppCircle>
@ -267,9 +267,8 @@ export const AppsPrivate = ({ myName }) => {
value={valueTabPrivateApp}
onChange={handleChange}
aria-label="basic tabs example"
variant={isMobile ? 'scrollable' : 'fullWidth'} // Scrollable on mobile, full width on desktop
variant={'fullWidth'}
scrollButtons="auto"
allowScrollButtonsMobile
sx={{
'& .MuiTabs-indicator': {
backgroundColor: theme.palette.background.default,
@ -283,7 +282,7 @@ export const AppsPrivate = ({ myName }) => {
'&.Mui-selected': {
color: theme.palette.text.primary,
},
fontSize: isMobile ? '0.75rem' : '1rem', // Adjust font size for mobile
fontSize: '1rem',
}}
/>
<Tab
@ -293,7 +292,7 @@ export const AppsPrivate = ({ myName }) => {
'&.Mui-selected': {
color: theme.palette.text.primary,
},
fontSize: isMobile ? '0.75rem' : '1rem', // Adjust font size for mobile
fontSize: '1rem',
}}
/>
</Tabs>

View File

@ -1,5 +1,5 @@
import { useContext, useEffect, useState } from 'react';
import { MyContext, isMobile } from '../../App';
import { MyContext } from '../../App';
import { Box, Typography } from '@mui/material';
import { AdminSpaceInner } from './AdminSpaceInner';
@ -29,10 +29,9 @@ export const AdminSpace = ({
return (
<div
style={{
// reference to change height
display: 'flex',
flexDirection: 'column',
height: isMobile ? `calc(${rootHeight} - 127px` : 'calc(100vh - 70px)',
height: 'calc(100vh - 70px)',
left: hide && '-1000px',
opacity: hide ? 0 : 1,
position: hide ? 'fixed' : 'relative',

View File

@ -21,7 +21,6 @@ import ArrowBackIcon from '@mui/icons-material/ArrowBack';
import {
getArbitraryEndpointReact,
getBaseApiReact,
isMobile,
pauseAllQueues,
resumeAllQueues,
} from '../../App';
@ -56,12 +55,6 @@ export const AnnouncementDiscussion = ({
const clearEditorContent = () => {
if (editorRef.current) {
editorRef.current.chain().focus().clearContent().run();
if (isMobile) {
setTimeout(() => {
editorRef.current?.chain().blur().run();
setIsFocusedParent(false);
}, 200);
}
}
};
@ -278,7 +271,7 @@ export const AnnouncementDiscussion = ({
style={{
display: 'flex',
flexDirection: 'column',
height: isMobile ? '100%' : '100%',
height: '100%',
width: '100%',
}}
>

View File

@ -20,7 +20,6 @@ import { CustomizedSnackbars } from '../Snackbar/Snackbar';
import {
getBaseApiReact,
getBaseApiReactSocket,
isMobile,
pauseAllQueues,
resumeAllQueues,
} from '../../App';
@ -410,16 +409,6 @@ export const ChatDirect = ({
if (editorRef.current) {
setMessageSize(0);
editorRef.current.chain().focus().clearContent().run();
if (isMobile) {
setTimeout(() => {
editorRef.current?.chain().blur().run();
setIsFocusedParent(false);
executeEvent('sent-new-message-group', {});
setTimeout(() => {
triggerRerender();
}, 300);
}, 200);
}
}
};
useEffect(() => {
@ -547,108 +536,41 @@ export const ChatDirect = ({
background: theme.palette.background.default,
display: 'flex',
flexDirection: 'column',
height: isMobile ? '100%' : '100vh',
height: '100vh',
width: '100%',
}}
>
{!isMobile && (
<Box
onClick={close}
<Box
onClick={close}
sx={{
alignItems: 'center',
alignSelf: 'center',
background: theme.palette.background.default,
borderRadius: '3px',
cursor: 'pointer',
display: 'flex',
gap: '5px',
margin: '10px 0px',
padding: '4px 6px',
width: 'fit-content',
}}
>
<ArrowBackIcon
sx={{
alignItems: 'center',
alignSelf: 'center',
background: theme.palette.background.default,
borderRadius: '3px',
cursor: 'pointer',
display: 'flex',
gap: '5px',
margin: '10px 0px',
padding: '4px 6px',
width: 'fit-content',
color: theme.palette.text.primary,
fontSize: '20px',
}}
/>
<Typography
sx={{
color: theme.palette.text.primary,
fontSize: '14px',
}}
>
<ArrowBackIcon
sx={{
color: theme.palette.text.primary,
fontSize: '20px',
}}
/>
<Typography
sx={{
color: theme.palette.text.primary,
fontSize: '14px',
}}
>
Close Direct Chat
</Typography>
</Box>
)}
{isMobile && (
<Box
sx={{
alignItems: 'center',
display: 'flex',
height: '15px',
justifyContent: 'center',
marginTop: '14px',
width: '100%',
}}
>
<Box
sx={{
alignItems: 'center',
display: 'flex',
justifyContent: 'space-between',
width: '320px',
}}
>
<Box
sx={{
alignItems: 'center',
display: 'flex',
width: '50px',
}}
>
<ButtonBase
onClick={() => {
close();
}}
>
<ReturnIcon />
</ButtonBase>
</Box>
<Typography
sx={{
fontSize: '14px',
fontWeight: 600,
}}
>
{isNewChat
? ''
: selectedDirect?.name ||
selectedDirect?.address?.slice(0, 10) + '...'}
</Typography>
<Box
sx={{
alignItems: 'center',
display: 'flex',
justifyContent: 'flex-end',
width: '50px',
}}
>
<ButtonBase
onClick={() => {
setSelectedDirect(null);
setMobileViewModeKeepOpen('');
setNewChat(false);
}}
>
<ExitIcon />
</ButtonBase>
</Box>
</Box>
</Box>
)}
Close Direct Chat
</Typography>
</Box>
{isNewChat && (
<>
<Spacer height="30px" />
@ -685,7 +607,7 @@ export const ChatDirect = ({
flexShrink: 0,
minHeight: '150px',
overflow: 'hidden',
padding: isMobile ? '10px' : '20px',
padding: '20px',
position: isFocusedParent ? 'fixed' : 'relative',
top: isFocusedParent ? '0px' : 'unset',
width: '100%',
@ -753,7 +675,7 @@ export const ChatDirect = ({
setEditorRef={setEditorRef}
onEnter={sendMessage}
isChat
disableEnter={isMobile ? true : false}
disableEnter={false}
setIsFocusedParent={setIsFocusedParent}
/>
{messageSize > 750 && (

View File

@ -20,7 +20,6 @@ import { LoadingSnackbar } from '../Snackbar/LoadingSnackbar';
import {
getBaseApiReact,
getBaseApiReactSocket,
isMobile,
MyContext,
pauseAllQueues,
resumeAllQueues,
@ -746,16 +745,6 @@ export const ChatGroup = ({
if (editorRef.current) {
setMessageSize(0);
editorRef.current.chain().focus().clearContent().run();
if (isMobile) {
setTimeout(() => {
editorRef.current?.chain().blur().run();
setIsFocusedParent(false);
executeEvent('sent-new-message-group', {});
setTimeout(() => {
triggerRerender();
}, 300);
}, 200);
}
}
};
@ -1109,7 +1098,7 @@ export const ChatGroup = ({
setEditorRef={setEditorRef}
onEnter={sendMessage}
isChat
disableEnter={isMobile ? true : false}
disableEnter={false}
isFocusedParent={isFocusedParent}
setIsFocusedParent={setIsFocusedParent}
membersWithNames={members}

View File

@ -26,7 +26,6 @@ import {
MyContext,
getArbitraryEndpointReact,
getBaseApiReact,
isMobile,
pauseAllQueues,
resumeAllQueues,
} from '../../App';
@ -262,15 +261,6 @@ export const GroupAnnouncements = ({
const clearEditorContent = () => {
if (editorRef.current) {
editorRef.current.chain().focus().clearContent().run();
if (isMobile) {
setTimeout(() => {
editorRef.current?.chain().blur().run();
setIsFocusedParent(false);
setTimeout(() => {
triggerRerender();
}, 300);
}, 200);
}
}
};
@ -529,12 +519,9 @@ export const GroupAnnouncements = ({
return (
<div
style={{
// reference to change height
height: isMobile
? `calc(${rootHeight} - 127px`
: 'calc(100vh - 70px)',
display: 'flex',
flexDirection: 'column',
height: 'calc(100vh - 70px)',
left: hide && '-1000px',
position: hide && 'fixed',
visibility: hide && 'hidden',
@ -576,26 +563,24 @@ export const GroupAnnouncements = ({
width: '100%',
}}
>
{!isMobile && (
<Box
<Box
sx={{
alignItems: 'center',
display: 'flex',
fontSize: '20px',
gap: '20px',
justifyContent: 'center',
padding: '25px',
width: '100%',
}}
>
<CampaignIcon
sx={{
alignItems: 'center',
display: 'flex',
fontSize: '20px',
gap: '20px',
justifyContent: 'center',
padding: '25px',
width: '100%',
fontSize: '30px',
}}
>
<CampaignIcon
sx={{
fontSize: '30px',
}}
/>
Group Announcements
</Box>
)}
/>
Group Announcements
</Box>
<Spacer height={'25px'} />
</div>

View File

@ -1,6 +1,6 @@
import { useContext, useEffect, useState } from 'react';
import { GroupMail } from '../Group/Forum/GroupMail';
import { MyContext, isMobile } from '../../App';
import { MyContext } from '../../App';
export const GroupForum = ({
selectedGroup,

View File

@ -23,7 +23,6 @@ import DeveloperModeIcon from '@mui/icons-material/DeveloperMode';
import Compressor from 'compressorjs';
import Mention from '@tiptap/extension-mention';
import ImageResize from 'tiptap-extension-resize-image'; // Import the ResizeImage extension
import { isMobile } from '../../App';
import tippy from 'tippy.js';
import 'tippy.js/dist/tippy.css';
import { ReactRenderer } from '@tiptap/react';
@ -137,7 +136,7 @@ const MenuBar = ({
color: editor.isActive('bold')
? theme.palette.text.primary
: theme.palette.text.secondary,
padding: isMobile ? '5px' : 'revert',
padding: 'revert',
}}
>
<FormatBoldIcon />
@ -149,7 +148,7 @@ const MenuBar = ({
color: editor.isActive('italic')
? theme.palette.text.primary
: theme.palette.text.secondary,
padding: isMobile ? '5px' : 'revert',
padding: 'revert',
}}
>
<FormatItalicIcon />
@ -161,7 +160,7 @@ const MenuBar = ({
color: editor.isActive('strike')
? theme.palette.text.primary
: theme.palette.text.secondary,
padding: isMobile ? '5px' : 'revert',
padding: 'revert',
}}
>
<StrikethroughSIcon />
@ -173,7 +172,7 @@ const MenuBar = ({
color: editor.isActive('code')
? theme.palette.text.primary
: theme.palette.text.secondary,
padding: isMobile ? '5px' : 'revert',
padding: 'revert',
}}
>
<CodeIcon />
@ -188,7 +187,7 @@ const MenuBar = ({
editor.isActive('code')
? theme.palette.text.primary
: theme.palette.text.secondary,
padding: isMobile ? '5px' : 'revert',
padding: 'revert',
}}
>
<FormatClearIcon />
@ -199,7 +198,7 @@ const MenuBar = ({
color: editor.isActive('bulletList')
? theme.palette.text.primary
: theme.palette.text.secondary,
padding: isMobile ? '5px' : 'revert',
padding: 'revert',
}}
>
<FormatListBulletedIcon />
@ -210,7 +209,7 @@ const MenuBar = ({
color: editor.isActive('orderedList')
? theme.palette.text.primary
: theme.palette.text.secondary,
padding: isMobile ? '5px' : 'revert',
padding: 'revert',
}}
>
<FormatListNumberedIcon />
@ -221,7 +220,7 @@ const MenuBar = ({
color: editor.isActive('codeBlock')
? theme.palette.text.primary
: theme.palette.text.secondary,
padding: isMobile ? '5px' : 'revert',
padding: 'revert',
}}
>
<DeveloperModeIcon />
@ -232,7 +231,7 @@ const MenuBar = ({
color: editor.isActive('blockquote')
? theme.palette.text.primary
: theme.palette.text.secondary,
padding: isMobile ? '5px' : 'revert',
padding: 'revert',
}}
>
<FormatQuoteIcon />
@ -240,7 +239,7 @@ const MenuBar = ({
<IconButton
onClick={() => editor.chain().focus().setHorizontalRule().run()}
disabled={!editor.can().chain().focus().setHorizontalRule().run()}
sx={{ color: 'gray', padding: isMobile ? '5px' : 'revert' }}
sx={{ color: 'gray', padding: 'revert' }}
>
<HorizontalRuleIcon />
</IconButton>
@ -252,7 +251,7 @@ const MenuBar = ({
color: editor.isActive('heading', { level: 1 })
? theme.palette.text.primary
: theme.palette.text.secondary,
padding: isMobile ? '5px' : 'revert',
padding: 'revert',
}}
>
<FormatHeadingIcon fontSize="small" />
@ -260,7 +259,7 @@ const MenuBar = ({
<IconButton
onClick={() => editor.chain().focus().undo().run()}
disabled={!editor.can().chain().focus().undo().run()}
sx={{ color: 'gray', padding: isMobile ? '5px' : 'revert' }}
sx={{ color: 'gray', padding: 'revert' }}
>
<UndoIcon />
</IconButton>
@ -313,7 +312,7 @@ const MenuBar = ({
onClick={triggerImageUpload}
sx={{
color: theme.palette.text.secondary,
padding: isMobile ? '5px' : 'revert',
padding: 'revert',
}}
>
<ImageIcon />
@ -398,11 +397,6 @@ export default ({
usersRef.current = users; // Keep users up-to-date
}, [users]);
const handleFocus = () => {
if (!isMobile) return;
setIsFocusedParent(true);
};
const handleBlur = () => {
const htmlContent = editorRef.current.getHTML();
if (!htmlContent?.trim() || htmlContent?.trim() === '<p></p>') {
@ -499,7 +493,7 @@ export default ({
>
<EditorProvider
slotBefore={
(isFocusedParent || !isMobile || overrideMobile) && (
(isFocusedParent || overrideMobile) && (
<MenuBar
setEditorRef={setEditorRefFunc}
isChat={isChat}
@ -511,21 +505,15 @@ export default ({
extensions={[...extensionsFiltered, ...additionalExtensions]}
content={content}
onCreate={({ editor }) => {
editor.on('focus', handleFocus); // Listen for focus event
editor.on('blur', handleBlur); // Listen for blur event
}}
onUpdate={({ editor }) => {
editor.on('focus', handleFocus); // Ensure focus is updated
editor.on('blur', handleBlur); // Ensure blur is updated
}}
editorProps={{
attributes: {
class: 'tiptap-prosemirror',
style: isMobile
? `overflow: auto; min-height: ${
customEditorHeight ? '200px' : '0px'
}; max-height:calc(100svh - ${customEditorHeight || '140px'})`
: `overflow: auto; max-height: 250px`,
style: `overflow: auto; max-height: 250px`,
},
handleKeyDown(view, event) {
if (

View File

@ -1,6 +1,5 @@
import Box from '@mui/material/Box';
import Drawer from '@mui/material/Drawer';
import { isMobile } from '../../App';
export const DrawerComponent = ({ open, setOpen, children }) => {
const toggleDrawer = (newOpen: boolean) => () => {
setOpen(newOpen);
@ -9,10 +8,7 @@ export const DrawerComponent = ({ open, setOpen, children }) => {
return (
<div>
<Drawer open={open} onClose={toggleDrawer(false)}>
<Box
sx={{ width: isMobile ? '100vw' : '400px', height: '100%' }}
role="presentation"
>
<Box sx={{ width: '400px', height: '100%' }} role="presentation">
{children}
</Box>
</Drawer>

View File

@ -1,23 +1,25 @@
import React, { useContext, useEffect, useMemo, useRef, useState } from 'react'
import ReactDOM from 'react-dom'
import { Box, IconButton, Slider } from '@mui/material'
import { CircularProgress, Typography } from '@mui/material'
import { Key } from 'ts-key-enum'
import React, { useContext, useEffect, useMemo, useRef, useState } from 'react';
import ReactDOM from 'react-dom';
import { Box, IconButton, Slider } from '@mui/material';
import { CircularProgress, Typography } from '@mui/material';
import { Key } from 'ts-key-enum';
import {
PlayArrow,
Pause,
VolumeUp,
Fullscreen,
PictureInPicture, VolumeOff, Calculate
} from '@mui/icons-material'
import { styled } from '@mui/system'
import { Refresh } from '@mui/icons-material'
PictureInPicture,
VolumeOff,
Calculate,
} from '@mui/icons-material';
import { styled } from '@mui/system';
import { Refresh } from '@mui/icons-material';
import { Menu, MenuItem } from '@mui/material'
import { MoreVert as MoreIcon } from '@mui/icons-material'
import { GlobalContext, getBaseApiReact } from '../../App'
import { resourceKeySelector } from '../../atoms/global'
import { useRecoilValue } from 'recoil'
import { Menu, MenuItem } from '@mui/material';
import { MoreVert as MoreIcon } from '@mui/icons-material';
import { GlobalContext, getBaseApiReact } from '../../App';
import { resourceKeySelector } from '../../atoms/global';
import { useRecoilValue } from 'recoil';
const VideoContainer = styled(Box)`
position: relative;
display: flex;
@ -28,14 +30,14 @@ const VideoContainer = styled(Box)`
height: 100%;
margin: 0px;
padding: 0px;
`
`;
const VideoElement = styled('video')`
width: 100%;
height: auto;
max-height: calc(100vh - 150px);
background: rgb(33, 33, 33);
`
`;
const ControlsContainer = styled(Box)`
position: absolute;
@ -47,18 +49,18 @@ const ControlsContainer = styled(Box)`
right: 0;
padding: 8px;
background-color: rgba(0, 0, 0, 0.6);
`
`;
interface VideoPlayerProps {
src?: string
poster?: string
name?: string
identifier?: string
service?: string
autoplay?: boolean
from?: string | null
customStyle?: any
user?: string
src?: string;
poster?: string;
name?: string;
identifier?: string;
service?: string;
autoplay?: boolean;
from?: string | null;
customStyle?: any;
user?: string;
}
export const VideoPlayer: React.FC<VideoPlayerProps> = ({
@ -69,33 +71,30 @@ export const VideoPlayer: React.FC<VideoPlayerProps> = ({
autoplay = true,
from = null,
customStyle = {},
node
node,
}) => {
const keyIdentifier = useMemo(()=> {
if(name && identifier && service){
return `${service}-${name}-${identifier}`
const keyIdentifier = useMemo(() => {
if (name && identifier && service) {
return `${service}-${name}-${identifier}`;
} else {
return undefined
return undefined;
}
}, [service, name, identifier])
}, [service, name, identifier]);
const download = useRecoilValue(resourceKeySelector(keyIdentifier));
const { downloadResource } = useContext(GlobalContext);
const videoRef = useRef<HTMLVideoElement | null>(null)
const [playing, setPlaying] = useState(false)
const [volume, setVolume] = useState(1)
const [mutedVolume, setMutedVolume] = useState(1)
const [isMuted, setIsMuted] = useState(false)
const [progress, setProgress] = useState(0)
const [isLoading, setIsLoading] = useState(false)
const [canPlay, setCanPlay] = useState(false)
const [startPlay, setStartPlay] = useState(false)
const [isMobileView, setIsMobileView] = useState(false)
const [playbackRate, setPlaybackRate] = useState(1)
const [anchorEl, setAnchorEl] = useState(null)
const reDownload = useRef<boolean>(false)
const videoRef = useRef<HTMLVideoElement | null>(null);
const [playing, setPlaying] = useState(false);
const [volume, setVolume] = useState(1);
const [mutedVolume, setMutedVolume] = useState(1);
const [isMuted, setIsMuted] = useState(false);
const [progress, setProgress] = useState(0);
const [isLoading, setIsLoading] = useState(false);
const [canPlay, setCanPlay] = useState(false);
const [startPlay, setStartPlay] = useState(false);
const [playbackRate, setPlaybackRate] = useState(1);
const [anchorEl, setAnchorEl] = useState(null);
const reDownload = useRef<boolean>(false);
const resetVideoState = () => {
// Reset all states to their initial values
@ -107,10 +106,9 @@ export const VideoPlayer: React.FC<VideoPlayerProps> = ({
setIsLoading(false);
setCanPlay(false);
setStartPlay(false);
setIsMobileView(false);
setPlaybackRate(1);
setAnchorEl(null);
// Reset refs to their initial values
if (videoRef.current) {
videoRef.current.pause(); // Ensure the video is paused
@ -120,18 +118,19 @@ export const VideoPlayer: React.FC<VideoPlayerProps> = ({
};
const src = useMemo(() => {
if(name && identifier && service){
return `${node || getBaseApiReact()}/arbitrary/${service}/${name}/${identifier}`
}
return ''
}, [service, name, identifier])
if (name && identifier && service) {
return `${node || getBaseApiReact()}/arbitrary/${service}/${name}/${identifier}`;
}
return '';
}, [service, name, identifier]);
useEffect(() => {
resetVideoState();
}, [keyIdentifier]);
useEffect(()=> {
resetVideoState()
}, [keyIdentifier])
const resourceStatus = useMemo(() => {
return download?.status || {}
}, [download])
return download?.status || {};
}, [download]);
const minSpeed = 0.25;
const maxSpeed = 4.0;
@ -139,306 +138,339 @@ export const VideoPlayer: React.FC<VideoPlayerProps> = ({
const updatePlaybackRate = (newSpeed: number) => {
if (videoRef.current) {
if (newSpeed > maxSpeed || newSpeed < minSpeed)
newSpeed = minSpeed
videoRef.current.playbackRate = newSpeed
setPlaybackRate(newSpeed)
if (newSpeed > maxSpeed || newSpeed < minSpeed) newSpeed = minSpeed;
videoRef.current.playbackRate = newSpeed;
setPlaybackRate(newSpeed);
}
}
};
const increaseSpeed = (wrapOverflow = true) => {
const changedSpeed = playbackRate + speedChange
let newSpeed = wrapOverflow ? changedSpeed : Math.min(changedSpeed, maxSpeed)
const changedSpeed = playbackRate + speedChange;
let newSpeed = wrapOverflow
? changedSpeed
: Math.min(changedSpeed, maxSpeed);
if (videoRef.current) {
updatePlaybackRate(newSpeed);
}
}
};
const decreaseSpeed = () => {
if (videoRef.current) {
updatePlaybackRate(playbackRate - speedChange);
}
}
};
const togglePlay = async () => {
if (!videoRef.current) return
setStartPlay(true)
if (!videoRef.current) return;
setStartPlay(true);
if (!src || resourceStatus?.status !== 'READY') {
ReactDOM.flushSync(() => {
setIsLoading(true)
})
getSrc()
setIsLoading(true);
});
getSrc();
}
if (playing) {
videoRef.current.pause()
videoRef.current.pause();
} else {
videoRef.current.play()
videoRef.current.play();
}
setPlaying(!playing)
}
setPlaying(!playing);
};
const onVolumeChange = (_: any, value: number | number[]) => {
if (!videoRef.current) return
videoRef.current.volume = value as number
setVolume(value as number)
setIsMuted(false)
}
if (!videoRef.current) return;
videoRef.current.volume = value as number;
setVolume(value as number);
setIsMuted(false);
};
const onProgressChange = (_: any, value: number | number[]) => {
if (!videoRef.current) return
videoRef.current.currentTime = value as number
setProgress(value as number)
if (!videoRef.current) return;
videoRef.current.currentTime = value as number;
setProgress(value as number);
if (!playing) {
videoRef.current.play()
setPlaying(true)
videoRef.current.play();
setPlaying(true);
}
}
};
const handleEnded = () => {
setPlaying(false)
}
setPlaying(false);
};
const updateProgress = () => {
if (!videoRef.current) return
setProgress(videoRef.current.currentTime)
}
if (!videoRef.current) return;
setProgress(videoRef.current.currentTime);
};
const [isFullscreen, setIsFullscreen] = useState(false)
const [isFullscreen, setIsFullscreen] = useState(false);
const enterFullscreen = () => {
if (!videoRef.current) return
if (!videoRef.current) return;
if (videoRef.current.requestFullscreen) {
videoRef.current.requestFullscreen()
videoRef.current.requestFullscreen();
}
}
};
const exitFullscreen = () => {
if (document.exitFullscreen) {
document.exitFullscreen()
document.exitFullscreen();
}
}
};
const toggleFullscreen = () => {
isFullscreen ? exitFullscreen() : enterFullscreen()
}
isFullscreen ? exitFullscreen() : enterFullscreen();
};
useEffect(() => {
const handleFullscreenChange = () => {
setIsFullscreen(!!document.fullscreenElement)
}
setIsFullscreen(!!document.fullscreenElement);
};
document.addEventListener('fullscreenchange', handleFullscreenChange)
document.addEventListener('fullscreenchange', handleFullscreenChange);
return () => {
document.removeEventListener('fullscreenchange', handleFullscreenChange)
}
}, [])
document.removeEventListener('fullscreenchange', handleFullscreenChange);
};
}, []);
const handleCanPlay = () => {
setIsLoading(false)
setCanPlay(true)
}
setIsLoading(false);
setCanPlay(true);
};
const getSrc = React.useCallback(async () => {
if (!name || !identifier || !service) return
if (!name || !identifier || !service) return;
try {
downloadResource({
name,
service,
identifier
})
} catch (error) {
console.error(error)
identifier,
});
} catch (error) {
console.error(error);
}
}, [identifier, name, service])
}, [identifier, name, service]);
function formatTime(seconds: number): string {
seconds = Math.floor(seconds)
let minutes: number | string = Math.floor(seconds / 60)
let hours: number | string = Math.floor(minutes / 60)
seconds = Math.floor(seconds);
let minutes: number | string = Math.floor(seconds / 60);
let hours: number | string = Math.floor(minutes / 60);
let remainingSeconds: number | string = seconds % 60
let remainingMinutes: number | string = minutes % 60
let remainingSeconds: number | string = seconds % 60;
let remainingMinutes: number | string = minutes % 60;
if (remainingSeconds < 10) {
remainingSeconds = '0' + remainingSeconds
remainingSeconds = '0' + remainingSeconds;
}
if (remainingMinutes < 10) {
remainingMinutes = '0' + remainingMinutes
remainingMinutes = '0' + remainingMinutes;
}
if (hours === 0) {
hours = ''
}
else {
hours = hours + ':'
hours = '';
} else {
hours = hours + ':';
}
return hours + remainingMinutes + ':' + remainingSeconds
return hours + remainingMinutes + ':' + remainingSeconds;
}
const reloadVideo = () => {
if (!videoRef.current) return
const currentTime = videoRef.current.currentTime
videoRef.current.src = src
videoRef.current.load()
videoRef.current.currentTime = currentTime
if (!videoRef.current) return;
const currentTime = videoRef.current.currentTime;
videoRef.current.src = src;
videoRef.current.load();
videoRef.current.currentTime = currentTime;
if (playing) {
videoRef.current.play()
videoRef.current.play();
}
}
};
useEffect(() => {
if (
resourceStatus?.status === 'DOWNLOADED' &&
reDownload?.current === false
) {
getSrc()
reDownload.current = true
getSrc();
reDownload.current = true;
}
}, [getSrc, resourceStatus])
}, [getSrc, resourceStatus]);
const handleMenuOpen = (event: any) => {
setAnchorEl(event.currentTarget)
}
setAnchorEl(event.currentTarget);
};
const handleMenuClose = () => {
setAnchorEl(null)
}
setAnchorEl(null);
};
useEffect(() => {
const videoWidth = videoRef?.current?.offsetWidth
if (videoWidth && videoWidth <= 600) {
setIsMobileView(true)
}
}, [canPlay])
const videoWidth = videoRef?.current?.offsetWidth;
}, [canPlay]);
const getDownloadProgress = (current: number, total: number) => {
const progress = current / total * 100;
return Number.isNaN(progress) ? '' : progress.toFixed(0) + '%'
}
const progress = (current / total) * 100;
return Number.isNaN(progress) ? '' : progress.toFixed(0) + '%';
};
const mute = () => {
setIsMuted(true)
setMutedVolume(volume)
setVolume(0)
if (videoRef.current) videoRef.current.volume = 0
}
setIsMuted(true);
setMutedVolume(volume);
setVolume(0);
if (videoRef.current) videoRef.current.volume = 0;
};
const unMute = () => {
setIsMuted(false)
setVolume(mutedVolume)
if (videoRef.current) videoRef.current.volume = mutedVolume
}
setIsMuted(false);
setVolume(mutedVolume);
if (videoRef.current) videoRef.current.volume = mutedVolume;
};
const toggleMute = () => {
isMuted ? unMute() : mute();
}
};
const changeVolume = (volumeChange: number) => {
if (videoRef.current) {
const minVolume = 0;
const maxVolume = 1;
let newVolume = volumeChange + volume;
let newVolume = volumeChange + volume
newVolume = Math.max(newVolume, minVolume);
newVolume = Math.min(newVolume, maxVolume);
newVolume = Math.max(newVolume, minVolume)
newVolume = Math.min(newVolume, maxVolume)
setIsMuted(false)
setMutedVolume(newVolume)
videoRef.current.volume = newVolume
setIsMuted(false);
setMutedVolume(newVolume);
videoRef.current.volume = newVolume;
setVolume(newVolume);
}
}
};
const setProgressRelative = (secondsChange: number) => {
if (videoRef.current) {
const currentTime = videoRef.current?.currentTime
const minTime = 0
const maxTime = videoRef.current?.duration || 100
const currentTime = videoRef.current?.currentTime;
const minTime = 0;
const maxTime = videoRef.current?.duration || 100;
let newTime = currentTime + secondsChange;
newTime = Math.max(newTime, minTime)
newTime = Math.min(newTime, maxTime)
newTime = Math.max(newTime, minTime);
newTime = Math.min(newTime, maxTime);
videoRef.current.currentTime = newTime;
setProgress(newTime);
}
}
};
const setProgressAbsolute = (videoPercent: number) => {
if (videoRef.current) {
videoPercent = Math.min(videoPercent, 100)
videoPercent = Math.max(videoPercent, 0)
const finalTime = videoRef.current?.duration * videoPercent / 100
videoRef.current.currentTime = finalTime
videoPercent = Math.min(videoPercent, 100);
videoPercent = Math.max(videoPercent, 0);
const finalTime = (videoRef.current?.duration * videoPercent) / 100;
videoRef.current.currentTime = finalTime;
setProgress(finalTime);
}
}
};
const keyboardShortcutsDown = (e: React.KeyboardEvent<HTMLDivElement>) => {
e.preventDefault()
e.preventDefault();
switch (e.key) {
case Key.Add: increaseSpeed(false); break;
case '+': increaseSpeed(false); break;
case '>': increaseSpeed(false); break;
case Key.Add:
increaseSpeed(false);
break;
case '+':
increaseSpeed(false);
break;
case '>':
increaseSpeed(false);
break;
case Key.Subtract: decreaseSpeed(); break;
case '-': decreaseSpeed(); break;
case '<': decreaseSpeed(); break;
case Key.Subtract:
decreaseSpeed();
break;
case '-':
decreaseSpeed();
break;
case '<':
decreaseSpeed();
break;
case Key.ArrowLeft: {
if (e.shiftKey) setProgressRelative(-300);
else if (e.ctrlKey) setProgressRelative(-60);
else if (e.altKey) setProgressRelative(-10);
else setProgressRelative(-5);
} break;
case Key.ArrowLeft:
{
if (e.shiftKey) setProgressRelative(-300);
else if (e.ctrlKey) setProgressRelative(-60);
else if (e.altKey) setProgressRelative(-10);
else setProgressRelative(-5);
}
break;
case Key.ArrowRight: {
if (e.shiftKey) setProgressRelative(300);
else if (e.ctrlKey) setProgressRelative(60);
else if (e.altKey) setProgressRelative(10);
else setProgressRelative(5);
} break;
case Key.ArrowRight:
{
if (e.shiftKey) setProgressRelative(300);
else if (e.ctrlKey) setProgressRelative(60);
else if (e.altKey) setProgressRelative(10);
else setProgressRelative(5);
}
break;
case Key.ArrowDown: changeVolume(-0.05); break;
case Key.ArrowUp: changeVolume(0.05); break;
case Key.ArrowDown:
changeVolume(-0.05);
break;
case Key.ArrowUp:
changeVolume(0.05);
break;
}
}
};
const keyboardShortcutsUp = (e: React.KeyboardEvent<HTMLDivElement>) => {
e.preventDefault()
e.preventDefault();
switch (e.key) {
case ' ': togglePlay(); break;
case 'm': toggleMute(); break;
case ' ':
togglePlay();
break;
case 'm':
toggleMute();
break;
case 'f': enterFullscreen(); break;
case Key.Escape: exitFullscreen(); break;
case 'f':
enterFullscreen();
break;
case Key.Escape:
exitFullscreen();
break;
case '0': setProgressAbsolute(0); break;
case '1': setProgressAbsolute(10); break;
case '2': setProgressAbsolute(20); break;
case '3': setProgressAbsolute(30); break;
case '4': setProgressAbsolute(40); break;
case '5': setProgressAbsolute(50); break;
case '6': setProgressAbsolute(60); break;
case '7': setProgressAbsolute(70); break;
case '8': setProgressAbsolute(80); break;
case '9': setProgressAbsolute(90); break;
case '0':
setProgressAbsolute(0);
break;
case '1':
setProgressAbsolute(10);
break;
case '2':
setProgressAbsolute(20);
break;
case '3':
setProgressAbsolute(30);
break;
case '4':
setProgressAbsolute(40);
break;
case '5':
setProgressAbsolute(50);
break;
case '6':
setProgressAbsolute(60);
break;
case '7':
setProgressAbsolute(70);
break;
case '8':
setProgressAbsolute(80);
break;
case '9':
setProgressAbsolute(90);
break;
}
}
};
return (
<VideoContainer
@ -451,7 +483,6 @@ export const VideoPlayer: React.FC<VideoPlayerProps> = ({
height: '100%',
}}
>
{isLoading && (
<Box
position="absolute"
@ -467,40 +498,44 @@ export const VideoPlayer: React.FC<VideoPlayerProps> = ({
sx={{
display: 'flex',
flexDirection: 'column',
gap: '10px'
gap: '10px',
}}
>
<CircularProgress color="secondary" />
<Typography
variant="subtitle2"
component="div"
sx={{
color: 'white',
fontSize: '15px',
textAlign: 'center'
}}
>
{resourceStatus?.status === 'REFETCHING' ? (
<>
<>
{getDownloadProgress(resourceStatus?.localChunkCount, resourceStatus?.totalChunkCount)}
</>
<> Refetching data in 25 seconds</>
</>
) : resourceStatus?.status === 'DOWNLOADED' ? (
<>Download Completed: building tutorial video...</>
) : resourceStatus?.status !== 'READY' ? (
<Typography
variant="subtitle2"
component="div"
sx={{
color: 'white',
fontSize: '15px',
textAlign: 'center',
}}
>
{resourceStatus?.status === 'REFETCHING' ? (
<>
<>
{getDownloadProgress(resourceStatus?.localChunkCount || 0, resourceStatus?.totalChunkCount || 100)}
{getDownloadProgress(
resourceStatus?.localChunkCount,
resourceStatus?.totalChunkCount
)}
</>
) : (
<>Fetching tutorial from the Qortal Network...</>
)}
</Typography>
<> Refetching data in 25 seconds</>
</>
) : resourceStatus?.status === 'DOWNLOADED' ? (
<>Download Completed: building tutorial video...</>
) : resourceStatus?.status !== 'READY' ? (
<>
{getDownloadProgress(
resourceStatus?.localChunkCount || 0,
resourceStatus?.totalChunkCount || 100
)}
</>
) : (
<>Fetching tutorial from the Qortal Network...</>
)}
</Typography>
</Box>
)}
{((!src && !isLoading) || !startPlay) && (
@ -516,59 +551,61 @@ export const VideoPlayer: React.FC<VideoPlayerProps> = ({
zIndex={500}
bgcolor="rgba(0, 0, 0, 0.6)"
onClick={() => {
togglePlay()
togglePlay();
}}
sx={{
cursor: 'pointer'
cursor: 'pointer',
}}
>
<PlayArrow
sx={{
width: '50px',
height: '50px',
color: 'white'
color: 'white',
}}
/>
</Box>
)}
<Box sx={{
display: 'flex',
flexGrow: 1,
width: '100%',
height: 'calc(100% - 60px)',
}}>
<VideoElement
id={identifier}
ref={videoRef}
src={!startPlay ? '' : resourceStatus?.status === 'READY' ? src : ''}
poster={!startPlay ? poster : ""}
onTimeUpdate={updateProgress}
autoPlay={autoplay}
onClick={togglePlay}
onEnded={handleEnded}
// onLoadedMetadata={handleLoadedMetadata}
onCanPlay={handleCanPlay}
preload="metadata"
style={{
<Box
sx={{
display: 'flex',
flexGrow: 1,
width: '100%',
height: '100%',
...customStyle
height: 'calc(100% - 60px)',
}}
/>
>
<VideoElement
id={identifier}
ref={videoRef}
src={!startPlay ? '' : resourceStatus?.status === 'READY' ? src : ''}
poster={!startPlay ? poster : ''}
onTimeUpdate={updateProgress}
autoPlay={autoplay}
onClick={togglePlay}
onEnded={handleEnded}
// onLoadedMetadata={handleLoadedMetadata}
onCanPlay={handleCanPlay}
preload="metadata"
style={{
width: '100%',
height: '100%',
...customStyle,
}}
/>
</Box>
<ControlsContainer
sx={{
position: 'relative',
background: 'var(--bg-primary)',
width: '100%',
flexShrink: 0
flexShrink: 0,
}}
>
{isMobileView && canPlay ? (
{canPlay ? (
<>
<IconButton
sx={{
color: 'rgba(255, 255, 255, 0.7)'
color: 'rgba(255, 255, 255, 0.7)',
}}
onClick={togglePlay}
>
@ -577,77 +614,7 @@ export const VideoPlayer: React.FC<VideoPlayerProps> = ({
<IconButton
sx={{
color: 'rgba(255, 255, 255, 0.7)',
marginLeft: '15px'
}}
onClick={reloadVideo}
>
<Refresh />
</IconButton>
<Slider
value={progress}
onChange={onProgressChange}
min={0}
max={videoRef.current?.duration || 100}
sx={{ flexGrow: 1, mx: 2 }}
/>
<IconButton
edge="end"
color="inherit"
aria-label="menu"
onClick={handleMenuOpen}
>
<MoreIcon />
</IconButton>
<Menu
id="simple-menu"
anchorEl={anchorEl}
keepMounted
open={Boolean(anchorEl)}
onClose={handleMenuClose}
PaperProps={{
style: {
width: '250px'
}
}}
>
<MenuItem>
<VolumeUp />
<Slider
value={volume}
onChange={onVolumeChange}
min={0}
max={1}
step={0.01} />
</MenuItem>
<MenuItem onClick={() => increaseSpeed()}>
<Typography
sx={{
color: 'rgba(255, 255, 255, 0.7)',
fontSize: '14px'
}}
>
Speed: {playbackRate}x
</Typography>
</MenuItem>
<MenuItem onClick={toggleFullscreen}>
<Fullscreen />
</MenuItem>
</Menu>
</>
) : canPlay ? (
<>
<IconButton
sx={{
color: 'rgba(255, 255, 255, 0.7)'
}}
onClick={togglePlay}
>
{playing ? <Pause /> : <PlayArrow />}
</IconButton>
<IconButton
sx={{
color: 'rgba(255, 255, 255, 0.7)',
marginLeft: '15px'
marginLeft: '15px',
}}
onClick={reloadVideo}
>
@ -669,7 +636,7 @@ export const VideoPlayer: React.FC<VideoPlayerProps> = ({
!videoRef.current?.duration || !progress
? 'hidden'
: 'visible',
flexShrink: 0
flexShrink: 0,
}}
>
{progress && videoRef.current?.duration && formatTime(progress)}/
@ -680,7 +647,7 @@ export const VideoPlayer: React.FC<VideoPlayerProps> = ({
<IconButton
sx={{
color: 'rgba(255, 255, 255, 0.7)',
marginRight: '10px'
marginRight: '10px',
}}
onClick={toggleMute}
>
@ -694,14 +661,14 @@ export const VideoPlayer: React.FC<VideoPlayerProps> = ({
step={0.01}
sx={{
maxWidth: '100px',
color: 'var(--Mail-Background)'
color: 'var(--Mail-Background)',
}}
/>
<IconButton
sx={{
color: 'rgba(255, 255, 255, 0.7)',
fontSize: '14px',
marginLeft: '5px'
marginLeft: '5px',
}}
onClick={(e) => increaseSpeed()}
>
@ -709,7 +676,7 @@ export const VideoPlayer: React.FC<VideoPlayerProps> = ({
</IconButton>
<IconButton
sx={{
color: 'rgba(255, 255, 255, 0.7)'
color: 'rgba(255, 255, 255, 0.7)',
}}
onClick={toggleFullscreen}
>
@ -719,5 +686,5 @@ export const VideoPlayer: React.FC<VideoPlayerProps> = ({
) : null}
</ControlsContainer>
</VideoContainer>
)
}
);
};

View File

@ -26,7 +26,7 @@ import { AddGroupList } from './AddGroupList';
import { UserListOfInvites } from './UserListOfInvites';
import { CustomizedSnackbars } from '../Snackbar/Snackbar';
import { getFee } from '../../background';
import { MyContext, isMobile } from '../../App';
import { MyContext } from '../../App';
import { subscribeToEvent, unsubscribeFromEvent } from '../../utils/events';
export const Label = styled('label')`
@ -231,7 +231,7 @@ export const AddGroup = ({ address, open, setOpen }) => {
value={value}
onChange={handleChange}
aria-label="basic tabs example"
variant={isMobile ? 'scrollable' : 'fullWidth'} // Scrollable on mobile, full width on desktop
variant={'fullWidth'}
scrollButtons="auto"
allowScrollButtonsMobile
sx={{
@ -247,7 +247,7 @@ export const AddGroup = ({ address, open, setOpen }) => {
'&.Mui-selected': {
color: theme.palette.text.primary,
},
fontSize: isMobile ? '0.75rem' : '1rem', // Adjust font size for mobile
fontSize: '1rem',
}}
/>
<Tab
@ -257,7 +257,7 @@ export const AddGroup = ({ address, open, setOpen }) => {
'&.Mui-selected': {
color: theme.palette.text.primary,
},
fontSize: isMobile ? '0.75rem' : '1rem', // Adjust font size for mobile
fontSize: '1rem',
}}
/>
<Tab
@ -267,7 +267,7 @@ export const AddGroup = ({ address, open, setOpen }) => {
'&.Mui-selected': {
color: theme.palette.text.primary,
},
fontSize: isMobile ? '0.75rem' : '1rem', // Adjust font size for mobile
fontSize: '1rem',
}}
/>
</Tabs>

View File

@ -61,11 +61,7 @@ import {
unsubscribeFromEvent,
} from '../../../utils/events';
import RefreshIcon from '@mui/icons-material/Refresh';
import {
getArbitraryEndpointReact,
getBaseApiReact,
isMobile,
} from '../../../App';
import { getArbitraryEndpointReact, getBaseApiReact } from '../../../App';
import { WrapperUserAction } from '../../WrapperUserAction';
import { addDataPublishesFunc, getDataPublishesFunc } from '../Group';
const filterOptions = ['Recently active', 'Newest', 'Oldest'];
@ -754,7 +750,6 @@ export const GroupMail = ({
<ThreadSingleTitle
sx={{
fontWeight: shouldAppearLighter && 300,
fontSize: isMobile && '18px',
}}
>
{thread?.threadData?.title}

View File

@ -1,18 +1,9 @@
import React, { useEffect, useRef, useState } from 'react';
import {
Box,
Button,
CircularProgress,
Input,
Typography,
} from '@mui/material';
import { Box, CircularProgress, Input } from '@mui/material';
import ShortUniqueId from 'short-unique-id';
import CloseIcon from '@mui/icons-material/Close';
import ModalCloseSVG from '../../../assets/svgs/ModalClose.svg';
import ComposeIconSVG from '../../../assets/svgs/ComposeIcon.svg';
import {
AttachmentContainer,
CloseContainer,
@ -22,7 +13,6 @@ import {
InstanceFooter,
InstanceListContainer,
InstanceListHeader,
NewMessageAttachmentImg,
NewMessageCloseImg,
NewMessageHeaderP,
NewMessageInputRow,
@ -32,16 +22,9 @@ import {
import { ReusableModal } from './ReusableModal';
import { Spacer } from '../../../common/Spacer';
import { formatBytes } from '../../../utils/Size';
import { CreateThreadIcon } from '../../../assets/Icons/CreateThreadIcon';
import { SendNewMessage } from '../../../assets/Icons/SendNewMessage';
import { TextEditor } from './TextEditor';
import {
MyContext,
isMobile,
pauseAllQueues,
resumeAllQueues,
} from '../../../App';
import { MyContext, pauseAllQueues, resumeAllQueues } from '../../../App';
import { getFee } from '../../../background';
import TipTap from '../../Chat/TipTap';
import { MessageDisplay } from '../../Chat/MessageDisplay';
@ -411,8 +394,8 @@ export const NewThread = ({
>
<ComposeContainer
sx={{
padding: isMobile ? '5px' : '15px',
justifyContent: isMobile ? 'flex-start' : 'revert',
padding: '15px',
justifyContent: 'revert',
}}
onClick={() => setIsOpen(true)}
>
@ -423,7 +406,7 @@ export const NewThread = ({
<ReusableModal
open={isOpen}
customStyles={{
maxHeight: isMobile ? '95svh' : '95vh',
maxHeight: '95vh',
maxWidth: '950px',
height: '700px',
borderRadius: '12px 12px 0px 0px',
@ -434,8 +417,8 @@ export const NewThread = ({
>
<InstanceListHeader
sx={{
height: isMobile ? 'auto' : '50px',
padding: isMobile ? '5px' : '20px 42px',
height: '50px',
padding: '20px 42px',
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
@ -457,7 +440,7 @@ export const NewThread = ({
<InstanceListContainer
sx={{
backgroundColor: '#434448',
padding: isMobile ? '5px' : '20px 42px',
padding: '20px 42px',
height: 'calc(100% - 165px)',
flexShrink: 0,
}}
@ -481,7 +464,7 @@ export const NewThread = ({
color: 'white',
'& .MuiInput-input::placeholder': {
color: 'rgba(255,255,255, 0.70) !important',
fontSize: isMobile ? '14px' : '20px',
fontSize: '20px',
fontStyle: 'normal',
fontWeight: 400,
lineHeight: '120%', // 24px
@ -509,7 +492,9 @@ export const NewThread = ({
<MessageDisplay htmlContent={postReply?.textContentV2} />
</Box>
)}
{!isMobile && <Spacer height="30px" />}
<Spacer height="30px" />
<Box
sx={{
maxHeight: '40vh',
@ -530,12 +515,13 @@ export const NewThread = ({
/> */}
</Box>
</InstanceListContainer>
<InstanceFooter
sx={{
backgroundColor: '#434448',
padding: isMobile ? '5px' : '20px 42px',
padding: '20px 42px',
alignItems: 'center',
height: isMobile ? 'auto' : '90px',
height: '90px',
}}
>
<NewMessageSendButton onClick={sendMail}>
@ -553,9 +539,11 @@ export const NewThread = ({
<CircularProgress sx={{}} size={'12px'} />
</Box>
)}
<NewMessageSendP>
{isMessage ? 'Post' : 'Create Thread'}
</NewMessageSendP>
{isMessage ? (
<SendNewMessage opacity={1} height="25px" width="25px" />
) : (
@ -564,6 +552,7 @@ export const NewThread = ({
</NewMessageSendButton>
</InstanceFooter>
</ReusableModal>
<CustomizedSnackbars
open={openSnack}
setOpen={setOpenSnack}

View File

@ -1,13 +1,12 @@
import React from 'react'
import { Box, Modal, useTheme } from '@mui/material'
import { isMobile } from '../../../App'
import React from 'react';
import { Box, Modal, useTheme } from '@mui/material';
interface MyModalProps {
open: boolean
onClose?: () => void
onSubmit?: (obj: any) => Promise<void>
children: any
customStyles?: any
open: boolean;
onClose?: () => void;
onSubmit?: (obj: any) => Promise<void>;
children: any;
customStyles?: any;
}
export const ReusableModal: React.FC<MyModalProps> = ({
@ -15,9 +14,10 @@ export const ReusableModal: React.FC<MyModalProps> = ({
onClose,
onSubmit,
children,
customStyles = {}
customStyles = {},
}) => {
const theme = useTheme()
const theme = useTheme();
return (
<Modal
open={open}
@ -32,27 +32,27 @@ export const ReusableModal: React.FC<MyModalProps> = ({
},
}}
disableAutoFocus
disableEnforceFocus
disableRestoreFocus
disableEnforceFocus
disableRestoreFocus
>
<Box
sx={{
position: 'absolute',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
width: isMobile ? '95%' : '75%',
bgcolor: theme.palette.primary.main,
boxShadow: 24,
p: 4,
display: 'flex',
flexDirection: 'column',
gap: 2,
...customStyles
left: '50%',
p: 4,
position: 'absolute',
top: '50%',
transform: 'translate(-50%, -50%)',
width: '75%',
...customStyles,
}}
>
{children}
</Box>
</Modal>
)
}
);
};

View File

@ -41,11 +41,7 @@ import {
import { LoadingSnackbar } from '../../Snackbar/LoadingSnackbar';
import { subscribeToEvent, unsubscribeFromEvent } from '../../../utils/events';
import RefreshIcon from '@mui/icons-material/Refresh';
import {
getArbitraryEndpointReact,
getBaseApiReact,
isMobile,
} from '../../../App';
import { getArbitraryEndpointReact, getBaseApiReact } from '../../../App';
import {
ArrowDownward as ArrowDownwardIcon,
ArrowUpward as ArrowUpwardIcon,
@ -602,23 +598,18 @@ export const Thread = ({
<Box
sx={{
display: 'flex',
gap: isMobile ? '45px' : '35px',
gap: '35px',
alignItems: 'center',
padding: isMobile && '5px',
}}
>
<ShowMessageReturnButton
sx={{
padding: isMobile && '5px',
minWidth: isMobile && '50px',
}}
onClick={() => {
setMessages([]);
closeThread();
}}
>
<MailIconImg src={ReturnSVG} />
{!isMobile && <ComposeP>Return to Threads</ComposeP>}
<ComposeP>Return to Threads</ComposeP>
</ShowMessageReturnButton>
{/* Conditionally render the scroll buttons */}
{showScrollButton &&
@ -628,7 +619,7 @@ export const Thread = ({
sx={{
color: 'white',
cursor: 'pointer',
fontSize: isMobile ? '28px' : '36px',
fontSize: '36px',
}}
/>
</ButtonBase>
@ -638,7 +629,7 @@ export const Thread = ({
sx={{
color: 'white',
cursor: 'pointer',
fontSize: isMobile ? '28px' : '36px',
fontSize: '36px',
}}
/>
</ButtonBase>
@ -655,38 +646,30 @@ export const Thread = ({
>
<div ref={threadBeginningRef} />
<ThreadContainer>
<Spacer height={isMobile ? '10px' : '30px'} />
<Spacer height={'30px'} />
<Box
sx={{
width: '100%',
alignItems: 'center',
display: 'flex',
justifyContent: 'space-between',
width: '100%',
}}
>
<GroupNameP
sx={{
fontSize: isMobile && '18px',
}}
>
{currentThread?.threadData?.title}
</GroupNameP>
<GroupNameP>{currentThread?.threadData?.title}</GroupNameP>
</Box>
<Spacer height={'15px'} />
<Box
sx={{
width: '100%',
alignItems: 'center',
display: 'flex',
justifyContent: 'center',
gap: '5px',
justifyContent: 'center',
width: '100%',
}}
>
<Button
sx={{
padding: isMobile && '5px',
fontSize: isMobile && '14px',
textTransformation: 'capitalize',
}}
onClick={() => {
@ -705,8 +688,6 @@ export const Thread = ({
</Button>
<Button
sx={{
padding: isMobile && '5px',
fontSize: isMobile && '14px',
textTransformation: 'capitalize',
}}
onClick={() => {
@ -725,8 +706,6 @@ export const Thread = ({
</Button>
<Button
sx={{
padding: isMobile && '5px',
fontSize: isMobile && '14px',
textTransformation: 'capitalize',
}}
onClick={() => {
@ -745,8 +724,6 @@ export const Thread = ({
</Button>
<Button
sx={{
padding: isMobile && '5px',
fontSize: isMobile && '14px',
textTransformation: 'capitalize',
}}
onClick={() => {
@ -764,7 +741,9 @@ export const Thread = ({
Last
</Button>
</Box>
<Spacer height={isMobile ? '10px' : '30px'} />
<Spacer height={'30px'} />
{combinedListTempAndReal.map((message, index, list) => {
let fullMessage = message;
@ -780,17 +759,17 @@ export const Thread = ({
>
<Box
style={{
width: '100%',
borderRadius: '8px',
flexDirection: 'column',
overflow: 'hidden',
position: 'relative',
flexDirection: 'column',
width: '100%',
}}
>
<Box
sx={{
display: 'flex',
alignItems: 'flex-start',
display: 'flex',
gap: '10px',
}}
>
@ -812,6 +791,7 @@ export const Thread = ({
{message?.name?.charAt(0)}
</Avatar>
</WrapperUserAction>
<ThreadInfoColumn>
<WrapperUserAction
disabled={userInfo?.name === message?.name}
@ -822,18 +802,20 @@ export const Thread = ({
{message?.name}
</ThreadInfoColumnNameP>
</WrapperUserAction>
<ThreadInfoColumnTime>
{formatTimestampForum(message?.created)}
</ThreadInfoColumnTime>
</ThreadInfoColumn>
</Box>
<Box
sx={{
width: '100%',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
width: '100%',
}}
>
<Typography
@ -876,17 +858,17 @@ export const Thread = ({
>
<Box
style={{
width: '100%',
borderRadius: '8px',
flexDirection: 'column',
overflow: 'hidden',
position: 'relative',
flexDirection: 'column',
width: '100%',
}}
>
<Box
sx={{
display: 'flex',
alignItems: 'flex-start',
display: 'flex',
gap: '10px',
}}
>
@ -908,6 +890,7 @@ export const Thread = ({
{message?.name?.charAt(0)}
</Avatar>
</WrapperUserAction>
<ThreadInfoColumn>
<WrapperUserAction
disabled={userInfo?.name === message?.name}
@ -918,18 +901,20 @@ export const Thread = ({
{message?.name}
</ThreadInfoColumnNameP>
</WrapperUserAction>
<ThreadInfoColumnTime>
{formatTimestampForum(message?.created)}
</ThreadInfoColumnTime>
</ThreadInfoColumn>
</Box>
<Box
sx={{
width: '100%',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
width: '100%',
}}
>
<CustomLoader />
@ -952,9 +937,9 @@ export const Thread = ({
<Spacer height="20px" />
<Box
sx={{
width: '100%',
display: 'flex',
justifyContent: 'flex-end',
width: '100%',
}}
>
<Button
@ -997,8 +982,6 @@ export const Thread = ({
>
<Button
sx={{
padding: isMobile && '5px',
fontSize: isMobile && '14px',
textTransformation: 'capitalize',
}}
onClick={() => {
@ -1017,8 +1000,6 @@ export const Thread = ({
</Button>
<Button
sx={{
padding: isMobile && '5px',
fontSize: isMobile && '14px',
textTransformation: 'capitalize',
}}
onClick={() => {
@ -1037,8 +1018,6 @@ export const Thread = ({
</Button>
<Button
sx={{
padding: isMobile && '5px',
fontSize: isMobile && '14px',
textTransformation: 'capitalize',
}}
onClick={() => {
@ -1057,8 +1036,6 @@ export const Thread = ({
</Button>
<Button
sx={{
padding: isMobile && '5px',
fontSize: isMobile && '14px',
textTransformation: 'capitalize',
}}
onClick={() => {

View File

@ -37,7 +37,6 @@ import {
clearAllQueues,
getArbitraryEndpointReact,
getBaseApiReact,
isMobile,
pauseAllQueues,
resumeAllQueues,
} from '../../App';
@ -1096,12 +1095,8 @@ export const Group = ({
return;
}
if (findDirect) {
if (!isMobile) {
setDesktopSideView('directs');
setDesktopViewMode('home');
} else {
setMobileViewModeKeepOpen('messaging');
}
setDesktopSideView('directs');
setDesktopViewMode('home');
setSelectedDirect(null);
setNewChat(false);
@ -1136,11 +1131,7 @@ export const Group = ({
);
if (findDirect) {
if (!isMobile) {
setDesktopSideView('directs');
} else {
setMobileViewModeKeepOpen('messaging');
}
setDesktopSideView('directs');
setSelectedDirect(null);
setNewChat(false);
@ -1162,11 +1153,7 @@ export const Group = ({
getTimestampEnterChat();
}, 200);
} else {
if (!isMobile) {
setDesktopSideView('directs');
} else {
setMobileViewModeKeepOpen('messaging');
}
setDesktopSideView('directs');
setNewChat(true);
setTimeout(() => {
executeEvent('setDirectToValueNewChat', {
@ -1284,9 +1271,7 @@ export const Group = ({
setupGroupWebsocketInterval.current = null;
settimeoutForRefetchSecretKey.current = null;
initiatedGetMembers.current = false;
if (!isMobile) {
setDesktopViewMode('home');
}
setDesktopViewMode('home');
};
const logoutEventFunc = () => {
@ -1303,34 +1288,7 @@ export const Group = ({
}, []);
const openAppsMode = () => {
if (isMobile) {
setMobileViewMode('apps');
}
if (!isMobile) {
setDesktopViewMode('apps');
}
if (isMobile) {
setIsOpenSideViewDirects(false);
setIsOpenSideViewGroups(false);
setGroupSection('default');
setSelectedGroup(null);
setNewChat(false);
setSelectedDirect(null);
setSecretKey(null);
setGroupOwner(null);
lastFetchedSecretKey.current = null;
initiatedGetMembers.current = false;
setSecretKeyPublishDate(null);
setAdmins([]);
setSecretKeyDetails(null);
setAdminsWithNames([]);
setMembers([]);
setMemberCountFromSecretKeyData(null);
setTriedToFetchSecretKey(false);
setFirstSecretKeyInCreation(false);
setIsOpenSideViewDirects(false);
setIsOpenSideViewGroups(false);
}
setDesktopViewMode('apps');
};
useEffect(() => {
@ -1372,9 +1330,7 @@ export const Group = ({
setTriedToFetchSecretKey(false);
setFirstSecretKeyInCreation(false);
setGroupSection('chat');
if (!isMobile) {
setDesktopViewMode('chat');
}
setDesktopViewMode('chat');
window
.sendMessage('addTimestampEnterChat', {
@ -1430,9 +1386,7 @@ export const Group = ({
setTriedToFetchSecretKey(false);
setFirstSecretKeyInCreation(false);
setGroupSection('announcement');
if (!isMobile) {
setDesktopViewMode('chat');
}
setDesktopViewMode('chat');
window
.sendMessage('addGroupNotificationTimestamp', {
timestamp: Date.now(),
@ -1496,9 +1450,7 @@ export const Group = ({
setFirstSecretKeyInCreation(false);
setGroupSection('forum');
setDefaultThread(data);
if (!isMobile) {
setDesktopViewMode('chat');
}
setDesktopViewMode('chat');
setTimeout(() => {
setSelectedGroup(findGroup);
setMobileViewMode('group');
@ -1521,12 +1473,6 @@ export const Group = ({
};
const goToHome = async () => {
if (isMobile) {
setMobileViewMode('home');
}
if (!isMobile) {
// TODO: empty block. Check it!
}
setDesktopViewMode('home');
await new Promise((res) => {
@ -1614,26 +1560,38 @@ export const Group = ({
borderRadius: '0px 15px 15px 0px',
display: 'flex',
flexDirection: 'column',
height: isMobile ? `calc(${rootHeight} - 45px)` : '100%',
width: isMobile ? '100%' : '380px',
height: '100%',
width: '380px',
}}
>
{!isMobile && (
<Box
sx={{
alignItems: 'center',
display: 'flex',
gap: '10px',
justifyContent: 'center',
width: '100%',
<Box
sx={{
alignItems: 'center',
display: 'flex',
gap: '10px',
justifyContent: 'center',
width: '100%',
}}
>
<ButtonBase
onClick={() => {
setDesktopSideView('groups');
}}
>
<ButtonBase
onClick={() => {
setDesktopSideView('groups');
}}
<IconWrapper
color={
groupChatHasUnread || groupsAnnHasUnread
? 'var(--unread)'
: desktopSideView === 'groups'
? theme.palette.text.primary
: theme.palette.text.secondary
}
label="Groups"
selected={desktopSideView === 'groups'}
customWidth="75px"
>
<IconWrapper
<HubsIcon
height={24}
color={
groupChatHasUnread || groupsAnnHasUnread
? 'var(--unread)'
@ -1641,29 +1599,28 @@ export const Group = ({
? theme.palette.text.primary
: theme.palette.text.secondary
}
label="Groups"
selected={desktopSideView === 'groups'}
customWidth="75px"
>
<HubsIcon
height={24}
color={
groupChatHasUnread || groupsAnnHasUnread
? 'var(--unread)'
: desktopSideView === 'groups'
? theme.palette.text.primary
: theme.palette.text.secondary
}
/>
</IconWrapper>
</ButtonBase>
<ButtonBase
onClick={() => {
setDesktopSideView('directs');
}}
/>
</IconWrapper>
</ButtonBase>
<ButtonBase
onClick={() => {
setDesktopSideView('directs');
}}
>
<IconWrapper
customWidth="75px"
color={
directChatHasUnread
? 'var(--unread)'
: desktopSideView === 'directs'
? theme.palette.text.primary
: theme.palette.text.secondary
}
label="Messaging"
selected={desktopSideView === 'directs'}
>
<IconWrapper
customWidth="75px"
<MessagingIcon
height={24}
color={
directChatHasUnread
? 'var(--unread)'
@ -1671,23 +1628,10 @@ export const Group = ({
? theme.palette.text.primary
: theme.palette.text.secondary
}
label="Messaging"
selected={desktopSideView === 'directs'}
>
<MessagingIcon
height={24}
color={
directChatHasUnread
? 'var(--unread)'
: desktopSideView === 'directs'
? theme.palette.text.primary
: theme.palette.text.secondary
}
/>
</IconWrapper>
</ButtonBase>
</Box>
)}
/>
</IconWrapper>
</ButtonBase>
</Box>
<div
style={{
@ -1843,30 +1787,42 @@ export const Group = ({
<div
style={{
display: 'flex',
width: isMobile ? '100%' : '380px',
width: '380px',
flexDirection: 'column',
alignItems: 'flex-start',
height: isMobile ? `calc(${rootHeight} - 45px)` : '100%',
background: !isMobile && theme.palette.background.default,
borderRadius: !isMobile && '0px 15px 15px 0px',
height: '100%',
background: theme.palette.background.default,
borderRadius: '0px 15px 15px 0px',
}}
>
{!isMobile && (
<Box
sx={{
width: '100%',
alignItems: 'center',
justifyContent: 'center',
display: 'flex',
gap: '10px',
<Box
sx={{
width: '100%',
alignItems: 'center',
justifyContent: 'center',
display: 'flex',
gap: '10px',
}}
>
<ButtonBase
onClick={() => {
setDesktopSideView('groups');
}}
>
<ButtonBase
onClick={() => {
setDesktopSideView('groups');
}}
<IconWrapper
color={
groupChatHasUnread || groupsAnnHasUnread
? 'var(--unread)'
: desktopSideView === 'groups'
? theme.palette.text.primary
: theme.palette.text.secondary
}
label="Groups"
selected={desktopSideView === 'groups'}
customWidth="75px"
>
<IconWrapper
<HubsIcon
height={24}
color={
groupChatHasUnread || groupsAnnHasUnread
? 'var(--unread)'
@ -1874,29 +1830,28 @@ export const Group = ({
? theme.palette.text.primary
: theme.palette.text.secondary
}
label="Groups"
selected={desktopSideView === 'groups'}
customWidth="75px"
>
<HubsIcon
height={24}
color={
groupChatHasUnread || groupsAnnHasUnread
? 'var(--unread)'
: desktopSideView === 'groups'
? theme.palette.text.primary
: theme.palette.text.secondary
}
/>
</IconWrapper>
</ButtonBase>
<ButtonBase
onClick={() => {
setDesktopSideView('directs');
}}
/>
</IconWrapper>
</ButtonBase>
<ButtonBase
onClick={() => {
setDesktopSideView('directs');
}}
>
<IconWrapper
customWidth="75px"
color={
directChatHasUnread
? 'var(--unread)'
: desktopSideView === 'directs'
? theme.palette.text.primary
: theme.palette.text.secondary
}
label="Messaging"
selected={desktopSideView === 'directs'}
>
<IconWrapper
customWidth="75px"
<MessagingIcon
height={24}
color={
directChatHasUnread
? 'var(--unread)'
@ -1904,23 +1859,10 @@ export const Group = ({
? theme.palette.text.primary
: theme.palette.text.secondary
}
label="Messaging"
selected={desktopSideView === 'directs'}
>
<MessagingIcon
height={24}
color={
directChatHasUnread
? 'var(--unread)'
: desktopSideView === 'directs'
? theme.palette.text.primary
: theme.palette.text.secondary
}
/>
</IconWrapper>
</ButtonBase>
</Box>
)}
/>
</IconWrapper>
</ButtonBase>
</Box>
<div
style={{
@ -2181,38 +2123,35 @@ export const Group = ({
alignItems: 'flex-start',
display: 'flex',
flexDirection: 'row',
height: isMobile ? '100%' : '100%',
height: '100%',
width: '100%',
}}
>
{!isMobile &&
((desktopViewMode !== 'apps' && desktopViewMode !== 'dev') ||
isOpenSideViewGroups) && (
<DesktopSideBar
desktopViewMode={desktopViewMode}
toggleSideViewGroups={toggleSideViewGroups}
toggleSideViewDirects={toggleSideViewDirects}
goToHome={goToHome}
mode={appsMode}
setMode={setAppsMode}
setDesktopSideView={setDesktopSideView}
hasUnreadDirects={directChatHasUnread}
isApps={desktopViewMode === 'apps'}
myName={userInfo?.name}
isGroups={isOpenSideViewGroups}
isDirects={isOpenSideViewDirects}
hasUnreadGroups={groupChatHasUnread || groupsAnnHasUnread}
setDesktopViewMode={setDesktopViewMode}
/>
)}
{((desktopViewMode !== 'apps' && desktopViewMode !== 'dev') ||
isOpenSideViewGroups) && (
<DesktopSideBar
desktopViewMode={desktopViewMode}
toggleSideViewGroups={toggleSideViewGroups}
toggleSideViewDirects={toggleSideViewDirects}
goToHome={goToHome}
mode={appsMode}
setMode={setAppsMode}
setDesktopSideView={setDesktopSideView}
hasUnreadDirects={directChatHasUnread}
isApps={desktopViewMode === 'apps'}
myName={userInfo?.name}
isGroups={isOpenSideViewGroups}
isDirects={isOpenSideViewDirects}
hasUnreadGroups={groupChatHasUnread || groupsAnnHasUnread}
setDesktopViewMode={setDesktopViewMode}
/>
)}
{!isMobile &&
desktopViewMode === 'chat' &&
{desktopViewMode === 'chat' &&
desktopSideView !== 'directs' &&
renderGroups()}
{!isMobile &&
desktopViewMode === 'chat' &&
{desktopViewMode === 'chat' &&
desktopSideView === 'directs' &&
renderDirects()}
@ -2231,66 +2170,10 @@ export const Group = ({
{newChat && (
<>
{isMobile && (
<Box
sx={{
alignItems: 'center',
display: 'flex',
height: '15px',
justifyContent: 'center',
marginTop: '14px',
width: '100%',
}}
>
<Box
sx={{
alignItems: 'center',
display: 'flex',
justifyContent: 'space-between',
width: '320px',
}}
>
<Box
sx={{
alignItems: 'center',
display: 'flex',
width: '50px',
}}
>
<ButtonBase
onClick={() => {
close();
}}
>
<ReturnIcon />
</ButtonBase>
</Box>
<Box
sx={{
alignItems: 'center',
display: 'flex',
justifyContent: 'flex-end',
width: '50px',
}}
>
<ButtonBase
onClick={() => {
setSelectedDirect(null);
setMobileViewModeKeepOpen('');
}}
>
<ExitIcon />
</ButtonBase>
</Box>
</Box>
</Box>
)}
<Box
sx={{
background: theme.palette.background.default,
bottom: !(desktopViewMode === 'chat') ? 'unset' : '0px',
height: isMobile && `calc(${rootHeight} - 45px)`,
left: !(desktopViewMode === 'chat') ? '-100000px' : '0px',
opacity: !(desktopViewMode === 'chat') ? 0 : 1,
position: 'absolute',
@ -2352,7 +2235,7 @@ export const Group = ({
: '0px',
}}
>
{!isMobile && (
{
<DesktopHeader
isPrivate={isPrivate}
selectedGroup={selectedGroup}
@ -2387,15 +2270,14 @@ export const Group = ({
setGroupSection={setGroupSection}
isForum={groupSection === 'forum'}
/>
)}
}
<Box
sx={{
position: 'relative',
flexGrow: 1,
display: 'flex',
// reference to change height
height: isMobile ? 'calc(100% - 82px)' : 'calc(100vh - 70px)',
flexGrow: 1,
height: 'calc(100vh - 70px)',
position: 'relative',
}}
>
{triedToFetchSecretKey && (
@ -2449,15 +2331,13 @@ export const Group = ({
(!secretKeyPublishDate && !firstSecretKeyInCreation) ? (
<div
style={{
display: 'flex',
width: '100%',
height: isMobile
? `calc(${rootHeight} - 113px)`
: 'calc(100vh - 70px)',
flexDirection: 'column',
alignItems: 'flex-start',
padding: '20px',
display: 'flex',
flexDirection: 'column',
height: 'calc(100vh - 70px)',
overflow: 'auto',
padding: '20px',
width: '100%',
}}
>
{' '}
@ -2605,7 +2485,6 @@ export const Group = ({
sx={{
background: theme.palette.background.default,
bottom: !(desktopViewMode === 'chat') ? 'unset' : '0px',
height: isMobile && `calc(${rootHeight} - 45px)`,
left: !(desktopViewMode === 'chat') ? '-100000px' : '0px',
opacity: !(desktopViewMode === 'chat') ? 0 : 1,
position: 'absolute',
@ -2642,7 +2521,7 @@ export const Group = ({
</>
)}
{!isMobile && (
{
<AppsDesktop
toggleSideViewGroups={toggleSideViewGroups}
toggleSideViewDirects={toggleSideViewDirects}
@ -2660,8 +2539,8 @@ export const Group = ({
isApps={desktopViewMode === 'apps'}
desktopViewMode={desktopViewMode}
/>
)}
{!isMobile && (
}
{
<AppsDevMode
toggleSideViewGroups={toggleSideViewGroups}
toggleSideViewDirects={toggleSideViewDirects}
@ -2679,9 +2558,9 @@ export const Group = ({
desktopViewMode={desktopViewMode}
isApps={desktopViewMode === 'apps'}
/>
)}
}
{!isMobile && (
{
<HomeDesktop
name={userInfo?.name}
refreshHomeDataFunc={refreshHomeDataFunc}
@ -2699,7 +2578,7 @@ export const Group = ({
setDesktopViewMode={setDesktopViewMode}
desktopViewMode={desktopViewMode}
/>
)}
}
</Box>
<AuthenticatedContainerInnerRight
@ -2708,7 +2587,6 @@ export const Group = ({
width: '31px',
padding: '5px',
display:
isMobile ||
desktopViewMode === 'apps' ||
desktopViewMode === 'dev' ||
desktopViewMode === 'chat'

View File

@ -9,7 +9,7 @@ import { executeEvent } from '../../utils/events';
import { Box, ButtonBase, Collapse, Typography, useTheme } from '@mui/material';
import { getGroupNames } from './UserListOfInvites';
import { CustomLoader } from '../../common/CustomLoader';
import { getBaseApiReact, isMobile } from '../../App';
import { getBaseApiReact } from '../../App';
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
import ExpandLessIcon from '@mui/icons-material/ExpandLess';
@ -97,7 +97,7 @@ export const GroupInvites = ({ myAddress, setOpenAddGroup }) => {
borderRadius: '19px',
display: 'flex',
flexDirection: 'column',
height: isMobile ? '165px' : '250px',
height: '250px',
padding: '20px',
width: '322px',
}}

View File

@ -1,244 +1,277 @@
import * as React from "react";
import List from "@mui/material/List";
import ListItem from "@mui/material/ListItem";
import ListItemButton from "@mui/material/ListItemButton";
import ListItemIcon from "@mui/material/ListItemIcon";
import ListItemText from "@mui/material/ListItemText";
import Checkbox from "@mui/material/Checkbox";
import IconButton from "@mui/material/IconButton";
import CommentIcon from "@mui/icons-material/Comment";
import InfoIcon from "@mui/icons-material/Info";
import { RequestQueueWithPromise } from "../../utils/queue/queue";
import * as React from 'react';
import List from '@mui/material/List';
import ListItem from '@mui/material/ListItem';
import ListItemButton from '@mui/material/ListItemButton';
import ListItemText from '@mui/material/ListItemText';
import IconButton from '@mui/material/IconButton';
import { RequestQueueWithPromise } from '../../utils/queue/queue';
import GroupAddIcon from '@mui/icons-material/GroupAdd';
import { executeEvent } from "../../utils/events";
import { Box, ButtonBase, Collapse, Typography } from "@mui/material";
import { Spacer } from "../../common/Spacer";
import { CustomLoader } from "../../common/CustomLoader";
import { getBaseApi } from "../../background";
import { MyContext, getBaseApiReact, isMobile } from "../../App";
import { myGroupsWhereIAmAdminAtom } from "../../atoms/global";
import { useSetRecoilState } from "recoil";
import { executeEvent } from '../../utils/events';
import { Box, ButtonBase, Collapse, Typography } from '@mui/material';
import { CustomLoader } from '../../common/CustomLoader';
import { MyContext, getBaseApiReact } from '../../App';
import { myGroupsWhereIAmAdminAtom } from '../../atoms/global';
import { useSetRecoilState } from 'recoil';
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
import ExpandLessIcon from '@mui/icons-material/ExpandLess';
export const requestQueueGroupJoinRequests = new RequestQueueWithPromise(2)
export const requestQueueGroupJoinRequests = new RequestQueueWithPromise(2);
export const GroupJoinRequests = ({ myAddress, groups, setOpenManageMembers, getTimestampEnterChat, setSelectedGroup, setGroupSection, setMobileViewMode, setDesktopViewMode }) => {
const [isExpanded, setIsExpanded] = React.useState(false)
const [groupsWithJoinRequests, setGroupsWithJoinRequests] = React.useState([])
const [loading, setLoading] = React.useState(true)
const {txList, setTxList} = React.useContext(MyContext)
const setMyGroupsWhereIAmAdmin = useSetRecoilState(
myGroupsWhereIAmAdminAtom
export const GroupJoinRequests = ({
myAddress,
groups,
setOpenManageMembers,
getTimestampEnterChat,
setSelectedGroup,
setGroupSection,
setMobileViewMode,
setDesktopViewMode,
}) => {
const [isExpanded, setIsExpanded] = React.useState(false);
const [groupsWithJoinRequests, setGroupsWithJoinRequests] = React.useState(
[]
);
const [loading, setLoading] = React.useState(true);
const { txList, setTxList } = React.useContext(MyContext);
const setMyGroupsWhereIAmAdmin = useSetRecoilState(myGroupsWhereIAmAdminAtom);
const getJoinRequests = async ()=> {
const getJoinRequests = async () => {
try {
setLoading(true)
let groupsAsAdmin = []
const getAllGroupsAsAdmin = groups.filter((item)=> item.groupId !== '0').map(async (group)=> {
const isAdminResponse = await requestQueueGroupJoinRequests.enqueue(()=> {
return fetch(
`${getBaseApiReact()}/groups/members/${group.groupId}?limit=0&onlyAdmins=true`
setLoading(true);
let groupsAsAdmin = [];
const getAllGroupsAsAdmin = groups
.filter((item) => item.groupId !== '0')
.map(async (group) => {
const isAdminResponse = await requestQueueGroupJoinRequests.enqueue(
() => {
return fetch(
`${getBaseApiReact()}/groups/members/${group.groupId}?limit=0&onlyAdmins=true`
);
}
);
})
const isAdminData = await isAdminResponse.json()
const isAdminData = await isAdminResponse.json();
const findMyself = isAdminData?.members?.find((member)=> member.member === myAddress)
if(findMyself){
groupsAsAdmin.push(group)
}
return true
})
const findMyself = isAdminData?.members?.find(
(member) => member.member === myAddress
);
await Promise.all(getAllGroupsAsAdmin)
setMyGroupsWhereIAmAdmin(groupsAsAdmin)
const res = await Promise.all(groupsAsAdmin.map(async (group)=> {
if (findMyself) {
groupsAsAdmin.push(group);
}
return true;
});
const joinRequestResponse = await requestQueueGroupJoinRequests.enqueue(()=> {
return fetch(
`${getBaseApiReact()}/groups/joinrequests/${group.groupId}`
);
})
await Promise.all(getAllGroupsAsAdmin);
setMyGroupsWhereIAmAdmin(groupsAsAdmin);
const res = await Promise.all(
groupsAsAdmin.map(async (group) => {
const joinRequestResponse =
await requestQueueGroupJoinRequests.enqueue(() => {
return fetch(
`${getBaseApiReact()}/groups/joinrequests/${group.groupId}`
);
});
const joinRequestData = await joinRequestResponse.json()
return {
group,
data: joinRequestData
}
}))
setGroupsWithJoinRequests(res)
const joinRequestData = await joinRequestResponse.json();
return {
group,
data: joinRequestData,
};
})
);
setGroupsWithJoinRequests(res);
} catch (error) {
} finally {
setLoading(false)
setLoading(false);
}
}
};
React.useEffect(() => {
if (myAddress && groups.length > 0) {
getJoinRequests()
getJoinRequests();
} else {
setLoading(false)
setLoading(false);
}
}, [myAddress, groups]);
const filteredJoinRequests = React.useMemo(()=> {
return groupsWithJoinRequests.map((group)=> {
const filteredGroupRequests = group?.data?.filter((gd)=> {
const findJoinRequsetInTxList = txList?.find((tx)=> tx?.groupId === group?.group?.groupId && tx?.qortalAddress === gd?.joiner && tx?.type === 'join-request-accept')
const filteredJoinRequests = React.useMemo(() => {
return groupsWithJoinRequests.map((group) => {
const filteredGroupRequests = group?.data?.filter((gd) => {
const findJoinRequsetInTxList = txList?.find(
(tx) =>
tx?.groupId === group?.group?.groupId &&
tx?.qortalAddress === gd?.joiner &&
tx?.type === 'join-request-accept'
);
if(findJoinRequsetInTxList) return false
return true
})
if (findJoinRequsetInTxList) return false;
return true;
});
return {
...group,
data: filteredGroupRequests
}
})
}, [groupsWithJoinRequests, txList])
data: filteredGroupRequests,
};
});
}, [groupsWithJoinRequests, txList]);
return (
<Box sx={{
width: "100%",
display: "flex",
flexDirection: "column",
alignItems: 'center'
}}>
<Box
sx={{
width: '100%',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
}}
>
<ButtonBase
sx={{
width: "322px",
display: "flex",
flexDirection: "row",
width: '322px',
display: 'flex',
flexDirection: 'row',
padding: '0px 20px',
gap: '10px',
justifyContent: 'flex-start'
justifyContent: 'flex-start',
}}
onClick={()=> setIsExpanded((prev)=> !prev)}
onClick={() => setIsExpanded((prev) => !prev)}
>
<Typography
sx={{
fontSize: "1rem",
fontSize: '1rem',
}}
>
Join Requests {filteredJoinRequests?.filter((group)=> group?.data?.length > 0)?.length > 0 && ` (${filteredJoinRequests?.filter((group)=> group?.data?.length > 0)?.length})`}
Join Requests{' '}
{filteredJoinRequests?.filter((group) => group?.data?.length > 0)
?.length > 0 &&
` (${filteredJoinRequests?.filter((group) => group?.data?.length > 0)?.length})`}
</Typography>
{isExpanded ? <ExpandLessIcon sx={{
marginLeft: 'auto'
}} /> : (
<ExpandMoreIcon sx={{
marginLeft: 'auto'
}}/>
)}
{isExpanded ? (
<ExpandLessIcon
sx={{
marginLeft: 'auto',
}}
/>
) : (
<ExpandMoreIcon
sx={{
marginLeft: 'auto',
}}
/>
)}
</ButtonBase>
<Collapse in={isExpanded} timeout="auto" unmountOnExit>
<Box
sx={{
width: "322px",
height: isMobile ? "165px" : "250px",
display: "flex",
flexDirection: "column",
bgcolor: "background.paper",
padding: "20px",
borderRadius: '19px'
}}
>
{loading && filteredJoinRequests.length === 0 && (
<Box sx={{
width: '100%',
display: 'flex',
justifyContent: 'center'
}}>
<CustomLoader />
</Box>
)}
{!loading && (filteredJoinRequests.length === 0 || filteredJoinRequests?.filter((group)=> group?.data?.length > 0).length === 0) && (
<Box
sx={{
width: "100%",
display: "flex",
justifyContent: "center",
alignItems: 'center',
height: '100%',
}}
>
<Typography
sx={{
fontSize: "11px",
fontWeight: 400,
color: 'rgba(255, 255, 255, 0.2)'
}}
>
Nothing to display
</Typography>
</Box>
)}
<List className="scrollable-container" sx={{ width: "100%", maxWidth: 360, bgcolor: "background.paper", maxHeight: '300px', overflow: 'auto' }}>
{filteredJoinRequests?.map((group)=> {
if(group?.data?.length === 0) return null
return (
<ListItem
key={group?.groupId}
onClick={()=> {
setSelectedGroup(group?.group)
setMobileViewMode('group')
getTimestampEnterChat()
setGroupSection("announcement")
setOpenManageMembers(true)
if(!isMobile){
setDesktopViewMode('chat')
}
setTimeout(() => {
executeEvent("openGroupJoinRequest", {});
}, 300);
}}
<Collapse in={isExpanded} timeout="auto" unmountOnExit>
<Box
sx={{
marginBottom: '20px'
}}
disablePadding
secondaryAction={
<IconButton edge="end" aria-label="comments">
<GroupAddIcon
sx={{
color: "white",
fontSize: '18px'
}}
/>
</IconButton>
}
>
<ListItemButton sx={{
padding: "0px",
}} disableRipple role={undefined} dense>
<ListItemText sx={{
"& .MuiTypography-root": {
fontSize: "13px",
fontWeight: 400,
},
}} primary={`${group?.group?.groupName} has ${group?.data?.length} pending join requests.`} />
</ListItemButton>
</ListItem>
)
width: '322px',
height: '250px',
})}
</List>
</Box>
</Collapse>
display: 'flex',
flexDirection: 'column',
bgcolor: 'background.paper',
padding: '20px',
borderRadius: '19px',
}}
>
{loading && filteredJoinRequests.length === 0 && (
<Box
sx={{
width: '100%',
display: 'flex',
justifyContent: 'center',
}}
>
<CustomLoader />
</Box>
)}
{!loading &&
(filteredJoinRequests.length === 0 ||
filteredJoinRequests?.filter((group) => group?.data?.length > 0)
.length === 0) && (
<Box
sx={{
width: '100%',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
height: '100%',
}}
>
<Typography
sx={{
fontSize: '11px',
fontWeight: 400,
color: 'rgba(255, 255, 255, 0.2)',
}}
>
Nothing to display
</Typography>
</Box>
)}
<List
className="scrollable-container"
sx={{
width: '100%',
maxWidth: 360,
bgcolor: 'background.paper',
maxHeight: '300px',
overflow: 'auto',
}}
>
{filteredJoinRequests?.map((group) => {
if (group?.data?.length === 0) return null;
return (
<ListItem
key={group?.groupId}
onClick={() => {
setSelectedGroup(group?.group);
setMobileViewMode('group');
getTimestampEnterChat();
setGroupSection('announcement');
setOpenManageMembers(true);
setDesktopViewMode('chat');
setTimeout(() => {
executeEvent('openGroupJoinRequest', {});
}, 300);
}}
sx={{
marginBottom: '20px',
}}
disablePadding
secondaryAction={
<IconButton edge="end" aria-label="comments">
<GroupAddIcon
sx={{
color: 'white',
fontSize: '18px',
}}
/>
</IconButton>
}
>
<ListItemButton
sx={{
padding: '0px',
}}
disableRipple
role={undefined}
dense
>
<ListItemText
sx={{
'& .MuiTypography-root': {
fontSize: '13px',
fontWeight: 400,
},
}}
primary={`${group?.group?.groupName} has ${group?.data?.length} pending join requests.`}
/>
</ListItemButton>
</ListItem>
);
})}
</List>
</Box>
</Collapse>
</Box>
);
};

View File

@ -16,19 +16,12 @@ import {
DialogContent,
DialogContentText,
DialogTitle,
ListItem,
ListItemAvatar,
ListItemButton,
ListItemText,
MenuItem,
Popover,
Select,
TextField,
Typography,
} from '@mui/material';
import { getNameInfo } from './Group';
import { getBaseApi, getFee } from '../../background';
import { LoadingButton } from '@mui/lab';
import LockIcon from '@mui/icons-material/Lock';
import NoEncryptionGmailerrorredIcon from '@mui/icons-material/NoEncryptionGmailerrorred';
@ -36,7 +29,6 @@ import {
MyContext,
getArbitraryEndpointReact,
getBaseApiReact,
isMobile,
} from '../../App';
import { Spacer } from '../../common/Spacer';
import { CustomLoader } from '../../common/CustomLoader';
@ -51,7 +43,6 @@ import { Label } from './AddGroup';
import ShortUniqueId from 'short-unique-id';
import { CustomizedSnackbars } from '../Snackbar/Snackbar';
import { getGroupNames } from './UserListOfInvites';
import { WrapperUserAction } from '../WrapperUserAction';
import { useVirtualizer } from '@tanstack/react-virtual';
import ErrorBoundary from '../../common/ErrorBoundary';
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
@ -390,6 +381,7 @@ export const ListOfGroupPromotions = () => {
/>
)}
</ButtonBase>
<Box
style={{
width: '330px',
@ -401,7 +393,7 @@ export const ListOfGroupPromotions = () => {
<>
<Box
sx={{
width: isMobile ? '320px' : '750px',
width: '750px',
maxWidth: '90%',
display: 'flex',
flexDirection: 'column',
@ -422,6 +414,7 @@ export const ListOfGroupPromotions = () => {
fontWeight: 600,
}}
></Typography>
<Button
variant="contained"
onClick={() => setIsShowModal(true)}
@ -432,18 +425,19 @@ export const ListOfGroupPromotions = () => {
Add Promotion
</Button>
</Box>
<Spacer height="10px" />
</Box>
<Box
sx={{
width: isMobile ? '320px' : '750px',
maxWidth: '90%',
maxHeight: '700px',
bgcolor: 'background.paper',
borderRadius: '19px',
display: 'flex',
flexDirection: 'column',
bgcolor: 'background.paper',
maxHeight: '700px',
maxWidth: '90%',
padding: '20px 0px',
borderRadius: '19px',
width: '750px',
}}
>
{loading && promotions.length === 0 && (
@ -589,6 +583,7 @@ export const ListOfGroupPromotions = () => {
>
Group name: {` ${promotion?.groupName}`}
</Typography>
<Typography
sx={{
fontSize: '13px',
@ -598,6 +593,7 @@ export const ListOfGroupPromotions = () => {
Number of members:{' '}
{` ${promotion?.memberCount}`}
</Typography>
{promotion?.description && (
<Typography
sx={{
@ -608,6 +604,7 @@ export const ListOfGroupPromotions = () => {
{promotion?.description}
</Typography>
)}
{promotion?.isOpen === false && (
<Typography
sx={{
@ -620,7 +617,9 @@ export const ListOfGroupPromotions = () => {
your request
</Typography>
)}
<Spacer height="5px" />
<Box
sx={{
display: 'flex',
@ -638,6 +637,7 @@ export const ListOfGroupPromotions = () => {
>
Close
</LoadingButton>
<LoadingButton
loading={isLoadingJoinGroup}
loadingPosition="start"
@ -682,6 +682,7 @@ export const ListOfGroupPromotions = () => {
>
{promotion?.name?.charAt(0)}
</Avatar>
<Typography
sx={{
fontWight: 600,
@ -692,6 +693,7 @@ export const ListOfGroupPromotions = () => {
{promotion?.name}
</Typography>
</Box>
<Typography
sx={{
fontWight: 600,
@ -702,7 +704,9 @@ export const ListOfGroupPromotions = () => {
{promotion?.groupName}
</Typography>
</Box>
<Spacer height="20px" />
<Box
sx={{
display: 'flex',
@ -735,7 +739,9 @@ export const ListOfGroupPromotions = () => {
: 'Private group'}
</Typography>
</Box>
<Spacer height="20px" />
<Typography
sx={{
fontWight: 600,
@ -745,7 +751,9 @@ export const ListOfGroupPromotions = () => {
>
{promotion?.data}
</Typography>
<Spacer height="20px" />
<Box
sx={{
display: 'flex',
@ -767,6 +775,7 @@ export const ListOfGroupPromotions = () => {
</Button>
</Box>
</Box>
<Spacer height="50px" />
</ErrorBoundary>
</div>
@ -779,6 +788,7 @@ export const ListOfGroupPromotions = () => {
</Box>
</>
</Collapse>
<Spacer height="20px" />
{isShowModal && (

View File

@ -1,21 +1,14 @@
import * as React from "react";
import List from "@mui/material/List";
import ListItem from "@mui/material/ListItem";
import ListItemButton from "@mui/material/ListItemButton";
import ListItemIcon from "@mui/material/ListItemIcon";
import ListItemText from "@mui/material/ListItemText";
import Checkbox from "@mui/material/Checkbox";
import IconButton from "@mui/material/IconButton";
import CommentIcon from "@mui/icons-material/Comment";
import InfoIcon from "@mui/icons-material/Info";
import GroupAddIcon from "@mui/icons-material/GroupAdd";
import { executeEvent } from "../../utils/events";
import { Box, Typography } from "@mui/material";
import { Spacer } from "../../common/Spacer";
import { getGroupNames } from "./UserListOfInvites";
import { CustomLoader } from "../../common/CustomLoader";
import VisibilityIcon from "@mui/icons-material/Visibility";
import { isMobile } from "../../App";
import * as React from 'react';
import List from '@mui/material/List';
import ListItem from '@mui/material/ListItem';
import ListItemButton from '@mui/material/ListItemButton';
import ListItemText from '@mui/material/ListItemText';
import IconButton from '@mui/material/IconButton';
import { executeEvent } from '../../utils/events';
import { Box, Typography } from '@mui/material';
import { Spacer } from '../../common/Spacer';
import { CustomLoader } from '../../common/CustomLoader';
import VisibilityIcon from '@mui/icons-material/Visibility';
export const ListOfThreadPostsWatched = () => {
const [posts, setPosts] = React.useState([]);
@ -24,33 +17,33 @@ export const ListOfThreadPostsWatched = () => {
const getPosts = async () => {
try {
await new Promise((res, rej) => {
window.sendMessage("getThreadActivity", {})
.then((response) => {
if (!response?.error) {
if (!response) {
res(null);
window
.sendMessage('getThreadActivity', {})
.then((response) => {
if (!response?.error) {
if (!response) {
res(null);
return;
}
const uniquePosts = response.reduce((acc, current) => {
const x = acc.find(
(item) => item?.thread?.threadId === current?.thread?.threadId
);
if (!x) {
return acc.concat([current]);
} else {
return acc;
}
}, []);
setPosts(uniquePosts);
res(uniquePosts);
return;
}
const uniquePosts = response.reduce((acc, current) => {
const x = acc.find(
(item) => item?.thread?.threadId === current?.thread?.threadId
);
if (!x) {
return acc.concat([current]);
} else {
return acc;
}
}, []);
setPosts(uniquePosts);
res(uniquePosts);
return;
}
rej(response.error);
})
.catch((error) => {
rej(error.message || "An error occurred");
});
rej(response.error);
})
.catch((error) => {
rej(error.message || 'An error occurred');
});
});
} catch (error) {
} finally {
@ -63,49 +56,50 @@ export const ListOfThreadPostsWatched = () => {
}, []);
return (
<Box sx={{
width: "100%",
display: "flex",
flexDirection: "column",
alignItems: 'center'
}}>
<Box
sx={{
width: '100%',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
}}
>
<Box
sx={{
width: "322px",
display: "flex",
flexDirection: "column",
width: '322px',
display: 'flex',
flexDirection: 'column',
padding: '0px 20px',
}}
>
<Typography
sx={{
fontSize: "13px",
fontSize: '13px',
fontWeight: 600,
}}
>
New Thread Posts:
</Typography>
<Spacer height="10px" />
<Spacer height="10px" />
</Box>
<Box
sx={{
width: "322px",
height: isMobile ? "165px" : "250px",
display: "flex",
flexDirection: "column",
bgcolor: "background.paper",
padding: "20px",
borderRadius: '19px'
bgcolor: 'background.paper',
borderRadius: '19px',
display: 'flex',
flexDirection: 'column',
height: '250px',
padding: '20px',
width: '322px',
}}
>
{loading && posts.length === 0 && (
<Box
sx={{
width: "100%",
display: "flex",
justifyContent: "center",
width: '100%',
display: 'flex',
justifyContent: 'center',
}}
>
<CustomLoader />
@ -114,19 +108,18 @@ export const ListOfThreadPostsWatched = () => {
{!loading && posts.length === 0 && (
<Box
sx={{
width: "100%",
display: "flex",
justifyContent: "center",
width: '100%',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
height: '100%',
}}
>
<Typography
sx={{
fontSize: "11px",
fontSize: '11px',
fontWeight: 400,
color: 'rgba(255, 255, 255, 0.2)'
color: 'rgba(255, 255, 255, 0.2)',
}}
>
Nothing to display
@ -134,47 +127,46 @@ export const ListOfThreadPostsWatched = () => {
</Box>
)}
{posts?.length > 0 && (
<List
className="scrollable-container"
sx={{
width: "100%",
maxWidth: 360,
bgcolor: "background.paper",
maxHeight: "300px",
overflow: "auto",
}}
>
{posts?.map((post) => {
return (
<ListItem
key={post?.thread?.threadId}
onClick={() => {
executeEvent("openThreadNewPost", {
data: post,
});
}}
disablePadding
secondaryAction={
<IconButton edge="end" aria-label="comments">
<VisibilityIcon
sx={{
color: "red",
}}
/>
</IconButton>
}
>
<ListItemButton disableRipple role={undefined} dense>
<ListItemText
primary={`New post in ${post?.thread?.threadData?.title}`}
/>
</ListItemButton>
</ListItem>
);
})}
</List>
<List
className="scrollable-container"
sx={{
width: '100%',
maxWidth: 360,
bgcolor: 'background.paper',
maxHeight: '300px',
overflow: 'auto',
}}
>
{posts?.map((post) => {
return (
<ListItem
key={post?.thread?.threadId}
onClick={() => {
executeEvent('openThreadNewPost', {
data: post,
});
}}
disablePadding
secondaryAction={
<IconButton edge="end" aria-label="comments">
<VisibilityIcon
sx={{
color: 'red',
}}
/>
</IconButton>
}
>
<ListItemButton disableRipple role={undefined} dense>
<ListItemText
primary={`New post in ${post?.thread?.threadData?.title}`}
/>
</ListItemButton>
</ListItem>
);
})}
</List>
)}
</Box>
</Box>
);

View File

@ -1,10 +1,6 @@
import * as React from 'react';
import Button from '@mui/material/Button';
import Dialog from '@mui/material/Dialog';
import ListItemText from '@mui/material/ListItemText';
import ListItemButton from '@mui/material/ListItemButton';
import List from '@mui/material/List';
import Divider from '@mui/material/Divider';
import AppBar from '@mui/material/AppBar';
import Toolbar from '@mui/material/Toolbar';
import IconButton from '@mui/material/IconButton';
@ -19,7 +15,7 @@ import { ListOfBans } from './ListOfBans';
import { ListOfJoinRequests } from './ListOfJoinRequests';
import { Box, ButtonBase, Card, Tab, Tabs } from '@mui/material';
import { CustomizedSnackbars } from '../Snackbar/Snackbar';
import { MyContext, getBaseApiReact, isMobile } from '../../App';
import { MyContext, getBaseApiReact } from '../../App';
import { getGroupMembers, getNames } from './Group';
import { LoadingSnackbar } from '../Snackbar/LoadingSnackbar';
import { getFee } from '../../background';
@ -27,6 +23,7 @@ import { LoadingButton } from '@mui/lab';
import { subscribeToEvent, unsubscribeFromEvent } from '../../utils/events';
import { Spacer } from '../../common/Spacer';
import InsertLinkIcon from '@mui/icons-material/InsertLink';
function a11yProps(index: number) {
return {
id: `simple-tab-${index}`,
@ -193,9 +190,9 @@ export const ManageMembers = ({
<Box
sx={{
bgcolor: '#27282c',
color: 'white',
flexGrow: 1,
overflowY: 'auto',
color: 'white',
}}
>
<Box sx={{ borderBottom: 1, borderColor: 'divider' }}>
@ -221,9 +218,10 @@ export const ManageMembers = ({
'&.Mui-selected': {
color: 'white',
},
fontSize: isMobile ? '0.75rem' : '1rem', // Adjust font size for mobile
fontSize: '1rem',
}}
/>
<Tab
label="Invite new member"
{...a11yProps(1)}
@ -231,9 +229,10 @@ export const ManageMembers = ({
'&.Mui-selected': {
color: 'white',
},
fontSize: isMobile ? '0.75rem' : '1rem',
fontSize: '1rem',
}}
/>
<Tab
label="List of invites"
{...a11yProps(2)}
@ -241,9 +240,10 @@ export const ManageMembers = ({
'&.Mui-selected': {
color: 'white',
},
fontSize: isMobile ? '0.75rem' : '1rem',
fontSize: '1rem',
}}
/>
<Tab
label="List of bans"
{...a11yProps(3)}
@ -251,9 +251,10 @@ export const ManageMembers = ({
'&.Mui-selected': {
color: 'white',
},
fontSize: isMobile ? '0.75rem' : '1rem',
fontSize: '1rem',
}}
/>
<Tab
label="Join requests"
{...a11yProps(4)}
@ -261,11 +262,12 @@ export const ManageMembers = ({
'&.Mui-selected': {
color: 'white',
},
fontSize: isMobile ? '0.75rem' : '1rem',
fontSize: '1rem',
}}
/>
</Tabs>
</Box>
<Card
sx={{
padding: '10px',
@ -274,10 +276,13 @@ export const ManageMembers = ({
>
<Box>
<Typography>GroupId: {groupInfo?.groupId}</Typography>
<Typography>GroupName: {groupInfo?.groupName}</Typography>
<Typography>
Number of members: {groupInfo?.memberCount}
</Typography>
<ButtonBase
sx={{
gap: '10px',
@ -290,7 +295,9 @@ export const ManageMembers = ({
<InsertLinkIcon /> <Typography>Join Group Link</Typography>
</ButtonBase>
</Box>
<Spacer height="20px" />
{selectedGroup?.groupId && !isOwner && (
<LoadingButton
size="small"
@ -317,7 +324,9 @@ export const ManageMembers = ({
>
Load members with names
</Button>
<Spacer height="10px" />
<ListOfMembers
members={membersWithNames || []}
groupId={selectedGroup?.groupId}
@ -397,12 +406,14 @@ export const ManageMembers = ({
</Box>
)}
</Box>
<CustomizedSnackbars
open={openSnack}
setOpen={setOpenSnack}
info={infoSnack}
setInfo={setInfoSnack}
/>
<LoadingSnackbar
open={isLoadingMembers}
info={{

View File

@ -6,7 +6,7 @@ import ListItemIcon from '@mui/material/ListItemIcon';
import ListItemText from '@mui/material/ListItemText';
import moment from 'moment';
import { Box, ButtonBase, Collapse, Typography, useTheme } from '@mui/material';
import { getBaseApiReact, isMobile } from '../../App';
import { getBaseApiReact } from '../../App';
import MailIcon from '@mui/icons-material/Mail';
import MailOutlineIcon from '@mui/icons-material/MailOutline';
import { executeEvent } from '../../utils/events';
@ -180,7 +180,7 @@ export const QMailMessages = ({ userName, userAddress }) => {
borderRadius: '19px',
display: 'flex',
flexDirection: 'column',
height: isMobile ? '165px' : '250px',
height: '250px',
overflow: 'auto',
padding: '20px',
width: '322px',

View File

@ -1,28 +1,23 @@
import * as React from "react";
import List from "@mui/material/List";
import ListItem from "@mui/material/ListItem";
import ListItemButton from "@mui/material/ListItemButton";
import ListItemIcon from "@mui/material/ListItemIcon";
import ListItemText from "@mui/material/ListItemText";
import Checkbox from "@mui/material/Checkbox";
import IconButton from "@mui/material/IconButton";
import CommentIcon from "@mui/icons-material/Comment";
import InfoIcon from "@mui/icons-material/Info";
import { Box, Typography } from "@mui/material";
import { Spacer } from "../../common/Spacer";
import { isMobile } from "../../App";
import { QMailMessages } from "./QMailMessages";
import { executeEvent } from "../../utils/events";
import * as React from 'react';
import List from '@mui/material/List';
import ListItem from '@mui/material/ListItem';
import ListItemButton from '@mui/material/ListItemButton';
import ListItemIcon from '@mui/material/ListItemIcon';
import ListItemText from '@mui/material/ListItemText';
import { Box, Typography } from '@mui/material';
import { Spacer } from '../../common/Spacer';
import { QMailMessages } from './QMailMessages';
import { executeEvent } from '../../utils/events';
export const ThingsToDoInitial = ({ myAddress, name, hasGroups, balance, userInfo }) => {
export const ThingsToDoInitial = ({
myAddress,
name,
hasGroups,
balance,
userInfo,
}) => {
const [checked1, setChecked1] = React.useState(false);
const [checked2, setChecked2] = React.useState(false);
// const [checked3, setChecked3] = React.useState(false);
// React.useEffect(() => {
// if (hasGroups) setChecked3(true);
// }, [hasGroups]);
React.useEffect(() => {
if (balance && +balance >= 6) {
@ -30,111 +25,114 @@ export const ThingsToDoInitial = ({ myAddress, name, hasGroups, balance, userInf
}
}, [balance]);
React.useEffect(() => {
if (name) setChecked2(true);
}, [name]);
const isLoaded = React.useMemo(() => {
if (userInfo !== null) return true;
return false;
}, [userInfo]);
const isLoaded = React.useMemo(()=> {
if(userInfo !== null) return true
return false
}, [ userInfo])
const hasDoneNameAndBalanceAndIsLoaded = React.useMemo(() => {
if (isLoaded && checked1 && checked2) return true;
return false;
}, [checked1, isLoaded, checked2]);
const hasDoneNameAndBalanceAndIsLoaded = React.useMemo(()=> {
if(isLoaded && checked1 && checked2) return true
return false
}, [checked1, isLoaded, checked2])
if(hasDoneNameAndBalanceAndIsLoaded){
return (
<QMailMessages userAddress={userInfo?.address} userName={userInfo?.name} />
);
}
if(!isLoaded) return null
if (hasDoneNameAndBalanceAndIsLoaded) {
return (
<QMailMessages
userAddress={userInfo?.address}
userName={userInfo?.name}
/>
);
}
if (!isLoaded) return null;
return (
<Box
sx={{
width: "100%",
display: "flex",
flexDirection: "column",
alignItems: "center",
width: '100%',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
}}
>
<Box
sx={{
width: "322px",
display: "flex",
flexDirection: "column",
padding: "0px 20px",
width: '322px',
display: 'flex',
flexDirection: 'column',
padding: '0px 20px',
}}
>
<Typography
sx={{
fontSize: "1rem",
fontSize: '1rem',
fontWeight: 600,
}}
>
{!isLoaded ? 'Loading...' : 'Getting Started' }
{!isLoaded ? 'Loading...' : 'Getting Started'}
</Typography>
<Spacer height="10px" />
</Box>
<Box
sx={{
width: "322px",
display: "flex",
flexDirection: "column",
bgcolor: "background.paper",
padding: "20px",
borderRadius: "19px",
width: '322px',
display: 'flex',
flexDirection: 'column',
bgcolor: 'background.paper',
padding: '20px',
borderRadius: '19px',
}}
>
{isLoaded && (
<List sx={{ width: "100%", maxWidth: 360 }}>
<ListItem
disablePadding
sx={{
marginBottom: '20px'
}}
>
<ListItemButton
sx={{
padding: "0px",
}}
disableRipple
role={undefined}
dense
onClick={()=> {
executeEvent("openBuyQortInfo", {})
}}
>
<ListItemText
sx={{
"& .MuiTypography-root": {
fontSize: "1rem",
fontWeight: 400,
},
}}
primary={`Have at least 6 QORT in your wallet`}
/>
<ListItemIcon
sx={{
justifyContent: "flex-end",
}}
>
<Box
sx={{
height: "18px",
width: "18px",
borderRadius: "50%",
backgroundColor: checked1 ? "rgba(9, 182, 232, 1)" : "transparent",
outline: "1px solid rgba(9, 182, 232, 1)",
}}
/>
{/* <Checkbox
<List sx={{ width: '100%', maxWidth: 360 }}>
<ListItem
disablePadding
sx={{
marginBottom: '20px',
}}
>
<ListItemButton
sx={{
padding: '0px',
}}
disableRipple
role={undefined}
dense
onClick={() => {
executeEvent('openBuyQortInfo', {});
}}
>
<ListItemText
sx={{
'& .MuiTypography-root': {
fontSize: '1rem',
fontWeight: 400,
},
}}
primary={`Have at least 6 QORT in your wallet`}
/>
<ListItemIcon
sx={{
justifyContent: 'flex-end',
}}
>
<Box
sx={{
height: '18px',
width: '18px',
borderRadius: '50%',
backgroundColor: checked1
? 'rgba(9, 182, 232, 1)'
: 'transparent',
outline: '1px solid rgba(9, 182, 232, 1)',
}}
/>
{/* <Checkbox
edge="start"
checked={checked1}
tabIndex={-1}
@ -149,52 +147,64 @@ if(!isLoaded) return null
},
}}
/> */}
</ListItemIcon>
</ListItemButton>
</ListItem>
<ListItem
sx={{
marginBottom: '20px'
}}
// secondaryAction={
// <IconButton edge="end" aria-label="comments">
// <InfoIcon
// sx={{
// color: "white",
// }}
// />
// </IconButton>
// }
disablePadding
>
<ListItemButton sx={{
padding: "0px",
}} disableRipple role={undefined} dense>
<ListItemText onClick={() => {
executeEvent('openRegisterName', {})
}} sx={{
"& .MuiTypography-root": {
fontSize: "1rem",
fontWeight: 400,
},
}} primary={`Register a name`} />
<ListItemIcon sx={{
justifyContent: "flex-end",
}}>
<Box
sx={{
height: "18px",
width: "18px",
borderRadius: "50%",
backgroundColor: checked2 ? "rgba(9, 182, 232, 1)" : "transparent",
outline: "1px solid rgba(9, 182, 232, 1)",
}}
/>
</ListItemIcon>
</ListItemButton>
</ListItem>
{/* <ListItem
</ListItemIcon>
</ListItemButton>
</ListItem>
<ListItem
sx={{
marginBottom: '20px',
}}
// secondaryAction={
// <IconButton edge="end" aria-label="comments">
// <InfoIcon
// sx={{
// color: "white",
// }}
// />
// </IconButton>
// }
disablePadding
>
<ListItemButton
sx={{
padding: '0px',
}}
disableRipple
role={undefined}
dense
>
<ListItemText
onClick={() => {
executeEvent('openRegisterName', {});
}}
sx={{
'& .MuiTypography-root': {
fontSize: '1rem',
fontWeight: 400,
},
}}
primary={`Register a name`}
/>
<ListItemIcon
sx={{
justifyContent: 'flex-end',
}}
>
<Box
sx={{
height: '18px',
width: '18px',
borderRadius: '50%',
backgroundColor: checked2
? 'rgba(9, 182, 232, 1)'
: 'transparent',
outline: '1px solid rgba(9, 182, 232, 1)',
}}
/>
</ListItemIcon>
</ListItemButton>
</ListItem>
{/* <ListItem
disablePadding
>
<ListItemButton sx={{
@ -222,9 +232,8 @@ if(!isLoaded) return null
</ListItemIcon>
</ListItemButton>
</ListItem> */}
</List>
</List>
)}
</Box>
</Box>
);

View File

@ -1,9 +1,8 @@
import React, { useState, useRef, useEffect } from 'react';
import { useState, useRef, useEffect } from 'react';
import ReactDOM from 'react-dom';
import Picker, { EmojiStyle, Theme } from 'emoji-picker-react';
import './ReactionPicker.css';
import { ButtonBase } from '@mui/material';
import { isMobile } from '../App';
export const ReactionPicker = ({ onReaction }) => {
const [showPicker, setShowPicker] = useState(false);
@ -30,7 +29,7 @@ export const ReactionPicker = ({ onReaction }) => {
} else {
// Get the button's position
const buttonRect = buttonRef.current.getBoundingClientRect();
const pickerWidth = isMobile ? 300 : 350; // Adjust based on picker width
const pickerWidth = 350;
// Calculate position to align the right edge of the picker with the button's right edge
setPickerPosition({
@ -90,15 +89,15 @@ export const ReactionPicker = ({ onReaction }) => {
}}
>
<Picker
height={isMobile ? 350 : 450}
width={isMobile ? 300 : 350}
reactionsDefaultOpen={true}
onReactionClick={handleReaction}
onEmojiClick={handlePicker}
allowExpandReactions={true}
autoFocusSearch={false}
theme={Theme.DARK}
emojiStyle={EmojiStyle.NATIVE}
height="450"
onEmojiClick={handlePicker}
onReactionClick={handleReaction}
reactionsDefaultOpen={true}
theme={Theme.DARK}
width="350"
/>
</div>,
document.body

View File

@ -12,7 +12,7 @@ import PendingIcon from '@mui/icons-material/Pending';
import TaskAltIcon from '@mui/icons-material/TaskAlt';
import ExpandLess from '@mui/icons-material/ExpandLess';
import ExpandMore from '@mui/icons-material/ExpandMore';
import { MyContext, getBaseApiReact, isMobile } from '../../App';
import { MyContext, getBaseApiReact } from '../../App';
import { executeEvent } from '../../utils/events';
export const TaskManager = ({ getUserInfo }) => {
@ -141,8 +141,7 @@ export const TaskManager = ({ getUserInfo }) => {
});
}, [txList]);
if (isMobile || txList?.length === 0 || txList.every((item) => item?.done))
return null;
if (txList?.length === 0 || txList.every((item) => item?.done)) return null;
return (
<>

View File

@ -1,67 +1,85 @@
import { useCallback, useEffect } from 'react';
import { isMobile } from './App';
export const useAppFullScreen = (setFullScreen) => {
const enterFullScreen = useCallback(() => {
const element = document.documentElement; // Target the entire HTML document
if (element.requestFullscreen) {
element.requestFullscreen();
} else if (element.mozRequestFullScreen) { // Firefox
element.mozRequestFullScreen();
} else if (element.webkitRequestFullscreen) { // Chrome, Safari and Opera
element.webkitRequestFullscreen();
} else if (element.msRequestFullscreen) { // IE/Edge
element.msRequestFullscreen();
}
}, []);
const enterFullScreen = useCallback(() => {
const element = document.documentElement; // Target the entire HTML document
if (element.requestFullscreen) {
element.requestFullscreen();
} else if (element.mozRequestFullScreen) {
// Firefox
element.mozRequestFullScreen();
} else if (element.webkitRequestFullscreen) {
// Chrome, Safari and Opera
element.webkitRequestFullscreen();
} else if (element.msRequestFullscreen) {
// IE/Edge
element.msRequestFullscreen();
}
}, []);
const exitFullScreen = useCallback(() => {
if (document.fullscreenElement) {
document.exitFullscreen();
} else if (document.mozFullScreenElement) {
document.mozCancelFullScreen();
} else if (document.webkitFullscreenElement) {
document.webkitExitFullscreen();
} else if (document.msFullscreenElement) {
document.msExitFullscreen();
}
}, []);
const exitFullScreen = useCallback(() => {
if (document.fullscreenElement) {
document.exitFullscreen();
} else if (document.mozFullScreenElement) {
document.mozCancelFullScreen();
} else if (document.webkitFullscreenElement) {
document.webkitExitFullscreen();
} else if (document.msFullscreenElement) {
document.msExitFullscreen();
}
}, []);
const toggleFullScreen = useCallback(() => {
if(!isMobile || isMobile) return
if (document.fullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement || document.msFullscreenElement) {
exitFullScreen();
setFullScreen(false)
} else {
enterFullScreen();
setFullScreen(true)
}
}, [enterFullScreen, exitFullScreen]);
const toggleFullScreen = useCallback(() => {
if (
document.fullscreenElement ||
document.mozFullScreenElement ||
document.webkitFullscreenElement ||
document.msFullscreenElement
) {
exitFullScreen();
setFullScreen(false);
} else {
enterFullScreen();
setFullScreen(true);
}
}, [enterFullScreen, exitFullScreen]);
// Listen for changes to fullscreen state
useEffect(() => {
const handleFullScreenChange = () => {
if (document.fullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement || document.msFullscreenElement) {
} else {
setFullScreen(false);
}
};
// Listen for changes to fullscreen state
useEffect(() => {
const handleFullScreenChange = () => {
if (
document.fullscreenElement ||
document.mozFullScreenElement ||
document.webkitFullscreenElement ||
document.msFullscreenElement
) {
// TODO check empty block
} else {
setFullScreen(false);
}
};
document.addEventListener('fullscreenchange', handleFullScreenChange);
document.addEventListener('webkitfullscreenchange', handleFullScreenChange); // Safari
document.addEventListener('mozfullscreenchange', handleFullScreenChange); // Firefox
document.addEventListener('MSFullscreenChange', handleFullScreenChange); // IE/Edge
document.addEventListener('fullscreenchange', handleFullScreenChange);
document.addEventListener('webkitfullscreenchange', handleFullScreenChange); // Safari
document.addEventListener('mozfullscreenchange', handleFullScreenChange); // Firefox
document.addEventListener('MSFullscreenChange', handleFullScreenChange); // IE/Edge
return () => {
document.removeEventListener('fullscreenchange', handleFullScreenChange);
document.removeEventListener('webkitfullscreenchange', handleFullScreenChange);
document.removeEventListener('mozfullscreenchange', handleFullScreenChange);
document.removeEventListener('MSFullscreenChange', handleFullScreenChange);
};
}, []);
return () => {
document.removeEventListener('fullscreenchange', handleFullScreenChange);
document.removeEventListener(
'webkitfullscreenchange',
handleFullScreenChange
);
document.removeEventListener(
'mozfullscreenchange',
handleFullScreenChange
);
document.removeEventListener(
'MSFullscreenChange',
handleFullScreenChange
);
};
}, []);
return { enterFullScreen, exitFullScreen, toggleFullScreen };
return { enterFullScreen, exitFullScreen, toggleFullScreen };
};