mirror of
https://github.com/Qortal/Qortal-Hub.git
synced 2025-06-12 03:07:00 +00:00
catch error with console
This commit is contained in:
parent
f9f9895cb1
commit
cf335a6d0a
@ -1,13 +1,20 @@
|
|||||||
import React from 'react';
|
import { useTheme } from '@mui/material';
|
||||||
|
|
||||||
export const ExitIcon= ({ color = 'white', height, width }) => {
|
export const ExitIcon = () => {
|
||||||
return (
|
const theme = useTheme();
|
||||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<path d="M2 0L0 2L4 6L0 10L2 12L6 8L10 12L12 10L8 6L12 2L10 0L6 4L2 0Z" fill={color}/>
|
|
||||||
</svg>
|
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
);
|
<svg
|
||||||
};
|
width="12"
|
||||||
|
height="12"
|
||||||
|
viewBox="0 0 12 12"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M2 0L0 2L4 6L0 10L2 12L6 8L10 12L12 10L8 6L12 2L10 0L6 4L2 0Z"
|
||||||
|
fill={theme.palette.text.primary}
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
@ -1,14 +1,20 @@
|
|||||||
import React from 'react';
|
import { useTheme } from '@mui/material';
|
||||||
|
|
||||||
export const ReturnIcon= ({ color = 'white', height, width }) => {
|
export const ReturnIcon = () => {
|
||||||
return (
|
const theme = useTheme();
|
||||||
<svg width="20" height="13" viewBox="0 0 20 13" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<path d="M15.0937 3.73451H3.6243L5.84808 1.67047C6.04153 1.48456 6.14857 1.23558 6.14615 0.97713C6.14373 0.718684 6.03205 0.471459 5.83515 0.288703C5.63825 0.105948 5.37189 0.00228309 5.09344 3.72619e-05C4.815 -0.00220856 4.54674 0.0971438 4.34645 0.276696L0.310933 4.02233C0.111843 4.20718 0 4.45785 0 4.71922C0 4.98059 0.111843 5.23126 0.310933 5.41611L4.34645 9.16175C4.54674 9.3413 4.815 9.44065 5.09344 9.43841C5.37189 9.43616 5.63825 9.33249 5.83515 9.14974C6.03205 8.96698 6.14373 8.71976 6.14615 8.46131C6.14857 8.20287 6.04153 7.95388 5.84808 7.76797L3.6243 5.7059H15.0937C15.8316 5.7059 16.5393 5.97799 17.0611 6.4623C17.5829 6.94662 17.876 7.60349 17.876 8.28842C17.876 8.97335 17.5829 9.63022 17.0611 10.1145C16.5393 10.5989 15.8316 10.8709 15.0937 10.8709V12.8423C16.3949 12.8423 17.6429 12.3625 18.563 11.5085C19.4831 10.6545 20 9.49619 20 8.28842C20 7.08065 19.4831 5.92234 18.563 5.06832C17.6429 4.2143 16.3949 3.73451 15.0937 3.73451Z" fill={color}/>
|
|
||||||
</svg>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
);
|
<svg
|
||||||
};
|
fill="none"
|
||||||
|
height="13"
|
||||||
|
viewBox="0 0 20 13"
|
||||||
|
width="20"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M15.0937 3.73451H3.6243L5.84808 1.67047C6.04153 1.48456 6.14857 1.23558 6.14615 0.97713C6.14373 0.718684 6.03205 0.471459 5.83515 0.288703C5.63825 0.105948 5.37189 0.00228309 5.09344 3.72619e-05C4.815 -0.00220856 4.54674 0.0971438 4.34645 0.276696L0.310933 4.02233C0.111843 4.20718 0 4.45785 0 4.71922C0 4.98059 0.111843 5.23126 0.310933 5.41611L4.34645 9.16175C4.54674 9.3413 4.815 9.44065 5.09344 9.43841C5.37189 9.43616 5.63825 9.33249 5.83515 9.14974C6.03205 8.96698 6.14373 8.71976 6.14615 8.46131C6.14857 8.20287 6.04153 7.95388 5.84808 7.76797L3.6243 5.7059H15.0937C15.8316 5.7059 16.5393 5.97799 17.0611 6.4623C17.5829 6.94662 17.876 7.60349 17.876 8.28842C17.876 8.97335 17.5829 9.63022 17.0611 10.1145C16.5393 10.5989 15.8316 10.8709 15.0937 10.8709V12.8423C16.3949 12.8423 17.6429 12.3625 18.563 11.5085C19.4831 10.6545 20 9.49619 20 8.28842C20 7.08065 19.4831 5.92234 18.563 5.06832C17.6429 4.2143 16.3949 3.73451 15.0937 3.73451Z"
|
||||||
|
fill={theme.palette.text.primary}
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
1232
src/background.ts
1232
src/background.ts
File diff suppressed because it is too large
Load Diff
@ -1,4 +1,4 @@
|
|||||||
import React, { useContext, useMemo, useState } from "react";
|
import React, { useContext, useMemo, useState } from 'react';
|
||||||
import {
|
import {
|
||||||
AppCircle,
|
AppCircle,
|
||||||
AppCircleContainer,
|
AppCircleContainer,
|
||||||
@ -6,8 +6,8 @@ import {
|
|||||||
AppLibrarySubTitle,
|
AppLibrarySubTitle,
|
||||||
AppsContainer,
|
AppsContainer,
|
||||||
AppsParent,
|
AppsParent,
|
||||||
} from "./Apps-styles";
|
} from './Apps-styles';
|
||||||
import { Buffer } from "buffer";
|
import { Buffer } from 'buffer';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Avatar,
|
Avatar,
|
||||||
@ -20,17 +20,17 @@ import {
|
|||||||
DialogContentText,
|
DialogContentText,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
Input,
|
Input,
|
||||||
} from "@mui/material";
|
} from '@mui/material';
|
||||||
import { Add } from "@mui/icons-material";
|
import { Add } from '@mui/icons-material';
|
||||||
import { MyContext, getBaseApiReact, isMobile } from "../../App";
|
import { MyContext, getBaseApiReact, isMobile } from '../../App';
|
||||||
import LogoSelected from "../../assets/svgs/LogoSelected.svg";
|
import LogoSelected from '../../assets/svgs/LogoSelected.svg';
|
||||||
import { executeEvent } from "../../utils/events";
|
import { executeEvent } from '../../utils/events';
|
||||||
import { Spacer } from "../../common/Spacer";
|
import { Spacer } from '../../common/Spacer';
|
||||||
import { useModal } from "../../common/useModal";
|
import { useModal } from '../../common/useModal';
|
||||||
import { createEndpoint, isUsingLocal } from "../../background";
|
import { createEndpoint, isUsingLocal } from '../../background';
|
||||||
import { Label } from "../Group/AddGroup";
|
import { Label } from '../Group/AddGroup';
|
||||||
import ShortUniqueId from "short-unique-id";
|
import ShortUniqueId from 'short-unique-id';
|
||||||
import swaggerSVG from '../../assets/svgs/swagger.svg'
|
import swaggerSVG from '../../assets/svgs/swagger.svg';
|
||||||
const uid = new ShortUniqueId({ length: 8 });
|
const uid = new ShortUniqueId({ length: 8 });
|
||||||
|
|
||||||
export const AppsDevModeHome = ({
|
export const AppsDevModeHome = ({
|
||||||
@ -40,8 +40,8 @@ export const AppsDevModeHome = ({
|
|||||||
availableQapps,
|
availableQapps,
|
||||||
myName,
|
myName,
|
||||||
}) => {
|
}) => {
|
||||||
const [domain, setDomain] = useState("127.0.0.1");
|
const [domain, setDomain] = useState('127.0.0.1');
|
||||||
const [port, setPort] = useState("");
|
const [port, setPort] = useState('');
|
||||||
const [selectedPreviewFile, setSelectedPreviewFile] = useState(null);
|
const [selectedPreviewFile, setSelectedPreviewFile] = useState(null);
|
||||||
|
|
||||||
const { isShow, onCancel, onOk, show, message } = useModal();
|
const { isShow, onCancel, onOk, show, message } = useModal();
|
||||||
@ -58,7 +58,7 @@ export const AppsDevModeHome = ({
|
|||||||
const content = await window.electron.readFile(filePath);
|
const content = await window.electron.readFile(filePath);
|
||||||
return { buffer: content, filePath };
|
return { buffer: content, filePath };
|
||||||
} else {
|
} else {
|
||||||
console.log("No file selected.");
|
console.log('No file selected.');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const handleSelectDirectry = async (existingDirectoryPath) => {
|
const handleSelectDirectry = async (existingDirectoryPath) => {
|
||||||
@ -67,7 +67,7 @@ export const AppsDevModeHome = ({
|
|||||||
if (buffer) {
|
if (buffer) {
|
||||||
return { buffer, directoryPath };
|
return { buffer, directoryPath };
|
||||||
} else {
|
} else {
|
||||||
console.log("No file selected.");
|
console.log('No file selected.');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -78,34 +78,36 @@ export const AppsDevModeHome = ({
|
|||||||
setOpenSnackGlobal(true);
|
setOpenSnackGlobal(true);
|
||||||
|
|
||||||
setInfoSnackCustom({
|
setInfoSnackCustom({
|
||||||
type: "error",
|
type: 'error',
|
||||||
message:
|
message:
|
||||||
"Please use your local node for dev mode! Logout and use Local node.",
|
'Please use your local node for dev mode! Logout and use Local node.',
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const { portVal, domainVal } = await show({
|
const { portVal, domainVal } = await show({
|
||||||
message: "",
|
message: '',
|
||||||
publishFee: "",
|
publishFee: '',
|
||||||
});
|
});
|
||||||
const framework = domainVal + ":" + portVal;
|
const framework = domainVal + ':' + portVal;
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
`${getBaseApiReact()}/developer/proxy/start`,
|
`${getBaseApiReact()}/developer/proxy/start`,
|
||||||
{
|
{
|
||||||
method: "POST",
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "text/plain",
|
'Content-Type': 'text/plain',
|
||||||
},
|
},
|
||||||
body: framework,
|
body: framework,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
const responseData = await response.text();
|
const responseData = await response.text();
|
||||||
executeEvent("appsDevModeAddTab", {
|
executeEvent('appsDevModeAddTab', {
|
||||||
data: {
|
data: {
|
||||||
url: "http://127.0.0.1:" + responseData,
|
url: 'http://127.0.0.1:' + responseData,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} catch (error) {}
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const addPreviewApp = async (isRefresh, existingFilePath, tabId) => {
|
const addPreviewApp = async (isRefresh, existingFilePath, tabId) => {
|
||||||
@ -115,9 +117,9 @@ export const AppsDevModeHome = ({
|
|||||||
setOpenSnackGlobal(true);
|
setOpenSnackGlobal(true);
|
||||||
|
|
||||||
setInfoSnackCustom({
|
setInfoSnackCustom({
|
||||||
type: "error",
|
type: 'error',
|
||||||
message:
|
message:
|
||||||
"Please use your local node for dev mode! Logout and use Local node.",
|
'Please use your local node for dev mode! Logout and use Local node.',
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -125,8 +127,8 @@ export const AppsDevModeHome = ({
|
|||||||
setOpenSnackGlobal(true);
|
setOpenSnackGlobal(true);
|
||||||
|
|
||||||
setInfoSnackCustom({
|
setInfoSnackCustom({
|
||||||
type: "error",
|
type: 'error',
|
||||||
message: "You need a name to use preview",
|
message: 'You need a name to use preview',
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -137,29 +139,29 @@ export const AppsDevModeHome = ({
|
|||||||
setOpenSnackGlobal(true);
|
setOpenSnackGlobal(true);
|
||||||
|
|
||||||
setInfoSnackCustom({
|
setInfoSnackCustom({
|
||||||
type: "error",
|
type: 'error',
|
||||||
message: "Please select a file",
|
message: 'Please select a file',
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const postBody = Buffer.from(buffer).toString("base64");
|
const postBody = Buffer.from(buffer).toString('base64');
|
||||||
|
|
||||||
const endpoint = await createEndpoint(
|
const endpoint = await createEndpoint(
|
||||||
`/arbitrary/APP/${myName}/zip?preview=true`
|
`/arbitrary/APP/${myName}/zip?preview=true`
|
||||||
);
|
);
|
||||||
const response = await fetch(endpoint, {
|
const response = await fetch(endpoint, {
|
||||||
method: "POST",
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "text/plain",
|
'Content-Type': 'text/plain',
|
||||||
},
|
},
|
||||||
body: postBody,
|
body: postBody,
|
||||||
});
|
});
|
||||||
if (!response?.ok) throw new Error("Invalid zip");
|
if (!response?.ok) throw new Error('Invalid zip');
|
||||||
const previewPath = await response.text();
|
const previewPath = await response.text();
|
||||||
if (tabId) {
|
if (tabId) {
|
||||||
executeEvent("appsDevModeUpdateTab", {
|
executeEvent('appsDevModeUpdateTab', {
|
||||||
data: {
|
data: {
|
||||||
url: "http://127.0.0.1:12391" + previewPath,
|
url: 'http://127.0.0.1:12391' + previewPath,
|
||||||
isPreview: true,
|
isPreview: true,
|
||||||
filePath,
|
filePath,
|
||||||
refreshFunc: (tabId) => {
|
refreshFunc: (tabId) => {
|
||||||
@ -170,9 +172,9 @@ export const AppsDevModeHome = ({
|
|||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
executeEvent("appsDevModeAddTab", {
|
executeEvent('appsDevModeAddTab', {
|
||||||
data: {
|
data: {
|
||||||
url: "http://127.0.0.1:12391" + previewPath,
|
url: 'http://127.0.0.1:12391' + previewPath,
|
||||||
isPreview: true,
|
isPreview: true,
|
||||||
filePath,
|
filePath,
|
||||||
refreshFunc: (tabId) => {
|
refreshFunc: (tabId) => {
|
||||||
@ -192,9 +194,9 @@ export const AppsDevModeHome = ({
|
|||||||
setOpenSnackGlobal(true);
|
setOpenSnackGlobal(true);
|
||||||
|
|
||||||
setInfoSnackCustom({
|
setInfoSnackCustom({
|
||||||
type: "error",
|
type: 'error',
|
||||||
message:
|
message:
|
||||||
"Please use your local node for dev mode! Logout and use Local node.",
|
'Please use your local node for dev mode! Logout and use Local node.',
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -202,8 +204,8 @@ export const AppsDevModeHome = ({
|
|||||||
setOpenSnackGlobal(true);
|
setOpenSnackGlobal(true);
|
||||||
|
|
||||||
setInfoSnackCustom({
|
setInfoSnackCustom({
|
||||||
type: "error",
|
type: 'error',
|
||||||
message: "You need a name to use preview",
|
message: 'You need a name to use preview',
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -214,29 +216,29 @@ export const AppsDevModeHome = ({
|
|||||||
setOpenSnackGlobal(true);
|
setOpenSnackGlobal(true);
|
||||||
|
|
||||||
setInfoSnackCustom({
|
setInfoSnackCustom({
|
||||||
type: "error",
|
type: 'error',
|
||||||
message: "Please select a file",
|
message: 'Please select a file',
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const postBody = Buffer.from(buffer).toString("base64");
|
const postBody = Buffer.from(buffer).toString('base64');
|
||||||
|
|
||||||
const endpoint = await createEndpoint(
|
const endpoint = await createEndpoint(
|
||||||
`/arbitrary/APP/${myName}/zip?preview=true`
|
`/arbitrary/APP/${myName}/zip?preview=true`
|
||||||
);
|
);
|
||||||
const response = await fetch(endpoint, {
|
const response = await fetch(endpoint, {
|
||||||
method: "POST",
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "text/plain",
|
'Content-Type': 'text/plain',
|
||||||
},
|
},
|
||||||
body: postBody,
|
body: postBody,
|
||||||
});
|
});
|
||||||
if (!response?.ok) throw new Error("Invalid zip");
|
if (!response?.ok) throw new Error('Invalid zip');
|
||||||
const previewPath = await response.text();
|
const previewPath = await response.text();
|
||||||
if (tabId) {
|
if (tabId) {
|
||||||
executeEvent("appsDevModeUpdateTab", {
|
executeEvent('appsDevModeUpdateTab', {
|
||||||
data: {
|
data: {
|
||||||
url: "http://127.0.0.1:12391" + previewPath,
|
url: 'http://127.0.0.1:12391' + previewPath,
|
||||||
isPreview: true,
|
isPreview: true,
|
||||||
directoryPath,
|
directoryPath,
|
||||||
refreshFunc: (tabId) => {
|
refreshFunc: (tabId) => {
|
||||||
@ -247,9 +249,9 @@ export const AppsDevModeHome = ({
|
|||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
executeEvent("appsDevModeAddTab", {
|
executeEvent('appsDevModeAddTab', {
|
||||||
data: {
|
data: {
|
||||||
url: "http://127.0.0.1:12391" + previewPath,
|
url: 'http://127.0.0.1:12391' + previewPath,
|
||||||
isPreview: true,
|
isPreview: true,
|
||||||
directoryPath,
|
directoryPath,
|
||||||
refreshFunc: (tabId) => {
|
refreshFunc: (tabId) => {
|
||||||
@ -266,12 +268,12 @@ export const AppsDevModeHome = ({
|
|||||||
<>
|
<>
|
||||||
<AppsContainer
|
<AppsContainer
|
||||||
sx={{
|
sx={{
|
||||||
justifyContent: "flex-start",
|
justifyContent: 'flex-start',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<AppLibrarySubTitle
|
<AppLibrarySubTitle
|
||||||
sx={{
|
sx={{
|
||||||
fontSize: "30px",
|
fontSize: '30px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Dev Mode Apps
|
Dev Mode Apps
|
||||||
@ -280,8 +282,8 @@ export const AppsDevModeHome = ({
|
|||||||
<Spacer height="45px" />
|
<Spacer height="45px" />
|
||||||
<AppsContainer
|
<AppsContainer
|
||||||
sx={{
|
sx={{
|
||||||
gap: "75px",
|
gap: '75px',
|
||||||
justifyContent: "flex-start",
|
justifyContent: 'flex-start',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ButtonBase
|
<ButtonBase
|
||||||
@ -291,7 +293,7 @@ export const AppsDevModeHome = ({
|
|||||||
>
|
>
|
||||||
<AppCircleContainer
|
<AppCircleContainer
|
||||||
sx={{
|
sx={{
|
||||||
gap: !isMobile ? "10px" : "5px",
|
gap: !isMobile ? '10px' : '5px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<AppCircle>
|
<AppCircle>
|
||||||
@ -307,7 +309,7 @@ export const AppsDevModeHome = ({
|
|||||||
>
|
>
|
||||||
<AppCircleContainer
|
<AppCircleContainer
|
||||||
sx={{
|
sx={{
|
||||||
gap: !isMobile ? "10px" : "5px",
|
gap: !isMobile ? '10px' : '5px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<AppCircle>
|
<AppCircle>
|
||||||
@ -323,7 +325,7 @@ export const AppsDevModeHome = ({
|
|||||||
>
|
>
|
||||||
<AppCircleContainer
|
<AppCircleContainer
|
||||||
sx={{
|
sx={{
|
||||||
gap: !isMobile ? "10px" : "5px",
|
gap: !isMobile ? '10px' : '5px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<AppCircle>
|
<AppCircle>
|
||||||
@ -334,10 +336,10 @@ export const AppsDevModeHome = ({
|
|||||||
</ButtonBase>
|
</ButtonBase>
|
||||||
<ButtonBase
|
<ButtonBase
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
executeEvent("appsDevModeAddTab", {
|
executeEvent('appsDevModeAddTab', {
|
||||||
data: {
|
data: {
|
||||||
service: "APP",
|
service: 'APP',
|
||||||
name: "Q-Sandbox",
|
name: 'Q-Sandbox',
|
||||||
tabId: uid.rnd(),
|
tabId: uid.rnd(),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -345,16 +347,16 @@ export const AppsDevModeHome = ({
|
|||||||
>
|
>
|
||||||
<AppCircleContainer
|
<AppCircleContainer
|
||||||
sx={{
|
sx={{
|
||||||
gap: !isMobile ? "10px" : "5px",
|
gap: !isMobile ? '10px' : '5px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<AppCircle>
|
<AppCircle>
|
||||||
<Avatar
|
<Avatar
|
||||||
sx={{
|
sx={{
|
||||||
height: "42px",
|
height: '42px',
|
||||||
width: "42px",
|
width: '42px',
|
||||||
"& img": {
|
'& img': {
|
||||||
objectFit: "fill",
|
objectFit: 'fill',
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
alt="Q-Sandbox"
|
alt="Q-Sandbox"
|
||||||
@ -362,8 +364,8 @@ export const AppsDevModeHome = ({
|
|||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
style={{
|
style={{
|
||||||
width: "31px",
|
width: '31px',
|
||||||
height: "auto",
|
height: 'auto',
|
||||||
}}
|
}}
|
||||||
alt="center-icon"
|
alt="center-icon"
|
||||||
/>
|
/>
|
||||||
@ -374,27 +376,27 @@ export const AppsDevModeHome = ({
|
|||||||
</ButtonBase>
|
</ButtonBase>
|
||||||
<ButtonBase
|
<ButtonBase
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
executeEvent("appsDevModeAddTab", {
|
executeEvent('appsDevModeAddTab', {
|
||||||
data: {
|
data: {
|
||||||
url: "http://127.0.0.1:12391",
|
url: 'http://127.0.0.1:12391',
|
||||||
isPreview: false,
|
isPreview: false,
|
||||||
customIcon: swaggerSVG
|
customIcon: swaggerSVG,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<AppCircleContainer
|
<AppCircleContainer
|
||||||
sx={{
|
sx={{
|
||||||
gap: !isMobile ? "10px" : "5px",
|
gap: !isMobile ? '10px' : '5px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<AppCircle>
|
<AppCircle>
|
||||||
<Avatar
|
<Avatar
|
||||||
sx={{
|
sx={{
|
||||||
height: "42px",
|
height: '42px',
|
||||||
width: "42px",
|
width: '42px',
|
||||||
"& img": {
|
'& img': {
|
||||||
objectFit: "fill",
|
objectFit: 'fill',
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
alt="API"
|
alt="API"
|
||||||
@ -402,8 +404,8 @@ export const AppsDevModeHome = ({
|
|||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
style={{
|
style={{
|
||||||
width: "31px",
|
width: '31px',
|
||||||
height: "auto",
|
height: 'auto',
|
||||||
}}
|
}}
|
||||||
alt="center-icon"
|
alt="center-icon"
|
||||||
/>
|
/>
|
||||||
@ -419,20 +421,20 @@ export const AppsDevModeHome = ({
|
|||||||
aria-labelledby="alert-dialog-title"
|
aria-labelledby="alert-dialog-title"
|
||||||
aria-describedby="alert-dialog-description"
|
aria-describedby="alert-dialog-description"
|
||||||
onKeyDown={(e) => {
|
onKeyDown={(e) => {
|
||||||
if (e.key === "Enter" && domain && port) {
|
if (e.key === 'Enter' && domain && port) {
|
||||||
onOk({ portVal: port, domainVal: domain });
|
onOk({ portVal: port, domainVal: domain });
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<DialogTitle id="alert-dialog-title">
|
<DialogTitle id="alert-dialog-title">
|
||||||
{"Add custom framework"}
|
{'Add custom framework'}
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: "flex",
|
display: 'flex',
|
||||||
flexDirection: "column",
|
flexDirection: 'column',
|
||||||
gap: "5px",
|
gap: '5px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Label>Domain</Label>
|
<Label>Domain</Label>
|
||||||
@ -444,10 +446,10 @@ export const AppsDevModeHome = ({
|
|||||||
</Box>
|
</Box>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: "flex",
|
display: 'flex',
|
||||||
flexDirection: "column",
|
flexDirection: 'column',
|
||||||
gap: "5px",
|
gap: '5px',
|
||||||
marginTop: "15px",
|
marginTop: '15px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Label>Port</Label>
|
<Label>Port</Label>
|
||||||
|
@ -1,21 +1,7 @@
|
|||||||
import React, {
|
import { useContext, useEffect, useState } from 'react';
|
||||||
useCallback,
|
import { MyContext, isMobile } from '../../App';
|
||||||
useContext,
|
import { Box, Typography } from '@mui/material';
|
||||||
useEffect,
|
import { AdminSpaceInner } from './AdminSpaceInner';
|
||||||
useMemo,
|
|
||||||
useRef,
|
|
||||||
useState,
|
|
||||||
} from "react";
|
|
||||||
import { GroupMail } from "../Group/Forum/GroupMail";
|
|
||||||
import { MyContext, isMobile } from "../../App";
|
|
||||||
import { getRootHeight } from "../../utils/mobile/mobileUtils";
|
|
||||||
import { Box, Typography } from "@mui/material";
|
|
||||||
import { AdminSpaceInner } from "./AdminSpaceInner";
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export const AdminSpace = ({
|
export const AdminSpace = ({
|
||||||
selectedGroup,
|
selectedGroup,
|
||||||
@ -26,11 +12,11 @@ export const AdminSpace = ({
|
|||||||
isAdmin,
|
isAdmin,
|
||||||
myAddress,
|
myAddress,
|
||||||
hide,
|
hide,
|
||||||
defaultThread,
|
defaultThread,
|
||||||
setDefaultThread,
|
setDefaultThread,
|
||||||
setIsForceShowCreationKeyPopup
|
setIsForceShowCreationKeyPopup,
|
||||||
}) => {
|
}) => {
|
||||||
const { rootHeight } = useContext(MyContext);
|
const { rootHeight } = useContext(MyContext);
|
||||||
const [isMoved, setIsMoved] = useState(false);
|
const [isMoved, setIsMoved] = useState(false);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (hide) {
|
if (hide) {
|
||||||
@ -42,26 +28,37 @@ export const AdminSpace = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
// reference to change height
|
// reference to change height
|
||||||
height: isMobile ? `calc(${rootHeight} - 127px` : "calc(100vh - 70px)",
|
display: 'flex',
|
||||||
display: "flex",
|
flexDirection: 'column',
|
||||||
flexDirection: "column",
|
height: isMobile ? `calc(${rootHeight} - 127px` : 'calc(100vh - 70px)',
|
||||||
width: "100%",
|
left: hide && '-1000px',
|
||||||
opacity: hide ? 0 : 1,
|
opacity: hide ? 0 : 1,
|
||||||
visibility: hide && 'hidden',
|
position: hide ? 'fixed' : 'relative',
|
||||||
position: hide ? 'fixed' : 'relative',
|
visibility: hide && 'hidden',
|
||||||
left: hide && '-1000px'
|
width: '100%',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{!isAdmin && <Box sx={{
|
{!isAdmin && (
|
||||||
width: '100%',
|
<Box
|
||||||
display: 'flex',
|
sx={{
|
||||||
justifyContent: 'center',
|
display: 'flex',
|
||||||
paddingTop: '25px'
|
justifyContent: 'center',
|
||||||
}}><Typography>Sorry, this space is only for Admins.</Typography></Box>}
|
paddingTop: '25px',
|
||||||
{isAdmin && <AdminSpaceInner setIsForceShowCreationKeyPopup={setIsForceShowCreationKeyPopup} adminsWithNames={adminsWithNames} selectedGroup={selectedGroup} />}
|
width: '100%',
|
||||||
|
}}
|
||||||
</div>
|
>
|
||||||
|
<Typography>Sorry, this space is only for Admins.</Typography>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
{isAdmin && (
|
||||||
|
<AdminSpaceInner
|
||||||
|
setIsForceShowCreationKeyPopup={setIsForceShowCreationKeyPopup}
|
||||||
|
adminsWithNames={adminsWithNames}
|
||||||
|
selectedGroup={selectedGroup}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,39 +1,42 @@
|
|||||||
import React, { useCallback, useContext, useEffect, useState } from "react";
|
import { useCallback, useContext, useEffect, useState } from 'react';
|
||||||
import {
|
import {
|
||||||
MyContext,
|
MyContext,
|
||||||
getArbitraryEndpointReact,
|
getArbitraryEndpointReact,
|
||||||
getBaseApiReact,
|
getBaseApiReact,
|
||||||
} from "../../App";
|
} from '../../App';
|
||||||
import { Box, Button, Typography } from "@mui/material";
|
import { Box, Button, Typography } from '@mui/material';
|
||||||
import {
|
import {
|
||||||
decryptResource,
|
decryptResource,
|
||||||
getPublishesFromAdmins,
|
getPublishesFromAdmins,
|
||||||
validateSecretKey,
|
validateSecretKey,
|
||||||
} from "../Group/Group";
|
} from '../Group/Group';
|
||||||
import { getFee } from "../../background";
|
import { getFee } from '../../background';
|
||||||
import { base64ToUint8Array } from "../../qdn/encryption/group-encryption";
|
import { base64ToUint8Array } from '../../qdn/encryption/group-encryption';
|
||||||
import { uint8ArrayToObject } from "../../backgroundFunctions/encryption";
|
import { uint8ArrayToObject } from '../../backgroundFunctions/encryption';
|
||||||
import { formatTimestampForum } from "../../utils/time";
|
import { formatTimestampForum } from '../../utils/time';
|
||||||
import { Spacer } from "../../common/Spacer";
|
import { Spacer } from '../../common/Spacer';
|
||||||
|
|
||||||
export const getPublishesFromAdminsAdminSpace = async (
|
export const getPublishesFromAdminsAdminSpace = async (
|
||||||
admins: string[],
|
admins: string[],
|
||||||
groupId
|
groupId
|
||||||
) => {
|
) => {
|
||||||
const queryString = admins.map((name) => `name=${name}`).join("&");
|
const queryString = admins.map((name) => `name=${name}`).join('&');
|
||||||
const url = `${getBaseApiReact()}${getArbitraryEndpointReact()}?mode=ALL&service=DOCUMENT_PRIVATE&identifier=admins-symmetric-qchat-group-${groupId}&exactmatchnames=true&limit=0&reverse=true&${queryString}&prefix=true`;
|
const url = `${getBaseApiReact()}${getArbitraryEndpointReact()}?mode=ALL&service=DOCUMENT_PRIVATE&identifier=admins-symmetric-qchat-group-${groupId}&exactmatchnames=true&limit=0&reverse=true&${queryString}&prefix=true`;
|
||||||
const response = await fetch(url);
|
const response = await fetch(url);
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error("network error");
|
throw new Error('network error');
|
||||||
}
|
}
|
||||||
const adminData = await response.json();
|
const adminData = await response.json();
|
||||||
|
|
||||||
const filterId = adminData.filter(
|
const filterId = adminData.filter(
|
||||||
(data: any) => data.identifier === `admins-symmetric-qchat-group-${groupId}`
|
(data: any) => data.identifier === `admins-symmetric-qchat-group-${groupId}`
|
||||||
);
|
);
|
||||||
|
|
||||||
if (filterId?.length === 0) {
|
if (filterId?.length === 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const sortedData = filterId.sort((a: any, b: any) => {
|
const sortedData = filterId.sort((a: any, b: any) => {
|
||||||
// Get the most recent date for both a and b
|
// Get the most recent date for both a and b
|
||||||
const dateA = a.updated ? new Date(a.updated) : new Date(a.created);
|
const dateA = a.updated ? new Date(a.updated) : new Date(a.created);
|
||||||
@ -87,10 +90,11 @@ export const AdminSpaceInner = ({
|
|||||||
const dataint8Array = base64ToUint8Array(decryptedKey.data);
|
const dataint8Array = base64ToUint8Array(decryptedKey.data);
|
||||||
const decryptedKeyToObject = uint8ArrayToObject(dataint8Array);
|
const decryptedKeyToObject = uint8ArrayToObject(dataint8Array);
|
||||||
if (!validateSecretKey(decryptedKeyToObject))
|
if (!validateSecretKey(decryptedKeyToObject))
|
||||||
throw new Error("SecretKey is not valid");
|
throw new Error('SecretKey is not valid');
|
||||||
setAdminGroupSecretKey(decryptedKeyToObject);
|
setAdminGroupSecretKey(decryptedKeyToObject);
|
||||||
setAdminGroupSecretKeyPublishDetails(getLatestPublish);
|
setAdminGroupSecretKeyPublishDetails(getLatestPublish);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
} finally {
|
} finally {
|
||||||
setIsFetchingAdminGroupSecretKey(false);
|
setIsFetchingAdminGroupSecretKey(false);
|
||||||
}
|
}
|
||||||
@ -106,6 +110,7 @@ export const AdminSpaceInner = ({
|
|||||||
if (getLatestPublish === false) setGroupSecretKeyPublishDetails(false);
|
if (getLatestPublish === false) setGroupSecretKeyPublishDetails(false);
|
||||||
setGroupSecretKeyPublishDetails(getLatestPublish);
|
setGroupSecretKeyPublishDetails(getLatestPublish);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
} finally {
|
} finally {
|
||||||
setIsFetchingGroupSecretKey(false);
|
setIsFetchingGroupSecretKey(false);
|
||||||
}
|
}
|
||||||
@ -113,15 +118,17 @@ export const AdminSpaceInner = ({
|
|||||||
|
|
||||||
const createCommonSecretForAdmins = async () => {
|
const createCommonSecretForAdmins = async () => {
|
||||||
try {
|
try {
|
||||||
const fee = await getFee("ARBITRARY");
|
const fee = await getFee('ARBITRARY');
|
||||||
|
|
||||||
await show({
|
await show({
|
||||||
message: "Would you like to perform an ARBITRARY transaction?",
|
message: 'Would you like to perform an ARBITRARY transaction?',
|
||||||
publishFee: fee.fee + " QORT",
|
publishFee: fee.fee + ' QORT',
|
||||||
});
|
});
|
||||||
|
|
||||||
setIsLoadingPublishKey(true);
|
setIsLoadingPublishKey(true);
|
||||||
|
|
||||||
window
|
window
|
||||||
.sendMessage("encryptAndPublishSymmetricKeyGroupChatForAdmins", {
|
.sendMessage('encryptAndPublishSymmetricKeyGroupChatForAdmins', {
|
||||||
groupId: selectedGroup,
|
groupId: selectedGroup,
|
||||||
previousData: adminGroupSecretKey,
|
previousData: adminGroupSecretKey,
|
||||||
admins: adminsWithNames,
|
admins: adminsWithNames,
|
||||||
@ -129,27 +136,29 @@ export const AdminSpaceInner = ({
|
|||||||
.then((response) => {
|
.then((response) => {
|
||||||
if (!response?.error) {
|
if (!response?.error) {
|
||||||
setInfoSnackCustom({
|
setInfoSnackCustom({
|
||||||
type: "success",
|
type: 'success',
|
||||||
message:
|
message:
|
||||||
"Successfully re-encrypted secret key. It may take a couple of minutes for the changes to propagate. Refresh the group in 5 mins.",
|
'Successfully re-encrypted secret key. It may take a couple of minutes for the changes to propagate. Refresh the group in 5 mins.',
|
||||||
});
|
});
|
||||||
setOpenSnackGlobal(true);
|
setOpenSnackGlobal(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setInfoSnackCustom({
|
setInfoSnackCustom({
|
||||||
type: "error",
|
type: 'error',
|
||||||
message: response?.error || "unable to re-encrypt secret key",
|
message: response?.error || 'unable to re-encrypt secret key',
|
||||||
});
|
});
|
||||||
setOpenSnackGlobal(true);
|
setOpenSnackGlobal(true);
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
setInfoSnackCustom({
|
setInfoSnackCustom({
|
||||||
type: "error",
|
type: 'error',
|
||||||
message: error?.message || "unable to re-encrypt secret key",
|
message: error?.message || 'unable to re-encrypt secret key',
|
||||||
});
|
});
|
||||||
setOpenSnackGlobal(true);
|
setOpenSnackGlobal(true);
|
||||||
});
|
});
|
||||||
} catch (error) {}
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -159,27 +168,32 @@ export const AdminSpaceInner = ({
|
|||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
width: "100%",
|
alignItems: 'center',
|
||||||
display: "flex",
|
display: 'flex',
|
||||||
flexDirection: "column",
|
flexDirection: 'column',
|
||||||
padding: "10px",
|
padding: '10px',
|
||||||
alignItems: 'center'
|
width: '100%',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography sx={{
|
<Typography
|
||||||
fontSize: '14px'
|
sx={{
|
||||||
}}>Reminder: After publishing the key, it will take a couple of minutes for it to appear. Please just wait.</Typography>
|
fontSize: '14px',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Reminder: After publishing the key, it will take a couple of minutes for
|
||||||
|
it to appear. Please just wait.
|
||||||
|
</Typography>
|
||||||
<Spacer height="25px" />
|
<Spacer height="25px" />
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: "flex",
|
|
||||||
flexDirection: "column",
|
|
||||||
gap: "20px",
|
|
||||||
width: "300px",
|
|
||||||
maxWidth: "90%",
|
|
||||||
padding: '10px',
|
|
||||||
border: '1px solid gray',
|
border: '1px solid gray',
|
||||||
borderRadius: '6px'
|
borderRadius: '6px',
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
gap: '20px',
|
||||||
|
maxWidth: '90%',
|
||||||
|
padding: '10px',
|
||||||
|
width: '300px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{isFetchingGroupSecretKey && (
|
{isFetchingGroupSecretKey && (
|
||||||
@ -191,33 +205,47 @@ export const AdminSpaceInner = ({
|
|||||||
)}
|
)}
|
||||||
{groupSecretKeyPublishDetails && (
|
{groupSecretKeyPublishDetails && (
|
||||||
<Typography>
|
<Typography>
|
||||||
Last encryption date:{" "}
|
Last encryption date:{' '}
|
||||||
{formatTimestampForum(
|
{formatTimestampForum(
|
||||||
groupSecretKeyPublishDetails?.updated ||
|
groupSecretKeyPublishDetails?.updated ||
|
||||||
groupSecretKeyPublishDetails?.created
|
groupSecretKeyPublishDetails?.created
|
||||||
)}{" "}
|
)}{' '}
|
||||||
{` by ${groupSecretKeyPublishDetails?.name}`}
|
{` by ${groupSecretKeyPublishDetails?.name}`}
|
||||||
</Typography>
|
</Typography>
|
||||||
)}
|
)}
|
||||||
<Button disabled={isFetchingGroupSecretKey} onClick={()=> setIsForceShowCreationKeyPopup(true)} variant="contained">
|
<Button
|
||||||
|
disabled={isFetchingGroupSecretKey}
|
||||||
|
onClick={() => setIsForceShowCreationKeyPopup(true)}
|
||||||
|
variant="contained"
|
||||||
|
>
|
||||||
Publish group secret key
|
Publish group secret key
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Spacer height="20px" />
|
<Spacer height="20px" />
|
||||||
<Typography sx={{
|
|
||||||
fontSize: '14px'
|
<Typography
|
||||||
}}>This key is to encrypt GROUP related content. This is the only one used in this UI as of now. All group members will be able to see content encrypted with this key.</Typography>
|
sx={{
|
||||||
|
fontSize: '14px',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
This key is to encrypt GROUP related content. This is the only one
|
||||||
|
used in this UI as of now. All group members will be able to see
|
||||||
|
content encrypted with this key.
|
||||||
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Spacer height="25px" />
|
<Spacer height="25px" />
|
||||||
|
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: "flex",
|
|
||||||
flexDirection: "column",
|
|
||||||
gap: "20px",
|
|
||||||
width: "300px",
|
|
||||||
maxWidth: "90%",
|
|
||||||
padding: '10px',
|
|
||||||
border: '1px solid gray',
|
border: '1px solid gray',
|
||||||
borderRadius: '6px'
|
borderRadius: '6px',
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
gap: '20px',
|
||||||
|
maxWidth: '90%',
|
||||||
|
padding: '10px',
|
||||||
|
width: '300px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{isFetchingAdminGroupSecretKey && (
|
{isFetchingAdminGroupSecretKey && (
|
||||||
@ -228,20 +256,31 @@ export const AdminSpaceInner = ({
|
|||||||
)}
|
)}
|
||||||
{adminGroupSecretKeyPublishDetails && (
|
{adminGroupSecretKeyPublishDetails && (
|
||||||
<Typography>
|
<Typography>
|
||||||
Last encryption date:{" "}
|
Last encryption date:{' '}
|
||||||
{formatTimestampForum(
|
{formatTimestampForum(
|
||||||
adminGroupSecretKeyPublishDetails?.updated ||
|
adminGroupSecretKeyPublishDetails?.updated ||
|
||||||
adminGroupSecretKeyPublishDetails?.created
|
adminGroupSecretKeyPublishDetails?.created
|
||||||
)}
|
)}
|
||||||
</Typography>
|
</Typography>
|
||||||
)}
|
)}
|
||||||
<Button disabled={isFetchingAdminGroupSecretKey} onClick={createCommonSecretForAdmins} variant="contained">
|
<Button
|
||||||
|
disabled={isFetchingAdminGroupSecretKey}
|
||||||
|
onClick={createCommonSecretForAdmins}
|
||||||
|
variant="contained"
|
||||||
|
>
|
||||||
Publish admin secret key
|
Publish admin secret key
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Spacer height="20px" />
|
<Spacer height="20px" />
|
||||||
<Typography sx={{
|
|
||||||
fontSize: '14px'
|
<Typography
|
||||||
}}>This key is to encrypt ADMIN related content. Only admins would see content encrypted with it.</Typography>
|
sx={{
|
||||||
|
fontSize: '14px',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
This key is to encrypt ADMIN related content. Only admins would see
|
||||||
|
content encrypted with it.
|
||||||
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
@ -83,7 +83,9 @@ export const AnnouncementDiscussion = ({
|
|||||||
[`${identifier}-${name}`]: messageData,
|
[`${identifier}-${name}`]: messageData,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
} catch (error) {}
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const publishAnc = async ({ encryptedData, identifier }: any) => {
|
const publishAnc = async ({ encryptedData, identifier }: any) => {
|
||||||
@ -107,7 +109,9 @@ export const AnnouncementDiscussion = ({
|
|||||||
rej(error.message || 'An error occurred');
|
rej(error.message || 'An error occurred');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
} catch (error) {}
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const setTempData = async () => {
|
const setTempData = async () => {
|
||||||
@ -123,7 +127,9 @@ export const AnnouncementDiscussion = ({
|
|||||||
});
|
});
|
||||||
setTempPublishedList(tempData);
|
setTempPublishedList(tempData);
|
||||||
}
|
}
|
||||||
} catch (error) {}
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const publishComment = async () => {
|
const publishComment = async () => {
|
||||||
@ -235,7 +241,9 @@ export const AnnouncementDiscussion = ({
|
|||||||
for (const data of responseData) {
|
for (const data of responseData) {
|
||||||
getData({ name: data.name, identifier: data.identifier }, isPrivate);
|
getData({ name: data.name, identifier: data.identifier }, isPrivate);
|
||||||
}
|
}
|
||||||
} catch (error) {}
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const combinedListTempAndReal = useMemo(() => {
|
const combinedListTempAndReal = useMemo(() => {
|
||||||
|
@ -7,13 +7,12 @@ import React, {
|
|||||||
useState,
|
useState,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
|
|
||||||
import { objectToBase64 } from '../../qdn/encryption/group-encryption';
|
|
||||||
import { ChatList } from './ChatList';
|
import { ChatList } from './ChatList';
|
||||||
import '@chatscope/chat-ui-kit-styles/dist/default/styles.min.css';
|
import '@chatscope/chat-ui-kit-styles/dist/default/styles.min.css';
|
||||||
import Tiptap from './TipTap';
|
import Tiptap from './TipTap';
|
||||||
import { CustomButton } from '../../styles/App-styles';
|
import { CustomButton } from '../../styles/App-styles';
|
||||||
import CircularProgress from '@mui/material/CircularProgress';
|
import CircularProgress from '@mui/material/CircularProgress';
|
||||||
import { Box, ButtonBase, Input, Typography } from '@mui/material';
|
import { Box, ButtonBase, Input, Typography, useTheme } from '@mui/material';
|
||||||
import { LoadingSnackbar } from '../Snackbar/LoadingSnackbar';
|
import { LoadingSnackbar } from '../Snackbar/LoadingSnackbar';
|
||||||
import { getNameInfo } from '../Group/Group';
|
import { getNameInfo } from '../Group/Group';
|
||||||
import { Spacer } from '../../common/Spacer';
|
import { Spacer } from '../../common/Spacer';
|
||||||
@ -36,7 +35,7 @@ import ArrowBackIcon from '@mui/icons-material/ArrowBack';
|
|||||||
import ShortUniqueId from 'short-unique-id';
|
import ShortUniqueId from 'short-unique-id';
|
||||||
import { ReturnIcon } from '../../assets/Icons/ReturnIcon';
|
import { ReturnIcon } from '../../assets/Icons/ReturnIcon';
|
||||||
import { ExitIcon } from '../../assets/Icons/ExitIcon';
|
import { ExitIcon } from '../../assets/Icons/ExitIcon';
|
||||||
import { MessageItem, ReplyPreview } from './MessageItem';
|
import { ReplyPreview } from './MessageItem';
|
||||||
|
|
||||||
const uid = new ShortUniqueId({ length: 5 });
|
const uid = new ShortUniqueId({ length: 5 });
|
||||||
|
|
||||||
@ -52,6 +51,7 @@ export const ChatDirect = ({
|
|||||||
close,
|
close,
|
||||||
setMobileViewModeKeepOpen,
|
setMobileViewModeKeepOpen,
|
||||||
}) => {
|
}) => {
|
||||||
|
const theme = useTheme();
|
||||||
const { queueChats, addToQueue, processWithNewMessages } = useMessageQueue();
|
const { queueChats, addToQueue, processWithNewMessages } = useMessageQueue();
|
||||||
const [isFocusedParent, setIsFocusedParent] = useState(false);
|
const [isFocusedParent, setIsFocusedParent] = useState(false);
|
||||||
const [onEditMessage, setOnEditMessage] = useState(null);
|
const [onEditMessage, setOnEditMessage] = useState(null);
|
||||||
@ -87,7 +87,9 @@ export const ChatDirect = ({
|
|||||||
const publicKey = await getPublicKey(address);
|
const publicKey = await getPublicKey(address);
|
||||||
if (publicKeyOfRecipientRef.current !== selectedDirect?.address) return;
|
if (publicKeyOfRecipientRef.current !== selectedDirect?.address) return;
|
||||||
setPublicKeyOfRecipient(publicKey);
|
setPublicKeyOfRecipient(publicKey);
|
||||||
} catch (error) {}
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const tempMessages = useMemo(() => {
|
const tempMessages = useMemo(() => {
|
||||||
@ -167,6 +169,7 @@ export const ChatDirect = ({
|
|||||||
text: item.message,
|
text: item.message,
|
||||||
unread: item?.sender === myAddress ? false : true,
|
unread: item?.sender === myAddress ? false : true,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
setMessages((prev) => [...prev, ...formatted]);
|
setMessages((prev) => [...prev, ...formatted]);
|
||||||
setChatReferences((prev) => {
|
setChatReferences((prev) => {
|
||||||
const organizedChatReferences = { ...prev };
|
const organizedChatReferences = { ...prev };
|
||||||
@ -183,7 +186,9 @@ export const ChatDirect = ({
|
|||||||
{}),
|
{}),
|
||||||
edit: item,
|
edit: item,
|
||||||
};
|
};
|
||||||
} catch (error) {}
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
return organizedChatReferences;
|
return organizedChatReferences;
|
||||||
});
|
});
|
||||||
@ -214,7 +219,9 @@ export const ChatDirect = ({
|
|||||||
{}),
|
{}),
|
||||||
edit: item,
|
edit: item,
|
||||||
};
|
};
|
||||||
} catch (error) {}
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
return organizedChatReferences;
|
return organizedChatReferences;
|
||||||
});
|
});
|
||||||
@ -227,7 +234,9 @@ export const ChatDirect = ({
|
|||||||
rej(error.message || 'An error occurred');
|
rej(error.message || 'An error occurred');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
} catch (error) {}
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const forceCloseWebSocket = () => {
|
const forceCloseWebSocket = () => {
|
||||||
@ -368,7 +377,6 @@ export const ChatDirect = ({
|
|||||||
senderName: myName,
|
senderName: myName,
|
||||||
});
|
});
|
||||||
setNewChat(null);
|
setNewChat(null);
|
||||||
|
|
||||||
window
|
window
|
||||||
.sendMessage('addTimestampEnterChat', {
|
.sendMessage('addTimestampEnterChat', {
|
||||||
timestamp: Date.now(),
|
timestamp: Date.now(),
|
||||||
@ -396,7 +404,6 @@ export const ChatDirect = ({
|
|||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new Error(error);
|
throw new Error(error);
|
||||||
} finally {
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const clearEditorContent = () => {
|
const clearEditorContent = () => {
|
||||||
@ -537,39 +544,39 @@ export const ChatDirect = ({
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
height: isMobile ? '100%' : '100vh',
|
background: theme.palette.background.default,
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
|
height: isMobile ? '100%' : '100vh',
|
||||||
width: '100%',
|
width: '100%',
|
||||||
background: !isMobile && 'var(--bg-2)',
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{!isMobile && (
|
{!isMobile && (
|
||||||
<Box
|
<Box
|
||||||
onClick={close}
|
onClick={close}
|
||||||
sx={{
|
sx={{
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
gap: '5px',
|
alignSelf: 'center',
|
||||||
|
background: theme.palette.background.default,
|
||||||
|
borderRadius: '3px',
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
|
display: 'flex',
|
||||||
|
gap: '5px',
|
||||||
|
margin: '10px 0px',
|
||||||
padding: '4px 6px',
|
padding: '4px 6px',
|
||||||
width: 'fit-content',
|
width: 'fit-content',
|
||||||
borderRadius: '3px',
|
|
||||||
background: 'rgb(35, 36, 40)',
|
|
||||||
margin: '10px 0px',
|
|
||||||
alignSelf: 'center',
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ArrowBackIcon
|
<ArrowBackIcon
|
||||||
sx={{
|
sx={{
|
||||||
color: 'white',
|
color: theme.palette.text.primary,
|
||||||
fontSize: isMobile ? '20px' : '20px',
|
fontSize: '20px',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Typography
|
<Typography
|
||||||
sx={{
|
sx={{
|
||||||
color: 'white',
|
color: theme.palette.text.primary,
|
||||||
fontSize: isMobile ? '14px' : '14px',
|
fontSize: '14px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Close Direct Chat
|
Close Direct Chat
|
||||||
@ -579,26 +586,26 @@ export const ChatDirect = ({
|
|||||||
{isMobile && (
|
{isMobile && (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
width: '100%',
|
display: 'flex',
|
||||||
marginTop: '14px',
|
|
||||||
justifyContent: 'center',
|
|
||||||
height: '15px',
|
height: '15px',
|
||||||
|
justifyContent: 'center',
|
||||||
|
marginTop: '14px',
|
||||||
|
width: '100%',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
|
display: 'flex',
|
||||||
justifyContent: 'space-between',
|
justifyContent: 'space-between',
|
||||||
width: '320px',
|
width: '320px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
|
display: 'flex',
|
||||||
width: '50px',
|
width: '50px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@ -623,10 +630,10 @@ export const ChatDirect = ({
|
|||||||
</Typography>
|
</Typography>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
width: '50px',
|
display: 'flex',
|
||||||
justifyContent: 'flex-end',
|
justifyContent: 'flex-end',
|
||||||
|
width: '50px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ButtonBase
|
<ButtonBase
|
||||||
@ -670,42 +677,40 @@ export const ChatDirect = ({
|
|||||||
|
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
// position: 'fixed',
|
backgroundColor: theme.palette.background.default,
|
||||||
// bottom: '0px',
|
bottom: isFocusedParent ? '0px' : 'unset',
|
||||||
backgroundColor: '#232428',
|
boxSizing: 'border-box',
|
||||||
minHeight: isMobile ? '0px' : '150px',
|
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
|
flexShrink: 0,
|
||||||
|
minHeight: '150px',
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
width: '100%',
|
|
||||||
boxSizing: 'border-box',
|
|
||||||
padding: isMobile ? '10px' : '20px',
|
padding: isMobile ? '10px' : '20px',
|
||||||
position: isFocusedParent ? 'fixed' : 'relative',
|
position: isFocusedParent ? 'fixed' : 'relative',
|
||||||
bottom: isFocusedParent ? '0px' : 'unset',
|
|
||||||
top: isFocusedParent ? '0px' : 'unset',
|
top: isFocusedParent ? '0px' : 'unset',
|
||||||
|
width: '100%',
|
||||||
zIndex: isFocusedParent ? 5 : 'unset',
|
zIndex: isFocusedParent ? 5 : 'unset',
|
||||||
flexShrink: 0,
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
flexGrow: isMobile && 1,
|
flexGrow: 1,
|
||||||
overflow: !isMobile && 'auto',
|
|
||||||
flexShrink: 0,
|
flexShrink: 0,
|
||||||
width: 'calc(100% - 100px)',
|
|
||||||
justifyContent: 'flex-end',
|
justifyContent: 'flex-end',
|
||||||
|
overflow: 'auto',
|
||||||
|
width: 'calc(100% - 100px)',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{replyMessage && (
|
{replyMessage && (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
|
alignItems: 'flex-start',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
gap: '5px',
|
gap: '5px',
|
||||||
alignItems: 'flex-start',
|
|
||||||
width: 'calc(100% - 100px)',
|
|
||||||
justifyContent: 'flex-end',
|
justifyContent: 'flex-end',
|
||||||
|
width: 'calc(100% - 100px)',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ReplyPreview message={replyMessage} />
|
<ReplyPreview message={replyMessage} />
|
||||||
@ -723,9 +728,9 @@ export const ChatDirect = ({
|
|||||||
{onEditMessage && (
|
{onEditMessage && (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
|
alignItems: 'flex-start',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
gap: '5px',
|
gap: '5px',
|
||||||
alignItems: 'flex-start',
|
|
||||||
width: '100%',
|
width: '100%',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@ -735,7 +740,6 @@ export const ChatDirect = ({
|
|||||||
onClick={() => {
|
onClick={() => {
|
||||||
setReplyMessage(null);
|
setReplyMessage(null);
|
||||||
setOnEditMessage(null);
|
setOnEditMessage(null);
|
||||||
|
|
||||||
clearEditorContent();
|
clearEditorContent();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@ -756,9 +760,9 @@ export const ChatDirect = ({
|
|||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
width: '100%',
|
|
||||||
justifyContent: 'flex-start',
|
justifyContent: 'flex-start',
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
|
width: '100%',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography
|
<Typography
|
||||||
@ -774,12 +778,11 @@ export const ChatDirect = ({
|
|||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
width: '100px',
|
flexShrink: 0,
|
||||||
|
|
||||||
gap: '10px',
|
gap: '10px',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
flexShrink: 0,
|
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
|
width: '100px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<CustomButton
|
<CustomButton
|
||||||
@ -788,26 +791,28 @@ export const ChatDirect = ({
|
|||||||
sendMessage();
|
sendMessage();
|
||||||
}}
|
}}
|
||||||
style={{
|
style={{
|
||||||
marginTop: 'auto',
|
|
||||||
alignSelf: 'center',
|
alignSelf: 'center',
|
||||||
|
background: isSending
|
||||||
|
? theme.palette.background.default
|
||||||
|
: theme.palette.background.paper,
|
||||||
cursor: isSending ? 'default' : 'pointer',
|
cursor: isSending ? 'default' : 'pointer',
|
||||||
background: isSending && 'rgba(0, 0, 0, 0.8)',
|
|
||||||
flexShrink: 0,
|
flexShrink: 0,
|
||||||
|
marginTop: 'auto',
|
||||||
|
minWidth: 'auto',
|
||||||
padding: '5px',
|
padding: '5px',
|
||||||
width: '100px',
|
width: '100px',
|
||||||
minWidth: 'auto',
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{isSending && (
|
{isSending && (
|
||||||
<CircularProgress
|
<CircularProgress
|
||||||
size={18}
|
size={18}
|
||||||
sx={{
|
sx={{
|
||||||
|
color: theme.palette.text.primary,
|
||||||
|
left: '50%',
|
||||||
|
marginLeft: '-12px',
|
||||||
|
marginTop: '-12px',
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
top: '50%',
|
top: '50%',
|
||||||
left: '50%',
|
|
||||||
marginTop: '-12px',
|
|
||||||
marginLeft: '-12px',
|
|
||||||
color: 'white',
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@ -815,12 +820,14 @@ export const ChatDirect = ({
|
|||||||
</CustomButton>
|
</CustomButton>
|
||||||
</Box>
|
</Box>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<LoadingSnackbar
|
<LoadingSnackbar
|
||||||
open={isLoading}
|
open={isLoading}
|
||||||
info={{
|
info={{
|
||||||
message: 'Loading chat... please wait.',
|
message: 'Loading chat... please wait.',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<CustomizedSnackbars
|
<CustomizedSnackbars
|
||||||
open={openSnack}
|
open={openSnack}
|
||||||
setOpen={setOpenSnack}
|
setOpen={setOpenSnack}
|
||||||
|
@ -128,7 +128,9 @@ export const ChatGroup = ({
|
|||||||
rej(error.message || 'An error occurred');
|
rej(error.message || 'An error occurred');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
} catch (error) {}
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -192,7 +194,9 @@ export const ChatGroup = ({
|
|||||||
handleSecretKeyCreationInProgress();
|
handleSecretKeyCreationInProgress();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} catch (error) {}
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -578,7 +582,9 @@ export const ChatGroup = ({
|
|||||||
rej(error.message || 'An error occurred');
|
rej(error.message || 'An error occurred');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
} catch (error) {}
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const forceCloseWebSocket = () => {
|
const forceCloseWebSocket = () => {
|
||||||
@ -621,7 +627,9 @@ export const ChatGroup = ({
|
|||||||
middletierFunc(JSON.parse(e.data), selectedGroup);
|
middletierFunc(JSON.parse(e.data), selectedGroup);
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
}
|
}
|
||||||
} catch (error) {}
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
socketRef.current.onclose = () => {
|
socketRef.current.onclose = () => {
|
||||||
clearTimeout(groupSocketTimeoutRef.current);
|
clearTimeout(groupSocketTimeoutRef.current);
|
||||||
@ -700,7 +708,9 @@ export const ChatGroup = ({
|
|||||||
rej(error.message || 'An error occurred');
|
rej(error.message || 'An error occurred');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
} catch (error) {}
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const sendChatGroup = async ({
|
const sendChatGroup = async ({
|
||||||
|
@ -106,7 +106,9 @@ export const decryptPublishes = async (encryptedMessages: any[], secretKey) => {
|
|||||||
rej(error.message || 'An error occurred');
|
rej(error.message || 'An error occurred');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
} catch (error) {}
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
export const handleUnencryptedPublishes = (publishes) => {
|
export const handleUnencryptedPublishes = (publishes) => {
|
||||||
let publishesData = [];
|
let publishesData = [];
|
||||||
@ -117,7 +119,9 @@ export const handleUnencryptedPublishes = (publishes) => {
|
|||||||
if (decodedData) {
|
if (decodedData) {
|
||||||
publishesData.push({ decryptedData: decodedData });
|
publishesData.push({ decryptedData: decodedData });
|
||||||
}
|
}
|
||||||
} catch (error) {}
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
return publishesData;
|
return publishesData;
|
||||||
};
|
};
|
||||||
@ -236,7 +240,9 @@ export const GroupAnnouncements = ({
|
|||||||
rej(error.message || 'An error occurred');
|
rej(error.message || 'An error occurred');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
} catch (error) {}
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const publishAnc = async ({ encryptedData, identifier }: any) => {
|
const publishAnc = async ({ encryptedData, identifier }: any) => {
|
||||||
@ -286,7 +292,9 @@ export const GroupAnnouncements = ({
|
|||||||
});
|
});
|
||||||
setTempPublishedList(tempData);
|
setTempPublishedList(tempData);
|
||||||
}
|
}
|
||||||
} catch (error) {}
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const publishAnnouncement = async () => {
|
const publishAnnouncement = async () => {
|
||||||
@ -422,7 +430,9 @@ export const GroupAnnouncements = ({
|
|||||||
isPrivate
|
isPrivate
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} catch (error) {}
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const interval = useRef<any>(null);
|
const interval = useRef<any>(null);
|
||||||
@ -449,7 +459,9 @@ export const GroupAnnouncements = ({
|
|||||||
},
|
},
|
||||||
isPrivate
|
isPrivate
|
||||||
);
|
);
|
||||||
} catch (error) {}
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
setAnnouncements(responseData);
|
setAnnouncements(responseData);
|
||||||
return;
|
return;
|
||||||
@ -467,7 +479,9 @@ export const GroupAnnouncements = ({
|
|||||||
{ name: data.name, identifier: data.identifier },
|
{ name: data.name, identifier: data.identifier },
|
||||||
isPrivate
|
isPrivate
|
||||||
);
|
);
|
||||||
} catch (error) {}
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
setAnnouncements((prev) => [...newArray, ...prev]);
|
setAnnouncements((prev) => [...newArray, ...prev]);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
@ -1,42 +1,46 @@
|
|||||||
import React, { useEffect, useMemo, useRef, useState } from "react";
|
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { getBaseApiReact } from "../../App";
|
import { getBaseApiReact } from '../../App';
|
||||||
|
|
||||||
|
import { CustomizedSnackbars } from '../Snackbar/Snackbar';
|
||||||
|
|
||||||
import { CustomizedSnackbars } from "../Snackbar/Snackbar";
|
import { extractComponents } from '../Chat/MessageDisplay';
|
||||||
|
import { executeEvent } from '../../utils/events';
|
||||||
|
|
||||||
import { extractComponents } from "../Chat/MessageDisplay";
|
import { base64ToBlobUrl } from '../../utils/fileReading';
|
||||||
import { executeEvent } from "../../utils/events";
|
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil';
|
||||||
|
import {
|
||||||
import { base64ToBlobUrl } from "../../utils/fileReading";
|
blobControllerAtom,
|
||||||
import { useRecoilState, useRecoilValue, useSetRecoilState } from "recoil";
|
blobKeySelector,
|
||||||
import { blobControllerAtom, blobKeySelector, resourceKeySelector, selectedGroupIdAtom } from "../../atoms/global";
|
resourceKeySelector,
|
||||||
import { parseQortalLink } from "./embed-utils";
|
selectedGroupIdAtom,
|
||||||
import { PollCard } from "./PollEmbed";
|
} from '../../atoms/global';
|
||||||
import { ImageCard } from "./ImageEmbed";
|
import { parseQortalLink } from './embed-utils';
|
||||||
import { AttachmentCard } from "./AttachmentEmbed";
|
import { PollCard } from './PollEmbed';
|
||||||
import { decodeIfEncoded } from "../../utils/decode";
|
import { ImageCard } from './ImageEmbed';
|
||||||
|
import { AttachmentCard } from './AttachmentEmbed';
|
||||||
|
import { decodeIfEncoded } from '../../utils/decode';
|
||||||
|
|
||||||
const getPoll = async (name) => {
|
const getPoll = async (name) => {
|
||||||
const pollName = name;
|
const pollName = name;
|
||||||
const url = `${getBaseApiReact()}/polls/${pollName}`;
|
const url = `${getBaseApiReact()}/polls/${pollName}`;
|
||||||
|
|
||||||
const response = await fetch(url, {
|
const response = await fetch(url, {
|
||||||
method: "GET",
|
method: 'GET',
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
'Content-Type': 'application/json',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const responseData = await response.json();
|
const responseData = await response.json();
|
||||||
if (responseData?.message?.includes("POLL_NO_EXISTS")) {
|
if (responseData?.message?.includes('POLL_NO_EXISTS')) {
|
||||||
throw new Error("POLL_NO_EXISTS");
|
throw new Error('POLL_NO_EXISTS');
|
||||||
} else if (responseData?.pollName) {
|
} else if (responseData?.pollName) {
|
||||||
const urlVotes = `${getBaseApiReact()}/polls/votes/${pollName}`;
|
const urlVotes = `${getBaseApiReact()}/polls/votes/${pollName}`;
|
||||||
|
|
||||||
const responseVotes = await fetch(urlVotes, {
|
const responseVotes = await fetch(urlVotes, {
|
||||||
method: "GET",
|
method: 'GET',
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
'Content-Type': 'application/json',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -49,56 +53,64 @@ const getPoll = async (name) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const Embed = ({ embedLink }) => {
|
export const Embed = ({ embedLink }) => {
|
||||||
const [errorMsg, setErrorMsg] = useState("");
|
const [errorMsg, setErrorMsg] = useState('');
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const [poll, setPoll] = useState(null);
|
const [poll, setPoll] = useState(null);
|
||||||
const [type, setType] = useState("");
|
const [type, setType] = useState('');
|
||||||
const hasFetched = useRef(false);
|
const hasFetched = useRef(false);
|
||||||
const [openSnack, setOpenSnack] = useState(false);
|
const [openSnack, setOpenSnack] = useState(false);
|
||||||
const [infoSnack, setInfoSnack] = useState(null);
|
const [infoSnack, setInfoSnack] = useState(null);
|
||||||
const [external, setExternal] = useState(null);
|
const [external, setExternal] = useState(null);
|
||||||
const [imageUrl, setImageUrl] = useState("");
|
const [imageUrl, setImageUrl] = useState('');
|
||||||
const [parsedData, setParsedData] = useState(null);
|
const [parsedData, setParsedData] = useState(null);
|
||||||
const setBlobs = useSetRecoilState(blobControllerAtom);
|
const setBlobs = useSetRecoilState(blobControllerAtom);
|
||||||
const [selectedGroupId] = useRecoilState(selectedGroupIdAtom)
|
const [selectedGroupId] = useRecoilState(selectedGroupIdAtom);
|
||||||
const resourceData = useMemo(()=> {
|
const resourceData = useMemo(() => {
|
||||||
const parsedDataOnTheFly = parseQortalLink(embedLink);
|
const parsedDataOnTheFly = parseQortalLink(embedLink);
|
||||||
if(parsedDataOnTheFly?.service && parsedDataOnTheFly?.name && parsedDataOnTheFly?.identifier){
|
if (
|
||||||
|
parsedDataOnTheFly?.service &&
|
||||||
|
parsedDataOnTheFly?.name &&
|
||||||
|
parsedDataOnTheFly?.identifier
|
||||||
|
) {
|
||||||
return {
|
return {
|
||||||
service : parsedDataOnTheFly?.service,
|
service: parsedDataOnTheFly?.service,
|
||||||
name: parsedDataOnTheFly?.name,
|
name: parsedDataOnTheFly?.name,
|
||||||
identifier: parsedDataOnTheFly?.identifier,
|
identifier: parsedDataOnTheFly?.identifier,
|
||||||
fileName: parsedDataOnTheFly?.fileName ? decodeURIComponent(parsedDataOnTheFly?.fileName) : null,
|
fileName: parsedDataOnTheFly?.fileName
|
||||||
mimeType: parsedDataOnTheFly?.mimeType ? decodeURIComponent(parsedDataOnTheFly?.mimeType) : null,
|
? decodeURIComponent(parsedDataOnTheFly?.fileName)
|
||||||
key: parsedDataOnTheFly?.key ? decodeURIComponent(parsedDataOnTheFly?.key) : null,
|
: null,
|
||||||
}
|
mimeType: parsedDataOnTheFly?.mimeType
|
||||||
|
? decodeURIComponent(parsedDataOnTheFly?.mimeType)
|
||||||
|
: null,
|
||||||
|
key: parsedDataOnTheFly?.key
|
||||||
|
? decodeURIComponent(parsedDataOnTheFly?.key)
|
||||||
|
: null,
|
||||||
|
};
|
||||||
} else {
|
} else {
|
||||||
return null
|
return null;
|
||||||
}
|
}
|
||||||
}, [embedLink])
|
}, [embedLink]);
|
||||||
|
|
||||||
const keyIdentifier = useMemo(()=> {
|
const keyIdentifier = useMemo(() => {
|
||||||
|
if (resourceData) {
|
||||||
if(resourceData){
|
return `${resourceData.service}-${resourceData.name}-${resourceData.identifier}`;
|
||||||
return `${resourceData.service}-${resourceData.name}-${resourceData.identifier}`
|
|
||||||
} else {
|
} else {
|
||||||
return undefined
|
return undefined;
|
||||||
}
|
}
|
||||||
}, [resourceData])
|
}, [resourceData]);
|
||||||
const blobUrl = useRecoilValue(blobKeySelector(keyIdentifier));
|
const blobUrl = useRecoilValue(blobKeySelector(keyIdentifier));
|
||||||
|
|
||||||
const handlePoll = async (parsedData) => {
|
const handlePoll = async (parsedData) => {
|
||||||
try {
|
try {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
setErrorMsg("");
|
setErrorMsg('');
|
||||||
setType("POLL");
|
setType('POLL');
|
||||||
if (!parsedData?.name)
|
if (!parsedData?.name)
|
||||||
throw new Error("Invalid poll embed link. Missing name.");
|
throw new Error('Invalid poll embed link. Missing name.');
|
||||||
const pollRes = await getPoll(parsedData.name);
|
const pollRes = await getPoll(parsedData.name);
|
||||||
setPoll(pollRes);
|
setPoll(pollRes);
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setErrorMsg(error?.message || "Invalid embed link");
|
setErrorMsg(error?.message || 'Invalid embed link');
|
||||||
} finally {
|
} finally {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
}
|
}
|
||||||
@ -106,8 +118,8 @@ export const Embed = ({ embedLink }) => {
|
|||||||
|
|
||||||
const getImage = async ({ identifier, name, service }, key, parsedData) => {
|
const getImage = async ({ identifier, name, service }, key, parsedData) => {
|
||||||
try {
|
try {
|
||||||
if(blobUrl?.blobUrl){
|
if (blobUrl?.blobUrl) {
|
||||||
return blobUrl?.blobUrl
|
return blobUrl?.blobUrl;
|
||||||
}
|
}
|
||||||
let numberOfTries = 0;
|
let numberOfTries = 0;
|
||||||
let imageFinalUrl = null;
|
let imageFinalUrl = null;
|
||||||
@ -116,76 +128,76 @@ export const Embed = ({ embedLink }) => {
|
|||||||
const urlStatus = `${getBaseApiReact()}/arbitrary/resource/status/${service}/${name}/${identifier}?build=true`;
|
const urlStatus = `${getBaseApiReact()}/arbitrary/resource/status/${service}/${name}/${identifier}?build=true`;
|
||||||
|
|
||||||
const responseStatus = await fetch(urlStatus, {
|
const responseStatus = await fetch(urlStatus, {
|
||||||
method: "GET",
|
method: 'GET',
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
'Content-Type': 'application/json',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const responseData = await responseStatus.json();
|
const responseData = await responseStatus.json();
|
||||||
if (responseData?.status === "READY") {
|
if (responseData?.status === 'READY') {
|
||||||
if (parsedData?.encryptionType) {
|
if (parsedData?.encryptionType) {
|
||||||
const urlData = `${getBaseApiReact()}/arbitrary/${service}/${name}/${identifier}?encoding=base64`;
|
const urlData = `${getBaseApiReact()}/arbitrary/${service}/${name}/${identifier}?encoding=base64`;
|
||||||
|
|
||||||
const responseData = await fetch(urlData, {
|
const responseData = await fetch(urlData, {
|
||||||
method: "GET",
|
method: 'GET',
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
'Content-Type': 'application/json',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const data = await responseData.text();
|
const data = await responseData.text();
|
||||||
if (data) {
|
if (data) {
|
||||||
let decryptedData
|
let decryptedData;
|
||||||
try {
|
try {
|
||||||
if(key && encryptionType === 'private'){
|
if (key && encryptionType === 'private') {
|
||||||
decryptedData = await window.sendMessage(
|
decryptedData = await window.sendMessage(
|
||||||
"DECRYPT_DATA_WITH_SHARING_KEY",
|
'DECRYPT_DATA_WITH_SHARING_KEY',
|
||||||
|
|
||||||
{
|
{
|
||||||
encryptedData: data,
|
encryptedData: data,
|
||||||
key: decodeURIComponent(key),
|
key: decodeURIComponent(key),
|
||||||
}
|
}
|
||||||
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if(encryptionType === 'group'){
|
if (encryptionType === 'group') {
|
||||||
|
|
||||||
decryptedData = await window.sendMessage(
|
decryptedData = await window.sendMessage(
|
||||||
"DECRYPT_QORTAL_GROUP_DATA",
|
'DECRYPT_QORTAL_GROUP_DATA',
|
||||||
|
|
||||||
{
|
|
||||||
data64: data,
|
|
||||||
groupId: selectedGroupId,
|
|
||||||
}
|
|
||||||
|
|
||||||
);
|
|
||||||
|
|
||||||
}
|
{
|
||||||
|
data64: data,
|
||||||
|
groupId: selectedGroupId,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new Error('Unable to decrypt')
|
throw new Error('Unable to decrypt');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!decryptedData || decryptedData?.error) throw new Error("Could not decrypt data");
|
if (!decryptedData || decryptedData?.error)
|
||||||
imageFinalUrl = base64ToBlobUrl(decryptedData, parsedData?.mimeType ? decodeURIComponent(parsedData?.mimeType) : undefined)
|
throw new Error('Could not decrypt data');
|
||||||
setBlobs((prev=> {
|
imageFinalUrl = base64ToBlobUrl(
|
||||||
|
decryptedData,
|
||||||
|
parsedData?.mimeType
|
||||||
|
? decodeURIComponent(parsedData?.mimeType)
|
||||||
|
: undefined
|
||||||
|
);
|
||||||
|
setBlobs((prev) => {
|
||||||
return {
|
return {
|
||||||
...prev,
|
...prev,
|
||||||
[`${service}-${name}-${identifier}`]: {
|
[`${service}-${name}-${identifier}`]: {
|
||||||
blobUrl: imageFinalUrl,
|
blobUrl: imageFinalUrl,
|
||||||
timestamp: Date.now()
|
timestamp: Date.now(),
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
}))
|
});
|
||||||
} else {
|
} else {
|
||||||
throw new Error('No data for image')
|
throw new Error('No data for image');
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
imageFinalUrl = `${getBaseApiReact()}/arbitrary/${service}/${name}/${identifier}?async=true`;
|
imageFinalUrl = `${getBaseApiReact()}/arbitrary/${service}/${name}/${identifier}?async=true`;
|
||||||
|
|
||||||
// If parsedData is used here, it must be defined somewhere
|
// If parsedData is used here, it must be defined somewhere
|
||||||
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -203,18 +215,19 @@ export const Embed = ({ embedLink }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (imageFinalUrl) {
|
if (imageFinalUrl) {
|
||||||
|
|
||||||
return imageFinalUrl;
|
return imageFinalUrl;
|
||||||
} else {
|
} else {
|
||||||
setErrorMsg(
|
setErrorMsg(
|
||||||
"Unable to download IMAGE. Please try again later by clicking the refresh button"
|
'Unable to download IMAGE. Please try again later by clicking the refresh button'
|
||||||
);
|
);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error fetching image:", error);
|
console.error('Error fetching image:', error);
|
||||||
setErrorMsg(
|
setErrorMsg(
|
||||||
error?.error || error?.message || "An unexpected error occurred while trying to download the image"
|
error?.error ||
|
||||||
|
error?.message ||
|
||||||
|
'An unexpected error occurred while trying to download the image'
|
||||||
);
|
);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -223,25 +236,27 @@ export const Embed = ({ embedLink }) => {
|
|||||||
const handleImage = async (parsedData) => {
|
const handleImage = async (parsedData) => {
|
||||||
try {
|
try {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
setErrorMsg("");
|
setErrorMsg('');
|
||||||
if (!parsedData?.name || !parsedData?.service || !parsedData?.identifier)
|
if (!parsedData?.name || !parsedData?.service || !parsedData?.identifier)
|
||||||
throw new Error("Invalid image embed link. Missing param.");
|
throw new Error('Invalid image embed link. Missing param.');
|
||||||
let image = await getImage({
|
let image = await getImage(
|
||||||
name: parsedData.name,
|
{
|
||||||
service: parsedData.service,
|
name: parsedData.name,
|
||||||
identifier: parsedData?.identifier,
|
service: parsedData.service,
|
||||||
}, parsedData?.key, parsedData);
|
identifier: parsedData?.identifier,
|
||||||
|
},
|
||||||
setImageUrl(image);
|
parsedData?.key,
|
||||||
|
parsedData
|
||||||
|
);
|
||||||
|
|
||||||
|
setImageUrl(image);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setErrorMsg(error?.message || "Invalid embed link");
|
setErrorMsg(error?.message || 'Invalid embed link');
|
||||||
} finally {
|
} finally {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const handleLink = () => {
|
const handleLink = () => {
|
||||||
try {
|
try {
|
||||||
const parsedData = parseQortalLink(embedLink);
|
const parsedData = parseQortalLink(embedLink);
|
||||||
@ -254,28 +269,26 @@ export const Embed = ({ embedLink }) => {
|
|||||||
setExternal(res);
|
setExternal(res);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {}
|
||||||
|
|
||||||
}
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case "POLL":
|
case 'POLL':
|
||||||
{
|
{
|
||||||
handlePoll(parsedData);
|
handlePoll(parsedData);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "IMAGE":
|
case 'IMAGE':
|
||||||
setType("IMAGE");
|
setType('IMAGE');
|
||||||
|
|
||||||
|
break;
|
||||||
|
case 'ATTACHMENT':
|
||||||
|
setType('ATTACHMENT');
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case "ATTACHMENT":
|
|
||||||
setType("ATTACHMENT");
|
|
||||||
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setErrorMsg(error?.message || "Invalid embed link");
|
setErrorMsg(error?.message || 'Invalid embed link');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -284,13 +297,13 @@ export const Embed = ({ embedLink }) => {
|
|||||||
const parsedData = parseQortalLink(embedLink);
|
const parsedData = parseQortalLink(embedLink);
|
||||||
handleImage(parsedData);
|
handleImage(parsedData);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setErrorMsg(error?.message || "Invalid embed link");
|
setErrorMsg(error?.message || 'Invalid embed link');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const openExternal = () => {
|
const openExternal = () => {
|
||||||
executeEvent("addTab", { data: external });
|
executeEvent('addTab', { data: external });
|
||||||
executeEvent("open-apps-mode", {});
|
executeEvent('open-apps-mode', {});
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -299,8 +312,6 @@ export const Embed = ({ embedLink }) => {
|
|||||||
hasFetched.current = true;
|
hasFetched.current = true;
|
||||||
}, [embedLink]);
|
}, [embedLink]);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const resourceDetails = useRecoilValue(resourceKeySelector(keyIdentifier));
|
const resourceDetails = useRecoilValue(resourceKeySelector(keyIdentifier));
|
||||||
|
|
||||||
const { parsedType, encryptionType } = useMemo(() => {
|
const { parsedType, encryptionType } = useMemo(() => {
|
||||||
@ -312,15 +323,17 @@ export const Embed = ({ embedLink }) => {
|
|||||||
parsedType = parsedDataOnTheFly.type;
|
parsedType = parsedDataOnTheFly.type;
|
||||||
}
|
}
|
||||||
if (parsedDataOnTheFly?.encryptionType) {
|
if (parsedDataOnTheFly?.encryptionType) {
|
||||||
encryptionType = parsedDataOnTheFly?.encryptionType
|
encryptionType = parsedDataOnTheFly?.encryptionType;
|
||||||
}
|
}
|
||||||
} catch (error) {}
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
return { parsedType, encryptionType };
|
return { parsedType, encryptionType };
|
||||||
}, [embedLink]);
|
}, [embedLink]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{parsedType === "POLL" && (
|
{parsedType === 'POLL' && (
|
||||||
<PollCard
|
<PollCard
|
||||||
poll={poll}
|
poll={poll}
|
||||||
refresh={handleLink}
|
refresh={handleLink}
|
||||||
@ -332,7 +345,7 @@ export const Embed = ({ embedLink }) => {
|
|||||||
errorMsg={errorMsg}
|
errorMsg={errorMsg}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{parsedType === "IMAGE" && (
|
{parsedType === 'IMAGE' && (
|
||||||
<ImageCard
|
<ImageCard
|
||||||
image={imageUrl}
|
image={imageUrl}
|
||||||
owner={parsedData?.name}
|
owner={parsedData?.name}
|
||||||
@ -349,8 +362,8 @@ export const Embed = ({ embedLink }) => {
|
|||||||
)}
|
)}
|
||||||
{parsedType === 'ATTACHMENT' && (
|
{parsedType === 'ATTACHMENT' && (
|
||||||
<AttachmentCard
|
<AttachmentCard
|
||||||
resourceData={resourceData}
|
resourceData={resourceData}
|
||||||
resourceDetails={resourceDetails}
|
resourceDetails={resourceDetails}
|
||||||
owner={parsedData?.name}
|
owner={parsedData?.name}
|
||||||
refresh={fetchImage}
|
refresh={fetchImage}
|
||||||
setInfoSnack={setInfoSnack}
|
setInfoSnack={setInfoSnack}
|
||||||
@ -373,11 +386,3 @@ export const Embed = ({ embedLink }) => {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import React, { useContext, useEffect, useState } from "react";
|
import React, { useContext, useEffect, useState } from 'react';
|
||||||
import { MyContext } from "../../App";
|
import { MyContext } from '../../App';
|
||||||
import {
|
import {
|
||||||
Card,
|
Card,
|
||||||
CardContent,
|
CardContent,
|
||||||
@ -12,384 +12,389 @@ import {
|
|||||||
Box,
|
Box,
|
||||||
ButtonBase,
|
ButtonBase,
|
||||||
Divider,
|
Divider,
|
||||||
|
} from '@mui/material';
|
||||||
} from "@mui/material";
|
import { getNameInfo } from '../Group/Group';
|
||||||
import { getNameInfo } from "../Group/Group";
|
import PollIcon from '@mui/icons-material/Poll';
|
||||||
import PollIcon from "@mui/icons-material/Poll";
|
import { getFee } from '../../background';
|
||||||
import { getFee } from "../../background";
|
import RefreshIcon from '@mui/icons-material/Refresh';
|
||||||
import RefreshIcon from "@mui/icons-material/Refresh";
|
import { Spacer } from '../../common/Spacer';
|
||||||
import { Spacer } from "../../common/Spacer";
|
import OpenInNewIcon from '@mui/icons-material/OpenInNew';
|
||||||
import OpenInNewIcon from "@mui/icons-material/OpenInNew";
|
import { CustomLoader } from '../../common/CustomLoader';
|
||||||
import { CustomLoader } from "../../common/CustomLoader";
|
|
||||||
|
|
||||||
|
|
||||||
export const PollCard = ({
|
export const PollCard = ({
|
||||||
poll,
|
poll,
|
||||||
setInfoSnack,
|
setInfoSnack,
|
||||||
setOpenSnack,
|
setOpenSnack,
|
||||||
refresh,
|
refresh,
|
||||||
openExternal,
|
openExternal,
|
||||||
external,
|
external,
|
||||||
isLoadingParent,
|
isLoadingParent,
|
||||||
errorMsg,
|
errorMsg,
|
||||||
}) => {
|
}) => {
|
||||||
const [selectedOption, setSelectedOption] = useState("");
|
const [selectedOption, setSelectedOption] = useState('');
|
||||||
const [ownerName, setOwnerName] = useState("");
|
const [ownerName, setOwnerName] = useState('');
|
||||||
const [showResults, setShowResults] = useState(false);
|
const [showResults, setShowResults] = useState(false);
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
const { show, userInfo } = useContext(MyContext);
|
const { show, userInfo } = useContext(MyContext);
|
||||||
const [isLoadingSubmit, setIsLoadingSubmit] = useState(false);
|
const [isLoadingSubmit, setIsLoadingSubmit] = useState(false);
|
||||||
const handleVote = async () => {
|
const handleVote = async () => {
|
||||||
const fee = await getFee("VOTE_ON_POLL");
|
const fee = await getFee('VOTE_ON_POLL');
|
||||||
|
|
||||||
await show({
|
await show({
|
||||||
message: `Do you accept this VOTE_ON_POLL transaction? POLLS are public!`,
|
message: `Do you accept this VOTE_ON_POLL transaction? POLLS are public!`,
|
||||||
publishFee: fee.fee + " QORT",
|
publishFee: fee.fee + ' QORT',
|
||||||
});
|
});
|
||||||
setIsLoadingSubmit(true);
|
setIsLoadingSubmit(true);
|
||||||
|
|
||||||
window
|
window
|
||||||
.sendMessage(
|
.sendMessage(
|
||||||
"voteOnPoll",
|
'voteOnPoll',
|
||||||
{
|
{
|
||||||
pollName: poll?.info?.pollName,
|
pollName: poll?.info?.pollName,
|
||||||
optionIndex: +selectedOption,
|
optionIndex: +selectedOption,
|
||||||
},
|
},
|
||||||
60000
|
60000
|
||||||
)
|
)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
setIsLoadingSubmit(false);
|
setIsLoadingSubmit(false);
|
||||||
if (response.error) {
|
if (response.error) {
|
||||||
setInfoSnack({
|
|
||||||
type: "error",
|
|
||||||
message: response?.error || "Unable to vote.",
|
|
||||||
});
|
|
||||||
setOpenSnack(true);
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
setInfoSnack({
|
|
||||||
type: "success",
|
|
||||||
message:
|
|
||||||
"Successfully voted. Please wait a couple minutes for the network to propogate the changes.",
|
|
||||||
});
|
|
||||||
setOpenSnack(true);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
setIsLoadingSubmit(false);
|
|
||||||
setInfoSnack({
|
setInfoSnack({
|
||||||
type: "error",
|
type: 'error',
|
||||||
message: error?.message || "Unable to vote.",
|
message: response?.error || 'Unable to vote.',
|
||||||
|
});
|
||||||
|
setOpenSnack(true);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
setInfoSnack({
|
||||||
|
type: 'success',
|
||||||
|
message:
|
||||||
|
'Successfully voted. Please wait a couple minutes for the network to propogate the changes.',
|
||||||
});
|
});
|
||||||
setOpenSnack(true);
|
setOpenSnack(true);
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const getName = async (owner) => {
|
|
||||||
try {
|
|
||||||
const res = await getNameInfo(owner);
|
|
||||||
if (res) {
|
|
||||||
setOwnerName(res);
|
|
||||||
}
|
}
|
||||||
} catch (error) {}
|
})
|
||||||
};
|
.catch((error) => {
|
||||||
|
setIsLoadingSubmit(false);
|
||||||
useEffect(() => {
|
setInfoSnack({
|
||||||
if (poll?.info?.owner) {
|
type: 'error',
|
||||||
getName(poll.info.owner);
|
message: error?.message || 'Unable to vote.',
|
||||||
|
});
|
||||||
|
setOpenSnack(true);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const getName = async (owner) => {
|
||||||
|
try {
|
||||||
|
const res = await getNameInfo(owner);
|
||||||
|
if (res) {
|
||||||
|
setOwnerName(res);
|
||||||
}
|
}
|
||||||
}, [poll?.info?.owner]);
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
return (
|
}
|
||||||
<Card
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (poll?.info?.owner) {
|
||||||
|
getName(poll.info.owner);
|
||||||
|
}
|
||||||
|
}, [poll?.info?.owner]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card
|
||||||
|
sx={{
|
||||||
|
backgroundColor: '#1F2023',
|
||||||
|
height: isOpen ? 'auto' : '150px',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
backgroundColor: "#1F2023",
|
display: 'flex',
|
||||||
height: isOpen ? "auto" : "150px",
|
alignItems: 'center',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
padding: '16px 16px 0px 16px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: "flex",
|
display: 'flex',
|
||||||
alignItems: "center",
|
alignItems: 'center',
|
||||||
justifyContent: "space-between",
|
gap: '10px',
|
||||||
padding: "16px 16px 0px 16px",
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box
|
<PollIcon
|
||||||
sx={{
|
sx={{
|
||||||
display: "flex",
|
color: 'white',
|
||||||
alignItems: "center",
|
|
||||||
gap: "10px",
|
|
||||||
}}
|
}}
|
||||||
>
|
/>
|
||||||
<PollIcon
|
<Typography>POLL embed</Typography>
|
||||||
|
</Box>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: '10px',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ButtonBase>
|
||||||
|
<RefreshIcon
|
||||||
|
onClick={refresh}
|
||||||
sx={{
|
sx={{
|
||||||
color: "white",
|
fontSize: '24px',
|
||||||
|
color: 'white',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Typography>POLL embed</Typography>
|
</ButtonBase>
|
||||||
</Box>
|
{external && (
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
gap: "10px",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ButtonBase>
|
<ButtonBase>
|
||||||
<RefreshIcon
|
<OpenInNewIcon
|
||||||
onClick={refresh}
|
onClick={openExternal}
|
||||||
sx={{
|
sx={{
|
||||||
fontSize: "24px",
|
fontSize: '24px',
|
||||||
color: "white",
|
color: 'white',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</ButtonBase>
|
</ButtonBase>
|
||||||
{external && (
|
)}
|
||||||
<ButtonBase>
|
|
||||||
<OpenInNewIcon
|
|
||||||
onClick={openExternal}
|
|
||||||
sx={{
|
|
||||||
fontSize: "24px",
|
|
||||||
color: "white",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</ButtonBase>
|
|
||||||
)}
|
|
||||||
</Box>
|
|
||||||
</Box>
|
</Box>
|
||||||
<Box
|
</Box>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
padding: '8px 16px 8px 16px',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography
|
||||||
sx={{
|
sx={{
|
||||||
padding: "8px 16px 8px 16px",
|
fontSize: '12px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography
|
Created by {ownerName || poll?.info?.owner}
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
<Divider sx={{ borderColor: 'rgb(255 255 255 / 10%)' }} />
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
width: '100%',
|
||||||
|
alignItems: 'center',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{!isOpen && !errorMsg && (
|
||||||
|
<>
|
||||||
|
<Spacer height="5px" />
|
||||||
|
<Button
|
||||||
|
size="small"
|
||||||
|
variant="contained"
|
||||||
|
sx={{
|
||||||
|
backgroundColor: 'var(--green)',
|
||||||
|
}}
|
||||||
|
onClick={() => {
|
||||||
|
setIsOpen(true);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Show poll
|
||||||
|
</Button>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{isLoadingParent && isOpen && (
|
||||||
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
fontSize: "12px",
|
width: '100%',
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'center',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Created by {ownerName || poll?.info?.owner}
|
{' '}
|
||||||
</Typography>
|
<CustomLoader />{' '}
|
||||||
</Box>
|
</Box>
|
||||||
<Divider sx={{ borderColor: "rgb(255 255 255 / 10%)" }} />
|
)}
|
||||||
<Box
|
{errorMsg && (
|
||||||
sx={{
|
<Box
|
||||||
display: "flex",
|
|
||||||
flexDirection: "column",
|
|
||||||
width: "100%",
|
|
||||||
alignItems: "center",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{!isOpen && !errorMsg && (
|
|
||||||
<>
|
|
||||||
<Spacer height="5px" />
|
|
||||||
<Button
|
|
||||||
size="small"
|
|
||||||
variant="contained"
|
|
||||||
sx={{
|
|
||||||
backgroundColor: "var(--green)",
|
|
||||||
}}
|
|
||||||
onClick={() => {
|
|
||||||
setIsOpen(true);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Show poll
|
|
||||||
</Button>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
{isLoadingParent && isOpen && (
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
width: "100%",
|
|
||||||
display: "flex",
|
|
||||||
justifyContent: "center",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{" "}
|
|
||||||
<CustomLoader />{" "}
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
{errorMsg && (
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
width: "100%",
|
|
||||||
display: "flex",
|
|
||||||
justifyContent: "center",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{" "}
|
|
||||||
<Typography
|
|
||||||
sx={{
|
|
||||||
fontSize: "14px",
|
|
||||||
color: "var(--danger)",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{errorMsg}
|
|
||||||
</Typography>{" "}
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
display: isOpen ? "block" : "none",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<CardHeader
|
|
||||||
title={poll?.info?.pollName}
|
|
||||||
subheader={poll?.info?.description}
|
|
||||||
sx={{
|
sx={{
|
||||||
"& .MuiCardHeader-title": {
|
width: '100%',
|
||||||
fontSize: "18px", // Custom font size for title
|
display: 'flex',
|
||||||
},
|
justifyContent: 'center',
|
||||||
}}
|
}}
|
||||||
/>
|
>
|
||||||
<CardContent>
|
{' '}
|
||||||
<Typography
|
<Typography
|
||||||
sx={{
|
sx={{
|
||||||
fontSize: "18px",
|
fontSize: '14px',
|
||||||
|
color: 'var(--danger)',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Options
|
{errorMsg}
|
||||||
</Typography>
|
</Typography>{' '}
|
||||||
<RadioGroup
|
</Box>
|
||||||
value={selectedOption}
|
)}
|
||||||
onChange={(e) => setSelectedOption(e.target.value)}
|
</Box>
|
||||||
>
|
|
||||||
{poll?.info?.pollOptions?.map((option, index) => (
|
<Box
|
||||||
<FormControlLabel
|
sx={{
|
||||||
key={index}
|
display: isOpen ? 'block' : 'none',
|
||||||
value={index}
|
}}
|
||||||
control={
|
>
|
||||||
<Radio
|
<CardHeader
|
||||||
sx={{
|
title={poll?.info?.pollName}
|
||||||
color: "white", // Unchecked color
|
subheader={poll?.info?.description}
|
||||||
"&.Mui-checked": {
|
sx={{
|
||||||
color: "var(--green)", // Checked color
|
'& .MuiCardHeader-title': {
|
||||||
},
|
fontSize: '18px', // Custom font size for title
|
||||||
}}
|
},
|
||||||
/>
|
}}
|
||||||
}
|
/>
|
||||||
label={option?.optionName}
|
<CardContent>
|
||||||
sx={{
|
<Typography
|
||||||
"& .MuiFormControlLabel-label": {
|
sx={{
|
||||||
fontSize: "14px",
|
fontSize: '18px',
|
||||||
|
}}
|
||||||
},
|
>
|
||||||
}}
|
Options
|
||||||
/>
|
</Typography>
|
||||||
))}
|
<RadioGroup
|
||||||
</RadioGroup>
|
value={selectedOption}
|
||||||
<Box
|
onChange={(e) => setSelectedOption(e.target.value)}
|
||||||
sx={{
|
>
|
||||||
display: "flex",
|
{poll?.info?.pollOptions?.map((option, index) => (
|
||||||
alignItems: "center",
|
<FormControlLabel
|
||||||
gap: "20px",
|
key={index}
|
||||||
}}
|
value={index}
|
||||||
>
|
control={
|
||||||
<Button
|
<Radio
|
||||||
variant="contained"
|
sx={{
|
||||||
color="primary"
|
color: 'white', // Unchecked color
|
||||||
disabled={!selectedOption || isLoadingSubmit}
|
'&.Mui-checked': {
|
||||||
onClick={handleVote}
|
color: 'var(--green)', // Checked color
|
||||||
>
|
},
|
||||||
Vote
|
}}
|
||||||
</Button>
|
/>
|
||||||
<Typography
|
}
|
||||||
|
label={option?.optionName}
|
||||||
sx={{
|
sx={{
|
||||||
fontSize: "14px",
|
'& .MuiFormControlLabel-label': {
|
||||||
fontStyle: "italic",
|
fontSize: '14px',
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</RadioGroup>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: '20px',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
disabled={!selectedOption || isLoadingSubmit}
|
||||||
|
onClick={handleVote}
|
||||||
|
>
|
||||||
|
Vote
|
||||||
|
</Button>
|
||||||
|
<Typography
|
||||||
|
sx={{
|
||||||
|
fontSize: '14px',
|
||||||
|
fontStyle: 'italic',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{' '}
|
||||||
|
{`${poll?.votes?.totalVotes} ${
|
||||||
|
poll?.votes?.totalVotes === 1 ? ' vote' : ' votes'
|
||||||
|
}`}
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Spacer height="10px" />
|
||||||
|
<Typography
|
||||||
|
sx={{
|
||||||
|
fontSize: '14px',
|
||||||
|
visibility: poll?.votes?.votes?.find(
|
||||||
|
(item) => item?.voterPublicKey === userInfo?.publicKey
|
||||||
|
)
|
||||||
|
? 'visible'
|
||||||
|
: 'hidden',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
You've already voted.
|
||||||
|
</Typography>
|
||||||
|
<Spacer height="10px" />
|
||||||
|
{isLoadingSubmit && (
|
||||||
|
<Typography
|
||||||
|
sx={{
|
||||||
|
fontSize: '12px',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Is processing transaction, please wait...
|
||||||
|
</Typography>
|
||||||
|
)}
|
||||||
|
<ButtonBase
|
||||||
|
onClick={() => {
|
||||||
|
setShowResults((prev) => !prev);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{showResults ? 'hide ' : 'show '} results
|
||||||
|
</ButtonBase>
|
||||||
|
</CardContent>
|
||||||
|
{showResults && <PollResults votes={poll?.votes} />}
|
||||||
|
</Box>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const PollResults = ({ votes }) => {
|
||||||
|
const maxVotes = Math.max(
|
||||||
|
...votes?.voteCounts?.map((option) => option.voteCount)
|
||||||
|
);
|
||||||
|
const options = votes?.voteCounts;
|
||||||
|
return (
|
||||||
|
<Box sx={{ width: '100%', p: 2 }}>
|
||||||
|
{options
|
||||||
|
.sort((a, b) => b.voteCount - a.voteCount) // Sort options by votes (highest first)
|
||||||
|
.map((option, index) => (
|
||||||
|
<Box key={index} sx={{ mb: 2 }}>
|
||||||
|
<Box sx={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||||
|
<Typography
|
||||||
|
variant="body1"
|
||||||
|
sx={{
|
||||||
|
fontWeight: index === 0 ? 'bold' : 'normal',
|
||||||
|
fontSize: '14px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{" "}
|
{`${index + 1}. ${option.optionName}`}
|
||||||
{`${poll?.votes?.totalVotes} ${
|
</Typography>
|
||||||
poll?.votes?.totalVotes === 1 ? " vote" : " votes"
|
<Typography
|
||||||
}`}
|
variant="body1"
|
||||||
|
sx={{
|
||||||
|
fontWeight: index === 0 ? 'bold' : 'normal',
|
||||||
|
fontSize: '14px',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{option.voteCount} votes
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
|
<Box
|
||||||
<Spacer height="10px" />
|
|
||||||
<Typography
|
|
||||||
sx={{
|
sx={{
|
||||||
fontSize: "14px",
|
mt: 1,
|
||||||
visibility: poll?.votes?.votes?.find(
|
height: 10,
|
||||||
(item) => item?.voterPublicKey === userInfo?.publicKey
|
backgroundColor: '#e0e0e0',
|
||||||
)
|
borderRadius: 5,
|
||||||
? "visible"
|
overflow: 'hidden',
|
||||||
: "hidden",
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
You've already voted.
|
|
||||||
</Typography>
|
|
||||||
<Spacer height="10px" />
|
|
||||||
{isLoadingSubmit && (
|
|
||||||
<Typography
|
|
||||||
sx={{
|
|
||||||
fontSize: "12px",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Is processing transaction, please wait...
|
|
||||||
</Typography>
|
|
||||||
)}
|
|
||||||
<ButtonBase
|
|
||||||
onClick={() => {
|
|
||||||
setShowResults((prev) => !prev);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{showResults ? "hide " : "show "} results
|
|
||||||
</ButtonBase>
|
|
||||||
</CardContent>
|
|
||||||
{showResults && <PollResults votes={poll?.votes} />}
|
|
||||||
</Box>
|
|
||||||
</Card>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const PollResults = ({ votes }) => {
|
|
||||||
const maxVotes = Math.max(
|
|
||||||
...votes?.voteCounts?.map((option) => option.voteCount)
|
|
||||||
);
|
|
||||||
const options = votes?.voteCounts;
|
|
||||||
return (
|
|
||||||
<Box sx={{ width: "100%", p: 2 }}>
|
|
||||||
{options
|
|
||||||
.sort((a, b) => b.voteCount - a.voteCount) // Sort options by votes (highest first)
|
|
||||||
.map((option, index) => (
|
|
||||||
<Box key={index} sx={{ mb: 2 }}>
|
|
||||||
<Box sx={{ display: "flex", justifyContent: "space-between" }}>
|
|
||||||
<Typography
|
|
||||||
variant="body1"
|
|
||||||
sx={{ fontWeight: index === 0 ? "bold" : "normal" , fontSize: "14px"}}
|
|
||||||
>
|
|
||||||
{`${index + 1}. ${option.optionName}`}
|
|
||||||
</Typography>
|
|
||||||
<Typography
|
|
||||||
variant="body1"
|
|
||||||
sx={{ fontWeight: index === 0 ? "bold" : "normal" , fontSize: "14px"}}
|
|
||||||
>
|
|
||||||
{option.voteCount} votes
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
mt: 1,
|
width: `${(option.voteCount / maxVotes) * 100}%`,
|
||||||
height: 10,
|
height: '100%',
|
||||||
backgroundColor: "#e0e0e0",
|
backgroundColor: index === 0 ? '#3f51b5' : '#f50057',
|
||||||
borderRadius: 5,
|
transition: 'width 0.3s ease-in-out',
|
||||||
overflow: "hidden",
|
|
||||||
}}
|
}}
|
||||||
>
|
/>
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
width: `${(option.voteCount / maxVotes) * 100}%`,
|
|
||||||
height: "100%",
|
|
||||||
backgroundColor: index === 0 ? "#3f51b5" : "#f50057",
|
|
||||||
transition: "width 0.3s ease-in-out",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
</Box>
|
||||||
))}
|
</Box>
|
||||||
</Box>
|
))}
|
||||||
);
|
</Box>
|
||||||
};
|
);
|
||||||
|
};
|
||||||
|
@ -7,7 +7,7 @@ import {
|
|||||||
Popover,
|
Popover,
|
||||||
TextField,
|
TextField,
|
||||||
Typography,
|
Typography,
|
||||||
} from "@mui/material";
|
} from '@mui/material';
|
||||||
import React, {
|
import React, {
|
||||||
useCallback,
|
useCallback,
|
||||||
useContext,
|
useContext,
|
||||||
@ -15,20 +15,20 @@ import React, {
|
|||||||
useMemo,
|
useMemo,
|
||||||
useRef,
|
useRef,
|
||||||
useState,
|
useState,
|
||||||
} from "react";
|
} from 'react';
|
||||||
import {
|
import {
|
||||||
AutoSizer,
|
AutoSizer,
|
||||||
CellMeasurer,
|
CellMeasurer,
|
||||||
CellMeasurerCache,
|
CellMeasurerCache,
|
||||||
List,
|
List,
|
||||||
} from "react-virtualized";
|
} from 'react-virtualized';
|
||||||
import _ from "lodash";
|
import _ from 'lodash';
|
||||||
import { MyContext, getBaseApiReact } from "../../App";
|
import { MyContext, getBaseApiReact } from '../../App';
|
||||||
import { LoadingButton } from "@mui/lab";
|
import { LoadingButton } from '@mui/lab';
|
||||||
import { getBaseApi, getFee } from "../../background";
|
import { getBaseApi, getFee } from '../../background';
|
||||||
import LockIcon from '@mui/icons-material/Lock';
|
import LockIcon from '@mui/icons-material/Lock';
|
||||||
import NoEncryptionGmailerrorredIcon from '@mui/icons-material/NoEncryptionGmailerrorred';
|
import NoEncryptionGmailerrorredIcon from '@mui/icons-material/NoEncryptionGmailerrorred';
|
||||||
import { Spacer } from "../../common/Spacer";
|
import { Spacer } from '../../common/Spacer';
|
||||||
const cache = new CellMeasurerCache({
|
const cache = new CellMeasurerCache({
|
||||||
fixedWidth: true,
|
fixedWidth: true,
|
||||||
defaultHeight: 50,
|
defaultHeight: 50,
|
||||||
@ -41,7 +41,7 @@ export const AddGroupList = ({ setInfoSnack, setOpenSnack }) => {
|
|||||||
const [popoverAnchor, setPopoverAnchor] = useState(null); // Track which list item the popover is anchored to
|
const [popoverAnchor, setPopoverAnchor] = useState(null); // Track which list item the popover is anchored to
|
||||||
const [openPopoverIndex, setOpenPopoverIndex] = useState(null); // Track which list item has the popover open
|
const [openPopoverIndex, setOpenPopoverIndex] = useState(null); // Track which list item has the popover open
|
||||||
const listRef = useRef();
|
const listRef = useRef();
|
||||||
const [inputValue, setInputValue] = useState("");
|
const [inputValue, setInputValue] = useState('');
|
||||||
const [filteredItems, setFilteredItems] = useState(groups);
|
const [filteredItems, setFilteredItems] = useState(groups);
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
||||||
@ -72,9 +72,7 @@ export const AddGroupList = ({ setInfoSnack, setOpenSnack }) => {
|
|||||||
|
|
||||||
const getGroups = async () => {
|
const getGroups = async () => {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(
|
const response = await fetch(`${getBaseApiReact()}/groups/?limit=0`);
|
||||||
`${getBaseApiReact()}/groups/?limit=0`
|
|
||||||
);
|
|
||||||
const groupData = await response.json();
|
const groupData = await response.json();
|
||||||
const filteredGroup = groupData.filter(
|
const filteredGroup = groupData.filter(
|
||||||
(item) => !memberGroups.find((group) => group.groupId === item.groupId)
|
(item) => !memberGroups.find((group) => group.groupId === item.groupId)
|
||||||
@ -103,23 +101,25 @@ export const AddGroupList = ({ setInfoSnack, setOpenSnack }) => {
|
|||||||
const handleJoinGroup = async (group, isOpen) => {
|
const handleJoinGroup = async (group, isOpen) => {
|
||||||
try {
|
try {
|
||||||
const groupId = group.groupId;
|
const groupId = group.groupId;
|
||||||
const fee = await getFee('JOIN_GROUP')
|
const fee = await getFee('JOIN_GROUP');
|
||||||
await show({
|
await show({
|
||||||
message: "Would you like to perform an JOIN_GROUP transaction?" ,
|
message: 'Would you like to perform an JOIN_GROUP transaction?',
|
||||||
publishFee: fee.fee + ' QORT'
|
publishFee: fee.fee + ' QORT',
|
||||||
})
|
});
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
await new Promise((res, rej) => {
|
await new Promise((res, rej) => {
|
||||||
window.sendMessage("joinGroup", {
|
window
|
||||||
groupId,
|
.sendMessage('joinGroup', {
|
||||||
})
|
groupId,
|
||||||
|
})
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
if (!response?.error) {
|
if (!response?.error) {
|
||||||
setInfoSnack({
|
setInfoSnack({
|
||||||
type: "success",
|
type: 'success',
|
||||||
message: "Successfully requested to join group. It may take a couple of minutes for the changes to propagate",
|
message:
|
||||||
|
'Successfully requested to join group. It may take a couple of minutes for the changes to propagate',
|
||||||
});
|
});
|
||||||
|
|
||||||
if (isOpen) {
|
if (isOpen) {
|
||||||
setTxList((prev) => [
|
setTxList((prev) => [
|
||||||
{
|
{
|
||||||
@ -145,14 +145,14 @@ export const AddGroupList = ({ setInfoSnack, setOpenSnack }) => {
|
|||||||
...prev,
|
...prev,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
setOpenSnack(true);
|
setOpenSnack(true);
|
||||||
handlePopoverClose();
|
handlePopoverClose();
|
||||||
res(response);
|
res(response);
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
setInfoSnack({
|
setInfoSnack({
|
||||||
type: "error",
|
type: 'error',
|
||||||
message: response?.error,
|
message: response?.error,
|
||||||
});
|
});
|
||||||
setOpenSnack(true);
|
setOpenSnack(true);
|
||||||
@ -161,18 +161,18 @@ export const AddGroupList = ({ setInfoSnack, setOpenSnack }) => {
|
|||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
setInfoSnack({
|
setInfoSnack({
|
||||||
type: "error",
|
type: 'error',
|
||||||
message: error.message || "An error occurred",
|
message: error.message || 'An error occurred',
|
||||||
});
|
});
|
||||||
setOpenSnack(true);
|
setOpenSnack(true);
|
||||||
rej(error);
|
rej(error);
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
} catch (error) {} finally {
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
} finally {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -195,30 +195,30 @@ export const AddGroupList = ({ setInfoSnack, setOpenSnack }) => {
|
|||||||
anchorEl={popoverAnchor}
|
anchorEl={popoverAnchor}
|
||||||
onClose={handlePopoverClose}
|
onClose={handlePopoverClose}
|
||||||
anchorOrigin={{
|
anchorOrigin={{
|
||||||
vertical: "bottom",
|
vertical: 'bottom',
|
||||||
horizontal: "center",
|
horizontal: 'center',
|
||||||
}}
|
}}
|
||||||
transformOrigin={{
|
transformOrigin={{
|
||||||
vertical: "top",
|
vertical: 'top',
|
||||||
horizontal: "center",
|
horizontal: 'center',
|
||||||
}}
|
}}
|
||||||
style={{ marginTop: "8px" }}
|
style={{ marginTop: '8px' }}
|
||||||
>
|
>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
width: "325px",
|
width: '325px',
|
||||||
height: "250px",
|
height: '250px',
|
||||||
display: "flex",
|
display: 'flex',
|
||||||
flexDirection: "column",
|
flexDirection: 'column',
|
||||||
alignItems: "center",
|
alignItems: 'center',
|
||||||
gap: "10px",
|
gap: '10px',
|
||||||
padding: "10px",
|
padding: '10px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography>Join {group?.groupName}</Typography>
|
<Typography>Join {group?.groupName}</Typography>
|
||||||
<Typography>
|
<Typography>
|
||||||
{group?.isOpen === false &&
|
{group?.isOpen === false &&
|
||||||
"This is a closed/private group, so you will need to wait until an admin accepts your request"}
|
'This is a closed/private group, so you will need to wait until an admin accepts your request'}
|
||||||
</Typography>
|
</Typography>
|
||||||
<LoadingButton
|
<LoadingButton
|
||||||
loading={isLoading}
|
loading={isLoading}
|
||||||
@ -234,16 +234,20 @@ export const AddGroupList = ({ setInfoSnack, setOpenSnack }) => {
|
|||||||
onClick={(event) => handlePopoverOpen(event, index)}
|
onClick={(event) => handlePopoverOpen(event, index)}
|
||||||
>
|
>
|
||||||
{group?.isOpen === false && (
|
{group?.isOpen === false && (
|
||||||
<LockIcon sx={{
|
<LockIcon
|
||||||
color: 'var(--green)'
|
sx={{
|
||||||
}} />
|
color: 'var(--green)',
|
||||||
)}
|
}}
|
||||||
{group?.isOpen === true && (
|
/>
|
||||||
<NoEncryptionGmailerrorredIcon sx={{
|
)}
|
||||||
color: 'var(--danger)'
|
{group?.isOpen === true && (
|
||||||
}} />
|
<NoEncryptionGmailerrorredIcon
|
||||||
)}
|
sx={{
|
||||||
<Spacer width="15px" />
|
color: 'var(--danger)',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<Spacer width="15px" />
|
||||||
<ListItemText
|
<ListItemText
|
||||||
primary={group?.groupName}
|
primary={group?.groupName}
|
||||||
secondary={group?.description}
|
secondary={group?.description}
|
||||||
@ -257,11 +261,13 @@ export const AddGroupList = ({ setInfoSnack, setOpenSnack }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box sx={{
|
<Box
|
||||||
display: 'flex',
|
sx={{
|
||||||
flexDirection: 'column',
|
display: 'flex',
|
||||||
flexGrow: 1
|
flexDirection: 'column',
|
||||||
}}>
|
flexGrow: 1,
|
||||||
|
}}
|
||||||
|
>
|
||||||
<p>Groups list</p>
|
<p>Groups list</p>
|
||||||
<TextField
|
<TextField
|
||||||
label="Search for Groups"
|
label="Search for Groups"
|
||||||
@ -272,10 +278,10 @@ export const AddGroupList = ({ setInfoSnack, setOpenSnack }) => {
|
|||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
position: "relative",
|
position: 'relative',
|
||||||
width: "100%",
|
width: '100%',
|
||||||
display: "flex",
|
display: 'flex',
|
||||||
flexDirection: "column",
|
flexDirection: 'column',
|
||||||
flexGrow: 1,
|
flexGrow: 1,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
@ -5,10 +5,10 @@ import React, {
|
|||||||
useMemo,
|
useMemo,
|
||||||
useRef,
|
useRef,
|
||||||
useState,
|
useState,
|
||||||
} from "react";
|
} from 'react';
|
||||||
import { Avatar, Box, Popover, Typography } from "@mui/material";
|
import { Avatar, Box, Popover, Typography } from '@mui/material';
|
||||||
// import { MAIL_SERVICE_TYPE, THREAD_SERVICE_TYPE } from "../../constants/mail";
|
// import { MAIL_SERVICE_TYPE, THREAD_SERVICE_TYPE } from "../../constants/mail";
|
||||||
import { Thread } from "./Thread";
|
import { Thread } from './Thread';
|
||||||
import {
|
import {
|
||||||
AllThreadP,
|
AllThreadP,
|
||||||
ArrowDownIcon,
|
ArrowDownIcon,
|
||||||
@ -38,61 +38,73 @@ import {
|
|||||||
ThreadSingleLastMessageP,
|
ThreadSingleLastMessageP,
|
||||||
ThreadSingleLastMessageSpanP,
|
ThreadSingleLastMessageSpanP,
|
||||||
ThreadSingleTitle,
|
ThreadSingleTitle,
|
||||||
} from "./Mail-styles";
|
} from './Mail-styles';
|
||||||
import ArrowForwardIosIcon from '@mui/icons-material/ArrowForwardIos';
|
import ArrowForwardIosIcon from '@mui/icons-material/ArrowForwardIos';
|
||||||
import { Spacer } from "../../../common/Spacer";
|
import { Spacer } from '../../../common/Spacer';
|
||||||
import { formatDate, formatTimestamp } from "../../../utils/time";
|
import { formatDate, formatTimestamp } from '../../../utils/time';
|
||||||
import LazyLoad from "../../../common/LazyLoad";
|
import LazyLoad from '../../../common/LazyLoad';
|
||||||
import { delay } from "../../../utils/helpers";
|
import { delay } from '../../../utils/helpers';
|
||||||
import { NewThread } from "./NewThread";
|
import { NewThread } from './NewThread';
|
||||||
import { getBaseApi } from "../../../background";
|
import { getBaseApi } from '../../../background';
|
||||||
import { decryptPublishes, getTempPublish, handleUnencryptedPublishes } from "../../Chat/GroupAnnouncements";
|
import {
|
||||||
import CheckSVG from "../../../assets/svgs/Check.svg";
|
decryptPublishes,
|
||||||
import SortSVG from "../../../assets/svgs/Sort.svg";
|
getTempPublish,
|
||||||
import ArrowDownSVG from "../../../assets/svgs/ArrowDown.svg";
|
handleUnencryptedPublishes,
|
||||||
import { LoadingSnackbar } from "../../Snackbar/LoadingSnackbar";
|
} from '../../Chat/GroupAnnouncements';
|
||||||
import { executeEvent, subscribeToEvent, unsubscribeFromEvent } from "../../../utils/events";
|
import CheckSVG from '../../../assets/svgs/Check.svg';
|
||||||
|
import SortSVG from '../../../assets/svgs/Sort.svg';
|
||||||
|
import ArrowDownSVG from '../../../assets/svgs/ArrowDown.svg';
|
||||||
|
import { LoadingSnackbar } from '../../Snackbar/LoadingSnackbar';
|
||||||
|
import {
|
||||||
|
executeEvent,
|
||||||
|
subscribeToEvent,
|
||||||
|
unsubscribeFromEvent,
|
||||||
|
} from '../../../utils/events';
|
||||||
import RefreshIcon from '@mui/icons-material/Refresh';
|
import RefreshIcon from '@mui/icons-material/Refresh';
|
||||||
import { getArbitraryEndpointReact, getBaseApiReact, isMobile } from "../../../App";
|
import {
|
||||||
import { WrapperUserAction } from "../../WrapperUserAction";
|
getArbitraryEndpointReact,
|
||||||
import { addDataPublishesFunc, getDataPublishesFunc } from "../Group";
|
getBaseApiReact,
|
||||||
const filterOptions = ["Recently active", "Newest", "Oldest"];
|
isMobile,
|
||||||
|
} from '../../../App';
|
||||||
|
import { WrapperUserAction } from '../../WrapperUserAction';
|
||||||
|
import { addDataPublishesFunc, getDataPublishesFunc } from '../Group';
|
||||||
|
const filterOptions = ['Recently active', 'Newest', 'Oldest'];
|
||||||
|
|
||||||
export const threadIdentifier = "DOCUMENT";
|
export const threadIdentifier = 'DOCUMENT';
|
||||||
export const GroupMail = ({
|
export const GroupMail = ({
|
||||||
selectedGroup,
|
selectedGroup,
|
||||||
userInfo,
|
userInfo,
|
||||||
getSecretKey,
|
getSecretKey,
|
||||||
secretKey,
|
secretKey,
|
||||||
defaultThread,
|
defaultThread,
|
||||||
setDefaultThread,
|
setDefaultThread,
|
||||||
hide,
|
hide,
|
||||||
isPrivate
|
isPrivate,
|
||||||
}) => {
|
}) => {
|
||||||
const [viewedThreads, setViewedThreads] = React.useState<any>({});
|
const [viewedThreads, setViewedThreads] = React.useState<any>({});
|
||||||
const [filterMode, setFilterMode] = useState<string>("Recently active");
|
const [filterMode, setFilterMode] = useState<string>('Recently active');
|
||||||
const [currentThread, setCurrentThread] = React.useState(null);
|
const [currentThread, setCurrentThread] = React.useState(null);
|
||||||
const [recentThreads, setRecentThreads] = useState<any[]>([]);
|
const [recentThreads, setRecentThreads] = useState<any[]>([]);
|
||||||
const [allThreads, setAllThreads] = useState<any[]>([]);
|
const [allThreads, setAllThreads] = useState<any[]>([]);
|
||||||
const [members, setMembers] = useState<any>(null);
|
const [members, setMembers] = useState<any>(null);
|
||||||
const [isOpenFilterList, setIsOpenFilterList] = useState<boolean>(false);
|
const [isOpenFilterList, setIsOpenFilterList] = useState<boolean>(false);
|
||||||
const anchorElInstanceFilter = useRef<any>(null);
|
const anchorElInstanceFilter = useRef<any>(null);
|
||||||
const [tempPublishedList, setTempPublishedList] = useState([])
|
const [tempPublishedList, setTempPublishedList] = useState([]);
|
||||||
const dataPublishes = useRef({})
|
const dataPublishes = useRef({});
|
||||||
|
|
||||||
const [isLoading, setIsLoading] = useState(false)
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const groupIdRef = useRef<any>(null);
|
const groupIdRef = useRef<any>(null);
|
||||||
const groupId = useMemo(() => {
|
const groupId = useMemo(() => {
|
||||||
return selectedGroup?.groupId;
|
return selectedGroup?.groupId;
|
||||||
}, [selectedGroup]);
|
}, [selectedGroup]);
|
||||||
|
|
||||||
useEffect(()=> {
|
useEffect(() => {
|
||||||
if(!groupId) return
|
if (!groupId) return;
|
||||||
(async ()=> {
|
(async () => {
|
||||||
const res = await getDataPublishesFunc(groupId, 'thread')
|
const res = await getDataPublishesFunc(groupId, 'thread');
|
||||||
dataPublishes.current = res || {}
|
dataPublishes.current = res || {};
|
||||||
})()
|
})();
|
||||||
}, [groupId])
|
}, [groupId]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (groupId !== groupIdRef?.current) {
|
if (groupId !== groupIdRef?.current) {
|
||||||
@ -103,55 +115,66 @@ export const GroupMail = ({
|
|||||||
}
|
}
|
||||||
}, [groupId]);
|
}, [groupId]);
|
||||||
|
|
||||||
const setTempData = async ()=> {
|
const setTempData = async () => {
|
||||||
try {
|
try {
|
||||||
const getTempAnnouncements = await getTempPublish()
|
const getTempAnnouncements = await getTempPublish();
|
||||||
|
|
||||||
if(getTempAnnouncements?.thread){
|
if (getTempAnnouncements?.thread) {
|
||||||
let tempData = []
|
let tempData = [];
|
||||||
Object.keys(getTempAnnouncements?.thread || {}).map((key)=> {
|
Object.keys(getTempAnnouncements?.thread || {}).map((key) => {
|
||||||
const value = getTempAnnouncements?.thread[key]
|
const value = getTempAnnouncements?.thread[key];
|
||||||
if(value?.data?.groupId === groupIdRef?.current){
|
if (value?.data?.groupId === groupIdRef?.current) {
|
||||||
tempData.push(value.data)
|
tempData.push(value.data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
setTempPublishedList(tempData);
|
||||||
}
|
}
|
||||||
|
} catch (error) {}
|
||||||
})
|
};
|
||||||
setTempPublishedList(tempData)
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
const getEncryptedResource = async ({ name, identifier, resource }, isPrivate) => {
|
|
||||||
let data = dataPublishes.current[`${name}-${identifier}`]
|
|
||||||
if(!data || (data?.update || data?.created !== (resource?.updated || resource?.created))){
|
|
||||||
const res = await fetch(
|
|
||||||
`${getBaseApiReact()}/arbitrary/DOCUMENT/${name}/${identifier}?encoding=base64`
|
|
||||||
);
|
|
||||||
if(!res?.ok) return
|
|
||||||
data = await res.text();
|
|
||||||
await addDataPublishesFunc({...resource, data}, groupId, 'thread')
|
|
||||||
|
|
||||||
|
const getEncryptedResource = async (
|
||||||
|
{ name, identifier, resource },
|
||||||
|
isPrivate
|
||||||
|
) => {
|
||||||
|
let data = dataPublishes.current[`${name}-${identifier}`];
|
||||||
|
if (
|
||||||
|
!data ||
|
||||||
|
data?.update ||
|
||||||
|
data?.created !== (resource?.updated || resource?.created)
|
||||||
|
) {
|
||||||
|
const res = await fetch(
|
||||||
|
`${getBaseApiReact()}/arbitrary/DOCUMENT/${name}/${identifier}?encoding=base64`
|
||||||
|
);
|
||||||
|
if (!res?.ok) return;
|
||||||
|
data = await res.text();
|
||||||
|
await addDataPublishesFunc({ ...resource, data }, groupId, 'thread');
|
||||||
} else {
|
} else {
|
||||||
data = data.data
|
data = data.data;
|
||||||
}
|
}
|
||||||
const response = isPrivate === false ? handleUnencryptedPublishes([data]) : await decryptPublishes([{ data }], secretKey);
|
const response =
|
||||||
|
isPrivate === false
|
||||||
|
? handleUnencryptedPublishes([data])
|
||||||
|
: await decryptPublishes([{ data }], secretKey);
|
||||||
|
|
||||||
const messageData = response[0];
|
const messageData = response[0];
|
||||||
return messageData.decryptedData;
|
return messageData.decryptedData;
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateThreadActivity = async ({threadId, qortalName, groupId, thread}) => {
|
const updateThreadActivity = async ({
|
||||||
|
threadId,
|
||||||
|
qortalName,
|
||||||
|
groupId,
|
||||||
|
thread,
|
||||||
|
}) => {
|
||||||
try {
|
try {
|
||||||
await new Promise((res, rej) => {
|
await new Promise((res, rej) => {
|
||||||
window.sendMessage("updateThreadActivity", {
|
window
|
||||||
threadId,
|
.sendMessage('updateThreadActivity', {
|
||||||
qortalName,
|
threadId,
|
||||||
groupId,
|
qortalName,
|
||||||
thread,
|
groupId,
|
||||||
})
|
thread,
|
||||||
|
})
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
if (!response?.error) {
|
if (!response?.error) {
|
||||||
res(response);
|
res(response);
|
||||||
@ -160,13 +183,10 @@ export const GroupMail = ({
|
|||||||
rej(response.error);
|
rej(response.error);
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
rej(error.message || "An error occurred");
|
rej(error.message || 'An error occurred');
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -174,9 +194,9 @@ export const GroupMail = ({
|
|||||||
const getAllThreads = React.useCallback(
|
const getAllThreads = React.useCallback(
|
||||||
async (groupId: string, mode: string, isInitial?: boolean) => {
|
async (groupId: string, mode: string, isInitial?: boolean) => {
|
||||||
try {
|
try {
|
||||||
setIsLoading(true)
|
setIsLoading(true);
|
||||||
const offset = isInitial ? 0 : allThreads.length;
|
const offset = isInitial ? 0 : allThreads.length;
|
||||||
const isReverse = mode === "Newest" ? true : false;
|
const isReverse = mode === 'Newest' ? true : false;
|
||||||
if (isInitial) {
|
if (isInitial) {
|
||||||
// dispatch(setIsLoadingCustom("Loading threads"));
|
// dispatch(setIsLoadingCustom("Loading threads"));
|
||||||
}
|
}
|
||||||
@ -184,9 +204,9 @@ export const GroupMail = ({
|
|||||||
|
|
||||||
const url = `${getBaseApiReact()}${getArbitraryEndpointReact()}?mode=ALL&service=${threadIdentifier}&identifier=${identifier}&limit=${20}&includemetadata=false&offset=${offset}&reverse=${isReverse}&prefix=true`;
|
const url = `${getBaseApiReact()}${getArbitraryEndpointReact()}?mode=ALL&service=${threadIdentifier}&identifier=${identifier}&limit=${20}&includemetadata=false&offset=${offset}&reverse=${isReverse}&prefix=true`;
|
||||||
const response = await fetch(url, {
|
const response = await fetch(url, {
|
||||||
method: "GET",
|
method: 'GET',
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
'Content-Type': 'application/json',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const responseData = await response.json();
|
const responseData = await response.json();
|
||||||
@ -209,21 +229,26 @@ export const GroupMail = ({
|
|||||||
let threadRes = null;
|
let threadRes = null;
|
||||||
try {
|
try {
|
||||||
threadRes = await Promise.race([
|
threadRes = await Promise.race([
|
||||||
getEncryptedResource({
|
getEncryptedResource(
|
||||||
name: message.name,
|
{
|
||||||
identifier: message.identifier,
|
name: message.name,
|
||||||
resource: message
|
identifier: message.identifier,
|
||||||
}, isPrivate),
|
resource: message,
|
||||||
|
},
|
||||||
|
isPrivate
|
||||||
|
),
|
||||||
delay(5000),
|
delay(5000),
|
||||||
]);
|
]);
|
||||||
} catch (error) {}
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
|
||||||
if (threadRes?.title) {
|
if (threadRes?.title) {
|
||||||
fullObject = {
|
fullObject = {
|
||||||
...message,
|
...message,
|
||||||
threadData: threadRes,
|
threadData: threadRes,
|
||||||
threadOwner: message?.name,
|
threadOwner: message?.name,
|
||||||
threadId: message.identifier
|
threadId: message.identifier,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -251,7 +276,7 @@ export const GroupMail = ({
|
|||||||
console.log({ error });
|
console.log({ error });
|
||||||
} finally {
|
} finally {
|
||||||
if (isInitial) {
|
if (isInitial) {
|
||||||
setIsLoading(false)
|
setIsLoading(false);
|
||||||
// dispatch(setIsLoadingCustom(null));
|
// dispatch(setIsLoadingCustom(null));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -261,21 +286,21 @@ export const GroupMail = ({
|
|||||||
const getMailMessages = React.useCallback(
|
const getMailMessages = React.useCallback(
|
||||||
async (groupId: string, members: any) => {
|
async (groupId: string, members: any) => {
|
||||||
try {
|
try {
|
||||||
setIsLoading(true)
|
setIsLoading(true);
|
||||||
|
|
||||||
const identifier = `thmsg-grp-${groupId}-thread-`;
|
const identifier = `thmsg-grp-${groupId}-thread-`;
|
||||||
const url = `${getBaseApiReact()}${getArbitraryEndpointReact()}?mode=ALL&service=${threadIdentifier}&identifier=${identifier}&limit=100&includemetadata=false&offset=${0}&reverse=true&prefix=true`;
|
const url = `${getBaseApiReact()}${getArbitraryEndpointReact()}?mode=ALL&service=${threadIdentifier}&identifier=${identifier}&limit=100&includemetadata=false&offset=${0}&reverse=true&prefix=true`;
|
||||||
const response = await fetch(url, {
|
const response = await fetch(url, {
|
||||||
method: "GET",
|
method: 'GET',
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
'Content-Type': 'application/json',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const responseData = await response.json();
|
const responseData = await response.json();
|
||||||
const messagesForThread: any = {};
|
const messagesForThread: any = {};
|
||||||
for (const message of responseData) {
|
for (const message of responseData) {
|
||||||
let str = message.identifier;
|
let str = message.identifier;
|
||||||
const parts = str.split("-");
|
const parts = str.split('-');
|
||||||
|
|
||||||
// Get the second last element
|
// Get the second last element
|
||||||
const secondLastId = parts[parts.length - 2];
|
const secondLastId = parts[parts.length - 2];
|
||||||
@ -295,16 +320,16 @@ export const GroupMail = ({
|
|||||||
})
|
})
|
||||||
.sort((a, b) => b.created - a.created)
|
.sort((a, b) => b.created - a.created)
|
||||||
.slice(0, 10);
|
.slice(0, 10);
|
||||||
|
|
||||||
let fullThreadArray: any = [];
|
let fullThreadArray: any = [];
|
||||||
const getMessageForThreads = newArray.map(async (message: any) => {
|
const getMessageForThreads = newArray.map(async (message: any) => {
|
||||||
try {
|
try {
|
||||||
const identifierQuery = message.threadId;
|
const identifierQuery = message.threadId;
|
||||||
const url = `${getBaseApiReact()}${getArbitraryEndpointReact()}?mode=ALL&service=${threadIdentifier}&identifier=${identifierQuery}&limit=1&includemetadata=false&offset=${0}&reverse=true&prefix=true`;
|
const url = `${getBaseApiReact()}${getArbitraryEndpointReact()}?mode=ALL&service=${threadIdentifier}&identifier=${identifierQuery}&limit=1&includemetadata=false&offset=${0}&reverse=true&prefix=true`;
|
||||||
const response = await fetch(url, {
|
const response = await fetch(url, {
|
||||||
method: "GET",
|
method: 'GET',
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
'Content-Type': 'application/json',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const responseData = await response.json();
|
const responseData = await response.json();
|
||||||
@ -324,11 +349,14 @@ export const GroupMail = ({
|
|||||||
fullThreadArray.push(fullObject);
|
fullThreadArray.push(fullObject);
|
||||||
} else {
|
} else {
|
||||||
let threadRes = await Promise.race([
|
let threadRes = await Promise.race([
|
||||||
getEncryptedResource({
|
getEncryptedResource(
|
||||||
name: thread.name,
|
{
|
||||||
identifier: message.threadId,
|
name: thread.name,
|
||||||
resource: thread
|
identifier: message.threadId,
|
||||||
}, isPrivate),
|
resource: thread,
|
||||||
|
},
|
||||||
|
isPrivate
|
||||||
|
),
|
||||||
delay(10000),
|
delay(10000),
|
||||||
]);
|
]);
|
||||||
if (threadRes?.title) {
|
if (threadRes?.title) {
|
||||||
@ -353,7 +381,7 @@ export const GroupMail = ({
|
|||||||
setRecentThreads(sorted);
|
setRecentThreads(sorted);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
} finally {
|
} finally {
|
||||||
setIsLoading(false)
|
setIsLoading(false);
|
||||||
// dispatch(setIsLoadingCustom(null));
|
// dispatch(setIsLoadingCustom(null));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -361,7 +389,6 @@ export const GroupMail = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const getMessages = React.useCallback(async () => {
|
const getMessages = React.useCallback(async () => {
|
||||||
|
|
||||||
// if ( !groupId || members?.length === 0) return;
|
// if ( !groupId || members?.length === 0) return;
|
||||||
if (!groupId || isPrivate === null) return;
|
if (!groupId || isPrivate === null) return;
|
||||||
|
|
||||||
@ -371,23 +398,23 @@ export const GroupMail = ({
|
|||||||
const interval = useRef<any>(null);
|
const interval = useRef<any>(null);
|
||||||
|
|
||||||
const firstMount = useRef(false);
|
const firstMount = useRef(false);
|
||||||
const filterModeRef = useRef("");
|
const filterModeRef = useRef('');
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if(hide) return
|
if (hide) return;
|
||||||
if (filterModeRef.current !== filterMode) {
|
if (filterModeRef.current !== filterMode) {
|
||||||
firstMount.current = false;
|
firstMount.current = false;
|
||||||
}
|
}
|
||||||
// if (groupId && !firstMount.current && members.length > 0) {
|
// if (groupId && !firstMount.current && members.length > 0) {
|
||||||
if (groupId && !firstMount.current && isPrivate !== null) {
|
if (groupId && !firstMount.current && isPrivate !== null) {
|
||||||
if (filterMode === "Recently active") {
|
if (filterMode === 'Recently active') {
|
||||||
getMessages();
|
getMessages();
|
||||||
} else if (filterMode === "Newest") {
|
} else if (filterMode === 'Newest') {
|
||||||
getAllThreads(groupId, "Newest", true);
|
getAllThreads(groupId, 'Newest', true);
|
||||||
} else if (filterMode === "Oldest") {
|
} else if (filterMode === 'Oldest') {
|
||||||
getAllThreads(groupId, "Oldest", true);
|
getAllThreads(groupId, 'Oldest', true);
|
||||||
}
|
}
|
||||||
setTempData()
|
setTempData();
|
||||||
firstMount.current = true;
|
firstMount.current = true;
|
||||||
}
|
}
|
||||||
}, [groupId, members, filterMode, hide, isPrivate]);
|
}, [groupId, members, filterMode, hide, isPrivate]);
|
||||||
@ -428,19 +455,16 @@ export const GroupMail = ({
|
|||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
let listOfThreadsToDisplay = recentThreads;
|
let listOfThreadsToDisplay = recentThreads;
|
||||||
if (filterMode === "Newest" || filterMode === "Oldest") {
|
if (filterMode === 'Newest' || filterMode === 'Oldest') {
|
||||||
listOfThreadsToDisplay = allThreads;
|
listOfThreadsToDisplay = allThreads;
|
||||||
}
|
}
|
||||||
|
|
||||||
const onSubmitNewThread = useCallback(
|
const onSubmitNewThread = useCallback(
|
||||||
(val: any) => {
|
(val: any) => {
|
||||||
if (filterMode === "Recently active") {
|
if (filterMode === 'Recently active') {
|
||||||
setRecentThreads((prev) => [val, ...prev]);
|
setRecentThreads((prev) => [val, ...prev]);
|
||||||
} else if (filterMode === "Newest") {
|
} else if (filterMode === 'Newest') {
|
||||||
setAllThreads((prev) => [val, ...prev]);
|
setAllThreads((prev) => [val, ...prev]);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -461,72 +485,77 @@ export const GroupMail = ({
|
|||||||
setIsOpenFilterList(false);
|
setIsOpenFilterList(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
const refetchThreadsLists = useCallback(()=> {
|
const refetchThreadsLists = useCallback(() => {
|
||||||
if (filterMode === "Recently active") {
|
if (filterMode === 'Recently active') {
|
||||||
getMessages();
|
getMessages();
|
||||||
} else if (filterMode === "Newest") {
|
} else if (filterMode === 'Newest') {
|
||||||
getAllThreads(groupId, "Newest", true);
|
getAllThreads(groupId, 'Newest', true);
|
||||||
} else if (filterMode === "Oldest") {
|
} else if (filterMode === 'Oldest') {
|
||||||
getAllThreads(groupId, "Oldest", true);
|
getAllThreads(groupId, 'Oldest', true);
|
||||||
}
|
}
|
||||||
}, [filterMode, isPrivate])
|
}, [filterMode, isPrivate]);
|
||||||
|
|
||||||
const updateThreadActivityCurrentThread = ()=> {
|
const updateThreadActivityCurrentThread = () => {
|
||||||
if(!currentThread) return
|
if (!currentThread) return;
|
||||||
const thread = currentThread
|
const thread = currentThread;
|
||||||
updateThreadActivity({
|
updateThreadActivity({
|
||||||
threadId: thread?.threadId, qortalName: thread?.threadData?.name, groupId: groupId, thread: thread
|
threadId: thread?.threadId,
|
||||||
})
|
qortalName: thread?.threadData?.name,
|
||||||
}
|
groupId: groupId,
|
||||||
|
thread: thread,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const setThreadFunc = (data)=> {
|
const setThreadFunc = (data) => {
|
||||||
const thread = data
|
const thread = data;
|
||||||
setCurrentThread(thread);
|
setCurrentThread(thread);
|
||||||
if(thread?.threadId && thread?.threadData?.name){
|
if (thread?.threadId && thread?.threadData?.name) {
|
||||||
updateThreadActivity({
|
updateThreadActivity({
|
||||||
threadId: thread?.threadId, qortalName: thread?.threadData?.name, groupId: groupId, thread: thread
|
threadId: thread?.threadId,
|
||||||
})
|
qortalName: thread?.threadData?.name,
|
||||||
}
|
groupId: groupId,
|
||||||
|
thread: thread,
|
||||||
|
});
|
||||||
|
}
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
executeEvent("threadFetchMode", {
|
executeEvent('threadFetchMode', {
|
||||||
mode: "last-page"
|
mode: 'last-page',
|
||||||
});
|
});
|
||||||
}, 300);
|
}, 300);
|
||||||
}
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
useEffect(()=> {
|
if (defaultThread) {
|
||||||
if(defaultThread){
|
setThreadFunc(defaultThread);
|
||||||
setThreadFunc(defaultThread)
|
setDefaultThread(null);
|
||||||
setDefaultThread(null)
|
|
||||||
}
|
}
|
||||||
}, [defaultThread])
|
}, [defaultThread]);
|
||||||
|
|
||||||
const combinedListTempAndReal = useMemo(() => {
|
const combinedListTempAndReal = useMemo(() => {
|
||||||
// Combine the two lists
|
// Combine the two lists
|
||||||
const transformTempPublishedList = tempPublishedList.map((item)=> {
|
const transformTempPublishedList = tempPublishedList.map((item) => {
|
||||||
return {
|
return {
|
||||||
...item,
|
...item,
|
||||||
threadData: item.tempData,
|
threadData: item.tempData,
|
||||||
threadOwner: item?.name,
|
threadOwner: item?.name,
|
||||||
threadId: item.identifier
|
threadId: item.identifier,
|
||||||
}
|
};
|
||||||
})
|
});
|
||||||
const combined = [...transformTempPublishedList, ...listOfThreadsToDisplay];
|
const combined = [...transformTempPublishedList, ...listOfThreadsToDisplay];
|
||||||
|
|
||||||
// Remove duplicates based on the "identifier"
|
// Remove duplicates based on the "identifier"
|
||||||
const uniqueItems = new Map();
|
const uniqueItems = new Map();
|
||||||
combined.forEach(item => {
|
combined.forEach((item) => {
|
||||||
uniqueItems.set(item.threadId, item); // This will overwrite duplicates, keeping the last occurrence
|
uniqueItems.set(item.threadId, item); // This will overwrite duplicates, keeping the last occurrence
|
||||||
});
|
});
|
||||||
|
|
||||||
// Convert the map back to an array and sort by "created" timestamp in descending order
|
// Convert the map back to an array and sort by "created" timestamp in descending order
|
||||||
const sortedList = Array.from(uniqueItems.values()).sort((a, b) =>
|
const sortedList = Array.from(uniqueItems.values()).sort((a, b) =>
|
||||||
filterMode === 'Oldest'
|
filterMode === 'Oldest'
|
||||||
? a.threadData?.createdAt - b.threadData?.createdAt
|
? a.threadData?.createdAt - b.threadData?.createdAt
|
||||||
: b.threadData?.createdAt - a.threadData?.createdAt
|
: b.threadData?.createdAt - a.threadData?.createdAt
|
||||||
);
|
);
|
||||||
|
|
||||||
return sortedList;
|
return sortedList;
|
||||||
}, [tempPublishedList, listOfThreadsToDisplay, filterMode]);
|
}, [tempPublishedList, listOfThreadsToDisplay, filterMode]);
|
||||||
|
|
||||||
@ -548,9 +577,9 @@ export const GroupMail = ({
|
|||||||
return (
|
return (
|
||||||
<GroupContainer
|
<GroupContainer
|
||||||
sx={{
|
sx={{
|
||||||
position: "relative",
|
position: 'relative',
|
||||||
overflow: "auto",
|
overflow: 'auto',
|
||||||
width: "100%",
|
width: '100%',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Popover
|
<Popover
|
||||||
@ -558,19 +587,19 @@ export const GroupMail = ({
|
|||||||
anchorEl={anchorElInstanceFilter.current}
|
anchorEl={anchorElInstanceFilter.current}
|
||||||
onClose={handleCloseThreadFilterList}
|
onClose={handleCloseThreadFilterList}
|
||||||
anchorOrigin={{
|
anchorOrigin={{
|
||||||
vertical: "bottom",
|
vertical: 'bottom',
|
||||||
horizontal: "right",
|
horizontal: 'right',
|
||||||
}}
|
}}
|
||||||
transformOrigin={{
|
transformOrigin={{
|
||||||
vertical: "top",
|
vertical: 'top',
|
||||||
horizontal: "right",
|
horizontal: 'right',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<InstanceListParent
|
<InstanceListParent
|
||||||
sx={{
|
sx={{
|
||||||
minHeight: "unset",
|
minHeight: 'unset',
|
||||||
width: "auto",
|
width: 'auto',
|
||||||
padding: "0px",
|
padding: '0px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<InstanceListHeader></InstanceListHeader>
|
<InstanceListHeader></InstanceListHeader>
|
||||||
@ -583,7 +612,7 @@ export const GroupMail = ({
|
|||||||
}}
|
}}
|
||||||
sx={{
|
sx={{
|
||||||
backgroundColor:
|
backgroundColor:
|
||||||
filterMode === filter ? "rgba(74, 158, 244, 1)" : "unset",
|
filterMode === filter ? 'rgba(74, 158, 244, 1)' : 'unset',
|
||||||
}}
|
}}
|
||||||
key={filter}
|
key={filter}
|
||||||
>
|
>
|
||||||
@ -606,12 +635,11 @@ export const GroupMail = ({
|
|||||||
</Popover>
|
</Popover>
|
||||||
<ThreadContainerFullWidth>
|
<ThreadContainerFullWidth>
|
||||||
<ThreadContainer>
|
<ThreadContainer>
|
||||||
|
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: "flex",
|
display: 'flex',
|
||||||
justifyContent: "space-between",
|
justifyContent: 'space-between',
|
||||||
alignItems: "center",
|
alignItems: 'center',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<NewThread
|
<NewThread
|
||||||
@ -626,7 +654,7 @@ export const GroupMail = ({
|
|||||||
/>
|
/>
|
||||||
<ComposeContainerBlank
|
<ComposeContainerBlank
|
||||||
sx={{
|
sx={{
|
||||||
height: "auto",
|
height: 'auto',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{selectedGroup && !currentThread && (
|
{selectedGroup && !currentThread && (
|
||||||
@ -647,17 +675,22 @@ export const GroupMail = ({
|
|||||||
</ComposeContainerBlank>
|
</ComposeContainerBlank>
|
||||||
</Box>
|
</Box>
|
||||||
<Spacer height="30px" />
|
<Spacer height="30px" />
|
||||||
<Box sx={{
|
<Box
|
||||||
display: 'flex',
|
sx={{
|
||||||
alignItems: 'center',
|
display: 'flex',
|
||||||
justifyContent: 'space-between'
|
alignItems: 'center',
|
||||||
}}>
|
justifyContent: 'space-between',
|
||||||
<AllThreadP>{filterMode}</AllThreadP>
|
}}
|
||||||
|
>
|
||||||
|
<AllThreadP>{filterMode}</AllThreadP>
|
||||||
|
|
||||||
<RefreshIcon onClick={refetchThreadsLists} sx={{
|
<RefreshIcon
|
||||||
color: 'white',
|
onClick={refetchThreadsLists}
|
||||||
cursor: 'pointer'
|
sx={{
|
||||||
}} />
|
color: 'white',
|
||||||
|
cursor: 'pointer',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
<Spacer height="30px" />
|
<Spacer height="30px" />
|
||||||
|
|
||||||
@ -668,112 +701,119 @@ export const GroupMail = ({
|
|||||||
];
|
];
|
||||||
const shouldAppearLighter =
|
const shouldAppearLighter =
|
||||||
hasViewedRecent &&
|
hasViewedRecent &&
|
||||||
filterMode === "Recently active" &&
|
filterMode === 'Recently active' &&
|
||||||
thread?.threadData?.createdAt < hasViewedRecent?.timestamp;
|
thread?.threadData?.createdAt < hasViewedRecent?.timestamp;
|
||||||
return (
|
return (
|
||||||
<SingleThreadParent
|
<SingleThreadParent
|
||||||
sx={{
|
sx={{
|
||||||
flexWrap: 'wrap',
|
flexWrap: 'wrap',
|
||||||
gap: '15px',
|
gap: '15px',
|
||||||
height: 'auto'
|
height: 'auto',
|
||||||
}}
|
}}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setCurrentThread(thread);
|
setCurrentThread(thread);
|
||||||
if(thread?.threadId && thread?.threadData?.name){
|
if (thread?.threadId && thread?.threadData?.name) {
|
||||||
updateThreadActivity({
|
updateThreadActivity({
|
||||||
threadId: thread?.threadId, qortalName: thread?.threadData?.name, groupId: groupId, thread: thread
|
threadId: thread?.threadId,
|
||||||
})
|
qortalName: thread?.threadData?.name,
|
||||||
|
groupId: groupId,
|
||||||
|
thread: thread,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|
||||||
<Avatar
|
<Avatar
|
||||||
sx={{
|
sx={{
|
||||||
height: "50px",
|
height: '50px',
|
||||||
width: "50px",
|
width: '50px',
|
||||||
}}
|
}}
|
||||||
src={`${getBaseApiReact()}/arbitrary/THUMBNAIL/${thread?.threadData?.name}/qortal_avatar?async=true`}
|
src={`${getBaseApiReact()}/arbitrary/THUMBNAIL/${thread?.threadData?.name}/qortal_avatar?async=true`}
|
||||||
alt={thread?.threadData?.name}
|
alt={thread?.threadData?.name}
|
||||||
>
|
>
|
||||||
{thread?.threadData?.name?.charAt(0)}
|
{thread?.threadData?.name?.charAt(0)}
|
||||||
</Avatar>
|
</Avatar>
|
||||||
|
|
||||||
<ThreadInfoColumn>
|
<ThreadInfoColumn>
|
||||||
|
|
||||||
<ThreadInfoColumnNameP>
|
<ThreadInfoColumnNameP>
|
||||||
<ThreadInfoColumnbyP>by </ThreadInfoColumnbyP>
|
<ThreadInfoColumnbyP>by </ThreadInfoColumnbyP>
|
||||||
{thread?.threadData?.name}
|
{thread?.threadData?.name}
|
||||||
</ThreadInfoColumnNameP>
|
</ThreadInfoColumnNameP>
|
||||||
|
|
||||||
<ThreadInfoColumnTime>
|
<ThreadInfoColumnTime>
|
||||||
{formatTimestamp(thread?.threadData?.createdAt)}
|
{formatTimestamp(thread?.threadData?.createdAt)}
|
||||||
</ThreadInfoColumnTime>
|
</ThreadInfoColumnTime>
|
||||||
</ThreadInfoColumn>
|
</ThreadInfoColumn>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
display: "flex",
|
display: 'flex',
|
||||||
flexDirection: "column",
|
flexDirection: 'column',
|
||||||
justifyContent: "center",
|
justifyContent: 'center',
|
||||||
width: '100%'
|
width: '100%',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ThreadSingleTitle
|
<ThreadSingleTitle
|
||||||
sx={{
|
sx={{
|
||||||
fontWeight: shouldAppearLighter && 300,
|
fontWeight: shouldAppearLighter && 300,
|
||||||
fontSize: isMobile && '18px'
|
fontSize: isMobile && '18px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{thread?.threadData?.title}
|
{thread?.threadData?.title}
|
||||||
</ThreadSingleTitle>
|
</ThreadSingleTitle>
|
||||||
<Spacer height="10px" />
|
<Spacer height="10px" />
|
||||||
{filterMode === "Recently active" && (
|
{filterMode === 'Recently active' && (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
display: "flex",
|
display: 'flex',
|
||||||
alignItems: "center",
|
alignItems: 'center',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ThreadSingleLastMessageP>
|
<ThreadSingleLastMessageP>
|
||||||
<ThreadSingleLastMessageSpanP>
|
<ThreadSingleLastMessageSpanP>
|
||||||
last message:{" "}
|
last message:{' '}
|
||||||
</ThreadSingleLastMessageSpanP>
|
</ThreadSingleLastMessageSpanP>
|
||||||
{formatDate(thread?.created)}
|
{formatDate(thread?.created)}
|
||||||
</ThreadSingleLastMessageP>
|
</ThreadSingleLastMessageP>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<Box onClick={()=> {
|
<Box
|
||||||
setTimeout(() => {
|
onClick={() => {
|
||||||
executeEvent("threadFetchMode", {
|
setTimeout(() => {
|
||||||
mode: "last-page"
|
executeEvent('threadFetchMode', {
|
||||||
});
|
mode: 'last-page',
|
||||||
}, 300);
|
});
|
||||||
|
}, 300);
|
||||||
|
}}
|
||||||
}} sx={{
|
sx={{
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
bottom: '2px',
|
bottom: '2px',
|
||||||
right: '2px',
|
right: '2px',
|
||||||
borderRadius: '5px',
|
borderRadius: '5px',
|
||||||
backgroundColor: '#27282c',
|
backgroundColor: '#27282c',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
gap: '10px',
|
gap: '10px',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
padding: '5px',
|
padding: '5px',
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
'&:hover': {
|
'&:hover': {
|
||||||
background: 'rgba(255, 255, 255, 0.60)'
|
background: 'rgba(255, 255, 255, 0.60)',
|
||||||
}
|
},
|
||||||
}}>
|
}}
|
||||||
<Typography sx={{
|
>
|
||||||
color: 'white',
|
<Typography
|
||||||
fontSize: '12px'
|
sx={{
|
||||||
}}>Last page</Typography>
|
color: 'white',
|
||||||
<ArrowForwardIosIcon sx={{
|
fontSize: '12px',
|
||||||
color: 'white',
|
}}
|
||||||
fontSize: '12px'
|
>
|
||||||
}} />
|
Last page
|
||||||
|
</Typography>
|
||||||
|
<ArrowForwardIosIcon
|
||||||
|
sx={{
|
||||||
|
color: 'white',
|
||||||
|
fontSize: '12px',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
</SingleThreadParent>
|
</SingleThreadParent>
|
||||||
);
|
);
|
||||||
@ -781,12 +821,12 @@ export const GroupMail = ({
|
|||||||
|
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
width: "100%",
|
width: '100%',
|
||||||
justifyContent: "center",
|
justifyContent: 'center',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{listOfThreadsToDisplay.length >= 20 &&
|
{listOfThreadsToDisplay.length >= 20 &&
|
||||||
filterMode !== "Recently active" && (
|
filterMode !== 'Recently active' && (
|
||||||
<LazyLoad
|
<LazyLoad
|
||||||
onLoadMore={() => getAllThreads(groupId, filterMode, false)}
|
onLoadMore={() => getAllThreads(groupId, filterMode, false)}
|
||||||
></LazyLoad>
|
></LazyLoad>
|
||||||
@ -797,7 +837,7 @@ export const GroupMail = ({
|
|||||||
<LoadingSnackbar
|
<LoadingSnackbar
|
||||||
open={isLoading}
|
open={isLoading}
|
||||||
info={{
|
info={{
|
||||||
message: "Loading threads... please wait.",
|
message: 'Loading threads... please wait.',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</GroupContainer>
|
</GroupContainer>
|
||||||
|
@ -1,11 +1,17 @@
|
|||||||
import React, { useEffect, useRef, useState } from "react";
|
import React, { useEffect, useRef, useState } from 'react';
|
||||||
import { Box, Button, CircularProgress, Input, Typography } from "@mui/material";
|
import {
|
||||||
import ShortUniqueId from "short-unique-id";
|
Box,
|
||||||
import CloseIcon from "@mui/icons-material/Close";
|
Button,
|
||||||
|
CircularProgress,
|
||||||
|
Input,
|
||||||
|
Typography,
|
||||||
|
} from '@mui/material';
|
||||||
|
import ShortUniqueId from 'short-unique-id';
|
||||||
|
import CloseIcon from '@mui/icons-material/Close';
|
||||||
|
|
||||||
import ModalCloseSVG from "../../../assets/svgs/ModalClose.svg";
|
import ModalCloseSVG from '../../../assets/svgs/ModalClose.svg';
|
||||||
|
|
||||||
import ComposeIconSVG from "../../../assets/svgs/ComposeIcon.svg";
|
import ComposeIconSVG from '../../../assets/svgs/ComposeIcon.svg';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
AttachmentContainer,
|
AttachmentContainer,
|
||||||
@ -22,20 +28,25 @@ import {
|
|||||||
NewMessageInputRow,
|
NewMessageInputRow,
|
||||||
NewMessageSendButton,
|
NewMessageSendButton,
|
||||||
NewMessageSendP,
|
NewMessageSendP,
|
||||||
} from "./Mail-styles";
|
} from './Mail-styles';
|
||||||
|
|
||||||
import { ReusableModal } from "./ReusableModal";
|
import { ReusableModal } from './ReusableModal';
|
||||||
import { Spacer } from "../../../common/Spacer";
|
import { Spacer } from '../../../common/Spacer';
|
||||||
import { formatBytes } from "../../../utils/Size";
|
import { formatBytes } from '../../../utils/Size';
|
||||||
import { CreateThreadIcon } from "../../../assets/svgs/CreateThreadIcon";
|
import { CreateThreadIcon } from '../../../assets/svgs/CreateThreadIcon';
|
||||||
import { SendNewMessage } from "../../../assets/svgs/SendNewMessage";
|
import { SendNewMessage } from '../../../assets/svgs/SendNewMessage';
|
||||||
import { TextEditor } from "./TextEditor";
|
import { TextEditor } from './TextEditor';
|
||||||
import { MyContext, isMobile, pauseAllQueues, resumeAllQueues } from "../../../App";
|
import {
|
||||||
import { getFee } from "../../../background";
|
MyContext,
|
||||||
import TipTap from "../../Chat/TipTap";
|
isMobile,
|
||||||
import { MessageDisplay } from "../../Chat/MessageDisplay";
|
pauseAllQueues,
|
||||||
import { CustomizedSnackbars } from "../../Snackbar/Snackbar";
|
resumeAllQueues,
|
||||||
import { saveTempPublish } from "../../Chat/GroupAnnouncements";
|
} from '../../../App';
|
||||||
|
import { getFee } from '../../../background';
|
||||||
|
import TipTap from '../../Chat/TipTap';
|
||||||
|
import { MessageDisplay } from '../../Chat/MessageDisplay';
|
||||||
|
import { CustomizedSnackbars } from '../../Snackbar/Snackbar';
|
||||||
|
import { saveTempPublish } from '../../Chat/GroupAnnouncements';
|
||||||
|
|
||||||
const uid = new ShortUniqueId({ length: 8 });
|
const uid = new ShortUniqueId({ length: 8 });
|
||||||
|
|
||||||
@ -54,21 +65,21 @@ export function objectToBase64(obj: any) {
|
|||||||
const jsonString = JSON.stringify(obj);
|
const jsonString = JSON.stringify(obj);
|
||||||
|
|
||||||
// Step 2: Create a Blob from the JSON string
|
// Step 2: Create a Blob from the JSON string
|
||||||
const blob = new Blob([jsonString], { type: "application/json" });
|
const blob = new Blob([jsonString], { type: 'application/json' });
|
||||||
|
|
||||||
// Step 3: Create a FileReader to read the Blob as a base64-encoded string
|
// Step 3: Create a FileReader to read the Blob as a base64-encoded string
|
||||||
return new Promise<string>((resolve, reject) => {
|
return new Promise<string>((resolve, reject) => {
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
reader.onloadend = () => {
|
reader.onloadend = () => {
|
||||||
if (typeof reader.result === "string") {
|
if (typeof reader.result === 'string') {
|
||||||
// Remove 'data:application/json;base64,' prefix
|
// Remove 'data:application/json;base64,' prefix
|
||||||
const base64 = reader.result.replace(
|
const base64 = reader.result.replace(
|
||||||
"data:application/json;base64,",
|
'data:application/json;base64,',
|
||||||
""
|
''
|
||||||
);
|
);
|
||||||
resolve(base64);
|
resolve(base64);
|
||||||
} else {
|
} else {
|
||||||
reject(new Error("Failed to read the Blob as a base64-encoded string"));
|
reject(new Error('Failed to read the Blob as a base64-encoded string'));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
reader.onerror = () => {
|
reader.onerror = () => {
|
||||||
@ -94,10 +105,11 @@ export const publishGroupEncryptedResource = async ({
|
|||||||
identifier,
|
identifier,
|
||||||
}) => {
|
}) => {
|
||||||
return new Promise((res, rej) => {
|
return new Promise((res, rej) => {
|
||||||
window.sendMessage("publishGroupEncryptedResource", {
|
window
|
||||||
encryptedData,
|
.sendMessage('publishGroupEncryptedResource', {
|
||||||
identifier,
|
encryptedData,
|
||||||
})
|
identifier,
|
||||||
|
})
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
if (!response?.error) {
|
if (!response?.error) {
|
||||||
res(response);
|
res(response);
|
||||||
@ -106,19 +118,19 @@ export const publishGroupEncryptedResource = async ({
|
|||||||
rej(response.error);
|
rej(response.error);
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
rej(error.message || "An error occurred");
|
rej(error.message || 'An error occurred');
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const encryptSingleFunc = async (data: string, secretKeyObject: any) => {
|
export const encryptSingleFunc = async (data: string, secretKeyObject: any) => {
|
||||||
try {
|
try {
|
||||||
return new Promise((res, rej) => {
|
return new Promise((res, rej) => {
|
||||||
window.sendMessage("encryptSingle", {
|
window
|
||||||
data,
|
.sendMessage('encryptSingle', {
|
||||||
secretKeyObject,
|
data,
|
||||||
})
|
secretKeyObject,
|
||||||
|
})
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
if (!response?.error) {
|
if (!response?.error) {
|
||||||
res(response);
|
res(response);
|
||||||
@ -127,11 +139,12 @@ export const encryptSingleFunc = async (data: string, secretKeyObject: any) => {
|
|||||||
rej(response.error);
|
rej(response.error);
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
rej(error.message || "An error occurred");
|
rej(error.message || 'An error occurred');
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
} catch (error) {}
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
export const NewThread = ({
|
export const NewThread = ({
|
||||||
groupInfo,
|
groupInfo,
|
||||||
@ -145,14 +158,14 @@ export const NewThread = ({
|
|||||||
postReply,
|
postReply,
|
||||||
myName,
|
myName,
|
||||||
setPostReply,
|
setPostReply,
|
||||||
isPrivate
|
isPrivate,
|
||||||
}: NewMessageProps) => {
|
}: NewMessageProps) => {
|
||||||
const { show } = React.useContext(MyContext);
|
const { show } = React.useContext(MyContext);
|
||||||
|
|
||||||
const [isOpen, setIsOpen] = useState<boolean>(false);
|
const [isOpen, setIsOpen] = useState<boolean>(false);
|
||||||
const [value, setValue] = useState("");
|
const [value, setValue] = useState('');
|
||||||
const [isSending, setIsSending] = useState(false);
|
const [isSending, setIsSending] = useState(false);
|
||||||
const [threadTitle, setThreadTitle] = useState<string>("");
|
const [threadTitle, setThreadTitle] = useState<string>('');
|
||||||
const [openSnack, setOpenSnack] = React.useState(false);
|
const [openSnack, setOpenSnack] = React.useState(false);
|
||||||
const [infoSnack, setInfoSnack] = React.useState(null);
|
const [infoSnack, setInfoSnack] = React.useState(null);
|
||||||
const editorRef = useRef(null);
|
const editorRef = useRef(null);
|
||||||
@ -168,44 +181,42 @@ export const NewThread = ({
|
|||||||
|
|
||||||
const closeModal = () => {
|
const closeModal = () => {
|
||||||
setIsOpen(false);
|
setIsOpen(false);
|
||||||
setValue("");
|
setValue('');
|
||||||
if(setPostReply){
|
if (setPostReply) {
|
||||||
setPostReply(null)
|
setPostReply(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
async function publishQDNResource() {
|
async function publishQDNResource() {
|
||||||
try {
|
try {
|
||||||
pauseAllQueues()
|
pauseAllQueues();
|
||||||
if(isSending) return
|
if (isSending) return;
|
||||||
setIsSending(true)
|
setIsSending(true);
|
||||||
let name: string = "";
|
let name: string = '';
|
||||||
let errorMsg = "";
|
let errorMsg = '';
|
||||||
|
|
||||||
name = userInfo?.name || "";
|
name = userInfo?.name || '';
|
||||||
|
|
||||||
const missingFields: string[] = [];
|
const missingFields: string[] = [];
|
||||||
|
|
||||||
if (!isMessage && !threadTitle) {
|
if (!isMessage && !threadTitle) {
|
||||||
errorMsg = "Please provide a thread title";
|
errorMsg = 'Please provide a thread title';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!name) {
|
if (!name) {
|
||||||
errorMsg = "Cannot send a message without a access to your name";
|
errorMsg = 'Cannot send a message without a access to your name';
|
||||||
}
|
}
|
||||||
if (!groupInfo) {
|
if (!groupInfo) {
|
||||||
errorMsg = "Cannot access group information";
|
errorMsg = 'Cannot access group information';
|
||||||
}
|
}
|
||||||
|
|
||||||
// if (!description) missingFields.push('subject')
|
// if (!description) missingFields.push('subject')
|
||||||
if (missingFields.length > 0) {
|
if (missingFields.length > 0) {
|
||||||
const missingFieldsString = missingFields.join(", ");
|
const missingFieldsString = missingFields.join(', ');
|
||||||
const errMsg = `Missing: ${missingFieldsString}`;
|
const errMsg = `Missing: ${missingFieldsString}`;
|
||||||
errorMsg = errMsg;
|
errorMsg = errMsg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (errorMsg) {
|
if (errorMsg) {
|
||||||
// dispatch(
|
// dispatch(
|
||||||
// setNotification({
|
// setNotification({
|
||||||
@ -217,17 +228,17 @@ export const NewThread = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const htmlContent = editorRef.current.getHTML();
|
const htmlContent = editorRef.current.getHTML();
|
||||||
|
|
||||||
if (!htmlContent?.trim() || htmlContent?.trim() === "<p></p>")
|
if (!htmlContent?.trim() || htmlContent?.trim() === '<p></p>')
|
||||||
throw new Error("Please provide a first message to the thread");
|
throw new Error('Please provide a first message to the thread');
|
||||||
const fee = await getFee("ARBITRARY");
|
const fee = await getFee('ARBITRARY');
|
||||||
let feeToShow = fee.fee;
|
let feeToShow = fee.fee;
|
||||||
if (!isMessage) {
|
if (!isMessage) {
|
||||||
feeToShow = +feeToShow * 2;
|
feeToShow = +feeToShow * 2;
|
||||||
}
|
}
|
||||||
await show({
|
await show({
|
||||||
message: "Would you like to perform a ARBITRARY transaction?",
|
message: 'Would you like to perform a ARBITRARY transaction?',
|
||||||
publishFee: feeToShow + " QORT",
|
publishFee: feeToShow + ' QORT',
|
||||||
});
|
});
|
||||||
|
|
||||||
let reply = null;
|
let reply = null;
|
||||||
@ -245,20 +256,21 @@ export const NewThread = ({
|
|||||||
threadOwner: currentThread?.threadData?.name || name,
|
threadOwner: currentThread?.threadData?.name || name,
|
||||||
reply,
|
reply,
|
||||||
};
|
};
|
||||||
|
|
||||||
const secretKey = isPrivate === false ? null : await getSecretKey(false, true);
|
const secretKey =
|
||||||
|
isPrivate === false ? null : await getSecretKey(false, true);
|
||||||
if (!secretKey && isPrivate) {
|
if (!secretKey && isPrivate) {
|
||||||
throw new Error("Cannot get group secret key");
|
throw new Error('Cannot get group secret key');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isMessage) {
|
if (!isMessage) {
|
||||||
const idThread = uid.rnd();
|
const idThread = uid.rnd();
|
||||||
const idMsg = uid.rnd();
|
const idMsg = uid.rnd();
|
||||||
const messageToBase64 = await objectToBase64(mailObject);
|
const messageToBase64 = await objectToBase64(mailObject);
|
||||||
const encryptSingleFirstPost = isPrivate === false ? messageToBase64 : await encryptSingleFunc(
|
const encryptSingleFirstPost =
|
||||||
messageToBase64,
|
isPrivate === false
|
||||||
secretKey
|
? messageToBase64
|
||||||
);
|
: await encryptSingleFunc(messageToBase64, secretKey);
|
||||||
const threadObject = {
|
const threadObject = {
|
||||||
title: threadTitle,
|
title: threadTitle,
|
||||||
groupId: groupInfo.id,
|
groupId: groupInfo.id,
|
||||||
@ -267,10 +279,10 @@ export const NewThread = ({
|
|||||||
};
|
};
|
||||||
const threadToBase64 = await objectToBase64(threadObject);
|
const threadToBase64 = await objectToBase64(threadObject);
|
||||||
|
|
||||||
const encryptSingleThread = isPrivate === false ? threadToBase64 : await encryptSingleFunc(
|
const encryptSingleThread =
|
||||||
threadToBase64,
|
isPrivate === false
|
||||||
secretKey
|
? threadToBase64
|
||||||
);
|
: await encryptSingleFunc(threadToBase64, secretKey);
|
||||||
let identifierThread = `grp-${groupInfo.groupId}-thread-${idThread}`;
|
let identifierThread = `grp-${groupInfo.groupId}-thread-${idThread}`;
|
||||||
await publishGroupEncryptedResource({
|
await publishGroupEncryptedResource({
|
||||||
identifier: identifierThread,
|
identifier: identifierThread,
|
||||||
@ -288,23 +300,27 @@ export const NewThread = ({
|
|||||||
service: 'DOCUMENT',
|
service: 'DOCUMENT',
|
||||||
tempData: threadObject,
|
tempData: threadObject,
|
||||||
created: Date.now(),
|
created: Date.now(),
|
||||||
groupId: groupInfo.groupId
|
groupId: groupInfo.groupId,
|
||||||
}
|
};
|
||||||
const dataToSaveToStoragePost = {
|
const dataToSaveToStoragePost = {
|
||||||
name: myName,
|
name: myName,
|
||||||
identifier: identifierPost,
|
identifier: identifierPost,
|
||||||
service: 'DOCUMENT',
|
service: 'DOCUMENT',
|
||||||
tempData: mailObject,
|
tempData: mailObject,
|
||||||
created: Date.now(),
|
created: Date.now(),
|
||||||
threadId: identifierThread
|
threadId: identifierThread,
|
||||||
}
|
};
|
||||||
await saveTempPublish({data: dataToSaveToStorage, key: 'thread'})
|
await saveTempPublish({ data: dataToSaveToStorage, key: 'thread' });
|
||||||
await saveTempPublish({data: dataToSaveToStoragePost, key: 'thread-post'})
|
await saveTempPublish({
|
||||||
setInfoSnack({
|
data: dataToSaveToStoragePost,
|
||||||
type: "success",
|
key: 'thread-post',
|
||||||
message: "Successfully created thread. It may take some time for the publish to propagate",
|
|
||||||
});
|
});
|
||||||
setOpenSnack(true)
|
setInfoSnack({
|
||||||
|
type: 'success',
|
||||||
|
message:
|
||||||
|
'Successfully created thread. It may take some time for the publish to propagate',
|
||||||
|
});
|
||||||
|
setOpenSnack(true);
|
||||||
|
|
||||||
// dispatch(
|
// dispatch(
|
||||||
// setNotification({
|
// setNotification({
|
||||||
@ -313,35 +329,36 @@ export const NewThread = ({
|
|||||||
// })
|
// })
|
||||||
// );
|
// );
|
||||||
if (publishCallback) {
|
if (publishCallback) {
|
||||||
publishCallback()
|
publishCallback();
|
||||||
|
|
||||||
}
|
}
|
||||||
closeModal();
|
closeModal();
|
||||||
} else {
|
} else {
|
||||||
|
if (!currentThread) throw new Error('unable to locate thread Id');
|
||||||
if (!currentThread) throw new Error("unable to locate thread Id");
|
|
||||||
const idThread = currentThread.threadId;
|
const idThread = currentThread.threadId;
|
||||||
const messageToBase64 = await objectToBase64(mailObject);
|
const messageToBase64 = await objectToBase64(mailObject);
|
||||||
const encryptSinglePost = isPrivate === false ? messageToBase64 : await encryptSingleFunc(
|
const encryptSinglePost =
|
||||||
messageToBase64,
|
isPrivate === false
|
||||||
secretKey
|
? messageToBase64
|
||||||
);
|
: await encryptSingleFunc(messageToBase64, secretKey);
|
||||||
const idMsg = uid.rnd();
|
const idMsg = uid.rnd();
|
||||||
let identifier = `thmsg-${idThread}-${idMsg}`;
|
let identifier = `thmsg-${idThread}-${idMsg}`;
|
||||||
const res = await publishGroupEncryptedResource({
|
const res = await publishGroupEncryptedResource({
|
||||||
identifier: identifier,
|
identifier: identifier,
|
||||||
encryptedData: encryptSinglePost,
|
encryptedData: encryptSinglePost,
|
||||||
});
|
});
|
||||||
|
|
||||||
const dataToSaveToStoragePost = {
|
const dataToSaveToStoragePost = {
|
||||||
threadId: idThread,
|
threadId: idThread,
|
||||||
name: myName,
|
name: myName,
|
||||||
identifier: identifier,
|
identifier: identifier,
|
||||||
service: 'DOCUMENT',
|
service: 'DOCUMENT',
|
||||||
tempData: mailObject,
|
tempData: mailObject,
|
||||||
created: Date.now()
|
created: Date.now(),
|
||||||
}
|
};
|
||||||
await saveTempPublish({data: dataToSaveToStoragePost, key: 'thread-post'})
|
await saveTempPublish({
|
||||||
|
data: dataToSaveToStoragePost,
|
||||||
|
key: 'thread-post',
|
||||||
|
});
|
||||||
// await qortalRequest(multiplePublishMsg);
|
// await qortalRequest(multiplePublishMsg);
|
||||||
// dispatch(
|
// dispatch(
|
||||||
// setNotification({
|
// setNotification({
|
||||||
@ -350,12 +367,13 @@ export const NewThread = ({
|
|||||||
// })
|
// })
|
||||||
// );
|
// );
|
||||||
setInfoSnack({
|
setInfoSnack({
|
||||||
type: "success",
|
type: 'success',
|
||||||
message: "Successfully created post. It may take some time for the publish to propagate",
|
message:
|
||||||
|
'Successfully created post. It may take some time for the publish to propagate',
|
||||||
});
|
});
|
||||||
setOpenSnack(true)
|
setOpenSnack(true);
|
||||||
if(publishCallback){
|
if (publishCallback) {
|
||||||
publishCallback()
|
publishCallback();
|
||||||
}
|
}
|
||||||
// messageCallback({
|
// messageCallback({
|
||||||
// identifier,
|
// identifier,
|
||||||
@ -369,17 +387,16 @@ export const NewThread = ({
|
|||||||
|
|
||||||
closeModal();
|
closeModal();
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
if(error?.message){
|
if (error?.message) {
|
||||||
setInfoSnack({
|
setInfoSnack({
|
||||||
type: "error",
|
type: 'error',
|
||||||
message: error?.message,
|
message: error?.message,
|
||||||
});
|
});
|
||||||
setOpenSnack(true)
|
setOpenSnack(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
setIsSending(false);
|
setIsSending(false);
|
||||||
resumeAllQueues()
|
resumeAllQueues();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -389,56 +406,59 @@ export const NewThread = ({
|
|||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: "flex",
|
display: 'flex',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ComposeContainer
|
<ComposeContainer
|
||||||
sx={{
|
sx={{
|
||||||
padding: isMobile ? '5px' : "15px",
|
padding: isMobile ? '5px' : '15px',
|
||||||
justifyContent: isMobile ? 'flex-start' : 'revert'
|
justifyContent: isMobile ? 'flex-start' : 'revert',
|
||||||
}}
|
}}
|
||||||
onClick={() => setIsOpen(true)}
|
onClick={() => setIsOpen(true)}
|
||||||
>
|
>
|
||||||
<ComposeIcon src={ComposeIconSVG} />
|
<ComposeIcon src={ComposeIconSVG} />
|
||||||
<ComposeP>{currentThread ? "New Post" : "New Thread"}</ComposeP>
|
<ComposeP>{currentThread ? 'New Post' : 'New Thread'}</ComposeP>
|
||||||
</ComposeContainer>
|
</ComposeContainer>
|
||||||
|
|
||||||
<ReusableModal
|
<ReusableModal
|
||||||
open={isOpen}
|
open={isOpen}
|
||||||
customStyles={{
|
customStyles={{
|
||||||
maxHeight: isMobile ? '95svh' : "95vh",
|
maxHeight: isMobile ? '95svh' : '95vh',
|
||||||
maxWidth: "950px",
|
maxWidth: '950px',
|
||||||
height: "700px",
|
height: '700px',
|
||||||
borderRadius: "12px 12px 0px 0px",
|
borderRadius: '12px 12px 0px 0px',
|
||||||
background: "#434448",
|
background: '#434448',
|
||||||
padding: "0px",
|
padding: '0px',
|
||||||
gap: "0px",
|
gap: '0px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<InstanceListHeader
|
<InstanceListHeader
|
||||||
sx={{
|
sx={{
|
||||||
height: isMobile ? 'auto' : "50px",
|
height: isMobile ? 'auto' : '50px',
|
||||||
padding: isMobile ? '5px' : "20px 42px",
|
padding: isMobile ? '5px' : '20px 42px',
|
||||||
flexDirection: "row",
|
flexDirection: 'row',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: "space-between",
|
justifyContent: 'space-between',
|
||||||
backgroundColor: "#434448",
|
backgroundColor: '#434448',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<NewMessageHeaderP>
|
<NewMessageHeaderP>
|
||||||
{isMessage ? "Post Message" : "New Thread"}
|
{isMessage ? 'Post Message' : 'New Thread'}
|
||||||
</NewMessageHeaderP>
|
</NewMessageHeaderP>
|
||||||
<CloseContainer sx={{
|
<CloseContainer
|
||||||
height: '40px'
|
sx={{
|
||||||
}} onClick={closeModal}>
|
height: '40px',
|
||||||
|
}}
|
||||||
|
onClick={closeModal}
|
||||||
|
>
|
||||||
<NewMessageCloseImg src={ModalCloseSVG} />
|
<NewMessageCloseImg src={ModalCloseSVG} />
|
||||||
</CloseContainer>
|
</CloseContainer>
|
||||||
</InstanceListHeader>
|
</InstanceListHeader>
|
||||||
<InstanceListContainer
|
<InstanceListContainer
|
||||||
sx={{
|
sx={{
|
||||||
backgroundColor: "#434448",
|
backgroundColor: '#434448',
|
||||||
padding: isMobile ? '5px' : "20px 42px",
|
padding: isMobile ? '5px' : '20px 42px',
|
||||||
height: "calc(100% - 165px)",
|
height: 'calc(100% - 165px)',
|
||||||
flexShrink: 0,
|
flexShrink: 0,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@ -457,19 +477,19 @@ export const NewThread = ({
|
|||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
autoCorrect="off"
|
autoCorrect="off"
|
||||||
sx={{
|
sx={{
|
||||||
width: "100%",
|
width: '100%',
|
||||||
color: "white",
|
color: 'white',
|
||||||
"& .MuiInput-input::placeholder": {
|
'& .MuiInput-input::placeholder': {
|
||||||
color: "rgba(255,255,255, 0.70) !important",
|
color: 'rgba(255,255,255, 0.70) !important',
|
||||||
fontSize: isMobile ? '14px' : "20px",
|
fontSize: isMobile ? '14px' : '20px',
|
||||||
fontStyle: "normal",
|
fontStyle: 'normal',
|
||||||
fontWeight: 400,
|
fontWeight: 400,
|
||||||
lineHeight: "120%", // 24px
|
lineHeight: '120%', // 24px
|
||||||
letterSpacing: "0.15px",
|
letterSpacing: '0.15px',
|
||||||
opacity: 1,
|
opacity: 1,
|
||||||
},
|
},
|
||||||
"&:focus": {
|
'&:focus': {
|
||||||
outline: "none",
|
outline: 'none',
|
||||||
},
|
},
|
||||||
// Add any additional styles for the input here
|
// Add any additional styles for the input here
|
||||||
}}
|
}}
|
||||||
@ -481,21 +501,18 @@ export const NewThread = ({
|
|||||||
{postReply && postReply.textContentV2 && (
|
{postReply && postReply.textContentV2 && (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
width: "100%",
|
width: '100%',
|
||||||
maxHeight: "120px",
|
maxHeight: '120px',
|
||||||
overflow: "auto",
|
overflow: 'auto',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<MessageDisplay htmlContent={postReply?.textContentV2} />
|
<MessageDisplay htmlContent={postReply?.textContentV2} />
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
{!isMobile && (
|
{!isMobile && <Spacer height="30px" />}
|
||||||
<Spacer height="30px" />
|
|
||||||
|
|
||||||
)}
|
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
maxHeight: "40vh",
|
maxHeight: '40vh',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<TipTap
|
<TipTap
|
||||||
@ -515,41 +532,44 @@ export const NewThread = ({
|
|||||||
</InstanceListContainer>
|
</InstanceListContainer>
|
||||||
<InstanceFooter
|
<InstanceFooter
|
||||||
sx={{
|
sx={{
|
||||||
backgroundColor: "#434448",
|
backgroundColor: '#434448',
|
||||||
padding: isMobile ? '5px' : "20px 42px",
|
padding: isMobile ? '5px' : '20px 42px',
|
||||||
alignItems: "center",
|
alignItems: 'center',
|
||||||
height: isMobile ? 'auto' : "90px",
|
height: isMobile ? 'auto' : '90px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<NewMessageSendButton onClick={sendMail}>
|
<NewMessageSendButton onClick={sendMail}>
|
||||||
{isSending && (
|
{isSending && (
|
||||||
<Box sx={{height: '100%', position: 'absolute', width: '100%', display: 'flex', justifyContent: 'center', alignItems: 'center'}}>
|
<Box
|
||||||
<CircularProgress sx={{
|
sx={{
|
||||||
|
height: '100%',
|
||||||
}} size={'12px'} />
|
position: 'absolute',
|
||||||
|
width: '100%',
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CircularProgress sx={{}} size={'12px'} />
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
<NewMessageSendP>
|
<NewMessageSendP>
|
||||||
{isMessage ? "Post" : "Create Thread"}
|
{isMessage ? 'Post' : 'Create Thread'}
|
||||||
</NewMessageSendP>
|
</NewMessageSendP>
|
||||||
{isMessage ? (
|
{isMessage ? (
|
||||||
<SendNewMessage
|
<SendNewMessage opacity={1} height="25px" width="25px" />
|
||||||
opacity={1}
|
|
||||||
height="25px"
|
|
||||||
width="25px"
|
|
||||||
/>
|
|
||||||
) : (
|
) : (
|
||||||
<CreateThreadIcon
|
<CreateThreadIcon opacity={1} height="25px" width="25px" />
|
||||||
opacity={1}
|
|
||||||
height="25px"
|
|
||||||
width="25px"
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
</NewMessageSendButton>
|
</NewMessageSendButton>
|
||||||
</InstanceFooter>
|
</InstanceFooter>
|
||||||
|
|
||||||
</ReusableModal>
|
</ReusableModal>
|
||||||
<CustomizedSnackbars open={openSnack} setOpen={setOpenSnack} info={infoSnack} setInfo={setInfoSnack} />
|
<CustomizedSnackbars
|
||||||
|
open={openSnack}
|
||||||
|
setOpen={setOpenSnack}
|
||||||
|
info={infoSnack}
|
||||||
|
setInfo={setInfoSnack}
|
||||||
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -195,7 +195,9 @@ export const Thread = ({
|
|||||||
[message.identifier]: fullObject,
|
[message.identifier]: fullObject,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
} catch (error) {}
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const setTempData = async () => {
|
const setTempData = async () => {
|
||||||
@ -216,7 +218,9 @@ export const Thread = ({
|
|||||||
});
|
});
|
||||||
setTempPublishedList(tempData);
|
setTempPublishedList(tempData);
|
||||||
}
|
}
|
||||||
} catch (error) {}
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const getMailMessages = React.useCallback(
|
const getMailMessages = React.useCallback(
|
||||||
@ -461,7 +465,9 @@ export const Thread = ({
|
|||||||
} else {
|
} else {
|
||||||
fullArrayMsg.unshift(fullObject);
|
fullArrayMsg.unshift(fullObject);
|
||||||
}
|
}
|
||||||
} catch (error) {}
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
setMessages(fullArrayMsg);
|
setMessages(fullArrayMsg);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { LoadingButton } from "@mui/lab";
|
import { LoadingButton } from '@mui/lab';
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
Button,
|
Button,
|
||||||
@ -6,45 +6,46 @@ import {
|
|||||||
MenuItem,
|
MenuItem,
|
||||||
Select,
|
Select,
|
||||||
SelectChangeEvent,
|
SelectChangeEvent,
|
||||||
} from "@mui/material";
|
} from '@mui/material';
|
||||||
import React, { useState } from "react";
|
import React, { useState } from 'react';
|
||||||
import { Spacer } from "../../common/Spacer";
|
import { Spacer } from '../../common/Spacer';
|
||||||
import { Label } from "./AddGroup";
|
import { Label } from './AddGroup';
|
||||||
import { getFee } from "../../background";
|
import { getFee } from '../../background';
|
||||||
|
|
||||||
export const InviteMember = ({ groupId, setInfoSnack, setOpenSnack, show }) => {
|
export const InviteMember = ({ groupId, setInfoSnack, setOpenSnack, show }) => {
|
||||||
const [value, setValue] = useState("");
|
const [value, setValue] = useState('');
|
||||||
const [expiryTime, setExpiryTime] = useState<string>('259200');
|
const [expiryTime, setExpiryTime] = useState<string>('259200');
|
||||||
const [isLoadingInvite, setIsLoadingInvite] = useState(false)
|
const [isLoadingInvite, setIsLoadingInvite] = useState(false);
|
||||||
const inviteMember = async () => {
|
const inviteMember = async () => {
|
||||||
try {
|
try {
|
||||||
const fee = await getFee('GROUP_INVITE')
|
const fee = await getFee('GROUP_INVITE');
|
||||||
await show({
|
await show({
|
||||||
message: "Would you like to perform a GROUP_INVITE transaction?" ,
|
message: 'Would you like to perform a GROUP_INVITE transaction?',
|
||||||
publishFee: fee.fee + ' QORT'
|
publishFee: fee.fee + ' QORT',
|
||||||
})
|
});
|
||||||
setIsLoadingInvite(true)
|
setIsLoadingInvite(true);
|
||||||
if (!expiryTime || !value) return;
|
if (!expiryTime || !value) return;
|
||||||
new Promise((res, rej) => {
|
new Promise((res, rej) => {
|
||||||
window.sendMessage("inviteToGroup", {
|
window
|
||||||
groupId,
|
.sendMessage('inviteToGroup', {
|
||||||
qortalAddress: value,
|
groupId,
|
||||||
inviteTime: +expiryTime,
|
qortalAddress: value,
|
||||||
})
|
inviteTime: +expiryTime,
|
||||||
|
})
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
if (!response?.error) {
|
if (!response?.error) {
|
||||||
setInfoSnack({
|
setInfoSnack({
|
||||||
type: "success",
|
type: 'success',
|
||||||
message: `Successfully invited ${value}. It may take a couple of minutes for the changes to propagate`,
|
message: `Successfully invited ${value}. It may take a couple of minutes for the changes to propagate`,
|
||||||
});
|
});
|
||||||
setOpenSnack(true);
|
setOpenSnack(true);
|
||||||
res(response);
|
res(response);
|
||||||
|
|
||||||
setValue("");
|
setValue('');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setInfoSnack({
|
setInfoSnack({
|
||||||
type: "error",
|
type: 'error',
|
||||||
message: response?.error,
|
message: response?.error,
|
||||||
});
|
});
|
||||||
setOpenSnack(true);
|
setOpenSnack(true);
|
||||||
@ -52,16 +53,17 @@ export const InviteMember = ({ groupId, setInfoSnack, setOpenSnack, show }) => {
|
|||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
setInfoSnack({
|
setInfoSnack({
|
||||||
type: "error",
|
type: 'error',
|
||||||
message: error?.message || "An error occurred",
|
message: error?.message || 'An error occurred',
|
||||||
});
|
});
|
||||||
setOpenSnack(true);
|
setOpenSnack(true);
|
||||||
rej(error);
|
rej(error);
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
} catch (error) {} finally {
|
} catch (error) {
|
||||||
setIsLoadingInvite(false)
|
console.log(error);
|
||||||
|
} finally {
|
||||||
|
setIsLoadingInvite(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -72,8 +74,8 @@ export const InviteMember = ({ groupId, setInfoSnack, setOpenSnack, show }) => {
|
|||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: "flex",
|
display: 'flex',
|
||||||
flexDirection: "column",
|
flexDirection: 'column',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Invite member
|
Invite member
|
||||||
@ -83,8 +85,7 @@ export const InviteMember = ({ groupId, setInfoSnack, setOpenSnack, show }) => {
|
|||||||
placeholder="Name or address"
|
placeholder="Name or address"
|
||||||
onChange={(e) => setValue(e.target.value)}
|
onChange={(e) => setValue(e.target.value)}
|
||||||
/>
|
/>
|
||||||
<Spacer height="20px" />
|
<Spacer height="20px" />
|
||||||
|
|
||||||
<Label>Invitation Expiry Time</Label>
|
<Label>Invitation Expiry Time</Label>
|
||||||
<Select
|
<Select
|
||||||
labelId="demo-simple-select-label"
|
labelId="demo-simple-select-label"
|
||||||
@ -105,7 +106,14 @@ export const InviteMember = ({ groupId, setInfoSnack, setOpenSnack, show }) => {
|
|||||||
<MenuItem value={2592000}>30 days</MenuItem>
|
<MenuItem value={2592000}>30 days</MenuItem>
|
||||||
</Select>
|
</Select>
|
||||||
<Spacer height="20px" />
|
<Spacer height="20px" />
|
||||||
<LoadingButton variant="contained" loadingPosition="start" loading={isLoadingInvite} onClick={inviteMember}>Invite</LoadingButton>
|
<LoadingButton
|
||||||
|
variant="contained"
|
||||||
|
loadingPosition="start"
|
||||||
|
loading={isLoadingInvite}
|
||||||
|
onClick={inviteMember}
|
||||||
|
>
|
||||||
|
Invite
|
||||||
|
</LoadingButton>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -4,7 +4,7 @@ import React, {
|
|||||||
useEffect,
|
useEffect,
|
||||||
useRef,
|
useRef,
|
||||||
useState,
|
useState,
|
||||||
} from "react";
|
} from 'react';
|
||||||
import {
|
import {
|
||||||
Avatar,
|
Avatar,
|
||||||
Box,
|
Box,
|
||||||
@ -25,44 +25,44 @@ import {
|
|||||||
Select,
|
Select,
|
||||||
TextField,
|
TextField,
|
||||||
Typography,
|
Typography,
|
||||||
} from "@mui/material";
|
} from '@mui/material';
|
||||||
|
|
||||||
import { getNameInfo } from "./Group";
|
import { getNameInfo } from './Group';
|
||||||
import { getBaseApi, getFee } from "../../background";
|
import { getBaseApi, getFee } from '../../background';
|
||||||
import { LoadingButton } from "@mui/lab";
|
import { LoadingButton } from '@mui/lab';
|
||||||
import LockIcon from "@mui/icons-material/Lock";
|
import LockIcon from '@mui/icons-material/Lock';
|
||||||
import NoEncryptionGmailerrorredIcon from "@mui/icons-material/NoEncryptionGmailerrorred";
|
import NoEncryptionGmailerrorredIcon from '@mui/icons-material/NoEncryptionGmailerrorred';
|
||||||
import {
|
import {
|
||||||
MyContext,
|
MyContext,
|
||||||
getArbitraryEndpointReact,
|
getArbitraryEndpointReact,
|
||||||
getBaseApiReact,
|
getBaseApiReact,
|
||||||
isMobile,
|
isMobile,
|
||||||
} from "../../App";
|
} from '../../App';
|
||||||
import { Spacer } from "../../common/Spacer";
|
import { Spacer } from '../../common/Spacer';
|
||||||
import { CustomLoader } from "../../common/CustomLoader";
|
import { CustomLoader } from '../../common/CustomLoader';
|
||||||
import { RequestQueueWithPromise } from "../../utils/queue/queue";
|
import { RequestQueueWithPromise } from '../../utils/queue/queue';
|
||||||
import { useRecoilState } from "recoil";
|
import { useRecoilState } from 'recoil';
|
||||||
import {
|
import {
|
||||||
myGroupsWhereIAmAdminAtom,
|
myGroupsWhereIAmAdminAtom,
|
||||||
promotionTimeIntervalAtom,
|
promotionTimeIntervalAtom,
|
||||||
promotionsAtom,
|
promotionsAtom,
|
||||||
} from "../../atoms/global";
|
} from '../../atoms/global';
|
||||||
import { Label } from "./AddGroup";
|
import { Label } from './AddGroup';
|
||||||
import ShortUniqueId from "short-unique-id";
|
import ShortUniqueId from 'short-unique-id';
|
||||||
import { CustomizedSnackbars } from "../Snackbar/Snackbar";
|
import { CustomizedSnackbars } from '../Snackbar/Snackbar';
|
||||||
import { getGroupNames } from "./UserListOfInvites";
|
import { getGroupNames } from './UserListOfInvites';
|
||||||
import { WrapperUserAction } from "../WrapperUserAction";
|
import { WrapperUserAction } from '../WrapperUserAction';
|
||||||
import { useVirtualizer } from "@tanstack/react-virtual";
|
import { useVirtualizer } from '@tanstack/react-virtual';
|
||||||
import ErrorBoundary from "../../common/ErrorBoundary";
|
import ErrorBoundary from '../../common/ErrorBoundary';
|
||||||
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
|
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
|
||||||
import ExpandLessIcon from "@mui/icons-material/ExpandLess";
|
import ExpandLessIcon from '@mui/icons-material/ExpandLess';
|
||||||
export const requestQueuePromos = new RequestQueueWithPromise(20);
|
export const requestQueuePromos = new RequestQueueWithPromise(20);
|
||||||
|
|
||||||
export function utf8ToBase64(inputString: string): string {
|
export function utf8ToBase64(inputString: string): string {
|
||||||
// Encode the string as UTF-8
|
// Encode the string as UTF-8
|
||||||
const utf8String = encodeURIComponent(inputString).replace(
|
const utf8String = encodeURIComponent(inputString).replace(
|
||||||
/%([0-9A-F]{2})/g,
|
/%([0-9A-F]{2})/g,
|
||||||
(match, p1) => String.fromCharCode(Number("0x" + p1))
|
(match, p1) => String.fromCharCode(Number('0x' + p1))
|
||||||
);
|
);
|
||||||
|
|
||||||
// Convert the UTF-8 encoded string to base64
|
// Convert the UTF-8 encoded string to base64
|
||||||
@ -83,7 +83,7 @@ export const ListOfGroupPromotions = () => {
|
|||||||
const [selectedGroup, setSelectedGroup] = useState(null);
|
const [selectedGroup, setSelectedGroup] = useState(null);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [isShowModal, setIsShowModal] = useState(false);
|
const [isShowModal, setIsShowModal] = useState(false);
|
||||||
const [text, setText] = useState("");
|
const [text, setText] = useState('');
|
||||||
const [myGroupsWhereIAmAdmin, setMyGroupsWhereIAmAdmin] = useRecoilState(
|
const [myGroupsWhereIAmAdmin, setMyGroupsWhereIAmAdmin] = useRecoilState(
|
||||||
myGroupsWhereIAmAdminAtom
|
myGroupsWhereIAmAdminAtom
|
||||||
);
|
);
|
||||||
@ -115,10 +115,12 @@ export const ListOfGroupPromotions = () => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
try {
|
try {
|
||||||
(async () => {
|
(async () => {
|
||||||
const feeRes = await getFee("ARBITRARY");
|
const feeRes = await getFee('ARBITRARY');
|
||||||
setFee(feeRes?.fee);
|
setFee(feeRes?.fee);
|
||||||
})();
|
})();
|
||||||
} catch (error) {}
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
}, []);
|
}, []);
|
||||||
const getPromotions = useCallback(async () => {
|
const getPromotions = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
@ -126,9 +128,9 @@ export const ListOfGroupPromotions = () => {
|
|||||||
const identifier = `group-promotions-ui24-`;
|
const identifier = `group-promotions-ui24-`;
|
||||||
const url = `${getBaseApiReact()}${getArbitraryEndpointReact()}?mode=ALL&service=DOCUMENT&identifier=${identifier}&limit=100&includemetadata=false&reverse=true&prefix=true`;
|
const url = `${getBaseApiReact()}${getArbitraryEndpointReact()}?mode=ALL&service=DOCUMENT&identifier=${identifier}&limit=100&includemetadata=false&reverse=true&prefix=true`;
|
||||||
const response = await fetch(url, {
|
const response = await fetch(url, {
|
||||||
method: "GET",
|
method: 'GET',
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
'Content-Type': 'application/json',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const responseData = await response.json();
|
const responseData = await response.json();
|
||||||
@ -142,7 +144,7 @@ export const ListOfGroupPromotions = () => {
|
|||||||
promo.name
|
promo.name
|
||||||
}/${promo.identifier}`;
|
}/${promo.identifier}`;
|
||||||
const response = await fetch(url, {
|
const response = await fetch(url, {
|
||||||
method: "GET",
|
method: 'GET',
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -164,7 +166,7 @@ export const ListOfGroupPromotions = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error fetching promo:", error);
|
console.error('Error fetching promo:', error);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -222,10 +224,10 @@ export const ListOfGroupPromotions = () => {
|
|||||||
|
|
||||||
await new Promise((res, rej) => {
|
await new Promise((res, rej) => {
|
||||||
window
|
window
|
||||||
.sendMessage("publishOnQDN", {
|
.sendMessage('publishOnQDN', {
|
||||||
data: data,
|
data: data,
|
||||||
identifier: identifier,
|
identifier: identifier,
|
||||||
service: "DOCUMENT",
|
service: 'DOCUMENT',
|
||||||
})
|
})
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
if (!response?.error) {
|
if (!response?.error) {
|
||||||
@ -235,23 +237,23 @@ export const ListOfGroupPromotions = () => {
|
|||||||
rej(response.error);
|
rej(response.error);
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
rej(error.message || "An error occurred");
|
rej(error.message || 'An error occurred');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
setInfoSnack({
|
setInfoSnack({
|
||||||
type: "success",
|
type: 'success',
|
||||||
message:
|
message:
|
||||||
"Successfully published promotion. It may take a couple of minutes for the promotion to appear",
|
'Successfully published promotion. It may take a couple of minutes for the promotion to appear',
|
||||||
});
|
});
|
||||||
setOpenSnack(true);
|
setOpenSnack(true);
|
||||||
setText("");
|
setText('');
|
||||||
setSelectedGroup(null);
|
setSelectedGroup(null);
|
||||||
setIsShowModal(false);
|
setIsShowModal(false);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setInfoSnack({
|
setInfoSnack({
|
||||||
type: "error",
|
type: 'error',
|
||||||
message:
|
message:
|
||||||
error?.message || "Error publishing the promotion. Please try again",
|
error?.message || 'Error publishing the promotion. Please try again',
|
||||||
});
|
});
|
||||||
setOpenSnack(true);
|
setOpenSnack(true);
|
||||||
} finally {
|
} finally {
|
||||||
@ -262,30 +264,30 @@ export const ListOfGroupPromotions = () => {
|
|||||||
const handleJoinGroup = async (group, isOpen) => {
|
const handleJoinGroup = async (group, isOpen) => {
|
||||||
try {
|
try {
|
||||||
const groupId = group.groupId;
|
const groupId = group.groupId;
|
||||||
const fee = await getFee("JOIN_GROUP");
|
const fee = await getFee('JOIN_GROUP');
|
||||||
await show({
|
await show({
|
||||||
message: "Would you like to perform an JOIN_GROUP transaction?",
|
message: 'Would you like to perform an JOIN_GROUP transaction?',
|
||||||
publishFee: fee.fee + " QORT",
|
publishFee: fee.fee + ' QORT',
|
||||||
});
|
});
|
||||||
setIsLoadingJoinGroup(true);
|
setIsLoadingJoinGroup(true);
|
||||||
await new Promise((res, rej) => {
|
await new Promise((res, rej) => {
|
||||||
window
|
window
|
||||||
.sendMessage("joinGroup", {
|
.sendMessage('joinGroup', {
|
||||||
groupId,
|
groupId,
|
||||||
})
|
})
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
if (!response?.error) {
|
if (!response?.error) {
|
||||||
setInfoSnack({
|
setInfoSnack({
|
||||||
type: "success",
|
type: 'success',
|
||||||
message:
|
message:
|
||||||
"Successfully requested to join group. It may take a couple of minutes for the changes to propagate",
|
'Successfully requested to join group. It may take a couple of minutes for the changes to propagate',
|
||||||
});
|
});
|
||||||
|
|
||||||
if (isOpen) {
|
if (isOpen) {
|
||||||
setTxList((prev) => [
|
setTxList((prev) => [
|
||||||
{
|
{
|
||||||
...response,
|
...response,
|
||||||
type: "joined-group",
|
type: 'joined-group',
|
||||||
label: `Joined Group ${group?.groupName}: awaiting confirmation`,
|
label: `Joined Group ${group?.groupName}: awaiting confirmation`,
|
||||||
labelDone: `Joined Group ${group?.groupName}: success!`,
|
labelDone: `Joined Group ${group?.groupName}: success!`,
|
||||||
done: false,
|
done: false,
|
||||||
@ -297,7 +299,7 @@ export const ListOfGroupPromotions = () => {
|
|||||||
setTxList((prev) => [
|
setTxList((prev) => [
|
||||||
{
|
{
|
||||||
...response,
|
...response,
|
||||||
type: "joined-group-request",
|
type: 'joined-group-request',
|
||||||
label: `Requested to join Group ${group?.groupName}: awaiting confirmation`,
|
label: `Requested to join Group ${group?.groupName}: awaiting confirmation`,
|
||||||
labelDone: `Requested to join Group ${group?.groupName}: success!`,
|
labelDone: `Requested to join Group ${group?.groupName}: success!`,
|
||||||
done: false,
|
done: false,
|
||||||
@ -313,7 +315,7 @@ export const ListOfGroupPromotions = () => {
|
|||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
setInfoSnack({
|
setInfoSnack({
|
||||||
type: "error",
|
type: 'error',
|
||||||
message: response?.error,
|
message: response?.error,
|
||||||
});
|
});
|
||||||
setOpenSnack(true);
|
setOpenSnack(true);
|
||||||
@ -322,8 +324,8 @@ export const ListOfGroupPromotions = () => {
|
|||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
setInfoSnack({
|
setInfoSnack({
|
||||||
type: "error",
|
type: 'error',
|
||||||
message: error.message || "An error occurred",
|
message: error.message || 'An error occurred',
|
||||||
});
|
});
|
||||||
setOpenSnack(true);
|
setOpenSnack(true);
|
||||||
rej(error);
|
rej(error);
|
||||||
@ -339,55 +341,58 @@ export const ListOfGroupPromotions = () => {
|
|||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
width: "100%",
|
width: '100%',
|
||||||
display: "flex",
|
display: 'flex',
|
||||||
marginTop: "20px",
|
marginTop: '20px',
|
||||||
flexDirection: "column",
|
flexDirection: 'column',
|
||||||
alignItems: "center",
|
alignItems: 'center',
|
||||||
justifyContent: "center",
|
justifyContent: 'center',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box sx={{
|
<Box
|
||||||
display: 'flex',
|
sx={{
|
||||||
gap: '20px',
|
display: 'flex',
|
||||||
width: '100%',
|
gap: '20px',
|
||||||
justifyContent: 'space-between'
|
width: '100%',
|
||||||
}}>
|
justifyContent: 'space-between',
|
||||||
|
}}
|
||||||
|
>
|
||||||
<ButtonBase
|
<ButtonBase
|
||||||
sx={{
|
sx={{
|
||||||
display: "flex",
|
display: 'flex',
|
||||||
flexDirection: "row",
|
flexDirection: 'row',
|
||||||
padding: `0px ${isExpanded ? "24px" : "20px"}`,
|
padding: `0px ${isExpanded ? '24px' : '20px'}`,
|
||||||
gap: "10px",
|
gap: '10px',
|
||||||
justifyContent: "flex-start",
|
justifyContent: 'flex-start',
|
||||||
alignSelf: isExpanded && "flex-start",
|
alignSelf: isExpanded && 'flex-start',
|
||||||
}}
|
}}
|
||||||
onClick={() => setIsExpanded((prev) => !prev)}
|
onClick={() => setIsExpanded((prev) => !prev)}
|
||||||
>
|
>
|
||||||
<Typography
|
<Typography
|
||||||
sx={{
|
sx={{
|
||||||
fontSize: "1rem",
|
fontSize: '1rem',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Group promotions {promotions.length > 0 && ` (${promotions.length})`}
|
Group promotions{' '}
|
||||||
|
{promotions.length > 0 && ` (${promotions.length})`}
|
||||||
</Typography>
|
</Typography>
|
||||||
{isExpanded ? (
|
{isExpanded ? (
|
||||||
<ExpandLessIcon
|
<ExpandLessIcon
|
||||||
sx={{
|
sx={{
|
||||||
marginLeft: "auto",
|
marginLeft: 'auto',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<ExpandMoreIcon
|
<ExpandMoreIcon
|
||||||
sx={{
|
sx={{
|
||||||
marginLeft: "auto",
|
marginLeft: 'auto',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</ButtonBase>
|
</ButtonBase>
|
||||||
<Box
|
<Box
|
||||||
style={{
|
style={{
|
||||||
width: "330px",
|
width: '330px',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
@ -396,24 +401,24 @@ export const ListOfGroupPromotions = () => {
|
|||||||
<>
|
<>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
width: isMobile ? "320px" : "750px",
|
width: isMobile ? '320px' : '750px',
|
||||||
maxWidth: "90%",
|
maxWidth: '90%',
|
||||||
display: "flex",
|
display: 'flex',
|
||||||
flexDirection: "column",
|
flexDirection: 'column',
|
||||||
padding: "0px 20px",
|
padding: '0px 20px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
width: "100%",
|
width: '100%',
|
||||||
display: "flex",
|
display: 'flex',
|
||||||
justifyContent: "space-between",
|
justifyContent: 'space-between',
|
||||||
alignItems: "center",
|
alignItems: 'center',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography
|
<Typography
|
||||||
sx={{
|
sx={{
|
||||||
fontSize: "13px",
|
fontSize: '13px',
|
||||||
fontWeight: 600,
|
fontWeight: 600,
|
||||||
}}
|
}}
|
||||||
></Typography>
|
></Typography>
|
||||||
@ -421,7 +426,7 @@ export const ListOfGroupPromotions = () => {
|
|||||||
variant="contained"
|
variant="contained"
|
||||||
onClick={() => setIsShowModal(true)}
|
onClick={() => setIsShowModal(true)}
|
||||||
sx={{
|
sx={{
|
||||||
fontSize: "12px",
|
fontSize: '12px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Add Promotion
|
Add Promotion
|
||||||
@ -431,22 +436,22 @@ export const ListOfGroupPromotions = () => {
|
|||||||
</Box>
|
</Box>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
width: isMobile ? "320px" : "750px",
|
width: isMobile ? '320px' : '750px',
|
||||||
maxWidth: "90%",
|
maxWidth: '90%',
|
||||||
maxHeight: "700px",
|
maxHeight: '700px',
|
||||||
display: "flex",
|
display: 'flex',
|
||||||
flexDirection: "column",
|
flexDirection: 'column',
|
||||||
bgcolor: "background.paper",
|
bgcolor: 'background.paper',
|
||||||
padding: "20px 0px",
|
padding: '20px 0px',
|
||||||
borderRadius: "19px",
|
borderRadius: '19px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{loading && promotions.length === 0 && (
|
{loading && promotions.length === 0 && (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
width: "100%",
|
width: '100%',
|
||||||
display: "flex",
|
display: 'flex',
|
||||||
justifyContent: "center",
|
justifyContent: 'center',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<CustomLoader />
|
<CustomLoader />
|
||||||
@ -455,18 +460,18 @@ export const ListOfGroupPromotions = () => {
|
|||||||
{!loading && promotions.length === 0 && (
|
{!loading && promotions.length === 0 && (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
width: "100%",
|
width: '100%',
|
||||||
display: "flex",
|
display: 'flex',
|
||||||
justifyContent: "center",
|
justifyContent: 'center',
|
||||||
alignItems: "center",
|
alignItems: 'center',
|
||||||
height: "100%",
|
height: '100%',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography
|
<Typography
|
||||||
sx={{
|
sx={{
|
||||||
fontSize: "11px",
|
fontSize: '11px',
|
||||||
fontWeight: 400,
|
fontWeight: 400,
|
||||||
color: "rgba(255, 255, 255, 0.2)",
|
color: 'rgba(255, 255, 255, 0.2)',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Nothing to display
|
Nothing to display
|
||||||
@ -475,11 +480,11 @@ export const ListOfGroupPromotions = () => {
|
|||||||
)}
|
)}
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
height: "600px",
|
height: '600px',
|
||||||
position: "relative",
|
position: 'relative',
|
||||||
display: "flex",
|
display: 'flex',
|
||||||
flexDirection: "column",
|
flexDirection: 'column',
|
||||||
width: "100%",
|
width: '100%',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@ -487,24 +492,24 @@ export const ListOfGroupPromotions = () => {
|
|||||||
className="scrollable-container"
|
className="scrollable-container"
|
||||||
style={{
|
style={{
|
||||||
flexGrow: 1,
|
flexGrow: 1,
|
||||||
overflow: "auto",
|
overflow: 'auto',
|
||||||
position: "relative",
|
position: 'relative',
|
||||||
display: "flex",
|
display: 'flex',
|
||||||
height: "0px",
|
height: '0px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
height: rowVirtualizer.getTotalSize(),
|
height: rowVirtualizer.getTotalSize(),
|
||||||
width: "100%",
|
width: '100%',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
position: "absolute",
|
position: 'absolute',
|
||||||
top: 0,
|
top: 0,
|
||||||
left: 0,
|
left: 0,
|
||||||
width: "100%",
|
width: '100%',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{rowVirtualizer.getVirtualItems().map((virtualRow) => {
|
{rowVirtualizer.getVirtualItems().map((virtualRow) => {
|
||||||
@ -516,17 +521,17 @@ export const ListOfGroupPromotions = () => {
|
|||||||
ref={rowVirtualizer.measureElement} //measure dynamic row height
|
ref={rowVirtualizer.measureElement} //measure dynamic row height
|
||||||
key={promotion?.identifier}
|
key={promotion?.identifier}
|
||||||
style={{
|
style={{
|
||||||
position: "absolute",
|
position: 'absolute',
|
||||||
top: 0,
|
top: 0,
|
||||||
left: "50%", // Move to the center horizontally
|
left: '50%', // Move to the center horizontally
|
||||||
transform: `translateY(${virtualRow.start}px) translateX(-50%)`, // Adjust for centering
|
transform: `translateY(${virtualRow.start}px) translateX(-50%)`, // Adjust for centering
|
||||||
width: "100%", // Control width (90% of the parent)
|
width: '100%', // Control width (90% of the parent)
|
||||||
padding: "10px 0",
|
padding: '10px 0',
|
||||||
display: "flex",
|
display: 'flex',
|
||||||
alignItems: "center",
|
alignItems: 'center',
|
||||||
overscrollBehavior: "none",
|
overscrollBehavior: 'none',
|
||||||
flexDirection: "column",
|
flexDirection: 'column',
|
||||||
gap: "5px",
|
gap: '5px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ErrorBoundary
|
<ErrorBoundary
|
||||||
@ -538,47 +543,47 @@ export const ListOfGroupPromotions = () => {
|
|||||||
>
|
>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: "flex",
|
display: 'flex',
|
||||||
flexDirection: "column",
|
flexDirection: 'column',
|
||||||
width: "100%",
|
width: '100%',
|
||||||
padding: "0px 20px",
|
padding: '0px 20px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Popover
|
<Popover
|
||||||
open={openPopoverIndex === promotion?.groupId}
|
open={openPopoverIndex === promotion?.groupId}
|
||||||
anchorEl={popoverAnchor}
|
anchorEl={popoverAnchor}
|
||||||
onClose={(event, reason) => {
|
onClose={(event, reason) => {
|
||||||
if (reason === "backdropClick") {
|
if (reason === 'backdropClick') {
|
||||||
// Prevent closing on backdrop click
|
// Prevent closing on backdrop click
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
handlePopoverClose(); // Close only on other events like Esc key press
|
handlePopoverClose(); // Close only on other events like Esc key press
|
||||||
}}
|
}}
|
||||||
anchorOrigin={{
|
anchorOrigin={{
|
||||||
vertical: "top",
|
vertical: 'top',
|
||||||
horizontal: "center",
|
horizontal: 'center',
|
||||||
}}
|
}}
|
||||||
transformOrigin={{
|
transformOrigin={{
|
||||||
vertical: "bottom",
|
vertical: 'bottom',
|
||||||
horizontal: "center",
|
horizontal: 'center',
|
||||||
}}
|
}}
|
||||||
style={{ marginTop: "8px" }}
|
style={{ marginTop: '8px' }}
|
||||||
>
|
>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
width: "325px",
|
width: '325px',
|
||||||
height: "auto",
|
height: 'auto',
|
||||||
maxHeight: "400px",
|
maxHeight: '400px',
|
||||||
display: "flex",
|
display: 'flex',
|
||||||
flexDirection: "column",
|
flexDirection: 'column',
|
||||||
alignItems: "center",
|
alignItems: 'center',
|
||||||
gap: "10px",
|
gap: '10px',
|
||||||
padding: "10px",
|
padding: '10px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography
|
<Typography
|
||||||
sx={{
|
sx={{
|
||||||
fontSize: "13px",
|
fontSize: '13px',
|
||||||
fontWeight: 600,
|
fontWeight: 600,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@ -586,17 +591,17 @@ export const ListOfGroupPromotions = () => {
|
|||||||
</Typography>
|
</Typography>
|
||||||
<Typography
|
<Typography
|
||||||
sx={{
|
sx={{
|
||||||
fontSize: "13px",
|
fontSize: '13px',
|
||||||
fontWeight: 600,
|
fontWeight: 600,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Number of members:{" "}
|
Number of members:{' '}
|
||||||
{` ${promotion?.memberCount}`}
|
{` ${promotion?.memberCount}`}
|
||||||
</Typography>
|
</Typography>
|
||||||
{promotion?.description && (
|
{promotion?.description && (
|
||||||
<Typography
|
<Typography
|
||||||
sx={{
|
sx={{
|
||||||
fontSize: "13px",
|
fontSize: '13px',
|
||||||
fontWeight: 600,
|
fontWeight: 600,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@ -606,7 +611,7 @@ export const ListOfGroupPromotions = () => {
|
|||||||
{promotion?.isOpen === false && (
|
{promotion?.isOpen === false && (
|
||||||
<Typography
|
<Typography
|
||||||
sx={{
|
sx={{
|
||||||
fontSize: "13px",
|
fontSize: '13px',
|
||||||
fontWeight: 600,
|
fontWeight: 600,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@ -618,11 +623,11 @@ export const ListOfGroupPromotions = () => {
|
|||||||
<Spacer height="5px" />
|
<Spacer height="5px" />
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: "flex",
|
display: 'flex',
|
||||||
gap: "20px",
|
gap: '20px',
|
||||||
alignItems: "center",
|
alignItems: 'center',
|
||||||
width: "100%",
|
width: '100%',
|
||||||
justifyContent: "center",
|
justifyContent: 'center',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<LoadingButton
|
<LoadingButton
|
||||||
@ -652,23 +657,23 @@ export const ListOfGroupPromotions = () => {
|
|||||||
|
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: "flex",
|
display: 'flex',
|
||||||
alignItems: "center",
|
alignItems: 'center',
|
||||||
justifyContent: "space-between",
|
justifyContent: 'space-between',
|
||||||
width: "100%",
|
width: '100%',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: "flex",
|
display: 'flex',
|
||||||
alignItems: "center",
|
alignItems: 'center',
|
||||||
gap: "15px",
|
gap: '15px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Avatar
|
<Avatar
|
||||||
sx={{
|
sx={{
|
||||||
backgroundColor: "#27282c",
|
backgroundColor: '#27282c',
|
||||||
color: "white",
|
color: 'white',
|
||||||
}}
|
}}
|
||||||
alt={promotion?.name}
|
alt={promotion?.name}
|
||||||
src={`${getBaseApiReact()}/arbitrary/THUMBNAIL/${
|
src={`${getBaseApiReact()}/arbitrary/THUMBNAIL/${
|
||||||
@ -680,8 +685,8 @@ export const ListOfGroupPromotions = () => {
|
|||||||
<Typography
|
<Typography
|
||||||
sx={{
|
sx={{
|
||||||
fontWight: 600,
|
fontWight: 600,
|
||||||
fontFamily: "Inter",
|
fontFamily: 'Inter',
|
||||||
color: "cadetBlue",
|
color: 'cadetBlue',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{promotion?.name}
|
{promotion?.name}
|
||||||
@ -690,8 +695,8 @@ export const ListOfGroupPromotions = () => {
|
|||||||
<Typography
|
<Typography
|
||||||
sx={{
|
sx={{
|
||||||
fontWight: 600,
|
fontWight: 600,
|
||||||
fontFamily: "Inter",
|
fontFamily: 'Inter',
|
||||||
color: "cadetBlue",
|
color: 'cadetBlue',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{promotion?.groupName}
|
{promotion?.groupName}
|
||||||
@ -700,42 +705,42 @@ export const ListOfGroupPromotions = () => {
|
|||||||
<Spacer height="20px" />
|
<Spacer height="20px" />
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: "flex",
|
display: 'flex',
|
||||||
gap: "20px",
|
gap: '20px',
|
||||||
alignItems: "center",
|
alignItems: 'center',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{promotion?.isOpen === false && (
|
{promotion?.isOpen === false && (
|
||||||
<LockIcon
|
<LockIcon
|
||||||
sx={{
|
sx={{
|
||||||
color: "var(--green)",
|
color: 'var(--green)',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{promotion?.isOpen === true && (
|
{promotion?.isOpen === true && (
|
||||||
<NoEncryptionGmailerrorredIcon
|
<NoEncryptionGmailerrorredIcon
|
||||||
sx={{
|
sx={{
|
||||||
color: "var(--danger)",
|
color: 'var(--danger)',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<Typography
|
<Typography
|
||||||
sx={{
|
sx={{
|
||||||
fontSize: "15px",
|
fontSize: '15px',
|
||||||
fontWeight: 600,
|
fontWeight: 600,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{promotion?.isOpen
|
{promotion?.isOpen
|
||||||
? "Public group"
|
? 'Public group'
|
||||||
: "Private group"}
|
: 'Private group'}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
<Spacer height="20px" />
|
<Spacer height="20px" />
|
||||||
<Typography
|
<Typography
|
||||||
sx={{
|
sx={{
|
||||||
fontWight: 600,
|
fontWight: 600,
|
||||||
fontFamily: "Inter",
|
fontFamily: 'Inter',
|
||||||
color: "cadetBlue",
|
color: 'cadetBlue',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{promotion?.data}
|
{promotion?.data}
|
||||||
@ -743,9 +748,9 @@ export const ListOfGroupPromotions = () => {
|
|||||||
<Spacer height="20px" />
|
<Spacer height="20px" />
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: "flex",
|
display: 'flex',
|
||||||
justifyContent: "center",
|
justifyContent: 'center',
|
||||||
width: "100%",
|
width: '100%',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
@ -754,8 +759,8 @@ export const ListOfGroupPromotions = () => {
|
|||||||
handlePopoverOpen(event, promotion?.groupId)
|
handlePopoverOpen(event, promotion?.groupId)
|
||||||
}
|
}
|
||||||
sx={{
|
sx={{
|
||||||
fontSize: "12px",
|
fontSize: '12px',
|
||||||
color: "white",
|
color: 'white',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Join Group: {` ${promotion?.groupName}`}
|
Join Group: {` ${promotion?.groupName}`}
|
||||||
@ -783,7 +788,7 @@ export const ListOfGroupPromotions = () => {
|
|||||||
aria-describedby="alert-dialog-description"
|
aria-describedby="alert-dialog-description"
|
||||||
>
|
>
|
||||||
<DialogTitle id="alert-dialog-title">
|
<DialogTitle id="alert-dialog-title">
|
||||||
{"Promote your group to non-members"}
|
{'Promote your group to non-members'}
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
<DialogContentText id="alert-dialog-description">
|
<DialogContentText id="alert-dialog-description">
|
||||||
@ -791,14 +796,14 @@ export const ListOfGroupPromotions = () => {
|
|||||||
group.
|
group.
|
||||||
</DialogContentText>
|
</DialogContentText>
|
||||||
<DialogContentText id="alert-dialog-description2">
|
<DialogContentText id="alert-dialog-description2">
|
||||||
Max 200 characters. Publish Fee: {fee && fee} {" QORT"}
|
Max 200 characters. Publish Fee: {fee && fee} {' QORT'}
|
||||||
</DialogContentText>
|
</DialogContentText>
|
||||||
<Spacer height="20px" />
|
<Spacer height="20px" />
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: "flex",
|
display: 'flex',
|
||||||
flexDirection: "column",
|
flexDirection: 'column',
|
||||||
gap: "5px",
|
gap: '5px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Label>Select a group</Label>
|
<Label>Select a group</Label>
|
||||||
@ -832,11 +837,11 @@ export const ListOfGroupPromotions = () => {
|
|||||||
}}
|
}}
|
||||||
multiline={true}
|
multiline={true}
|
||||||
sx={{
|
sx={{
|
||||||
"& .MuiFormLabel-root": {
|
'& .MuiFormLabel-root': {
|
||||||
color: "white",
|
color: 'white',
|
||||||
},
|
},
|
||||||
"& .MuiFormLabel-root.Mui-focused": {
|
'& .MuiFormLabel-root.Mui-focused': {
|
||||||
color: "white",
|
color: 'white',
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
@ -1,36 +1,36 @@
|
|||||||
import * as React from "react";
|
import * as React from 'react';
|
||||||
import Button from "@mui/material/Button";
|
import Button from '@mui/material/Button';
|
||||||
import Dialog from "@mui/material/Dialog";
|
import Dialog from '@mui/material/Dialog';
|
||||||
import ListItemText from "@mui/material/ListItemText";
|
import ListItemText from '@mui/material/ListItemText';
|
||||||
import ListItemButton from "@mui/material/ListItemButton";
|
import ListItemButton from '@mui/material/ListItemButton';
|
||||||
import List from "@mui/material/List";
|
import List from '@mui/material/List';
|
||||||
import Divider from "@mui/material/Divider";
|
import Divider from '@mui/material/Divider';
|
||||||
import AppBar from "@mui/material/AppBar";
|
import AppBar from '@mui/material/AppBar';
|
||||||
import Toolbar from "@mui/material/Toolbar";
|
import Toolbar from '@mui/material/Toolbar';
|
||||||
import IconButton from "@mui/material/IconButton";
|
import IconButton from '@mui/material/IconButton';
|
||||||
import Typography from "@mui/material/Typography";
|
import Typography from '@mui/material/Typography';
|
||||||
import CloseIcon from "@mui/icons-material/Close";
|
import CloseIcon from '@mui/icons-material/Close';
|
||||||
import Slide from "@mui/material/Slide";
|
import Slide from '@mui/material/Slide';
|
||||||
import { TransitionProps } from "@mui/material/transitions";
|
import { TransitionProps } from '@mui/material/transitions';
|
||||||
import ListOfMembers from "./ListOfMembers";
|
import ListOfMembers from './ListOfMembers';
|
||||||
import { InviteMember } from "./InviteMember";
|
import { InviteMember } from './InviteMember';
|
||||||
import { ListOfInvites } from "./ListOfInvites";
|
import { ListOfInvites } from './ListOfInvites';
|
||||||
import { ListOfBans } from "./ListOfBans";
|
import { ListOfBans } from './ListOfBans';
|
||||||
import { ListOfJoinRequests } from "./ListOfJoinRequests";
|
import { ListOfJoinRequests } from './ListOfJoinRequests';
|
||||||
import { Box, ButtonBase, Card, Tab, Tabs } from "@mui/material";
|
import { Box, ButtonBase, Card, Tab, Tabs } from '@mui/material';
|
||||||
import { CustomizedSnackbars } from "../Snackbar/Snackbar";
|
import { CustomizedSnackbars } from '../Snackbar/Snackbar';
|
||||||
import { MyContext, getBaseApiReact, isMobile } from "../../App";
|
import { MyContext, getBaseApiReact, isMobile } from '../../App';
|
||||||
import { getGroupMembers, getNames } from "./Group";
|
import { getGroupMembers, getNames } from './Group';
|
||||||
import { LoadingSnackbar } from "../Snackbar/LoadingSnackbar";
|
import { LoadingSnackbar } from '../Snackbar/LoadingSnackbar';
|
||||||
import { getFee } from "../../background";
|
import { getFee } from '../../background';
|
||||||
import { LoadingButton } from "@mui/lab";
|
import { LoadingButton } from '@mui/lab';
|
||||||
import { subscribeToEvent, unsubscribeFromEvent } from "../../utils/events";
|
import { subscribeToEvent, unsubscribeFromEvent } from '../../utils/events';
|
||||||
import { Spacer } from "../../common/Spacer";
|
import { Spacer } from '../../common/Spacer';
|
||||||
import InsertLinkIcon from '@mui/icons-material/InsertLink';
|
import InsertLinkIcon from '@mui/icons-material/InsertLink';
|
||||||
function a11yProps(index: number) {
|
function a11yProps(index: number) {
|
||||||
return {
|
return {
|
||||||
id: `simple-tab-${index}`,
|
id: `simple-tab-${index}`,
|
||||||
"aria-controls": `simple-tabpanel-${index}`,
|
'aria-controls': `simple-tabpanel-${index}`,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,16 +50,16 @@ export const ManageMembers = ({
|
|||||||
selectedGroup,
|
selectedGroup,
|
||||||
|
|
||||||
isAdmin,
|
isAdmin,
|
||||||
isOwner
|
isOwner,
|
||||||
}) => {
|
}) => {
|
||||||
const [membersWithNames, setMembersWithNames] = React.useState([]);
|
const [membersWithNames, setMembersWithNames] = React.useState([]);
|
||||||
const [tab, setTab] = React.useState("create");
|
const [tab, setTab] = React.useState('create');
|
||||||
const [value, setValue] = React.useState(0);
|
const [value, setValue] = React.useState(0);
|
||||||
const [openSnack, setOpenSnack] = React.useState(false);
|
const [openSnack, setOpenSnack] = React.useState(false);
|
||||||
const [infoSnack, setInfoSnack] = React.useState(null);
|
const [infoSnack, setInfoSnack] = React.useState(null);
|
||||||
const [isLoadingMembers, setIsLoadingMembers] = React.useState(false)
|
const [isLoadingMembers, setIsLoadingMembers] = React.useState(false);
|
||||||
const [isLoadingLeave, setIsLoadingLeave] = React.useState(false)
|
const [isLoadingLeave, setIsLoadingLeave] = React.useState(false);
|
||||||
const [groupInfo, setGroupInfo] = React.useState(null)
|
const [groupInfo, setGroupInfo] = React.useState(null);
|
||||||
const handleChange = (event: React.SyntheticEvent, newValue: number) => {
|
const handleChange = (event: React.SyntheticEvent, newValue: number) => {
|
||||||
setValue(newValue);
|
setValue(newValue);
|
||||||
};
|
};
|
||||||
@ -69,20 +69,20 @@ export const ManageMembers = ({
|
|||||||
setOpen(false);
|
setOpen(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const handleLeaveGroup = async () => {
|
const handleLeaveGroup = async () => {
|
||||||
try {
|
try {
|
||||||
setIsLoadingLeave(true)
|
setIsLoadingLeave(true);
|
||||||
const fee = await getFee('LEAVE_GROUP')
|
const fee = await getFee('LEAVE_GROUP');
|
||||||
await show({
|
await show({
|
||||||
message: "Would you like to perform an LEAVE_GROUP transaction?" ,
|
message: 'Would you like to perform an LEAVE_GROUP transaction?',
|
||||||
publishFee: fee.fee + ' QORT'
|
publishFee: fee.fee + ' QORT',
|
||||||
})
|
});
|
||||||
|
|
||||||
await new Promise((res, rej) => {
|
await new Promise((res, rej) => {
|
||||||
window.sendMessage("leaveGroup", {
|
window
|
||||||
groupId: selectedGroup?.groupId,
|
.sendMessage('leaveGroup', {
|
||||||
})
|
groupId: selectedGroup?.groupId,
|
||||||
|
})
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
if (!response?.error) {
|
if (!response?.error) {
|
||||||
setTxList((prev) => [
|
setTxList((prev) => [
|
||||||
@ -98,8 +98,9 @@ export const ManageMembers = ({
|
|||||||
]);
|
]);
|
||||||
res(response);
|
res(response);
|
||||||
setInfoSnack({
|
setInfoSnack({
|
||||||
type: "success",
|
type: 'success',
|
||||||
message: "Successfully requested to leave group. It may take a couple of minutes for the changes to propagate",
|
message:
|
||||||
|
'Successfully requested to leave group. It may take a couple of minutes for the changes to propagate',
|
||||||
});
|
});
|
||||||
setOpenSnack(true);
|
setOpenSnack(true);
|
||||||
return;
|
return;
|
||||||
@ -107,57 +108,62 @@ export const ManageMembers = ({
|
|||||||
rej(response.error);
|
rej(response.error);
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
rej(error.message || "An error occurred");
|
rej(error.message || 'An error occurred');
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
} catch (error) {} finally {
|
} catch (error) {
|
||||||
setIsLoadingLeave(false)
|
console.log(error);
|
||||||
|
} finally {
|
||||||
|
setIsLoadingLeave(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const getMembersWithNames = React.useCallback(async (groupId) => {
|
const getMembersWithNames = React.useCallback(async (groupId) => {
|
||||||
try {
|
try {
|
||||||
setIsLoadingMembers(true)
|
setIsLoadingMembers(true);
|
||||||
const res = await getGroupMembers(groupId);
|
const res = await getGroupMembers(groupId);
|
||||||
const resWithNames = await getNames(res.members);
|
const resWithNames = await getNames(res.members);
|
||||||
setMembersWithNames(resWithNames);
|
setMembersWithNames(resWithNames);
|
||||||
setIsLoadingMembers(false)
|
setIsLoadingMembers(false);
|
||||||
} catch (error) {}
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const getMembers = async (groupId) => {
|
const getMembers = async (groupId) => {
|
||||||
try {
|
try {
|
||||||
const res = await getGroupMembers(groupId);
|
const res = await getGroupMembers(groupId);
|
||||||
setMembersWithNames(res?.members || []);
|
setMembersWithNames(res?.members || []);
|
||||||
} catch (error) {}
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
const getGroupInfo = async (groupId) => {
|
const getGroupInfo = async (groupId) => {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(
|
const response = await fetch(`${getBaseApiReact()}/groups/${groupId}`);
|
||||||
`${getBaseApiReact()}/groups/${groupId}`
|
const groupData = await response.json();
|
||||||
);
|
setGroupInfo(groupData);
|
||||||
const groupData = await response.json();
|
} catch (error) {
|
||||||
setGroupInfo(groupData)
|
console.log(error);
|
||||||
} catch (error) {}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
React.useEffect(()=> {
|
React.useEffect(() => {
|
||||||
if(selectedGroup?.groupId){
|
if (selectedGroup?.groupId) {
|
||||||
getMembers(selectedGroup?.groupId)
|
getMembers(selectedGroup?.groupId);
|
||||||
getGroupInfo(selectedGroup?.groupId)
|
getGroupInfo(selectedGroup?.groupId);
|
||||||
}
|
}
|
||||||
}, [selectedGroup?.groupId])
|
}, [selectedGroup?.groupId]);
|
||||||
|
|
||||||
const openGroupJoinRequestFunc = ()=> {
|
const openGroupJoinRequestFunc = () => {
|
||||||
setValue(4)
|
setValue(4);
|
||||||
}
|
};
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
subscribeToEvent("openGroupJoinRequest", openGroupJoinRequestFunc);
|
subscribeToEvent('openGroupJoinRequest', openGroupJoinRequestFunc);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
unsubscribeFromEvent("openGroupJoinRequest", openGroupJoinRequestFunc);
|
unsubscribeFromEvent('openGroupJoinRequest', openGroupJoinRequestFunc);
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
@ -169,7 +175,7 @@ export const ManageMembers = ({
|
|||||||
onClose={handleClose}
|
onClose={handleClose}
|
||||||
TransitionComponent={Transition}
|
TransitionComponent={Transition}
|
||||||
>
|
>
|
||||||
<AppBar sx={{ position: "relative", bgcolor: "#232428" }}>
|
<AppBar sx={{ position: 'relative', bgcolor: '#232428' }}>
|
||||||
<Toolbar>
|
<Toolbar>
|
||||||
<Typography sx={{ ml: 2, flex: 1 }} variant="h6" component="div">
|
<Typography sx={{ ml: 2, flex: 1 }} variant="h6" component="div">
|
||||||
Manage Members
|
Manage Members
|
||||||
@ -186,117 +192,136 @@ export const ManageMembers = ({
|
|||||||
</AppBar>
|
</AppBar>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
bgcolor: "#27282c",
|
bgcolor: '#27282c',
|
||||||
flexGrow: 1,
|
flexGrow: 1,
|
||||||
overflowY: "auto",
|
overflowY: 'auto',
|
||||||
color: "white",
|
color: 'white',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box sx={{ borderBottom: 1, borderColor: "divider" }}>
|
<Box sx={{ borderBottom: 1, borderColor: 'divider' }}>
|
||||||
<Tabs
|
<Tabs
|
||||||
value={value}
|
value={value}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
aria-label="basic tabs example"
|
aria-label="basic tabs example"
|
||||||
variant="scrollable" // Make tabs scrollable
|
variant="scrollable" // Make tabs scrollable
|
||||||
scrollButtons="auto" // Show scroll buttons automatically
|
scrollButtons="auto" // Show scroll buttons automatically
|
||||||
allowScrollButtonsMobile // Show scroll buttons on mobile as well
|
allowScrollButtonsMobile // Show scroll buttons on mobile as well
|
||||||
sx={{
|
sx={{
|
||||||
"& .MuiTabs-indicator": {
|
'& .MuiTabs-indicator': {
|
||||||
backgroundColor: "white",
|
backgroundColor: 'white',
|
||||||
},
|
},
|
||||||
maxWidth: '100%', // Ensure the tabs container fits within the available space
|
maxWidth: '100%', // Ensure the tabs container fits within the available space
|
||||||
overflow: 'hidden', // Prevents overflow on small screens
|
overflow: 'hidden', // Prevents overflow on small screens
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Tab
|
<Tab
|
||||||
label="List of members"
|
label="List of members"
|
||||||
{...a11yProps(0)}
|
{...a11yProps(0)}
|
||||||
sx={{
|
sx={{
|
||||||
"&.Mui-selected": {
|
'&.Mui-selected': {
|
||||||
color: "white",
|
color: 'white',
|
||||||
},
|
},
|
||||||
fontSize: isMobile ? '0.75rem' : '1rem', // Adjust font size for mobile
|
fontSize: isMobile ? '0.75rem' : '1rem', // Adjust font size for mobile
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Tab
|
<Tab
|
||||||
label="Invite new member"
|
label="Invite new member"
|
||||||
{...a11yProps(1)}
|
{...a11yProps(1)}
|
||||||
sx={{
|
sx={{
|
||||||
"&.Mui-selected": {
|
'&.Mui-selected': {
|
||||||
color: "white",
|
color: 'white',
|
||||||
},
|
},
|
||||||
fontSize: isMobile ? '0.75rem' : '1rem',
|
fontSize: isMobile ? '0.75rem' : '1rem',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Tab
|
<Tab
|
||||||
label="List of invites"
|
label="List of invites"
|
||||||
{...a11yProps(2)}
|
{...a11yProps(2)}
|
||||||
sx={{
|
sx={{
|
||||||
"&.Mui-selected": {
|
'&.Mui-selected': {
|
||||||
color: "white",
|
color: 'white',
|
||||||
},
|
},
|
||||||
fontSize: isMobile ? '0.75rem' : '1rem',
|
fontSize: isMobile ? '0.75rem' : '1rem',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Tab
|
<Tab
|
||||||
label="List of bans"
|
label="List of bans"
|
||||||
{...a11yProps(3)}
|
{...a11yProps(3)}
|
||||||
sx={{
|
sx={{
|
||||||
"&.Mui-selected": {
|
'&.Mui-selected': {
|
||||||
color: "white",
|
color: 'white',
|
||||||
},
|
},
|
||||||
fontSize: isMobile ? '0.75rem' : '1rem',
|
fontSize: isMobile ? '0.75rem' : '1rem',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Tab
|
<Tab
|
||||||
label="Join requests"
|
label="Join requests"
|
||||||
{...a11yProps(4)}
|
{...a11yProps(4)}
|
||||||
sx={{
|
sx={{
|
||||||
"&.Mui-selected": {
|
'&.Mui-selected': {
|
||||||
color: "white",
|
color: 'white',
|
||||||
},
|
},
|
||||||
fontSize: isMobile ? '0.75rem' : '1rem',
|
fontSize: isMobile ? '0.75rem' : '1rem',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</Box>
|
</Box>
|
||||||
<Card sx={{
|
<Card
|
||||||
padding: '10px',
|
sx={{
|
||||||
cursor: 'default',
|
padding: '10px',
|
||||||
}}>
|
cursor: 'default',
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Box>
|
<Box>
|
||||||
<Typography>GroupId: {groupInfo?.groupId}</Typography>
|
<Typography>GroupId: {groupInfo?.groupId}</Typography>
|
||||||
<Typography>GroupName: {groupInfo?.groupName}</Typography>
|
<Typography>GroupName: {groupInfo?.groupName}</Typography>
|
||||||
<Typography>Number of members: {groupInfo?.memberCount}</Typography>
|
<Typography>
|
||||||
<ButtonBase sx={{
|
Number of members: {groupInfo?.memberCount}
|
||||||
gap: '10px'
|
</Typography>
|
||||||
}} onClick={async ()=> {
|
<ButtonBase
|
||||||
const link = `qortal://use-group/action-join/groupid-${groupInfo?.groupId}`
|
sx={{
|
||||||
await navigator.clipboard.writeText(link);
|
gap: '10px',
|
||||||
}}><InsertLinkIcon /> <Typography>Join Group Link</Typography></ButtonBase>
|
}}
|
||||||
|
onClick={async () => {
|
||||||
|
const link = `qortal://use-group/action-join/groupid-${groupInfo?.groupId}`;
|
||||||
|
await navigator.clipboard.writeText(link);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<InsertLinkIcon /> <Typography>Join Group Link</Typography>
|
||||||
|
</ButtonBase>
|
||||||
</Box>
|
</Box>
|
||||||
<Spacer height="20px" />
|
<Spacer height="20px" />
|
||||||
{selectedGroup?.groupId && !isOwner && (
|
{selectedGroup?.groupId && !isOwner && (
|
||||||
<LoadingButton size="small" loading={isLoadingLeave} loadingPosition="start"
|
<LoadingButton
|
||||||
variant="contained" onClick={handleLeaveGroup}>
|
size="small"
|
||||||
Leave Group
|
loading={isLoadingLeave}
|
||||||
</LoadingButton>
|
loadingPosition="start"
|
||||||
)}
|
variant="contained"
|
||||||
|
onClick={handleLeaveGroup}
|
||||||
|
>
|
||||||
|
Leave Group
|
||||||
|
</LoadingButton>
|
||||||
|
)}
|
||||||
</Card>
|
</Card>
|
||||||
{value === 0 && (
|
{value === 0 && (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
width: "100%",
|
width: '100%',
|
||||||
padding: "25px",
|
padding: '25px',
|
||||||
maxWidth: '750px'
|
maxWidth: '750px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Button variant="contained" onClick={()=> getMembersWithNames(selectedGroup?.groupId)}>Load members with names</Button>
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
onClick={() => getMembersWithNames(selectedGroup?.groupId)}
|
||||||
|
>
|
||||||
|
Load members with names
|
||||||
|
</Button>
|
||||||
<Spacer height="10px" />
|
<Spacer height="10px" />
|
||||||
<ListOfMembers
|
<ListOfMembers
|
||||||
members={membersWithNames || []}
|
members={membersWithNames || []}
|
||||||
groupId={selectedGroup?.groupId}
|
groupId={selectedGroup?.groupId}
|
||||||
setOpenSnack={setOpenSnack}
|
setOpenSnack={setOpenSnack}
|
||||||
setInfoSnack={setInfoSnack}
|
setInfoSnack={setInfoSnack}
|
||||||
isAdmin={isAdmin}
|
isAdmin={isAdmin}
|
||||||
isOwner={isOwner}
|
isOwner={isOwner}
|
||||||
@ -304,64 +329,87 @@ export const ManageMembers = ({
|
|||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
{value === 1 && (
|
{value === 1 && (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
width: "100%",
|
width: '100%',
|
||||||
padding: "25px",
|
padding: '25px',
|
||||||
maxWidth: '750px'
|
maxWidth: '750px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<InviteMember show={show} groupId={selectedGroup?.groupId} setOpenSnack={setOpenSnack} setInfoSnack={setInfoSnack} />
|
<InviteMember
|
||||||
|
show={show}
|
||||||
|
groupId={selectedGroup?.groupId}
|
||||||
|
setOpenSnack={setOpenSnack}
|
||||||
|
setInfoSnack={setInfoSnack}
|
||||||
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{value === 2 && (
|
{value === 2 && (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
width: "100%",
|
width: '100%',
|
||||||
padding: "25px",
|
padding: '25px',
|
||||||
maxWidth: '750px'
|
maxWidth: '750px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ListOfInvites show={show} groupId={selectedGroup?.groupId} setOpenSnack={setOpenSnack} setInfoSnack={setInfoSnack} />
|
<ListOfInvites
|
||||||
|
show={show}
|
||||||
|
groupId={selectedGroup?.groupId}
|
||||||
|
setOpenSnack={setOpenSnack}
|
||||||
|
setInfoSnack={setInfoSnack}
|
||||||
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{value === 3 && (
|
{value === 3 && (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
width: "100%",
|
width: '100%',
|
||||||
padding: "25px",
|
padding: '25px',
|
||||||
maxWidth: '750px'
|
maxWidth: '750px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ListOfBans show={show} groupId={selectedGroup?.groupId} setOpenSnack={setOpenSnack} setInfoSnack={setInfoSnack} />
|
<ListOfBans
|
||||||
|
show={show}
|
||||||
|
groupId={selectedGroup?.groupId}
|
||||||
|
setOpenSnack={setOpenSnack}
|
||||||
|
setInfoSnack={setInfoSnack}
|
||||||
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{value === 4 && (
|
{value === 4 && (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
width: "100%",
|
width: '100%',
|
||||||
padding: "25px",
|
padding: '25px',
|
||||||
maxWidth: '750px'
|
maxWidth: '750px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ListOfJoinRequests show={show} setOpenSnack={setOpenSnack} setInfoSnack={setInfoSnack} groupId={selectedGroup?.groupId} />
|
<ListOfJoinRequests
|
||||||
|
show={show}
|
||||||
|
setOpenSnack={setOpenSnack}
|
||||||
|
setInfoSnack={setInfoSnack}
|
||||||
|
groupId={selectedGroup?.groupId}
|
||||||
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
<CustomizedSnackbars open={openSnack} setOpen={setOpenSnack} info={infoSnack} setInfo={setInfoSnack} />
|
<CustomizedSnackbars
|
||||||
|
open={openSnack}
|
||||||
|
setOpen={setOpenSnack}
|
||||||
|
info={infoSnack}
|
||||||
|
setInfo={setInfoSnack}
|
||||||
|
/>
|
||||||
<LoadingSnackbar
|
<LoadingSnackbar
|
||||||
open={isLoadingMembers}
|
open={isLoadingMembers}
|
||||||
info={{
|
info={{
|
||||||
message: "Loading member list with names... please wait.",
|
message: 'Loading member list with names... please wait.',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,22 +1,29 @@
|
|||||||
import React, { useContext, useEffect, useState } from "react";
|
import React, { useContext, useEffect, useState } from 'react';
|
||||||
import Logo2 from "../assets/svgs/Logo2.svg";
|
import Logo2 from '../assets/svgs/Logo2.svg';
|
||||||
import { MyContext, getArbitraryEndpointReact, getBaseApiReact } from "../App";
|
import { MyContext, getArbitraryEndpointReact, getBaseApiReact } from '../App';
|
||||||
import { Avatar, Box, Button, ButtonBase, Popover, Typography } from "@mui/material";
|
import {
|
||||||
import { Spacer } from "../common/Spacer";
|
Avatar,
|
||||||
import ImageUploader from "../common/ImageUploader";
|
Box,
|
||||||
import { getFee } from "../background";
|
Button,
|
||||||
import { fileToBase64 } from "../utils/fileReading";
|
ButtonBase,
|
||||||
import { LoadingButton } from "@mui/lab";
|
Popover,
|
||||||
|
Typography,
|
||||||
|
} from '@mui/material';
|
||||||
|
import { Spacer } from '../common/Spacer';
|
||||||
|
import ImageUploader from '../common/ImageUploader';
|
||||||
|
import { getFee } from '../background';
|
||||||
|
import { fileToBase64 } from '../utils/fileReading';
|
||||||
|
import { LoadingButton } from '@mui/lab';
|
||||||
import ErrorIcon from '@mui/icons-material/Error';
|
import ErrorIcon from '@mui/icons-material/Error';
|
||||||
|
|
||||||
export const MainAvatar = ({ myName, balance, setOpenSnack, setInfoSnack }) => {
|
export const MainAvatar = ({ myName, balance, setOpenSnack, setInfoSnack }) => {
|
||||||
const [hasAvatar, setHasAvatar] = useState(false);
|
const [hasAvatar, setHasAvatar] = useState(false);
|
||||||
const [avatarFile, setAvatarFile] = useState(null);
|
const [avatarFile, setAvatarFile] = useState(null);
|
||||||
const [tempAvatar, setTempAvatar] = useState(null)
|
const [tempAvatar, setTempAvatar] = useState(null);
|
||||||
const { show } = useContext(MyContext);
|
const { show } = useContext(MyContext);
|
||||||
|
|
||||||
const [anchorEl, setAnchorEl] = useState(null);
|
const [anchorEl, setAnchorEl] = useState(null);
|
||||||
const [isLoading, setIsLoading] = useState(false)
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
// Handle child element click to open Popover
|
// Handle child element click to open Popover
|
||||||
const handleChildClick = (event) => {
|
const handleChildClick = (event) => {
|
||||||
event.stopPropagation(); // Prevent parent onClick from firing
|
event.stopPropagation(); // Prevent parent onClick from firing
|
||||||
@ -37,93 +44,105 @@ const [isLoading, setIsLoading] = useState(false)
|
|||||||
const identifier = `qortal_avatar`;
|
const identifier = `qortal_avatar`;
|
||||||
const url = `${getBaseApiReact()}${getArbitraryEndpointReact()}?mode=ALL&service=THUMBNAIL&identifier=${identifier}&limit=1&name=${myName}&includemetadata=false&prefix=true`;
|
const url = `${getBaseApiReact()}${getArbitraryEndpointReact()}?mode=ALL&service=THUMBNAIL&identifier=${identifier}&limit=1&name=${myName}&includemetadata=false&prefix=true`;
|
||||||
const response = await fetch(url, {
|
const response = await fetch(url, {
|
||||||
method: "GET",
|
method: 'GET',
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
'Content-Type': 'application/json',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const responseData = await response.json();
|
const responseData = await response.json();
|
||||||
if (responseData?.length > 0) {
|
if (responseData?.length > 0) {
|
||||||
setHasAvatar(true);
|
setHasAvatar(true);
|
||||||
}
|
}
|
||||||
} catch (error) {}
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!myName) return;
|
if (!myName) return;
|
||||||
checkIfAvatarExists();
|
checkIfAvatarExists();
|
||||||
}, [myName]);
|
}, [myName]);
|
||||||
|
|
||||||
|
const publishAvatar = async () => {
|
||||||
const publishAvatar = async ()=> {
|
|
||||||
try {
|
try {
|
||||||
const fee = await getFee('ARBITRARY')
|
const fee = await getFee('ARBITRARY');
|
||||||
if(+balance < +fee.fee) throw new Error(`Publishing an Avatar requires ${fee.fee}`)
|
if (+balance < +fee.fee)
|
||||||
await show({
|
throw new Error(`Publishing an Avatar requires ${fee.fee}`);
|
||||||
message: "Would you like to publish an avatar?" ,
|
await show({
|
||||||
publishFee: fee.fee + ' QORT'
|
message: 'Would you like to publish an avatar?',
|
||||||
})
|
publishFee: fee.fee + ' QORT',
|
||||||
setIsLoading(true);
|
});
|
||||||
const avatarBase64 = await fileToBase64(avatarFile)
|
setIsLoading(true);
|
||||||
await new Promise((res, rej) => {
|
const avatarBase64 = await fileToBase64(avatarFile);
|
||||||
window.sendMessage("publishOnQDN", {
|
await new Promise((res, rej) => {
|
||||||
data: avatarBase64,
|
window
|
||||||
identifier: "qortal_avatar",
|
.sendMessage('publishOnQDN', {
|
||||||
service: "THUMBNAIL",
|
data: avatarBase64,
|
||||||
})
|
identifier: 'qortal_avatar',
|
||||||
.then((response) => {
|
service: 'THUMBNAIL',
|
||||||
if (!response?.error) {
|
})
|
||||||
res(response);
|
.then((response) => {
|
||||||
return;
|
if (!response?.error) {
|
||||||
}
|
res(response);
|
||||||
rej(response.error);
|
return;
|
||||||
})
|
}
|
||||||
.catch((error) => {
|
rej(response.error);
|
||||||
rej(error.message || "An error occurred");
|
})
|
||||||
});
|
.catch((error) => {
|
||||||
|
rej(error.message || 'An error occurred');
|
||||||
});
|
});
|
||||||
setAvatarFile(null);
|
});
|
||||||
setTempAvatar(`data:image/webp;base64,${avatarBase64}`)
|
setAvatarFile(null);
|
||||||
handleClose()
|
setTempAvatar(`data:image/webp;base64,${avatarBase64}`);
|
||||||
|
handleClose();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error?.message) {
|
if (error?.message) {
|
||||||
setOpenSnack(true)
|
setOpenSnack(true);
|
||||||
setInfoSnack({
|
setInfoSnack({
|
||||||
type: "error",
|
type: 'error',
|
||||||
message: error?.message,
|
message: error?.message,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
if(tempAvatar){
|
if (tempAvatar) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Avatar
|
<Avatar
|
||||||
|
sx={{
|
||||||
|
height: '138px',
|
||||||
|
width: '138px',
|
||||||
|
}}
|
||||||
|
src={tempAvatar}
|
||||||
|
alt={myName}
|
||||||
|
>
|
||||||
|
{myName?.charAt(0)}
|
||||||
|
</Avatar>
|
||||||
|
<ButtonBase onClick={handleChildClick}>
|
||||||
|
<Typography
|
||||||
sx={{
|
sx={{
|
||||||
height: "138px",
|
fontSize: '12px',
|
||||||
width: "138px",
|
opacity: 0.5,
|
||||||
}}
|
}}
|
||||||
src={tempAvatar}
|
|
||||||
alt={myName}
|
|
||||||
>
|
>
|
||||||
{myName?.charAt(0)}
|
change avatar
|
||||||
</Avatar>
|
</Typography>
|
||||||
<ButtonBase onClick={handleChildClick}>
|
</ButtonBase>
|
||||||
<Typography
|
<PopoverComp
|
||||||
sx={{
|
myName={myName}
|
||||||
fontSize: "12px",
|
avatarFile={avatarFile}
|
||||||
opacity: 0.5,
|
setAvatarFile={setAvatarFile}
|
||||||
}}
|
id={id}
|
||||||
>
|
open={open}
|
||||||
change avatar
|
anchorEl={anchorEl}
|
||||||
</Typography>
|
handleClose={handleClose}
|
||||||
</ButtonBase>
|
publishAvatar={publishAvatar}
|
||||||
<PopoverComp myName={myName} avatarFile={avatarFile} setAvatarFile={setAvatarFile} id={id} open={open} anchorEl={anchorEl} handleClose={handleClose} publishAvatar={publishAvatar} isLoading={isLoading} />
|
isLoading={isLoading}
|
||||||
</>
|
/>
|
||||||
);
|
</>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasAvatar) {
|
if (hasAvatar) {
|
||||||
@ -131,8 +150,8 @@ const [isLoading, setIsLoading] = useState(false)
|
|||||||
<>
|
<>
|
||||||
<Avatar
|
<Avatar
|
||||||
sx={{
|
sx={{
|
||||||
height: "138px",
|
height: '138px',
|
||||||
width: "138px",
|
width: '138px',
|
||||||
}}
|
}}
|
||||||
src={`${getBaseApiReact()}/arbitrary/THUMBNAIL/${myName}/qortal_avatar?async=true`}
|
src={`${getBaseApiReact()}/arbitrary/THUMBNAIL/${myName}/qortal_avatar?async=true`}
|
||||||
alt={myName}
|
alt={myName}
|
||||||
@ -142,14 +161,24 @@ const [isLoading, setIsLoading] = useState(false)
|
|||||||
<ButtonBase onClick={handleChildClick}>
|
<ButtonBase onClick={handleChildClick}>
|
||||||
<Typography
|
<Typography
|
||||||
sx={{
|
sx={{
|
||||||
fontSize: "12px",
|
fontSize: '12px',
|
||||||
opacity: 0.5,
|
opacity: 0.5,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
change avatar
|
change avatar
|
||||||
</Typography>
|
</Typography>
|
||||||
</ButtonBase>
|
</ButtonBase>
|
||||||
<PopoverComp myName={myName} avatarFile={avatarFile} setAvatarFile={setAvatarFile} id={id} open={open} anchorEl={anchorEl} handleClose={handleClose} publishAvatar={publishAvatar} isLoading={isLoading} />
|
<PopoverComp
|
||||||
|
myName={myName}
|
||||||
|
avatarFile={avatarFile}
|
||||||
|
setAvatarFile={setAvatarFile}
|
||||||
|
id={id}
|
||||||
|
open={open}
|
||||||
|
anchorEl={anchorEl}
|
||||||
|
handleClose={handleClose}
|
||||||
|
publishAvatar={publishAvatar}
|
||||||
|
isLoading={isLoading}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -160,42 +189,61 @@ const [isLoading, setIsLoading] = useState(false)
|
|||||||
<ButtonBase onClick={handleChildClick}>
|
<ButtonBase onClick={handleChildClick}>
|
||||||
<Typography
|
<Typography
|
||||||
sx={{
|
sx={{
|
||||||
fontSize: "12px",
|
fontSize: '12px',
|
||||||
opacity: 0.5,
|
opacity: 0.5,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
set avatar
|
set avatar
|
||||||
</Typography>
|
</Typography>
|
||||||
</ButtonBase>
|
</ButtonBase>
|
||||||
<PopoverComp myName={myName} avatarFile={avatarFile} setAvatarFile={setAvatarFile} id={id} open={open} anchorEl={anchorEl} handleClose={handleClose} publishAvatar={publishAvatar} isLoading={isLoading} />
|
<PopoverComp
|
||||||
|
myName={myName}
|
||||||
|
avatarFile={avatarFile}
|
||||||
|
setAvatarFile={setAvatarFile}
|
||||||
|
id={id}
|
||||||
|
open={open}
|
||||||
|
anchorEl={anchorEl}
|
||||||
|
handleClose={handleClose}
|
||||||
|
publishAvatar={publishAvatar}
|
||||||
|
isLoading={isLoading}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const PopoverComp = ({
|
||||||
const PopoverComp = ({avatarFile, setAvatarFile, id, open, anchorEl, handleClose, publishAvatar, isLoading, myName}) => {
|
avatarFile,
|
||||||
return (
|
setAvatarFile,
|
||||||
<Popover
|
id,
|
||||||
|
open,
|
||||||
|
anchorEl,
|
||||||
|
handleClose,
|
||||||
|
publishAvatar,
|
||||||
|
isLoading,
|
||||||
|
myName,
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<Popover
|
||||||
id={id}
|
id={id}
|
||||||
open={open}
|
open={open}
|
||||||
anchorEl={anchorEl}
|
anchorEl={anchorEl}
|
||||||
onClose={handleClose} // Close popover on click outside
|
onClose={handleClose} // Close popover on click outside
|
||||||
anchorOrigin={{
|
anchorOrigin={{
|
||||||
vertical: "bottom",
|
vertical: 'bottom',
|
||||||
horizontal: "center",
|
horizontal: 'center',
|
||||||
}}
|
}}
|
||||||
transformOrigin={{
|
transformOrigin={{
|
||||||
vertical: "top",
|
vertical: 'top',
|
||||||
horizontal: "center",
|
horizontal: 'center',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box sx={{ p: 2, display: "flex", flexDirection: "column", gap: 1 }}>
|
<Box sx={{ p: 2, display: 'flex', flexDirection: 'column', gap: 1 }}>
|
||||||
<Typography
|
<Typography
|
||||||
sx={{
|
sx={{
|
||||||
fontSize: "12px",
|
fontSize: '12px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
(500 KB max. for GIFS){" "}
|
(500 KB max. for GIFS){' '}
|
||||||
</Typography>
|
</Typography>
|
||||||
<ImageUploader onPick={(file) => setAvatarFile(file)}>
|
<ImageUploader onPick={(file) => setAvatarFile(file)}>
|
||||||
<Button variant="contained">Choose Image</Button>
|
<Button variant="contained">Choose Image</Button>
|
||||||
@ -203,23 +251,34 @@ const PopoverComp = ({avatarFile, setAvatarFile, id, open, anchorEl, handleClose
|
|||||||
{avatarFile?.name}
|
{avatarFile?.name}
|
||||||
<Spacer height="25px" />
|
<Spacer height="25px" />
|
||||||
{!myName && (
|
{!myName && (
|
||||||
<Box sx={{
|
<Box
|
||||||
|
sx={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
gap: '5px',
|
gap: '5px',
|
||||||
alignItems: 'center'
|
alignItems: 'center',
|
||||||
}}>
|
}}
|
||||||
<ErrorIcon sx={{
|
>
|
||||||
color: 'white'
|
<ErrorIcon
|
||||||
}} />
|
sx={{
|
||||||
<Typography>A registered name is required to set an avatar</Typography>
|
color: 'white',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Typography>
|
||||||
|
A registered name is required to set an avatar
|
||||||
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Spacer height="25px" />
|
<Spacer height="25px" />
|
||||||
<LoadingButton loading={isLoading} disabled={!avatarFile || !myName} onClick={publishAvatar} variant="contained">
|
<LoadingButton
|
||||||
|
loading={isLoading}
|
||||||
|
disabled={!avatarFile || !myName}
|
||||||
|
onClick={publishAvatar}
|
||||||
|
variant="contained"
|
||||||
|
>
|
||||||
Publish avatar
|
Publish avatar
|
||||||
</LoadingButton>
|
</LoadingButton>
|
||||||
</Box>
|
</Box>
|
||||||
</Popover>
|
</Popover>
|
||||||
)
|
);
|
||||||
};
|
};
|
||||||
|
@ -74,7 +74,9 @@ export const Minting = ({
|
|||||||
}
|
}
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
setMintingAccounts(data);
|
setMintingAccounts(data);
|
||||||
} catch (error) {}
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const accountIsMinting = useMemo(() => {
|
const accountIsMinting = useMemo(() => {
|
||||||
@ -199,7 +201,9 @@ export const Minting = ({
|
|||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
setRewardShares(data);
|
setRewardShares(data);
|
||||||
return data;
|
return data;
|
||||||
} catch (error) {}
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const addMintingAccount = useCallback(async (val) => {
|
const addMintingAccount = useCallback(async (val) => {
|
||||||
|
@ -60,7 +60,9 @@ export const TaskManager = ({ getUserInfo }) => {
|
|||||||
}
|
}
|
||||||
clearInterval(intervals.current[signature]);
|
clearInterval(intervals.current[signature]);
|
||||||
}
|
}
|
||||||
} catch (error) {}
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
stop = false;
|
stop = false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -187,7 +187,7 @@ export const CustomInput = styled(TextField)(({ theme }) => ({
|
|||||||
outline: 'none',
|
outline: 'none',
|
||||||
width: '183px', // Adjust the width as needed
|
width: '183px', // Adjust the width as needed
|
||||||
input: {
|
input: {
|
||||||
fontSize: 10,
|
fontSize: '12px',
|
||||||
fontFamily: 'Inter',
|
fontFamily: 'Inter',
|
||||||
fontWeight: 400,
|
fontWeight: 400,
|
||||||
color: theme.palette.text.primary,
|
color: theme.palette.text.primary,
|
||||||
@ -230,7 +230,7 @@ export const CustomLabel = styled(InputLabel)(({ theme }) => ({
|
|||||||
backgroundColor: theme.palette.background.default,
|
backgroundColor: theme.palette.background.default,
|
||||||
color: theme.palette.text.primary,
|
color: theme.palette.text.primary,
|
||||||
fontFamily: 'Inter',
|
fontFamily: 'Inter',
|
||||||
fontSize: '10px',
|
fontSize: '15px',
|
||||||
fontWeight: 400,
|
fontWeight: 400,
|
||||||
lineHeight: '12px',
|
lineHeight: '12px',
|
||||||
}));
|
}));
|
||||||
|
@ -106,7 +106,9 @@ export const useQortalGetSaveSettings = (myName, isAuthenticated) => {
|
|||||||
setSettingsQDNLastUpdated(0);
|
setSettingsQDNLastUpdated(0);
|
||||||
}
|
}
|
||||||
setCanSave(true);
|
setCanSave(true);
|
||||||
} catch (error) {}
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user