From cafe45f71242fedc536ffaa3a5ca499aa3864784 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Fri, 11 Apr 2025 22:59:52 +0200 Subject: [PATCH 01/31] Add prettier project configuration --- .prettierrc | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 .prettierrc diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..17c54ee --- /dev/null +++ b/.prettierrc @@ -0,0 +1,24 @@ +{ + "arrowParens": "always", + "bracketSpacing": true, + "endOfLine": "lf", + "htmlWhitespaceSensitivity": "css", + "insertPragma": false, + "singleAttributePerLine": false, + "bracketSameLine": false, + "jsxBracketSameLine": false, + "jsxSingleQuote": false, + "printWidth": 80, + "proseWrap": "preserve", + "quoteProps": "as-needed", + "requirePragma": false, + "semi": true, + "singleQuote": true, + "tabWidth": 2, + "trailingComma": "es5", + "useTabs": false, + "embeddedLanguageFormatting": "auto", + "vueIndentScriptAndStyle": false, + "experimentalTernaries": false, + "parser": "typescript" +} From a3bea46df12b561fb6c27304e656ebaaf9ba0049 Mon Sep 17 00:00:00 2001 From: PhilReact Date: Fri, 11 Apr 2025 15:56:46 +0300 Subject: [PATCH 02/31] give more info on block users --- src/atoms/global.ts | 5 + src/components/Group/BlockedUsersModal.tsx | 284 ++++++++++++++++----- src/components/Group/Group.tsx | 15 +- src/components/Group/useBlockUsers.tsx | 160 +++++++----- src/components/WrapperUserAction.tsx | 15 +- 5 files changed, 338 insertions(+), 141 deletions(-) diff --git a/src/atoms/global.ts b/src/atoms/global.ts index f05da75..443d833 100644 --- a/src/atoms/global.ts +++ b/src/atoms/global.ts @@ -177,4 +177,9 @@ export const mailsAtom = atom({ export const groupsPropertiesAtom = atom({ key: 'groupsPropertiesAtom', default: {}, +}); + +export const isOpenBlockedModalAtom = atom({ + key: 'isOpenBlockedModalAtom', + default: false, }); \ No newline at end of file diff --git a/src/components/Group/BlockedUsersModal.tsx b/src/components/Group/BlockedUsersModal.tsx index 84fa3fa..e81d207 100644 --- a/src/components/Group/BlockedUsersModal.tsx +++ b/src/components/Group/BlockedUsersModal.tsx @@ -10,15 +10,23 @@ import { Typography, } from "@mui/material"; import React, { useContext, useEffect, useState } from "react"; -import { MyContext } from "../../App"; +import { getBaseApiReact, MyContext } from "../../App"; import { Spacer } from "../../common/Spacer"; -import { executeEvent } from "../../utils/events"; - -export const BlockedUsersModal = ({ close }) => { +import { executeEvent, subscribeToEvent, unsubscribeFromEvent } from "../../utils/events"; +import { validateAddress } from "../../utils/validateAddress"; +import { getNameInfo, requestQueueMemberNames } from "./Group"; +import { useModal } from "../../common/useModal"; +import { useRecoilState } from "recoil"; +import { isOpenBlockedModalAtom } from "../../atoms/global"; +import InfoIcon from '@mui/icons-material/Info'; +export const BlockedUsersModal = () => { + const [isOpenBlockedModal, setIsOpenBlockedModal] = useRecoilState(isOpenBlockedModalAtom) const [hasChanged, setHasChanged] = useState(false); const [value, setValue] = useState(""); - - const { getAllBlockedUsers, removeBlockFromList, addToBlockList } = useContext(MyContext); + const [addressesWithNames, setAddressesWithNames] = useState({}) + const { isShow, onCancel, onOk, show, message } = useModal(); + const { getAllBlockedUsers, removeBlockFromList, addToBlockList } = + useContext(MyContext); const [blockedUsers, setBlockedUsers] = useState({ addresses: {}, names: {}, @@ -28,60 +36,157 @@ export const BlockedUsersModal = ({ close }) => { }; useEffect(() => { + if(!isOpenBlockedModal) return fetchBlockedUsers(); - }, []); + }, [isOpenBlockedModal]); + + const getNames = async () => { + // const validApi = await findUsableApi(); + const addresses = Object.keys(blockedUsers?.addresses) + const addressNames = {} + + + const getMemNames = addresses.map(async (address) => { + const name = await requestQueueMemberNames.enqueue(() => { + return getNameInfo(address); + }); + if (name) { + addressNames[address] = name + } + + + return true; + }); + + await Promise.all(getMemNames); + + setAddressesWithNames(addressNames) + }; + + const blockUser = async (e, user?: string) => { + try { + const valUser = user || value + if (!valUser) return; + const isAddress = validateAddress(valUser); + let userName = null; + let userAddress = null; + if (isAddress) { + userAddress = valUser; + const name = await getNameInfo(valUser); + if (name) { + userName = name; + } + } + if (!isAddress) { + const response = await fetch(`${getBaseApiReact()}/names/${valUser}`); + const data = await response.json(); + if (!data?.owner) throw new Error("Name does not exist"); + if (data?.owner) { + userAddress = data.owner; + userName = valUser; + } + } + if(!userName){ + await addToBlockList(userAddress, null); + fetchBlockedUsers(); + setHasChanged(true); + executeEvent('updateChatMessagesWithBlocks', true) + setValue('') + return + } + const responseModal = await show({ + userName, + userAddress, + }); + if (responseModal === "both") { + await addToBlockList(userAddress, userName); + } else if (responseModal === "address") { + await addToBlockList(userAddress, null); + } else if (responseModal === "name") { + await addToBlockList(null, userName); + } + fetchBlockedUsers(); + setHasChanged(true); + setValue('') + if(user){ + setIsOpenBlockedModal(false) + } + if(responseModal === 'both' || responseModal === 'address'){ + executeEvent('updateChatMessagesWithBlocks', true) + } + } catch (error) { + console.error(error); + } + }; + const blockUserFromOutsideModalFunc = (e) => { + const user = e.detail?.user; + setIsOpenBlockedModal(true) + blockUser(null, user) + }; + + useEffect(() => { + subscribeToEvent("blockUserFromOutside", blockUserFromOutsideModalFunc); + + return () => { + unsubscribeFromEvent("blockUserFromOutside", blockUserFromOutsideModalFunc); + }; + }, []); return ( - Blocked Users - - Blocked Users + - { - setValue(e.target.value); + > + - - - + > + { + setValue(e.target.value); + }} + /> + + + {Object.entries(blockedUsers?.addresses).length > 0 && ( <> - Blocked Users for Chat ( addresses ) + Blocked addresses- blocks processing of txs + + )} - + {Object.entries(blockedUsers?.addresses || {})?.map( ([key, value]) => { return ( @@ -90,18 +195,22 @@ export const BlockedUsersModal = ({ close }) => { display: "flex", alignItems: "center", gap: "10px", - width: '100%', - justifyContent: 'space-between' + width: "100%", + justifyContent: "space-between", }} > - {key} + {addressesWithNames[key] || key} + + + + {"Decide what to block"} + + + + Blocking {message?.userName || message?.userAddress} + + + Choose "block txs" or "all" to block chat messages + + + + + + + + ); }; diff --git a/src/components/Group/Group.tsx b/src/components/Group/Group.tsx index 64dc541..9f95a3a 100644 --- a/src/components/Group/Group.tsx +++ b/src/components/Group/Group.tsx @@ -75,9 +75,9 @@ import { MessagingIcon } from "../../assets/Icons/MessagingIcon"; import { formatEmailDate } from "./QMailMessages"; import { AdminSpace } from "../Chat/AdminSpace"; import { useRecoilState, useSetRecoilState } from "recoil"; -import { addressInfoControllerAtom, groupsPropertiesAtom, selectedGroupIdAtom } from "../../atoms/global"; +import { addressInfoControllerAtom, groupsPropertiesAtom, isOpenBlockedModalAtom, selectedGroupIdAtom } from "../../atoms/global"; import { sortArrayByTimestampAndGroupName } from "../../utils/time"; -import BlockIcon from '@mui/icons-material/Block'; +import PersonOffIcon from '@mui/icons-material/PersonOff'; import LockIcon from '@mui/icons-material/Lock'; import NoEncryptionGmailerrorredIcon from '@mui/icons-material/NoEncryptionGmailerrorred'; import { BlockedUsersModal } from "./BlockedUsersModal"; @@ -421,7 +421,7 @@ export const Group = ({ const [groupAnnouncements, setGroupAnnouncements] = React.useState({}); const [defaultThread, setDefaultThread] = React.useState(null); const [isOpenDrawer, setIsOpenDrawer] = React.useState(false); - const [isOpenBlockedUserModal, setIsOpenBlockedUserModal] = React.useState(false); + const setIsOpenBlockedUserModal = useSetRecoilState(isOpenBlockedModalAtom) const [hideCommonKeyPopup, setHideCommonKeyPopup] = React.useState(false); const [isLoadingGroupMessage, setIsLoadingGroupMessage] = React.useState(""); @@ -2035,7 +2035,7 @@ export const Group = ({ padding: '10px' }} > - )} - {isOpenBlockedUserModal && ( - { - setIsOpenBlockedUserModal(false) - }} /> - )} + + {selectedDirect && !newChat && ( <> diff --git a/src/components/Group/useBlockUsers.tsx b/src/components/Group/useBlockUsers.tsx index 05cbe90..eeb5361 100644 --- a/src/components/Group/useBlockUsers.tsx +++ b/src/components/Group/useBlockUsers.tsx @@ -19,7 +19,7 @@ export const useBlockedAddresses = () => { const isUserBlocked = useCallback((address, name)=> { try { if(!address) return false - if(userBlockedRef.current[address] || userNamesBlockedRef.current[name]) return true + if(userBlockedRef.current[address]) return true return false @@ -90,43 +90,13 @@ export const useBlockedAddresses = () => { }, []) const removeBlockFromList = useCallback(async (address, name)=> { - await new Promise((res, rej) => { - window.sendMessage("listActions", { - - type: 'remove', - items: name ? [name] : [address], - listName: name ? 'blockedNames' : 'blockedAddresses' - - }) - .then((response) => { - if (response.error) { - rej(response?.message); - return; - } else { - if(!name){ - const copyObject = {...userBlockedRef.current} - delete copyObject[address] - userBlockedRef.current = copyObject - } else { - const copyObject = {...userNamesBlockedRef.current} - delete copyObject[name] - userNamesBlockedRef.current = copyObject - } - - res(response); - } - }) - .catch((error) => { - console.error("Failed qortalRequest", error); - }); - }) - if(name && userBlockedRef.current[address]){ + if(name){ await new Promise((res, rej) => { window.sendMessage("listActions", { type: 'remove', - items: !name ? [name] : [address], - listName: !name ? 'blockedNames' : 'blockedAddresses' + items: [name] , + listName: 'blockedNames' }) .then((response) => { @@ -134,9 +104,12 @@ export const useBlockedAddresses = () => { rej(response?.message); return; } else { - const copyObject = {...userBlockedRef.current} - delete copyObject[address] - userBlockedRef.current = copyObject + + const copyObject = {...userNamesBlockedRef.current} + delete copyObject[name] + userNamesBlockedRef.current = copyObject + + res(response); } }) @@ -145,42 +118,95 @@ export const useBlockedAddresses = () => { }); }) } + + if(address){ + await new Promise((res, rej) => { + window.sendMessage("listActions", { + + type: 'remove', + items: [address], + listName: 'blockedAddresses' + + }) + .then((response) => { + if (response.error) { + rej(response?.message); + return; + } else { + + const copyObject = {...userBlockedRef.current} + delete copyObject[address] + userBlockedRef.current = copyObject + + + res(response); + } + }) + .catch((error) => { + console.error("Failed qortalRequest", error); + }); + }) + } + }, []) const addToBlockList = useCallback(async (address, name)=> { - await new Promise((res, rej) => { - window.sendMessage("listActions", { - - type: 'add', - items: name ? [name] : [address], - listName: name ? 'blockedNames' : 'blockedAddresses' - - }) - .then((response) => { - if (response.error) { - rej(response?.message); - return; - } else { - if(name){ - - const copyObject = {...userNamesBlockedRef.current} - copyObject[name] = true - userNamesBlockedRef.current = copyObject - }else { - const copyObject = {...userBlockedRef.current} - copyObject[address] = true - userBlockedRef.current = copyObject - - } + if(name){ + await new Promise((res, rej) => { + window.sendMessage("listActions", { - res(response); - } + type: 'add', + items: [name], + listName: 'blockedNames' + + }) + .then((response) => { + if (response.error) { + rej(response?.message); + return; + } else { + const copyObject = {...userNamesBlockedRef.current} + copyObject[name] = true + userNamesBlockedRef.current = copyObject + + + res(response); + } + }) + .catch((error) => { + console.error("Failed qortalRequest", error); + }); }) - .catch((error) => { - console.error("Failed qortalRequest", error); - }); - }) + } + if(address){ + await new Promise((res, rej) => { + window.sendMessage("listActions", { + + type: 'add', + items: [address], + listName: 'blockedAddresses' + + }) + .then((response) => { + if (response.error) { + rej(response?.message); + return; + } else { + + const copyObject = {...userBlockedRef.current} + copyObject[address] = true + userBlockedRef.current = copyObject + + res(response); + } + }) + .catch((error) => { + console.error("Failed qortalRequest", error); + }); + }) + } + }, []) return { diff --git a/src/components/WrapperUserAction.tsx b/src/components/WrapperUserAction.tsx index a3bcc3f..c074c16 100644 --- a/src/components/WrapperUserAction.tsx +++ b/src/components/WrapperUserAction.tsx @@ -169,12 +169,15 @@ useEffect(()=> { onClick={async () => { try { setIsLoading(true) - if(isAlreadyBlocked === true){ - await removeBlockFromList(address, name) - } else if(isAlreadyBlocked === false) { - await addToBlockList(address, name) - } - executeEvent('updateChatMessagesWithBlocks', true) + executeEvent("blockUserFromOutside", { + user: address + }) + // if(isAlreadyBlocked === true){ + // await removeBlockFromList(address, name) + // } else if(isAlreadyBlocked === false) { + // await addToBlockList(address, name) + // } + // executeEvent('updateChatMessagesWithBlocks', true) } catch (error) { console.error(error) } finally { From 4f0bea57b5772406efcf9542b1be95cdd1189e5e Mon Sep 17 00:00:00 2001 From: PhilReact Date: Fri, 11 Apr 2025 20:19:03 +0300 Subject: [PATCH 03/31] error snack for blocked user --- src/components/Group/BlockedUsersModal.tsx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/components/Group/BlockedUsersModal.tsx b/src/components/Group/BlockedUsersModal.tsx index e81d207..24b3f01 100644 --- a/src/components/Group/BlockedUsersModal.tsx +++ b/src/components/Group/BlockedUsersModal.tsx @@ -25,7 +25,7 @@ export const BlockedUsersModal = () => { const [value, setValue] = useState(""); const [addressesWithNames, setAddressesWithNames] = useState({}) const { isShow, onCancel, onOk, show, message } = useModal(); - const { getAllBlockedUsers, removeBlockFromList, addToBlockList } = + const { getAllBlockedUsers, removeBlockFromList, addToBlockList, setOpenSnackGlobal, setInfoSnackCustom } = useContext(MyContext); const [blockedUsers, setBlockedUsers] = useState({ addresses: {}, @@ -115,7 +115,12 @@ export const BlockedUsersModal = () => { executeEvent('updateChatMessagesWithBlocks', true) } } catch (error) { - console.error(error); + setOpenSnackGlobal(true); + + setInfoSnackCustom({ + type: "error", + message: error?.message || "Unable to block user", + }); } }; const blockUserFromOutsideModalFunc = (e) => { From 6d63194e6732ac045ca233252ace80c0575cec45 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Fri, 11 Apr 2025 23:44:03 +0200 Subject: [PATCH 04/31] Sort lines --- .prettierrc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.prettierrc b/.prettierrc index 17c54ee..bf662de 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,24 +1,24 @@ { "arrowParens": "always", + "bracketSameLine": false, "bracketSpacing": true, + "embeddedLanguageFormatting": "auto", "endOfLine": "lf", + "experimentalTernaries": false, "htmlWhitespaceSensitivity": "css", "insertPragma": false, - "singleAttributePerLine": false, - "bracketSameLine": false, "jsxBracketSameLine": false, "jsxSingleQuote": false, + "parser": "typescript", "printWidth": 80, "proseWrap": "preserve", "quoteProps": "as-needed", "requirePragma": false, "semi": true, + "singleAttributePerLine": false, "singleQuote": true, "tabWidth": 2, "trailingComma": "es5", "useTabs": false, - "embeddedLanguageFormatting": "auto", - "vueIndentScriptAndStyle": false, - "experimentalTernaries": false, - "parser": "typescript" + "vueIndentScriptAndStyle": false } From 15925c8f8d154a7e635fafd42f37a2e23cca2e7f Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Fri, 11 Apr 2025 23:45:30 +0200 Subject: [PATCH 05/31] Remove global parser --- .prettierrc | 1 - 1 file changed, 1 deletion(-) diff --git a/.prettierrc b/.prettierrc index bf662de..badfcf9 100644 --- a/.prettierrc +++ b/.prettierrc @@ -9,7 +9,6 @@ "insertPragma": false, "jsxBracketSameLine": false, "jsxSingleQuote": false, - "parser": "typescript", "printWidth": 80, "proseWrap": "preserve", "quoteProps": "as-needed", From 8f05749e2e14261dfb82ad129e332598be3b7a8e Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Fri, 11 Apr 2025 23:45:35 +0200 Subject: [PATCH 06/31] Format css --- src/index.css | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/index.css b/src/index.css index 6b24232..3fd5ed7 100644 --- a/src/index.css +++ b/src/index.css @@ -1,21 +1,21 @@ @font-face { - font-family: "Inter"; - src: url("./styles/fonts/Inter-SemiBold.ttf") format("truetype"); + font-family: 'Inter'; + src: url('./styles/fonts/Inter-SemiBold.ttf') format('truetype'); font-weight: 600; } @font-face { - font-family: "Inter"; - src: url("./styles/fonts/Inter-ExtraBold.ttf") format("truetype"); + font-family: 'Inter'; + src: url('./styles/fonts/Inter-ExtraBold.ttf') format('truetype'); font-weight: 800; } @font-face { - font-family: "Inter"; - src: url("./styles/fonts/Inter-Bold.ttf") format("truetype"); + font-family: 'Inter'; + src: url('./styles/fonts/Inter-Bold.ttf') format('truetype'); font-weight: 700; } @font-face { - font-family: "Inter"; - src: url("./styles/fonts/Inter-Regular.ttf") format("truetype"); + font-family: 'Inter'; + src: url('./styles/fonts/Inter-Regular.ttf') format('truetype'); font-weight: 400; } @@ -89,7 +89,7 @@ body { } @property --var1 { - syntax: ""; + syntax: ''; inherits: true; initial-value: transparent; } From 8e6f7c0f39e7dcb3c77273e884a2c2bd83fd62bf Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 12 Apr 2025 12:33:56 +0200 Subject: [PATCH 07/31] Add themeSelector --- src/ExtStates/NotAuthenticated.tsx | 836 +++++++++++++++-------------- 1 file changed, 445 insertions(+), 391 deletions(-) diff --git a/src/ExtStates/NotAuthenticated.tsx b/src/ExtStates/NotAuthenticated.tsx index 8625495..bfb33d4 100644 --- a/src/ExtStates/NotAuthenticated.tsx +++ b/src/ExtStates/NotAuthenticated.tsx @@ -1,11 +1,16 @@ -import React, { useCallback, useContext, useEffect, useRef, useState } from "react"; -import { Spacer } from "../common/Spacer"; -import { CustomButton, TextItalic, TextP, TextSpan } from "../App-styles"; +import React, { + useCallback, + useContext, + useEffect, + useRef, + useState, +} from 'react'; +import { Spacer } from '../common/Spacer'; +import { CustomButton, TextP, TextSpan } from '../App-styles'; import { Box, Button, ButtonBase, - Checkbox, Dialog, DialogActions, DialogContent, @@ -16,22 +21,20 @@ import { Switch, TextField, Typography, -} from "@mui/material"; -import Logo1 from "../assets/svgs/Logo1.svg"; -import Logo1Dark from "../assets/svgs/Logo1Dark.svg"; -import Info from "../assets/svgs/Info.svg"; + useTheme, +} from '@mui/material'; +import Logo1Dark from '../assets/svgs/Logo1Dark.svg'; import HelpIcon from '@mui/icons-material/Help'; -import { CustomizedSnackbars } from "../components/Snackbar/Snackbar"; -import { set } from "lodash"; -import { cleanUrl, gateways, isUsingLocal } from "../background"; -import { GlobalContext } from "../App"; +import { CustomizedSnackbars } from '../components/Snackbar/Snackbar'; +import { cleanUrl, gateways } from '../background'; +import { GlobalContext } from '../App'; import Tooltip, { TooltipProps, tooltipClasses } from '@mui/material/Tooltip'; +import ThemeSelector from '../components/Theme/ThemeSelector'; const manifestData = { - version: "0.5.3", + version: '0.5.3', }; - export const HtmlTooltip = styled(({ className, ...props }: TooltipProps) => ( ))(({ theme }) => ({ @@ -47,20 +50,18 @@ function removeTrailingSlash(url) { return url.replace(/\/+$/, ''); } - export const NotAuthenticated = ({ getRootProps, getInputProps, setExtstate, - apiKey, setApiKey, globalApiKey, handleSetGlobalApikey, currentNode, setCurrentNode, - useLocalNode, - setUseLocalNode + useLocalNode, + setUseLocalNode, }) => { const [isValidApiKey, setIsValidApiKey] = useState(null); const [hasLocalNode, setHasLocalNode] = useState(null); @@ -68,27 +69,28 @@ export const NotAuthenticated = ({ const [openSnack, setOpenSnack] = React.useState(false); const [infoSnack, setInfoSnack] = React.useState(null); const [show, setShow] = React.useState(false); - const [mode, setMode] = React.useState("list"); + const [mode, setMode] = React.useState('list'); const [customNodes, setCustomNodes] = React.useState(null); // const [currentNode, setCurrentNode] = React.useState({ // url: "http://127.0.0.1:12391", // }); const [importedApiKey, setImportedApiKey] = React.useState(null); //add and edit states - const [url, setUrl] = React.useState("https://"); - const [customApikey, setCustomApiKey] = React.useState(""); - const [showSelectApiKey, setShowSelectApiKey] = useState(false) - const [enteredApiKey, setEnteredApiKey] = useState('') + const [url, setUrl] = React.useState('https://'); + const [customApikey, setCustomApiKey] = React.useState(''); + const [showSelectApiKey, setShowSelectApiKey] = useState(false); + const [enteredApiKey, setEnteredApiKey] = useState(''); const [customNodeToSaveIndex, setCustomNodeToSaveIndex] = React.useState(null); - const { showTutorial, hasSeenGettingStarted } = useContext(GlobalContext); + const { showTutorial, hasSeenGettingStarted } = useContext(GlobalContext); + const theme = useTheme(); const importedApiKeyRef = useRef(null); const currentNodeRef = useRef(null); const hasLocalNodeRef = useRef(null); - const isLocal = cleanUrl(currentNode?.url) === "127.0.0.1:12391"; + const isLocal = cleanUrl(currentNode?.url) === '127.0.0.1:12391'; const handleFileChangeApiKey = (event) => { - setShowSelectApiKey(false) + setShowSelectApiKey(false); const file = event.target.files[0]; // Get the selected file if (file) { const reader = new FileReader(); @@ -96,34 +98,32 @@ export const NotAuthenticated = ({ const text = e.target.result; // Get the file content setImportedApiKey(text); // Store the file content in the state - if(customNodes){ - setCustomNodes((prev)=> { - const copyPrev = [...prev] - const findLocalIndex = copyPrev?.findIndex((item)=> item?.url === 'http://127.0.0.1:12391') - if(findLocalIndex === -1){ + if (customNodes) { + setCustomNodes((prev) => { + const copyPrev = [...prev]; + const findLocalIndex = copyPrev?.findIndex( + (item) => item?.url === 'http://127.0.0.1:12391' + ); + if (findLocalIndex === -1) { copyPrev.unshift({ - url: "http://127.0.0.1:12391", - apikey: text - }) + url: 'http://127.0.0.1:12391', + apikey: text, + }); } else { copyPrev[findLocalIndex] = { - url: "http://127.0.0.1:12391", - apikey: text - } + url: 'http://127.0.0.1:12391', + apikey: text, + }; } - window - .sendMessage("setCustomNodes", copyPrev) - .catch((error) => { + window.sendMessage('setCustomNodes', copyPrev).catch((error) => { console.error( - "Failed to set custom nodes:", - error.message || "An error occurred" + 'Failed to set custom nodes:', + error.message || 'An error occurred' ); }); - return copyPrev - }) - + return copyPrev; + }); } - }; reader.readAsText(file); // Read the file as text } @@ -133,22 +133,20 @@ export const NotAuthenticated = ({ try { const url = `http://127.0.0.1:12391/admin/status`; const response = await fetch(url, { - method: "GET", + method: 'GET', headers: { - "Content-Type": "application/json", + 'Content-Type': 'application/json', }, }); const data = await response.json(); if (data?.height) { setHasLocalNode(true); - return true + return true; } - return false - + return false; } catch (error) { - return false - - } + return false; + } }, []); useEffect(() => { @@ -157,25 +155,27 @@ export const NotAuthenticated = ({ useEffect(() => { window - .sendMessage("getCustomNodesFromStorage") + .sendMessage('getCustomNodesFromStorage') .then((response) => { - - setCustomNodes(response || []); - if(window?.electronAPI?.setAllowedDomains){ - window.electronAPI.setAllowedDomains(response?.map((node)=> node.url)) + setCustomNodes(response || []); + if (window?.electronAPI?.setAllowedDomains) { + window.electronAPI.setAllowedDomains( + response?.map((node) => node.url) + ); + } + if (Array.isArray(response)) { + const findLocal = response?.find( + (item) => item?.url === 'http://127.0.0.1:12391' + ); + if (findLocal && findLocal?.apikey) { + setImportedApiKey(findLocal?.apikey); } - if(Array.isArray(response)){ - const findLocal = response?.find((item)=> item?.url === 'http://127.0.0.1:12391') - if(findLocal && findLocal?.apikey){ - setImportedApiKey(findLocal?.apikey) - } - } - + } }) .catch((error) => { console.error( - "Failed to get custom nodes from storage:", - error.message || "An error occurred" + 'Failed to get custom nodes from storage:', + error.message || 'An error occurred' ); }); }, []); @@ -191,56 +191,58 @@ export const NotAuthenticated = ({ hasLocalNodeRef.current = hasLocalNode; }, [hasLocalNode]); - - const validateApiKey = useCallback(async (key, fromStartUp) => { try { - if(key === "isGateway") return - const isLocalKey = cleanUrl(key?.url) === "127.0.0.1:12391"; - if (fromStartUp && key?.url && key?.apikey && !isLocalKey && !gateways.some(gateway => key?.url?.includes(gateway))) { + if (key === 'isGateway') return; + const isLocalKey = cleanUrl(key?.url) === '127.0.0.1:12391'; + if ( + fromStartUp && + key?.url && + key?.apikey && + !isLocalKey && + !gateways.some((gateway) => key?.url?.includes(gateway)) + ) { setCurrentNode({ url: key?.url, apikey: key?.apikey, }); - let isValid = false + let isValid = false; - const url = `${key?.url}/admin/settings/localAuthBypassEnabled`; const response = await fetch(url); // Assuming the response is in plain text and will be 'true' or 'false' const data = await response.text(); - if(data && data === 'true'){ - isValid = true + if (data && data === 'true') { + isValid = true; } else { const url2 = `${key?.url}/admin/apikey/test?apiKey=${key?.apikey}`; const response2 = await fetch(url2); - + // Assuming the response is in plain text and will be 'true' or 'false' const data2 = await response2.text(); - if (data2 === "true") { - isValid = true + if (data2 === 'true') { + isValid = true; } } - + if (isValid) { setIsValidApiKey(true); setUseLocalNode(true); - return + return; } - } if (!currentNodeRef.current) return; - const stillHasLocal = await checkIfUserHasLocalNode() + const stillHasLocal = await checkIfUserHasLocalNode(); if (isLocalKey && !stillHasLocal && !fromStartUp) { - throw new Error("Please turn on your local node"); + throw new Error('Please turn on your local node'); } //check custom nodes // !gateways.some(gateway => apiKey?.url?.includes(gateway)) const isCurrentNodeLocal = - cleanUrl(currentNodeRef.current?.url) === "127.0.0.1:12391"; + cleanUrl(currentNodeRef.current?.url) === '127.0.0.1:12391'; if (isLocalKey && !isCurrentNodeLocal) { setIsValidApiKey(false); setUseLocalNode(false); @@ -248,90 +250,88 @@ export const NotAuthenticated = ({ } let payload = {}; - if (currentNodeRef.current?.url === "http://127.0.0.1:12391") { + if (currentNodeRef.current?.url === 'http://127.0.0.1:12391') { payload = { apikey: importedApiKeyRef.current || key?.apikey, url: currentNodeRef.current?.url, }; - if(!payload?.apikey){ + if (!payload?.apikey) { try { - const generateUrl = "http://127.0.0.1:12391/admin/apikey/generate"; + const generateUrl = 'http://127.0.0.1:12391/admin/apikey/generate'; const generateRes = await fetch(generateUrl, { - method: "POST", - }) + method: 'POST', + }); let res; - try { - res = await generateRes.clone().json(); - } catch (e) { - res = await generateRes.text(); - } + try { + res = await generateRes.clone().json(); + } catch (e) { + res = await generateRes.text(); + } if (res != null && !res.error && res.length >= 8) { payload = { apikey: res, url: currentNodeRef.current?.url, }; - + setImportedApiKey(res); // Store the file content in the state - - setCustomNodes((prev)=> { - const copyPrev = [...prev] - const findLocalIndex = copyPrev?.findIndex((item)=> item?.url === 'http://127.0.0.1:12391') - if(findLocalIndex === -1){ - copyPrev.unshift({ - url: "http://127.0.0.1:12391", - apikey: res - }) - } else { - copyPrev[findLocalIndex] = { - url: "http://127.0.0.1:12391", - apikey: res - } - } - window - .sendMessage("setCustomNodes", copyPrev) - .catch((error) => { - console.error( - "Failed to set custom nodes:", - error.message || "An error occurred" + + setCustomNodes((prev) => { + const copyPrev = [...prev]; + const findLocalIndex = copyPrev?.findIndex( + (item) => item?.url === 'http://127.0.0.1:12391' ); + if (findLocalIndex === -1) { + copyPrev.unshift({ + url: 'http://127.0.0.1:12391', + apikey: res, + }); + } else { + copyPrev[findLocalIndex] = { + url: 'http://127.0.0.1:12391', + apikey: res, + }; + } + window + .sendMessage('setCustomNodes', copyPrev) + .catch((error) => { + console.error( + 'Failed to set custom nodes:', + error.message || 'An error occurred' + ); + }); + return copyPrev; }); - return copyPrev - }) - - } } catch (error) { - console.error(error) + console.error(error); } } } else if (currentNodeRef.current) { payload = currentNodeRef.current; } - let isValid = false + let isValid = false; - const url = `${payload?.url}/admin/settings/localAuthBypassEnabled`; const response = await fetch(url); // Assuming the response is in plain text and will be 'true' or 'false' const data = await response.text(); - if(data && data === 'true'){ - isValid = true + if (data && data === 'true') { + isValid = true; } else { const url2 = `${payload?.url}/admin/apikey/test?apiKey=${payload?.apikey}`; const response2 = await fetch(url2); - + // Assuming the response is in plain text and will be 'true' or 'false' const data2 = await response2.text(); - if (data2 === "true") { - isValid = true + if (data2 === 'true') { + isValid = true; } } - if (isValid) { window - .sendMessage("setApiKey", payload) + .sendMessage('setApiKey', payload) .then((response) => { if (response) { handleSetGlobalApikey(payload); @@ -344,31 +344,30 @@ export const NotAuthenticated = ({ }) .catch((error) => { console.error( - "Failed to set API key:", - error.message || "An error occurred" + 'Failed to set API key:', + error.message || 'An error occurred' ); }); } else { setIsValidApiKey(false); setUseLocalNode(false); - if(!fromStartUp){ + if (!fromStartUp) { setInfoSnack({ - type: "error", - message: "Select a valid apikey", + type: 'error', + message: 'Select a valid apikey', }); setOpenSnack(true); } - } } catch (error) { setIsValidApiKey(false); setUseLocalNode(false); if (fromStartUp) { setCurrentNode({ - url: "http://127.0.0.1:12391", + url: 'http://127.0.0.1:12391', }); window - .sendMessage("setApiKey", "isGateway") + .sendMessage('setApiKey', 'isGateway') .then((response) => { if (response) { setApiKey(null); @@ -377,20 +376,20 @@ export const NotAuthenticated = ({ }) .catch((error) => { console.error( - "Failed to set API key:", - error.message || "An error occurred" + 'Failed to set API key:', + error.message || 'An error occurred' ); }); - return + return; } - if(!fromStartUp){ - setInfoSnack({ - type: "error", - message: error?.message || "Select a valid apikey", - }); - setOpenSnack(true); - } - console.error("Error validating API key:", error); + if (!fromStartUp) { + setInfoSnack({ + type: 'error', + message: error?.message || 'Select a valid apikey', + }); + setOpenSnack(true); + } + console.error('Error validating API key:', error); } }, []); @@ -401,7 +400,7 @@ export const NotAuthenticated = ({ }, [apiKey]); const addCustomNode = () => { - setMode("add-node"); + setMode('add-node'); }; const saveCustomNodes = (myNodes, isFullListOfNodes) => { let nodes = [...(myNodes || [])]; @@ -418,26 +417,28 @@ export const NotAuthenticated = ({ } setCustomNodes(nodes); - + setCustomNodeToSaveIndex(null); if (!nodes) return; window - .sendMessage("setCustomNodes", nodes) + .sendMessage('setCustomNodes', nodes) .then((response) => { if (response) { - setMode("list"); - setUrl("https://"); - setCustomApiKey(""); - if(window?.electronAPI?.setAllowedDomains){ - window.electronAPI.setAllowedDomains(nodes?.map((node) => node.url)) - } + setMode('list'); + setUrl('https://'); + setCustomApiKey(''); + if (window?.electronAPI?.setAllowedDomains) { + window.electronAPI.setAllowedDomains( + nodes?.map((node) => node.url) + ); + } // add alert if needed } }) .catch((error) => { console.error( - "Failed to set custom nodes:", - error.message || "An error occurred" + 'Failed to set custom nodes:', + error.message || 'An error occurred' ); }); }; @@ -448,8 +449,8 @@ export const NotAuthenticated = ({
@@ -457,40 +458,55 @@ export const NotAuthenticated = ({ - WELCOME TO - QORTAL + WELCOME TO + + {' '} + QORTAL + - + - - Your wallet is like your digital ID on Qortal, and is how you will login to the Qortal User Interface. It holds your public address and the Qortal name you will eventually choose. Every transaction you make is linked to your ID, and this is where you manage all your QORT and other tradeable cryptocurrencies on Qortal. - - } - > - setExtstate('wallets')}> - {/* */} - Accounts - + + + Your wallet is like your digital ID on Qortal, and is how you + will login to the Qortal User Interface. It holds your public + address and the Qortal name you will eventually choose. Every + transaction you make is linked to your ID, and this is where you + manage all your QORT and other tradeable cryptocurrencies on + Qortal. + + + } + > + setExtstate('wallets')}> + {/* */} + Accounts + {/* @@ -500,98 +516,119 @@ export const NotAuthenticated = ({ - New users start here! - - Creating an account means creating a new wallet and digital ID to start using Qortal. Once you have made your account, you can start doing things like obtaining some QORT, buying a name and avatar, publishing videos and blogs, and much more. - - } - > - { - setExtstate("create-wallet"); - }} - sx={{ - backgroundColor: hasSeenGettingStarted === false && 'var(--green)', - color: hasSeenGettingStarted === false && 'black', - "&:hover": { - backgroundColor: hasSeenGettingStarted === false && 'var(--green)', - color: hasSeenGettingStarted === false && 'black' - } - }} + disableHoverListener={hasSeenGettingStarted === true} + placement="right" + title={ + + + New users start here! + + + + Creating an account means creating a new wallet and digital ID + to start using Qortal. Once you have made your account, you can + start doing things like obtaining some QORT, buying a name and + avatar, publishing videos and blogs, and much more. + + + } > - Create account - + { + setExtstate('create-wallet'); + }} + sx={{ + backgroundColor: + hasSeenGettingStarted === false && 'var(--green)', + color: hasSeenGettingStarted === false && 'black', + '&:hover': { + backgroundColor: + hasSeenGettingStarted === false && 'var(--green)', + color: hasSeenGettingStarted === false && 'black', + }, + }} + > + Create account + - - {"Using node: "} {currentNode?.url} + {'Using node: '} {currentNode?.url} <> <> - For advanced users + + For advanced users + { if (response) { setApiKey(null); @@ -613,8 +650,8 @@ export const NotAuthenticated = ({ }) .catch((error) => { console.error( - "Failed to set API key:", - error.message || "An error occurred" + 'Failed to set API key:', + error.message || 'An error occurred' ); }); } @@ -623,18 +660,23 @@ export const NotAuthenticated = ({ defaultChecked /> } - label={`Use ${isLocal ? "Local" : "Custom"} Node`} + label={`Use ${isLocal ? 'Local' : 'Custom'} Node`} /> - {currentNode?.url === "http://127.0.0.1:12391" && ( + {currentNode?.url === 'http://127.0.0.1:12391' && ( <> - {`api key : ${importedApiKey}`} @@ -652,8 +694,8 @@ export const NotAuthenticated = ({ Build version: {manifestData?.version} @@ -673,60 +715,60 @@ export const NotAuthenticated = ({ aria-describedby="alert-dialog-description" fullWidth > - {"Custom nodes"} + {'Custom nodes'} - {mode === "list" && ( + {mode === 'list' && ( http://127.0.0.1:12391 )} - {mode === "add-node" && ( + {mode === 'add-node' && ( <> + type="file" + accept=".txt" + hidden + onChange={handleFileChangeApiKey} // File input handler + /> + - - + - - - + )} - { - showTutorial('create-account', true) - }} sx={{ - position: 'fixed', - bottom: '25px', - right: '25px' - }}> - - + { + showTutorial('create-account', true); + }} + sx={{ + position: 'fixed', + bottom: '25px', + right: '25px', + }} + > + + + + ); -}; \ No newline at end of file +}; From 5452973773b5288433d90f01354d497e664e83c5 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 12 Apr 2025 12:34:28 +0200 Subject: [PATCH 08/31] Remove background property --- src/App-styles.ts | 280 +++++++++++++++++++++++----------------------- 1 file changed, 141 insertions(+), 139 deletions(-) diff --git a/src/App-styles.ts b/src/App-styles.ts index 96112b3..9774f06 100644 --- a/src/App-styles.ts +++ b/src/App-styles.ts @@ -1,160 +1,157 @@ -import { Typography, Box, TextField, InputLabel } from "@mui/material"; -import { styled } from "@mui/system"; +import { Typography, Box, TextField, InputLabel } from '@mui/material'; +import { styled } from '@mui/system'; export const AppContainer = styled(Box)(({ theme }) => ({ - display: "flex", - alignItems: "center", - flexDirection: "column", - width: "100vw", - background: "rgba(39, 40, 44, 1)", - height: "100vh", - radius: "15px", + display: 'flex', + alignItems: 'center', + flexDirection: 'column', + width: '100vw', + height: '100vh', + radius: '15px', backgroundColor: theme.palette.background.default, color: theme.palette.text.primary, - overflow: 'hidden' + overflow: 'hidden', })); export const AuthenticatedContainer = styled(Box)(({ theme }) => ({ - display: "flex", - width: "100%", - height: "100%", - justifyContent: "space-between", + display: 'flex', + width: '100%', + height: '100%', + justifyContent: 'space-between', backgroundColor: theme.palette.background.default, color: theme.palette.text.primary, })); export const AuthenticatedContainerInnerLeft = styled(Box)(({ theme }) => ({ - display: "flex", - alignItems: "center", - flexDirection: "column", - height: "100%", - width: "100%", + display: 'flex', + alignItems: 'center', + flexDirection: 'column', + height: '100%', + width: '100%', backgroundColor: theme.palette.background.default, color: theme.palette.text.primary, })); export const AuthenticatedContainerInnerRight = styled(Box)(({ theme }) => ({ - display: "flex", - alignItems: "center", - flexDirection: "column", - width: "60px", - height: "100%", - background: "rgba(0, 0, 0, 0.1)", + display: 'flex', + alignItems: 'center', + flexDirection: 'column', + width: '60px', + height: '100%', backgroundColor: theme.palette.background.default, color: theme.palette.text.primary, })); export const AuthenticatedContainerInnerTop = styled(Box)(({ theme }) => ({ - display: "flex", - alignItems: "center", - justifyContent: "flex-start", - width: "100%px", - height: "60px", - background: "rgba(0, 0, 0, 0.1)", - padding: "20px", + display: 'flex', + alignItems: 'center', + justifyContent: 'flex-start', + width: '100%px', + height: '60px', + padding: '20px', backgroundColor: theme.palette.background.default, color: theme.palette.text.primary, })); export const TextP = styled(Typography)(({ theme }) => ({ - fontSize: "13px", + fontSize: '13px', fontWeight: 600, - fontFamily: "Inter", + fontFamily: 'Inter', backgroundColor: theme.palette.background.default, color: theme.palette.text.primary, })); -export const TextItalic = styled("span")(({ theme }) => ({ - fontSize: "13px", +export const TextItalic = styled('span')(({ theme }) => ({ + fontSize: '13px', fontWeight: 600, - fontFamily: "Inter", - fontStyle: "italic", + fontFamily: 'Inter', + fontStyle: 'italic', backgroundColor: theme.palette.background.default, color: theme.palette.text.primary, })); -export const TextSpan = styled("span")(({ theme }) => ({ - fontSize: "13px", - fontFamily: "Inter", +export const TextSpan = styled('span')(({ theme }) => ({ + fontSize: '13px', + fontFamily: 'Inter', fontWeight: 800, backgroundColor: theme.palette.background.default, color: theme.palette.text.primary, })); export const AddressBox = styled(Box)(({ theme }) => ({ - display: "flex", + display: 'flex', border: `1px solid ${ - theme.palette.mode === "dark" - ? "rgba(255, 255, 255, 0.5)" - : "rgba(0, 0, 0, 0.3)" + theme.palette.mode === 'dark' + ? 'rgba(255, 255, 255, 0.5)' + : 'rgba(0, 0, 0, 0.3)' }`, - justifyContent: "space-between", - alignItems: "center", - width: "auto", - height: "25px", - padding: "5px 15px", - gap: "5px", - borderRadius: "100px", - fontFamily: "Inter", - fontSize: "12px", + justifyContent: 'space-between', + alignItems: 'center', + width: 'auto', + height: '25px', + padding: '5px 15px', + gap: '5px', + borderRadius: '100px', + fontFamily: 'Inter', + fontSize: '12px', fontWeight: 600, - lineHeight: "14.52px", - textAlign: "left", + lineHeight: '14.52px', + textAlign: 'left', backgroundColor: theme.palette.background.default, color: theme.palette.text.primary, - cursor: "pointer", - transition: "all 0.2s", + cursor: 'pointer', + transition: 'all 0.2s', - "&:hover": { + '&:hover': { backgroundColor: - theme.palette.mode === "dark" - ? "rgba(41, 41, 43, 1)" - : "rgba(240, 240, 240, 1)", - color: theme.palette.mode === "dark" ? "#fff" : "#000", + theme.palette.mode === 'dark' + ? 'rgba(41, 41, 43, 1)' + : 'rgba(240, 240, 240, 1)', + color: theme.palette.mode === 'dark' ? '#fff' : '#000', - "svg path": { - fill: theme.palette.mode === "dark" ? "#fff" : "#000", + 'svg path': { + fill: theme.palette.mode === 'dark' ? '#fff' : '#000', }, }, })); export const CustomButton = styled(Box)(({ theme }) => ({ - boxSizing: "border-box", - padding: "15px 20px", - gap: "10px", + boxSizing: 'border-box', + padding: '15px 20px', + gap: '10px', border: `0.5px solid ${ - theme.palette.mode === "dark" - ? "rgba(255, 255, 255, 0.5)" - : "rgba(0, 0, 0, 0.3)" + theme.palette.mode === 'dark' + ? 'rgba(255, 255, 255, 0.5)' + : 'rgba(0, 0, 0, 0.3)' }`, - filter: "drop-shadow(1px 4px 10.5px rgba(0, 0, 0, 0.3))", - borderRadius: "5px", + filter: 'drop-shadow(1px 4px 10.5px rgba(0, 0, 0, 0.3))', + borderRadius: '5px', - display: "inline-flex", - justifyContent: "center", - alignItems: "center", + display: 'inline-flex', + justifyContent: 'center', + alignItems: 'center', - width: "fit-content", - minWidth: "160px", - cursor: "pointer", - transition: "all 0.2s", + width: 'fit-content', + minWidth: '160px', + cursor: 'pointer', + transition: 'all 0.2s', fontWeight: 600, - fontFamily: "Inter", - textAlign: "center", + fontFamily: 'Inter', + textAlign: 'center', backgroundColor: theme.palette.background.default, color: theme.palette.text.primary, - "&:hover": { + '&:hover': { backgroundColor: - theme.palette.mode === "dark" - ? "rgba(41, 41, 43, 1)" - : "rgba(230, 230, 230, 1)", - color: "#fff", + theme.palette.mode === 'dark' + ? 'rgba(41, 41, 43, 1)' + : 'rgba(230, 230, 230, 1)', + color: '#fff', - "svg path": { - fill: "#fff", + 'svg path': { + fill: '#fff', }, }, })); @@ -166,39 +163,44 @@ interface CustomButtonProps { export const CustomButtonAccept = styled(Box)( ({ bgColor, color, theme }) => ({ - boxSizing: "border-box", - padding: "15px 20px", - gap: "10px", + boxSizing: 'border-box', + padding: '15px 20px', + gap: '10px', border: `0.5px solid ${ - theme.palette.mode === "dark" - ? "rgba(255, 255, 255, 0.5)" - : "rgba(0, 0, 0, 0.3)" + theme.palette.mode === 'dark' + ? 'rgba(255, 255, 255, 0.5)' + : 'rgba(0, 0, 0, 0.3)' }`, - filter: "drop-shadow(1px 4px 10.5px rgba(0,0,0,0.3))", + filter: 'drop-shadow(1px 4px 10.5px rgba(0,0,0,0.3))', borderRadius: 5, - display: "inline-flex", - justifyContent: "center", - alignItems: "center", - width: "fit-content", - transition: "all 0.2s", + display: 'inline-flex', + justifyContent: 'center', + alignItems: 'center', + width: 'fit-content', + transition: 'all 0.2s', minWidth: 160, - cursor: "pointer", + cursor: 'pointer', fontWeight: 600, - fontFamily: "Inter", - textAlign: "center", + fontFamily: 'Inter', + textAlign: 'center', opacity: 0.7, // Color and backgroundColor with fallbacks - backgroundColor: bgColor || (theme.palette.mode === "dark" ? "#1d1d1d" : "#f5f5f5"), - color: color || (theme.palette.mode === "dark" ? "#fff" : "#000"), + backgroundColor: + bgColor || (theme.palette.mode === 'dark' ? '#1d1d1d' : '#f5f5f5'), + color: color || (theme.palette.mode === 'dark' ? '#fff' : '#000'), - "&:hover": { + '&:hover': { opacity: 1, - backgroundColor: bgColor || (theme.palette.mode === "dark" ? "rgba(41, 41, 43, 1)" : "rgba(230, 230, 230, 1)"), - color: color || "#fff", + backgroundColor: + bgColor || + (theme.palette.mode === 'dark' + ? 'rgba(41, 41, 43, 1)' + : 'rgba(230, 230, 230, 1)'), + color: color || '#fff', svg: { path: { - fill: color || "#fff", + fill: color || '#fff', }, }, }, @@ -206,53 +208,53 @@ export const CustomButtonAccept = styled(Box)( ); export const CustomInput = styled(TextField)(({ theme }) => ({ - width: "183px", // Adjust the width as needed - borderRadius: "5px", + width: '183px', // Adjust the width as needed + borderRadius: '5px', // backgroundColor: "rgba(30, 30, 32, 1)", - outline: "none", + outline: 'none', backgroundColor: theme.palette.background.default, color: theme.palette.text.primary, input: { fontSize: 10, - fontFamily: "Inter", + fontFamily: 'Inter', fontWeight: 400, - color: "white", - "&::placeholder": { + color: 'white', + '&::placeholder': { fontSize: 16, - color: "rgba(255, 255, 255, 0.2)", + color: 'rgba(255, 255, 255, 0.2)', }, - outline: "none", - padding: "10px", + outline: 'none', + padding: '10px', }, - "& .MuiOutlinedInput-root": { - "& fieldset": { - border: "0.5px solid rgba(255, 255, 255, 0.5)", + '& .MuiOutlinedInput-root': { + '& fieldset': { + border: '0.5px solid rgba(255, 255, 255, 0.5)', }, - "&:hover fieldset": { - border: "0.5px solid rgba(255, 255, 255, 0.5)", + '&:hover fieldset': { + border: '0.5px solid rgba(255, 255, 255, 0.5)', }, - "&.Mui-focused fieldset": { - border: "0.5px solid rgba(255, 255, 255, 0.5)", + '&.Mui-focused fieldset': { + border: '0.5px solid rgba(255, 255, 255, 0.5)', }, }, - "& .MuiInput-underline:before": { - borderBottom: "none", + '& .MuiInput-underline:before': { + borderBottom: 'none', }, - "& .MuiInput-underline:hover:not(.Mui-disabled):before": { - borderBottom: "none", + '& .MuiInput-underline:hover:not(.Mui-disabled):before': { + borderBottom: 'none', }, - "& .MuiInput-underline:after": { - borderBottom: "none", + '& .MuiInput-underline:after': { + borderBottom: 'none', }, })); export const CustomLabel = styled(InputLabel)(({ theme }) => ({ fontWeight: 400, - fontFamily: "Inter", - fontSize: "10px", - lineHeight: "12px", + fontFamily: 'Inter', + fontSize: '10px', + lineHeight: '12px', color: - theme.palette.mode === "dark" - ? "rgba(255, 255, 255, 0.5)" - : "rgba(0, 0, 0, 0.5)", -})); \ No newline at end of file + theme.palette.mode === 'dark' + ? 'rgba(255, 255, 255, 0.5)' + : 'rgba(0, 0, 0, 0.5)', +})); From a0759711b10dea8db885ca421588f57223debbf9 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 12 Apr 2025 15:30:32 +0200 Subject: [PATCH 09/31] Extend interface to allow style attribute --- src/assets/svgs/interfaces.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/assets/svgs/interfaces.ts b/src/assets/svgs/interfaces.ts index 0cbd14f..b53eeb4 100644 --- a/src/assets/svgs/interfaces.ts +++ b/src/assets/svgs/interfaces.ts @@ -1,6 +1,8 @@ -export interface SVGProps { - color: string - height: string - width: string - opacity?: number +import React from 'react'; + +export interface SVGProps extends React.SVGProps { + color?: string; + height?: string; + opacity?: number; + width?: string; } From c5cfaf3722689410a82d16c313cf8e6d51b97310 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 12 Apr 2025 15:30:45 +0200 Subject: [PATCH 10/31] Remove copy --- src/components/Group/Forum/Thread copy.tsx | 329 --------------------- 1 file changed, 329 deletions(-) delete mode 100644 src/components/Group/Forum/Thread copy.tsx diff --git a/src/components/Group/Forum/Thread copy.tsx b/src/components/Group/Forum/Thread copy.tsx deleted file mode 100644 index 9091a2c..0000000 --- a/src/components/Group/Forum/Thread copy.tsx +++ /dev/null @@ -1,329 +0,0 @@ -import React, { - FC, - useCallback, - useEffect, - useRef, - useState -} from 'react' - -import { - Box, - - Skeleton, - -} from '@mui/material' -import { ShowMessage } from './ShowMessageWithoutModal' -// import { -// setIsLoadingCustom, -// } from '../../state/features/globalSlice' -import { ComposeP, GroupContainer, GroupNameP, MailIconImg, ShowMessageReturnButton, SingleThreadParent, ThreadContainer, ThreadContainerFullWidth } from './Mail-styles' -import { Spacer } from '../../../common/Spacer' -import { threadIdentifier } from './GroupMail' -import LazyLoad from '../../../common/LazyLoad' -import ReturnSVG from '../../../assets/svgs/Return.svg' -import { NewThread } from './NewThread' -import { decryptPublishes } from '../../Chat/GroupAnnouncements' -import { getBaseApi } from '../../../background' -import { getArbitraryEndpointReact, getBaseApiReact } from '../../../App' -interface ThreadProps { - currentThread: any - groupInfo: any - closeThread: () => void - members: any -} - -const getEncryptedResource = async ({name, identifier, secretKey})=> { - - const res = await fetch( - `${getBaseApiReact()}/arbitrary/DOCUMENT/${name}/${identifier}?encoding=base64` - ); - const data = await res.text(); - const response = await decryptPublishes([{ data }], secretKey); - const messageData = response[0]; - return messageData.decryptedData - -} - -export const Thread = ({ - currentThread, - groupInfo, - closeThread, - members, - userInfo, - secretKey, - getSecretKey -}: ThreadProps) => { - const [messages, setMessages] = useState([]) - const [hashMapMailMessages, setHashMapMailMessages] = useState({}) - const secretKeyRef = useRef(null) - - - useEffect(() => { - secretKeyRef.current = secretKey; - }, [secretKey]); - const getIndividualMsg = async (message: any) => { - try { - const responseDataMessage = await getEncryptedResource({identifier: message.identifier, name: message.name, secretKey}) - - - const fullObject = { - ...message, - ...(responseDataMessage || {}), - id: message.identifier - } - setHashMapMailMessages((prev)=> { - return { - ...prev, - [message.identifier]: fullObject - } - }) - } catch (error) {} - } - - const getMailMessages = React.useCallback( - async (groupInfo: any, reset?: boolean, hideAlert?: boolean) => { - try { - if(!hideAlert){ - // dispatch(setIsLoadingCustom('Loading messages')) - - } - let threadId = groupInfo.threadId - - const offset = messages.length - const identifier = `thmsg-${threadId}` - const url = `${getBaseApiReact()}${getArbitraryEndpointReact()}?mode=ALL&service=${threadIdentifier}&identifier=${identifier}&limit=20&includemetadata=false&offset=${offset}&reverse=true&prefix=true` - const response = await fetch(url, { - method: 'GET', - headers: { - 'Content-Type': 'application/json' - } - }) - const responseData = await response.json() - let fullArrayMsg = reset ? [] : [...messages] - let newMessages: any[] = [] - for (const message of responseData) { - const index = fullArrayMsg.findIndex( - (p) => p.identifier === message.identifier - ) - if (index !== -1) { - fullArrayMsg[index] = message - } else { - fullArrayMsg.push(message) - getIndividualMsg(message) - } - } - setMessages(fullArrayMsg) - } catch (error) { - } finally { - if(!hideAlert){ - // dispatch(setIsLoadingCustom(null)) - } - } - }, - [messages, secretKey] - ) - const getMessages = React.useCallback(async () => { - if (!currentThread || !secretKey) return - await getMailMessages(currentThread, true) - }, [getMailMessages, currentThread, secretKey]) - const firstMount = useRef(false) - - const saveTimestamp = useCallback((currentThread: any, username?: string)=> { - if(!currentThread?.threadData?.groupId || !currentThread?.threadId || !username) return - const threadIdForLocalStorage = `qmail_threads_${currentThread?.threadData?.groupId}_${currentThread?.threadId}` - const threads = JSON.parse( - localStorage.getItem(`qmail_threads_viewedtimestamp_${username}`) || "{}" - ); - // Convert to an array of objects with identifier and all fields - let dataArray = Object.entries(threads).map(([identifier, value]) => ({ - identifier, - ...(value as any), - })); - - // Sort the array based on timestamp in descending order - dataArray.sort((a, b) => b.timestamp - a.timestamp); - - // Slice the array to keep only the first 500 elements - let latest500 = dataArray.slice(0, 500); - - // Convert back to the original object format - let latest500Data: any = {}; - latest500.forEach(item => { - const { identifier, ...rest } = item; - latest500Data[identifier] = rest; - }); - latest500Data[threadIdForLocalStorage] = { - timestamp: Date.now(), - } - localStorage.setItem( - `qmail_threads_viewedtimestamp_${username}`, - JSON.stringify(latest500Data) - ); - }, []) - useEffect(() => { - if (currentThread && secretKey) { - getMessages() - firstMount.current = true - // saveTimestamp(currentThread, user.name) - } - }, [ currentThread, secretKey]) - const messageCallback = useCallback((msg: any) => { - // dispatch(addToHashMapMail(msg)) - setMessages((prev) => [msg, ...prev]) - }, []) - - const interval = useRef(null) - - const checkNewMessages = React.useCallback( - async (groupInfo: any) => { - try { - let threadId = groupInfo.threadId - - const identifier = `thmsg-${threadId}` - const url = `${getBaseApiReact()}${getArbitraryEndpointReact()}?mode=ALL&service=${threadIdentifier}&identifier=${identifier}&limit=20&includemetadata=false&offset=${0}&reverse=true&prefix=true` - const response = await fetch(url, { - method: 'GET', - headers: { - 'Content-Type': 'application/json' - } - }) - const responseData = await response.json() - const latestMessage = messages[0] - if (!latestMessage) return - const findMessage = responseData?.findIndex( - (item: any) => item?.identifier === latestMessage?.identifier - ) - let sliceLength = responseData.length - if (findMessage !== -1) { - sliceLength = findMessage - } - const newArray = responseData.slice(0, findMessage).reverse() - let fullArrayMsg = [...messages] - for (const message of newArray) { - try { - - const responseDataMessage = await getEncryptedResource({identifier: message.identifier, name: message.name, secretKey: secretKeyRef.current}) - - const fullObject = { - ...message, - ...(responseDataMessage || {}), - id: message.identifier - } - setHashMapMailMessages((prev)=> { - return { - ...prev, - [message.identifier]: fullObject - } - }) - const index = messages.findIndex( - (p) => p.identifier === fullObject.identifier - ) - if (index !== -1) { - fullArrayMsg[index] = fullObject - } else { - fullArrayMsg.unshift(fullObject) - } - } catch (error) {} - } - setMessages(fullArrayMsg) - } catch (error) { - } finally { - } - }, - [messages] - ) - - const checkNewMessagesFunc = useCallback(() => { - let isCalling = false - interval.current = setInterval(async () => { - if (isCalling) return - isCalling = true - const res = await checkNewMessages(currentThread) - isCalling = false - }, 8000) - }, [checkNewMessages, currentThread]) - - useEffect(() => { - checkNewMessagesFunc() - return () => { - if (interval?.current) { - clearInterval(interval.current) - } - } - }, [checkNewMessagesFunc]) - - - - if (!currentThread) return null - return ( - - - - - - - - {currentThread?.threadData?.title} - - { - setMessages([]) - closeThread() - }}> - - Return to Threads - - - - {messages.map((message) => { - let fullMessage = message - - if (hashMapMailMessages[message?.identifier]) { - fullMessage = hashMapMailMessages[message.identifier] - return - } - - return ( - - - - - - ) - })} - - - {messages.length >= 20 && ( - getMailMessages(currentThread, false, true)}> - - )} - - - ) -} From dcfa7b258e73de6860ca9bc82ccda09de39c831d Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 12 Apr 2025 15:31:10 +0200 Subject: [PATCH 11/31] Refactor sReturn icon and set theme styles --- src/App.tsx | 4812 +++++++++-------- src/Wallets.tsx | 625 ++- src/assets/svgs/CreateThreadIcon.tsx | 14 +- src/assets/svgs/Return.tsx | 27 + src/assets/svgs/SaveIcon.tsx | 5 +- src/assets/svgs/SendNewMessage.tsx | 19 +- src/assets/svgs/StarEmpty.tsx | 24 +- src/assets/svgs/StarFilled.tsx | 2 - src/components/Apps/AppsLibrary.tsx | 383 +- src/components/Apps/AppsLibraryDesktop.tsx | 411 +- src/components/Desktop/DesktopFooter.tsx | 80 +- src/components/Group/Forum/Thread.tsx | 524 +- .../PasswordField/PasswordField.tsx | 89 +- 13 files changed, 3712 insertions(+), 3303 deletions(-) create mode 100644 src/assets/svgs/Return.tsx diff --git a/src/App.tsx b/src/App.tsx index a5dd940..6365115 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -5,8 +5,8 @@ import { useMemo, useRef, useState, -} from "react"; -import { useDropzone } from "react-dropzone"; +} from 'react'; +import { useDropzone } from 'react-dropzone'; import { Box, Button, @@ -19,32 +19,26 @@ import { DialogContentText, DialogTitle, FormControlLabel, - Input, - InputLabel, - Popover, Tooltip, Typography, -} from "@mui/material"; + useTheme, +} from '@mui/material'; import { JsonView, allExpanded, darkStyles } from 'react-json-view-lite'; import 'react-json-view-lite/dist/index.css'; -import { decryptStoredWallet } from "./utils/decryptWallet"; -import { CountdownCircleTimer } from "react-countdown-circle-timer"; -import Logo1 from "./assets/svgs/Logo1.svg"; -import Logo1Dark from "./assets/svgs/Logo1Dark.svg"; -import RefreshIcon from "@mui/icons-material/Refresh"; -import Logo2 from "./assets/svgs/Logo2.svg"; -import Copy from "./assets/svgs/Copy.svg"; -import ltcLogo from "./assets/ltc.png"; +import { decryptStoredWallet } from './utils/decryptWallet'; +import { CountdownCircleTimer } from 'react-countdown-circle-timer'; +import Logo1Dark from './assets/svgs/Logo1Dark.svg'; +import RefreshIcon from '@mui/icons-material/Refresh'; +import Copy from './assets/svgs/Copy.svg'; +import ltcLogo from './assets/ltc.png'; import PersonSearchIcon from '@mui/icons-material/PersonSearch'; -import qortLogo from "./assets/qort.png"; -import { CopyToClipboard } from "react-copy-to-clipboard"; -import Download from "./assets/svgs/Download.svg"; -import Logout from "./assets/svgs/Logout.svg"; -import Return from "./assets/svgs/Return.svg"; +import qortLogo from './assets/qort.png'; +import { CopyToClipboard } from 'react-copy-to-clipboard'; +import Download from './assets/svgs/Download.svg'; +import Logout from './assets/svgs/Logout.svg'; +import { Return } from './assets/svgs/Return.tsx'; import WarningIcon from '@mui/icons-material/Warning'; -import Success from "./assets/svgs/Success.svg"; -import Info from "./assets/svgs/Info.svg"; -import CloseIcon from "@mui/icons-material/Close"; +import Success from './assets/svgs/Success.svg'; import './utils/seedPhrase/RandomSentenceGenerator'; import EngineeringIcon from '@mui/icons-material/Engineering'; import AccountBalanceWalletIcon from '@mui/icons-material/AccountBalanceWallet'; @@ -54,11 +48,11 @@ import { generateRandomSentence, saveFileToDisk, saveSeedPhraseToDisk, -} from "./utils/generateWallet/generateWallet"; -import { kdf } from "./deps/kdf"; -import { generateSaveWalletData } from "./utils/generateWallet/storeWallet"; -import { crypto, walletVersion } from "./constants/decryptWallet"; -import PhraseWallet from "./utils/generateWallet/phrase-wallet"; +} from './utils/generateWallet/generateWallet'; +import { kdf } from './deps/kdf'; +import { generateSaveWalletData } from './utils/generateWallet/storeWallet'; +import { crypto, walletVersion } from './constants/decryptWallet'; +import PhraseWallet from './utils/generateWallet/phrase-wallet'; import { AddressBox, AppContainer, @@ -72,48 +66,44 @@ import { TextItalic, TextP, TextSpan, -} from "./App-styles"; -import { Spacer } from "./common/Spacer"; -import { Loader } from "./components/Loader"; -import { PasswordField, ErrorText } from "./components"; -import { ChatGroup } from "./components/Chat/ChatGroup"; -import { Group, requestQueueMemberNames } from "./components/Group/Group"; -import { TaskManager } from "./components/TaskManager/TaskManger"; -import { useModal } from "./common/useModal"; -import { LoadingButton } from "@mui/lab"; -import { Label } from "./components/Group/AddGroup"; -import { CustomizedSnackbars } from "./components/Snackbar/Snackbar"; -import SettingsIcon from "@mui/icons-material/Settings"; +} from './App-styles'; +import { Spacer } from './common/Spacer'; +import { Loader } from './components/Loader'; +import { PasswordField, ErrorText } from './components'; +import { ChatGroup } from './components/Chat/ChatGroup'; +import { Group, requestQueueMemberNames } from './components/Group/Group'; +import { TaskManager } from './components/TaskManager/TaskManger'; +import { useModal } from './common/useModal'; +import { Label } from './components/Group/AddGroup'; +import { CustomizedSnackbars } from './components/Snackbar/Snackbar'; +import SettingsIcon from '@mui/icons-material/Settings'; import HelpIcon from '@mui/icons-material/Help'; import { cleanUrl, - getFee, getProtocol, getWallets, groupApi, - groupApiLocal, groupApiSocket, - groupApiSocketLocal, storeWallets, -} from "./background"; +} from './background'; import { executeEvent, subscribeToEvent, unsubscribeFromEvent, -} from "./utils/events"; +} from './utils/events'; import { requestQueueCommentCount, requestQueuePublishedAccouncements, -} from "./components/Chat/GroupAnnouncements"; -import { requestQueueGroupJoinRequests } from "./components/Group/GroupJoinRequests"; -import { DrawerComponent } from "./components/Drawer/Drawer"; -import { AddressQRCode } from "./components/AddressQRCode"; -import { Settings } from "./components/Group/Settings"; -import { MainAvatar } from "./components/MainAvatar"; -import { useRetrieveDataLocalStorage } from "./useRetrieveDataLocalStorage"; -import { useQortalGetSaveSettings } from "./useQortalGetSaveSettings"; -import { useRecoilState, useResetRecoilState, useSetRecoilState } from "recoil"; +} from './components/Chat/GroupAnnouncements'; +import { requestQueueGroupJoinRequests } from './components/Group/GroupJoinRequests'; +import { DrawerComponent } from './components/Drawer/Drawer'; +import { AddressQRCode } from './components/AddressQRCode'; +import { Settings } from './components/Group/Settings'; +import { MainAvatar } from './components/MainAvatar'; +import { useRetrieveDataLocalStorage } from './useRetrieveDataLocalStorage'; +import { useQortalGetSaveSettings } from './useQortalGetSaveSettings'; +import { useRecoilState, useResetRecoilState, useSetRecoilState } from 'recoil'; import { canSaveSettingToQdnAtom, enabledDevModeAtom, @@ -129,53 +119,54 @@ import { settingsLocalLastUpdatedAtom, settingsQDNLastUpdatedAtom, sortablePinnedAppsAtom, -} from "./atoms/global"; -import { useAppFullScreen } from "./useAppFullscreen"; -import { NotAuthenticated } from "./ExtStates/NotAuthenticated"; +} from './atoms/global'; +import { useAppFullScreen } from './useAppFullscreen'; +import { NotAuthenticated } from './ExtStates/NotAuthenticated'; import { openIndexedDB, showSaveFilePicker, -} from "./components/Apps/useQortalMessageListener"; -import { fileToBase64 } from "./utils/fileReading"; -import { handleGetFileFromIndexedDB } from "./utils/indexedDB"; -import { CoreSyncStatus } from "./components/CoreSyncStatus"; -import { Wallets } from "./Wallets"; -import { RandomSentenceGenerator } from "./utils/seedPhrase/RandomSentenceGenerator"; -import { useFetchResources } from "./common/useFetchResources"; -import { Tutorials } from "./components/Tutorials/Tutorials"; -import { useHandleTutorials } from "./components/Tutorials/useHandleTutorials"; -import BoundedNumericTextField from "./common/BoundedNumericTextField"; -import { useHandleUserInfo } from "./components/Group/useHandleUserInfo"; -import { Minting } from "./components/Minting/Minting"; -import { isRunningGateway } from "./qortalRequests"; -import { QMailStatus } from "./components/QMailStatus"; -import { GlobalActions } from "./components/GlobalActions/GlobalActions"; -import { useBlockedAddresses } from "./components/Group/useBlockUsers"; -import { WalletIcon } from "./assets/Icons/WalletIcon"; -import { DrawerUserLookup } from "./components/Drawer/DrawerUserLookup"; -import { UserLookup } from "./components/UserLookup.tsx/UserLookup"; -import { RegisterName } from "./components/RegisterName"; -import { BuyQortInformation } from "./components/BuyQortInformation"; -import { QortPayment } from "./components/QortPayment"; -import { GeneralNotifications } from "./components/GeneralNotifications"; -import { PdfViewer } from "./common/PdfViewer"; +} from './components/Apps/useQortalMessageListener'; +import { fileToBase64 } from './utils/fileReading'; +import { handleGetFileFromIndexedDB } from './utils/indexedDB'; +import { CoreSyncStatus } from './components/CoreSyncStatus'; +import { Wallets } from './Wallets'; +import { RandomSentenceGenerator } from './utils/seedPhrase/RandomSentenceGenerator'; +import { useFetchResources } from './common/useFetchResources'; +import { Tutorials } from './components/Tutorials/Tutorials'; +import { useHandleTutorials } from './components/Tutorials/useHandleTutorials'; +import BoundedNumericTextField from './common/BoundedNumericTextField'; +import { useHandleUserInfo } from './components/Group/useHandleUserInfo'; +import { Minting } from './components/Minting/Minting'; +import { isRunningGateway } from './qortalRequests'; +import { QMailStatus } from './components/QMailStatus'; +import { GlobalActions } from './components/GlobalActions/GlobalActions'; +import { useBlockedAddresses } from './components/Group/useBlockUsers'; +import { WalletIcon } from './assets/Icons/WalletIcon'; +import { DrawerUserLookup } from './components/Drawer/DrawerUserLookup'; +import { UserLookup } from './components/UserLookup.tsx/UserLookup'; +import { RegisterName } from './components/RegisterName'; +import { BuyQortInformation } from './components/BuyQortInformation'; +import { QortPayment } from './components/QortPayment'; +import { GeneralNotifications } from './components/GeneralNotifications'; +import { PdfViewer } from './common/PdfViewer'; +import ThemeSelector from './components/Theme/ThemeSelector'; type extStates = - | "not-authenticated" - | "authenticated" - | "send-qort" - | "web-app-request-connection" - | "web-app-request-payment" - | "web-app-request-authentication" - | "download-wallet" - | "create-wallet" - | "transfer-success-regular" - | "transfer-success-request" - | "wallet-dropped" - | "web-app-request-buy-order" - | "buy-order-submitted" - | "wallets" - | "group"; + | 'not-authenticated' + | 'authenticated' + | 'send-qort' + | 'web-app-request-connection' + | 'web-app-request-payment' + | 'web-app-request-authentication' + | 'download-wallet' + | 'create-wallet' + | 'transfer-success-regular' + | 'transfer-success-request' + | 'wallet-dropped' + | 'web-app-request-buy-order' + | 'buy-order-submitted' + | 'wallets' + | 'group'; interface MyContextInterface { txList: any[]; @@ -199,8 +190,8 @@ const defaultValues: MyContextInterface = { onOk: () => {}, show: () => {}, message: { - publishFee: "", - message: "", + publishFee: '', + message: '', }, }; export let isMobile = false; @@ -221,9 +212,9 @@ const isMobileDevice = () => { if (isMobileDevice()) { isMobile = true; - console.log("Running on a mobile device"); + console.log('Running on a mobile device'); } else { - console.log("Running on a desktop"); + console.log('Running on a desktop'); } export const allQueues = { @@ -237,7 +228,7 @@ const controlAllQueues = (action) => { Object.keys(allQueues).forEach((key) => { const val = allQueues[key]; try { - if (typeof val[action] === "function") { + if (typeof val[action] === 'function') { val[action](); } } catch (error) { @@ -246,7 +237,6 @@ const controlAllQueues = (action) => { }); }; - export const clearAllQueues = () => { Object.keys(allQueues).forEach((key) => { const val = allQueues[key]; @@ -259,31 +249,34 @@ export const clearAllQueues = () => { }; export const pauseAllQueues = () => { - controlAllQueues("pause"); - window.sendMessage("pauseAllQueues", {}).catch((error) => { + controlAllQueues('pause'); + window.sendMessage('pauseAllQueues', {}).catch((error) => { console.error( - "Failed to pause all queues:", - error.message || "An error occurred" - ); - }); -}; -export const resumeAllQueues = () => { - controlAllQueues("resume"); - window.sendMessage("resumeAllQueues", {}).catch((error) => { - console.error( - "Failed to resume all queues:", - error.message || "An error occurred" + 'Failed to pause all queues:', + error.message || 'An error occurred' ); }); }; +export const resumeAllQueues = () => { + controlAllQueues('resume'); + window.sendMessage('resumeAllQueues', {}).catch((error) => { + console.error( + 'Failed to resume all queues:', + error.message || 'An error occurred' + ); + }); +}; const defaultValuesGlobal = { openTutorialModal: null, - setOpenTutorialModal: ()=> {} -} + setOpenTutorialModal: () => {}, +}; + export const MyContext = createContext(defaultValues); -export const GlobalContext = createContext(defaultValuesGlobal); + +export const GlobalContext = + createContext(defaultValuesGlobal); export let globalApiKey: string | null = null; @@ -313,6 +306,7 @@ export const getArbitraryEndpointReact = () => { return `/arbitrary/resources/searchsimple`; } }; + export const getBaseApiReactSocket = (customApi?: string) => { if (customApi) { return customApi; @@ -320,16 +314,17 @@ export const getBaseApiReactSocket = (customApi?: string) => { if (globalApiKey?.url) { return `${ - getProtocol(globalApiKey?.url) === "http" ? "ws://" : "wss://" + getProtocol(globalApiKey?.url) === 'http' ? 'ws://' : 'wss://' }${cleanUrl(globalApiKey?.url)}`; } else { return groupApiSocket; } }; + export const isMainWindow = true; function App() { - const [extState, setExtstate] = useState("not-authenticated"); - const [desktopViewMode, setDesktopViewMode] = useState("home"); + const [extState, setExtstate] = useState('not-authenticated'); + const [desktopViewMode, setDesktopViewMode] = useState('home'); const [backupjson, setBackupjson] = useState(null); const [rawWallet, setRawWallet] = useState(null); @@ -338,46 +333,53 @@ function App() { const [decryptedWallet, setdecryptedWallet] = useState(null); const [requestConnection, setRequestConnection] = useState(null); const [requestBuyOrder, setRequestBuyOrder] = useState(null); - const [authenticatedMode, setAuthenticatedMode] = useState("qort"); + const [authenticatedMode, setAuthenticatedMode] = useState('qort'); const [requestAuthentication, setRequestAuthentication] = useState(null); const [userInfo, setUserInfo] = useState(null); const [balance, setBalance] = useState(null); const [ltcBalance, setLtcBalance] = useState(null); - const [paymentTo, setPaymentTo] = useState(""); + const [paymentTo, setPaymentTo] = useState(''); const [paymentAmount, setPaymentAmount] = useState(0); - const [paymentPassword, setPaymentPassword] = useState(""); - const [sendPaymentError, setSendPaymentError] = useState(""); - const [sendPaymentSuccess, setSendPaymentSuccess] = useState(""); + const [paymentPassword, setPaymentPassword] = useState(''); + const [sendPaymentError, setSendPaymentError] = useState(''); + const [sendPaymentSuccess, setSendPaymentSuccess] = useState(''); const [countdown, setCountdown] = useState(null); const [walletToBeDownloaded, setWalletToBeDownloaded] = useState(null); const [walletToBeDownloadedPassword, setWalletToBeDownloadedPassword] = - useState(""); + useState(''); const [isMain, setIsMain] = useState(true); const isMainRef = useRef(false); - const [authenticatePassword, setAuthenticatePassword] = useState(""); + const [authenticatePassword, setAuthenticatePassword] = useState(''); const [sendqortState, setSendqortState] = useState(null); const [isLoading, setIsLoading] = useState(false); const [isLoadingSendCoin, setIsLoadingSendCoin] = useState(false); + const theme = useTheme(); const [ walletToBeDownloadedPasswordConfirm, setWalletToBeDownloadedPasswordConfirm, - ] = useState(""); + ] = useState(''); const [walletToBeDownloadedError, setWalletToBeDownloadedError] = - useState(""); + useState(''); const [walletToBeDecryptedError, setWalletToBeDecryptedError] = - useState(""); + useState(''); const [txList, setTxList] = useState([]); const [memberGroups, setMemberGroups] = useState([]); const [isFocused, setIsFocused] = useState(true); const [hasSettingsChanged, setHasSettingsChanged] = useRecoilState( hasSettingsChangedAtom ); - const balanceSetIntervalRef = useRef(null) - const {downloadResource} = useFetchResources() - const holdRefExtState = useRef("not-authenticated"); + const balanceSetIntervalRef = useRef(null); + const { downloadResource } = useFetchResources(); + const holdRefExtState = useRef('not-authenticated'); const isFocusedRef = useRef(true); - const {showTutorial, openTutorialModal, shownTutorialsInitiated, setOpenTutorialModal, hasSeenGettingStarted} = useHandleTutorials() + const { + showTutorial, + openTutorialModal, + shownTutorialsInitiated, + setOpenTutorialModal, + hasSeenGettingStarted, + } = useHandleTutorials(); const { isShow, onCancel, onOk, show, message } = useModal(); const { isShow: isShowUnsavedChanges, @@ -393,7 +395,7 @@ function App() { show: showInfo, message: messageInfo, } = useModal(); - + const { onCancel: onCancelQortalRequest, onOk: onOkQortalRequest, @@ -409,75 +411,80 @@ function App() { message: messageQortalRequestExtension, } = useModal(); - const [isRunningPublicNode, setIsRunningPublicNode] = useState(false) + const [isRunningPublicNode, setIsRunningPublicNode] = useState(false); const [infoSnack, setInfoSnack] = useState(null); const [openSnack, setOpenSnack] = useState(false); const [hasLocalNode, setHasLocalNode] = useState(false); const [isOpenDrawerProfile, setIsOpenDrawerProfile] = useState(false); - const [isOpenDrawerLookup, setIsOpenDrawerLookup] = useState(false) - const [apiKey, setApiKey] = useState(""); + const [isOpenDrawerLookup, setIsOpenDrawerLookup] = useState(false); + const [apiKey, setApiKey] = useState(''); const [isOpenSendQort, setIsOpenSendQort] = useState(false); const [isOpenSendQortSuccess, setIsOpenSendQortSuccess] = useState(false); - const [rootHeight, setRootHeight] = useState("100%"); - const {isUserBlocked, + const [rootHeight, setRootHeight] = useState('100%'); + const { + isUserBlocked, addToBlockList, - removeBlockFromList, getAllBlockedUsers} = useBlockedAddresses() - const [currentNode, setCurrentNode] = useState({ - url: "http://127.0.0.1:12391", - }); - const [useLocalNode, setUseLocalNode] = useState(false); - - const [confirmRequestRead, setConfirmRequestRead] = useState(false); + removeBlockFromList, + getAllBlockedUsers, + } = useBlockedAddresses(); + const [currentNode, setCurrentNode] = useState({ + url: 'http://127.0.0.1:12391', + }); + const [useLocalNode, setUseLocalNode] = useState(false); + + const [confirmRequestRead, setConfirmRequestRead] = useState(false); const [isSettingsOpen, setIsSettingsOpen] = useState(false); - const [showSeed, setShowSeed] = useState(false) - const [creationStep, setCreationStep] = useState(1) - const {getIndividualUserInfo} = useHandleUserInfo() + const [showSeed, setShowSeed] = useState(false); + const [creationStep, setCreationStep] = useState(1); + const { getIndividualUserInfo } = useHandleUserInfo(); const qortalRequestCheckbox1Ref = useRef(null); useRetrieveDataLocalStorage(userInfo?.address); - useQortalGetSaveSettings(userInfo?.name, extState === "authenticated"); + useQortalGetSaveSettings(userInfo?.name, extState === 'authenticated'); const [fullScreen, setFullScreen] = useRecoilState(fullScreenAtom); const [isEnabledDevMode, setIsEnabledDevMode] = useRecoilState(enabledDevModeAtom); - const setIsDisabledEditorEnter = useSetRecoilState(isDisabledEditorEnterAtom) - const [isOpenMinting, setIsOpenMinting] = useState(false) + const setIsDisabledEditorEnter = useSetRecoilState(isDisabledEditorEnterAtom); + const [isOpenMinting, setIsOpenMinting] = useState(false); const { toggleFullScreen } = useAppFullScreen(setFullScreen); - const generatorRef = useRef(null) - const exportSeedphrase = ()=> { - const seedPhrase = generatorRef.current.parsedString - saveSeedPhraseToDisk(seedPhrase) - } + const generatorRef = useRef(null); + const exportSeedphrase = () => { + const seedPhrase = generatorRef.current.parsedString; + saveSeedPhraseToDisk(seedPhrase); + }; const passwordRef = useRef(null); useEffect(() => { - if (extState === "wallet-dropped" && passwordRef.current) { + if (extState === 'wallet-dropped' && passwordRef.current) { passwordRef.current.focus(); } }, [extState]); useEffect(() => { - const isDevModeFromStorage = localStorage.getItem("isEnabledDevMode"); + const isDevModeFromStorage = localStorage.getItem('isEnabledDevMode'); if (isDevModeFromStorage) { setIsEnabledDevMode(JSON.parse(isDevModeFromStorage)); } }, []); - useEffect(()=> { - isRunningGateway().then((res)=> { - setIsRunningPublicNode(res) - }).catch((error)=> { - console.error(error) + useEffect(() => { + isRunningGateway() + .then((res) => { + setIsRunningPublicNode(res); }) - }, [extState]) + .catch((error) => { + console.error(error); + }); + }, [extState]); - useEffect(()=> { - if(!shownTutorialsInitiated) return - if(extState === 'not-authenticated'){ - showTutorial('create-account') - } else if(extState === "create-wallet" && walletToBeDownloaded){ - showTutorial('important-information') - } else if(extState === "authenticated"){ - showTutorial('getting-started') + useEffect(() => { + if (!shownTutorialsInitiated) return; + if (extState === 'not-authenticated') { + showTutorial('create-account'); + } else if (extState === 'create-wallet' && walletToBeDownloaded) { + showTutorial('important-information'); + } else if (extState === 'authenticated') { + showTutorial('getting-started'); } - }, [extState, walletToBeDownloaded, shownTutorialsInitiated]) + }, [extState, walletToBeDownloaded, shownTutorialsInitiated]); useEffect(() => { // Attach a global event listener for double-click @@ -486,12 +493,12 @@ function App() { }; // Add the event listener to the root HTML document - document.documentElement.addEventListener("dblclick", handleDoubleClick); + document.documentElement.addEventListener('dblclick', handleDoubleClick); // Clean up the event listener on unmount return () => { document.documentElement.removeEventListener( - "dblclick", + 'dblclick', handleDoubleClick ); }; @@ -500,7 +507,9 @@ function App() { const resetAtomSortablePinnedAppsAtom = useResetRecoilState( sortablePinnedAppsAtom ); - const resetAtomIsUsingImportExportSettingsAtom = useResetRecoilState(isUsingImportExportSettingsAtom) + const resetAtomIsUsingImportExportSettingsAtom = useResetRecoilState( + isUsingImportExportSettingsAtom + ); const resetAtomCanSaveSettingToQdnAtom = useResetRecoilState( canSaveSettingToQdnAtom ); @@ -511,22 +520,25 @@ function App() { settingsLocalLastUpdatedAtom ); const resetAtomOldPinnedAppsAtom = useResetRecoilState(oldPinnedAppsAtom); - const resetAtomQMailLastEnteredTimestampAtom = useResetRecoilState(qMailLastEnteredTimestampAtom) - const resetAtomMailsAtom = useResetRecoilState(mailsAtom) - const resetGroupPropertiesAtom = useResetRecoilState(groupsPropertiesAtom) - const resetLastPaymentSeenTimestampAtom = useResetRecoilState(lastPaymentSeenTimestampAtom) + const resetAtomQMailLastEnteredTimestampAtom = useResetRecoilState( + qMailLastEnteredTimestampAtom + ); + const resetAtomMailsAtom = useResetRecoilState(mailsAtom); + const resetGroupPropertiesAtom = useResetRecoilState(groupsPropertiesAtom); + const resetLastPaymentSeenTimestampAtom = useResetRecoilState( + lastPaymentSeenTimestampAtom + ); const resetAllRecoil = () => { resetAtomSortablePinnedAppsAtom(); resetAtomCanSaveSettingToQdnAtom(); resetAtomSettingsQDNLastUpdatedAtom(); resetAtomSettingsLocalLastUpdatedAtom(); resetAtomOldPinnedAppsAtom(); - resetAtomIsUsingImportExportSettingsAtom() - resetAtomQMailLastEnteredTimestampAtom() - resetAtomMailsAtom() - resetGroupPropertiesAtom() - resetLastPaymentSeenTimestampAtom() - + resetAtomIsUsingImportExportSettingsAtom(); + resetAtomQMailLastEnteredTimestampAtom(); + resetAtomMailsAtom(); + resetGroupPropertiesAtom(); + resetLastPaymentSeenTimestampAtom(); }; useEffect(() => { if (!isMobile) return; @@ -536,21 +548,21 @@ function App() { ? window.visualViewport.height : window.innerHeight; // Set the height to the root element (usually #root) - document.getElementById("root").style.height = height + "px"; - setRootHeight(height + "px"); + document.getElementById('root').style.height = height + 'px'; + setRootHeight(height + 'px'); }; // Set the initial height resetHeight(); // Add event listeners for resize and visualViewport changes - window.addEventListener("resize", resetHeight); - window.visualViewport?.addEventListener("resize", resetHeight); + window.addEventListener('resize', resetHeight); + window.visualViewport?.addEventListener('resize', resetHeight); // Clean up the event listeners when the component unmounts return () => { - window.removeEventListener("resize", resetHeight); - window.visualViewport?.removeEventListener("resize", resetHeight); + window.removeEventListener('resize', resetHeight); + window.visualViewport?.removeEventListener('resize', resetHeight); }; }, []); const handleSetGlobalApikey = (key) => { @@ -560,48 +572,46 @@ function App() { try { setIsLoading(true); window - .sendMessage("getApiKey") - .then((response) => { - if (response) { - handleSetGlobalApikey(response); - setApiKey(response); - } - }) - .catch((error) => { - console.error( - "Failed to get API key:", - error?.message || "An error occurred" - ); - }).finally(()=> { - window - .sendMessage("getWalletInfo") + .sendMessage('getApiKey') .then((response) => { - if (response && response?.walletInfo) { - setRawWallet(response?.walletInfo); - if ( - holdRefExtState.current === "web-app-request-payment" || - holdRefExtState.current === "web-app-request-connection" || - holdRefExtState.current === "web-app-request-buy-order" - ) - return; - if (response?.hasKeyPair) { - setExtstate("authenticated"); - } else { - setExtstate("wallet-dropped"); - } + if (response) { + handleSetGlobalApikey(response); + setApiKey(response); } }) .catch((error) => { - console.error("Failed to get wallet info:", error); + console.error( + 'Failed to get API key:', + error?.message || 'An error occurred' + ); + }) + .finally(() => { + window + .sendMessage('getWalletInfo') + .then((response) => { + if (response && response?.walletInfo) { + setRawWallet(response?.walletInfo); + if ( + holdRefExtState.current === 'web-app-request-payment' || + holdRefExtState.current === 'web-app-request-connection' || + holdRefExtState.current === 'web-app-request-buy-order' + ) + return; + if (response?.hasKeyPair) { + setExtstate('authenticated'); + } else { + setExtstate('wallet-dropped'); + } + } + }) + .catch((error) => { + console.error('Failed to get wallet info:', error); + }); }); - }) } catch (error) { - } finally { setIsLoading(false); - } - }, []); useEffect(() => { if (extState) { @@ -609,19 +619,17 @@ function App() { } }, [extState]); - useEffect(()=> { + useEffect(() => { try { - const val = localStorage.getItem('settings-disable-editor-enter'); - if(val){ - const parsedVal = JSON.parse(val) - if(parsedVal === false || parsedVal === true){ - setIsDisabledEditorEnter(parsedVal) + const val = localStorage.getItem('settings-disable-editor-enter'); + if (val) { + const parsedVal = JSON.parse(val); + if (parsedVal === false || parsedVal === true) { + setIsDisabledEditorEnter(parsedVal); } } - } catch (error) { - - } - }, []) + } catch (error) {} + }, []); useEffect(() => { isFocusedRef.current = isFocused; @@ -648,12 +656,12 @@ function App() { // }, [extState]); const address = useMemo(() => { - if (!rawWallet?.address0) return ""; + if (!rawWallet?.address0) return ''; return rawWallet.address0; }, [rawWallet]); const { getRootProps, getInputProps } = useDropzone({ accept: { - "application/json": [".json"], // Only accept JSON files + 'application/json': ['.json'], // Only accept JSON files }, maxFiles: 1, onDrop: async (acceptedFiles) => { @@ -661,8 +669,8 @@ function App() { const fileContents = await new Promise((resolve, reject) => { const reader = new FileReader(); - reader.onabort = () => reject("File reading was aborted"); - reader.onerror = () => reject("File reading has failed"); + reader.onabort = () => reject('File reading was aborted'); + reader.onerror = () => reject('File reading has failed'); reader.onload = () => { // Resolve the promise with the reader result when reading completes resolve(reader.result); @@ -676,26 +684,26 @@ function App() { let pf: any; try { - if (typeof fileContents !== "string") return; + if (typeof fileContents !== 'string') return; pf = JSON.parse(fileContents); } catch (e) {} try { const requiredFields = [ - "address0", - "salt", - "iv", - "version", - "encryptedSeed", - "mac", - "kdfThreads", + 'address0', + 'salt', + 'iv', + 'version', + 'encryptedSeed', + 'mac', + 'kdfThreads', ]; for (const field of requiredFields) { - if (!(field in pf)) throw new Error(field + " not found in JSON"); + if (!(field in pf)) throw new Error(field + ' not found in JSON'); } setRawWallet(pf); // setExtstate("authenticated"); - setExtstate("wallet-dropped"); + setExtstate('wallet-dropped'); setdecryptedWallet(null); } catch (e) { console.log(e); @@ -724,57 +732,57 @@ function App() { }; }; - - const balanceSetInterval = ()=> { + const balanceSetInterval = () => { try { - if(balanceSetIntervalRef?.current){ + if (balanceSetIntervalRef?.current) { clearInterval(balanceSetIntervalRef?.current); } let isCalling = false; - balanceSetIntervalRef.current = setInterval(async () => { + balanceSetIntervalRef.current = setInterval(async () => { if (isCalling) return; isCalling = true; window - .sendMessage("balance") - .then((response) => { - if (!response?.error && !isNaN(+response)) { - setBalance(response); - } - isCalling = false; - }) - .catch((error) => { - console.error("Failed to get balance:", error); - isCalling = false; - }); + .sendMessage('balance') + .then((response) => { + if (!response?.error && !isNaN(+response)) { + setBalance(response); + } + isCalling = false; + }) + .catch((error) => { + console.error('Failed to get balance:', error); + isCalling = false; + }); }, 40000); } catch (error) { - console.error(error) + console.error(error); } - } + }; const getBalanceFunc = () => { setQortBalanceLoading(true); window - .sendMessage("balance") + .sendMessage('balance') .then((response) => { if (!response?.error && !isNaN(+response)) { setBalance(response); } - + setQortBalanceLoading(false); }) .catch((error) => { - console.error("Failed to get balance:", error); + console.error('Failed to get balance:', error); setQortBalanceLoading(false); - }).finally(()=> { - balanceSetInterval() + }) + .finally(() => { + balanceSetInterval(); }); }; const getLtcBalanceFunc = () => { setLtcBalanceLoading(true); window - .sendMessage("ltcBalance") + .sendMessage('ltcBalance') .then((response) => { if (!response?.error && !isNaN(+response)) { setLtcBalance(response); @@ -782,11 +790,10 @@ function App() { setLtcBalanceLoading(false); }) .catch((error) => { - console.error("Failed to get LTC balance:", error); + console.error('Failed to get LTC balance:', error); setLtcBalanceLoading(false); }); }; - const clearAllStates = () => { setRequestConnection(null); @@ -794,19 +801,19 @@ function App() { }; const qortalRequestPermissonFromExtension = async (message, event) => { - if (message.action === "QORTAL_REQUEST_PERMISSION") { + if (message.action === 'QORTAL_REQUEST_PERMISSION') { try { if (message?.payload?.checkbox1) { qortalRequestCheckbox1Ref.current = message?.payload?.checkbox1?.value || false; } - setConfirmRequestRead(false) + setConfirmRequestRead(false); await showQortalRequestExtension(message?.payload); if (qortalRequestCheckbox1Ref.current) { event.source.postMessage( { - action: "QORTAL_REQUEST_PERMISSION_RESPONSE", + action: 'QORTAL_REQUEST_PERMISSION_RESPONSE', requestId: message?.requestId, result: { accepted: true, @@ -819,7 +826,7 @@ function App() { } event.source.postMessage( { - action: "QORTAL_REQUEST_PERMISSION_RESPONSE", + action: 'QORTAL_REQUEST_PERMISSION_RESPONSE', requestId: message?.requestId, result: { accepted: true, @@ -830,7 +837,7 @@ function App() { } catch (error) { event.source.postMessage( { - action: "QORTAL_REQUEST_PERMISSION_RESPONSE", + action: 'QORTAL_REQUEST_PERMISSION_RESPONSE', requestId: message?.requestId, result: { accepted: false, @@ -850,43 +857,43 @@ function App() { } const message = event.data; - if (message?.action === "CHECK_FOCUS") { + if (message?.action === 'CHECK_FOCUS') { event.source.postMessage( - { action: "CHECK_FOCUS_RESPONSE", isFocused: isFocusedRef.current }, + { action: 'CHECK_FOCUS_RESPONSE', isFocused: isFocusedRef.current }, event.origin ); - } else if (message.action === "NOTIFICATION_OPEN_DIRECT") { - executeEvent("openDirectMessage", { + } else if (message.action === 'NOTIFICATION_OPEN_DIRECT') { + executeEvent('openDirectMessage', { from: message.payload.from, }); - } else if (message.action === "NOTIFICATION_OPEN_GROUP") { - executeEvent("openGroupMessage", { + } else if (message.action === 'NOTIFICATION_OPEN_GROUP') { + executeEvent('openGroupMessage', { from: message.payload.from, }); - } else if (message.action === "NOTIFICATION_OPEN_ANNOUNCEMENT_GROUP") { - executeEvent("openGroupAnnouncement", { + } else if (message.action === 'NOTIFICATION_OPEN_ANNOUNCEMENT_GROUP') { + executeEvent('openGroupAnnouncement', { from: message.payload.from, }); - } else if (message.action === "NOTIFICATION_OPEN_THREAD_NEW_POST") { - executeEvent("openThreadNewPost", { + } else if (message.action === 'NOTIFICATION_OPEN_THREAD_NEW_POST') { + executeEvent('openThreadNewPost', { data: message.payload.data, }); } else if ( - message.action === "QORTAL_REQUEST_PERMISSION" && + message.action === 'QORTAL_REQUEST_PERMISSION' && message?.isFromExtension ) { qortalRequestPermissonFromExtension(message, event); - } else if (message?.action === "getFileFromIndexedDB") { + } else if (message?.action === 'getFileFromIndexedDB') { handleGetFileFromIndexedDB(event); } }; // Attach the event listener - window.addEventListener("message", messageHandler); + window.addEventListener('message', messageHandler); // Clean up the event listener on component unmount return () => { - window.removeEventListener("message", messageHandler); + window.removeEventListener('message', messageHandler); }; }, []); @@ -906,7 +913,6 @@ function App() { // REMOVED FOR MOBILE APP }; - const getUserInfo = useCallback(async (useTimer?: boolean) => { try { if (useTimer) { @@ -917,14 +923,14 @@ function App() { }); } window - .sendMessage("userInfo") + .sendMessage('userInfo') .then((response) => { if (response && !response.error) { setUserInfo(response); } }) .catch((error) => { - console.error("Failed to get user info:", error); + console.error('Failed to get user info:', error); }); getBalanceFunc(); @@ -938,13 +944,13 @@ function App() { useEffect(() => { return () => { - console.log("exit"); + console.log('exit'); }; }, []); useEffect(() => { if ( - authenticatedMode === "ltc" && + authenticatedMode === 'ltc' && !ltcBalanceLoading && ltcBalance === null ) { @@ -954,9 +960,9 @@ function App() { const confirmPasswordToDownload = async () => { try { - setWalletToBeDownloadedError(""); + setWalletToBeDownloadedError(''); if (!walletToBeDownloadedPassword) { - setSendPaymentError("Please enter your password"); + setSendPaymentError('Please enter your password'); return; } setIsLoading(true); @@ -985,40 +991,41 @@ function App() { } }; - const saveWalletToLocalStorage = async (newWallet)=> { + const saveWalletToLocalStorage = async (newWallet) => { try { - getWallets().then((res)=> { - - if(res && Array.isArray(res)){ - const wallets = [...res, newWallet] - storeWallets(wallets) - } else { - storeWallets([newWallet]) - } - setIsLoading(false) - }).catch((error)=> { - console.error(error) - setIsLoading(false) - }) + getWallets() + .then((res) => { + if (res && Array.isArray(res)) { + const wallets = [...res, newWallet]; + storeWallets(wallets); + } else { + storeWallets([newWallet]); + } + setIsLoading(false); + }) + .catch((error) => { + console.error(error); + setIsLoading(false); + }); } catch (error) { - console.error(error) + console.error(error); } - } + }; const createAccountFunc = async () => { try { if (!walletToBeDownloadedPassword) { - setWalletToBeDownloadedError("Please enter a password"); + setWalletToBeDownloadedError('Please enter a password'); return; } if (!walletToBeDownloadedPasswordConfirm) { - setWalletToBeDownloadedError("Please confirm your password"); + setWalletToBeDownloadedError('Please confirm your password'); return; } if ( walletToBeDownloadedPasswordConfirm !== walletToBeDownloadedPassword ) { - setWalletToBeDownloadedError("Password fields do not match!"); + setWalletToBeDownloadedError('Password fields do not match!'); return; } setIsLoading(true); @@ -1034,21 +1041,21 @@ function App() { () => {} ); window - .sendMessage("decryptWallet", { + .sendMessage('decryptWallet', { password: walletToBeDownloadedPassword, wallet, }) .then((response) => { if (response && !response.error) { setRawWallet(wallet); - saveWalletToLocalStorage(wallet) + saveWalletToLocalStorage(wallet); setWalletToBeDownloaded({ wallet, qortAddress: wallet.address0, }); window - .sendMessage("userInfo") + .sendMessage('userInfo') .then((response2) => { setIsLoading(false); if (response2 && !response2.error) { @@ -1057,7 +1064,7 @@ function App() { }) .catch((error) => { setIsLoading(false); - console.error("Failed to get user info:", error); + console.error('Failed to get user info:', error); }); getBalanceFunc(); @@ -1068,7 +1075,7 @@ function App() { }) .catch((error) => { setIsLoading(false); - console.error("Failed to decrypt wallet:", error); + console.error('Failed to decrypt wallet:', error); }); } catch (error: any) { setWalletToBeDownloadedError(error?.message); @@ -1081,50 +1088,49 @@ function App() { if (hasSettingsChanged) { await showUnsavedChanges({ message: - "Your settings have changed. If you logout you will lose your changes. Click on the save button in the header to keep your changed settings.", + 'Your settings have changed. If you logout you will lose your changes. Click on the save button in the header to keep your changed settings.', }); - } else if(extState === 'authenticated') { + } else if (extState === 'authenticated') { await showUnsavedChanges({ - message: - "Are you sure you would like to logout?", + message: 'Are you sure you would like to logout?', }); } window - .sendMessage("logout", {}) + .sendMessage('logout', {}) .then((response) => { if (response) { - executeEvent("logout-event", {}); + executeEvent('logout-event', {}); resetAllStates(); } }) .catch((error) => { console.error( - "Failed to log out:", - error.message || "An error occurred" + 'Failed to log out:', + error.message || 'An error occurred' ); }); } catch (error) {} }; const returnToMain = () => { - setPaymentTo(""); + setPaymentTo(''); setPaymentAmount(0); - setPaymentPassword(""); - setSendPaymentError(""); - setSendPaymentSuccess(""); + setPaymentPassword(''); + setSendPaymentError(''); + setSendPaymentSuccess(''); setCountdown(null); setWalletToBeDownloaded(null); - setWalletToBeDownloadedPassword(""); - setShowSeed(false) - setCreationStep(1) - setExtstate("authenticated"); + setWalletToBeDownloadedPassword(''); + setShowSeed(false); + setCreationStep(1); + setExtstate('authenticated'); setIsOpenSendQort(false); setIsOpenSendQortSuccess(false); }; const resetAllStates = () => { - setExtstate("not-authenticated"); - setAuthenticatedMode("qort"); + setExtstate('not-authenticated'); + setAuthenticatedMode('qort'); setBackupjson(null); setRawWallet(null); setdecryptedWallet(null); @@ -1134,25 +1140,25 @@ function App() { setUserInfo(null); setBalance(null); setLtcBalance(null); - setPaymentTo(""); + setPaymentTo(''); setPaymentAmount(0); - setPaymentPassword(""); - setSendPaymentError(""); - setSendPaymentSuccess(""); + setPaymentPassword(''); + setSendPaymentError(''); + setSendPaymentSuccess(''); setCountdown(null); setWalletToBeDownloaded(null); - setWalletToBeDownloadedPassword(""); - setShowSeed(false) - setCreationStep(1) + setWalletToBeDownloadedPassword(''); + setShowSeed(false); + setCreationStep(1); - setWalletToBeDownloadedPasswordConfirm(""); - setWalletToBeDownloadedError(""); + setWalletToBeDownloadedPasswordConfirm(''); + setWalletToBeDownloadedError(''); setSendqortState(null); setHasLocalNode(false); setTxList([]); setMemberGroups([]); resetAllRecoil(); - if(balanceSetIntervalRef?.current){ + if (balanceSetIntervalRef?.current) { clearInterval(balanceSetIntervalRef?.current); } }; @@ -1165,7 +1171,7 @@ function App() { const authenticateWallet = async () => { try { setIsLoading(true); - setWalletToBeDecryptedError(""); + setWalletToBeDecryptedError(''); await new Promise((res) => { setTimeout(() => { res(); @@ -1173,7 +1179,7 @@ function App() { }); window .sendMessage( - "decryptWallet", + 'decryptWallet', { password: authenticatePassword, wallet: rawWallet, @@ -1182,12 +1188,12 @@ function App() { ) .then((response) => { if (response && !response.error) { - setAuthenticatePassword(""); - setExtstate("authenticated"); - setWalletToBeDecryptedError(""); + setAuthenticatePassword(''); + setExtstate('authenticated'); + setWalletToBeDecryptedError(''); window - .sendMessage("userInfo") + .sendMessage('userInfo') .then((response) => { setIsLoading(false); if (response && !response.error) { @@ -1196,20 +1202,20 @@ function App() { }) .catch((error) => { setIsLoading(false); - console.error("Failed to get user info:", error); + console.error('Failed to get user info:', error); }); getBalanceFunc(); window - .sendMessage("getWalletInfo") + .sendMessage('getWalletInfo') .then((response) => { if (response && response.walletInfo) { setRawWallet(response.walletInfo); } }) .catch((error) => { - console.error("Failed to get wallet info:", error); + console.error('Failed to get wallet info:', error); }); } else if (response?.error) { setIsLoading(false); @@ -1218,10 +1224,10 @@ function App() { }) .catch((error) => { setIsLoading(false); - console.error("Failed to decrypt wallet:", error); + console.error('Failed to decrypt wallet:', error); }); } catch (error) { - setWalletToBeDecryptedError("Unable to authenticate. Wrong password"); + setWalletToBeDecryptedError('Unable to authenticate. Wrong password'); } }; @@ -1231,10 +1237,10 @@ function App() { const handleFocus = () => { setIsFocused(true); if (isMobile) { - window.sendMessage("clearAllNotifications", {}).catch((error) => { + window.sendMessage('clearAllNotifications', {}).catch((error) => { console.error( - "Failed to clear notifications:", - error.message || "An error occurred" + 'Failed to clear notifications:', + error.message || 'An error occurred' ); }); } @@ -1246,18 +1252,18 @@ function App() { }; // Attach the event listeners - window.addEventListener("focus", handleFocus); - window.addEventListener("blur", handleBlur); + window.addEventListener('focus', handleFocus); + window.addEventListener('blur', handleBlur); // Optionally, listen for visibility changes const handleVisibilityChange = () => { - if (document.visibilityState === "visible") { + if (document.visibilityState === 'visible') { setIsFocused(true); if (isMobile) { - window.sendMessage("clearAllNotifications", {}).catch((error) => { + window.sendMessage('clearAllNotifications', {}).catch((error) => { console.error( - "Failed to clear notifications:", - error.message || "An error occurred" + 'Failed to clear notifications:', + error.message || 'An error occurred' ); }); } @@ -1266,13 +1272,13 @@ function App() { } }; - document.addEventListener("visibilitychange", handleVisibilityChange); + document.addEventListener('visibilitychange', handleVisibilityChange); // Cleanup the event listeners on component unmount return () => { - window.removeEventListener("focus", handleFocus); - window.removeEventListener("blur", handleBlur); - document.removeEventListener("visibilitychange", handleVisibilityChange); + window.removeEventListener('focus', handleFocus); + window.removeEventListener('blur', handleBlur); + document.removeEventListener('visibilitychange', handleVisibilityChange); }; }, []); @@ -1282,15 +1288,15 @@ function App() { setOpenSnack(true); setInfoSnack({ type, - message + message, }); }; useEffect(() => { - subscribeToEvent("openGlobalSnackBar", openGlobalSnackBarFunc); + subscribeToEvent('openGlobalSnackBar', openGlobalSnackBarFunc); return () => { - unsubscribeFromEvent("openGlobalSnackBar", openGlobalSnackBarFunc); + unsubscribeFromEvent('openGlobalSnackBar', openGlobalSnackBarFunc); }; }, []); @@ -1302,268 +1308,285 @@ function App() { }; useEffect(() => { - subscribeToEvent("openPaymentInternal", openPaymentInternal); + subscribeToEvent('openPaymentInternal', openPaymentInternal); return () => { - unsubscribeFromEvent("openPaymentInternal", openPaymentInternal); + unsubscribeFromEvent('openPaymentInternal', openPaymentInternal); }; }, []); - - - const renderProfileLeft = ()=> { - - return - - - {authenticatedMode === "qort" && ( - LITECOIN WALLET} - placement="left" - arrow - sx={{ fontSize: "24" }} - slotProps={{ - tooltip: { - sx: { - color: "#ffffff", - backgroundColor: "#444444", - }, + const renderProfileLeft = () => { + return ( + + + + {authenticatedMode === 'qort' && ( + + LITECOIN WALLET + + } + placement="left" + arrow + sx={{ fontSize: '24' }} + slotProps={{ + tooltip: { + sx: { + color: '#ffffff', + backgroundColor: '#444444', }, - arrow: { - sx: { - color: "#444444", - }, + }, + arrow: { + sx: { + color: '#444444', }, - }} - > - { - - setAuthenticatedMode("ltc"); - }} - src={ltcLogo} - style={{ - cursor: "pointer", - width: "20px", - height: "auto", - }} - /> - - )} - {authenticatedMode === "ltc" && ( - QORTAL WALLET} - placement="left" - arrow - sx={{ fontSize: "24" }} - slotProps={{ - tooltip: { - sx: { - color: "#ffffff", - backgroundColor: "#444444", - }, - }, - arrow: { - sx: { - color: "#444444", - }, - }, - }} - > - { - setAuthenticatedMode("qort"); - }} - src={qortLogo} - style={{ - cursor: "pointer", - width: "20px", - height: "auto", - }} - /> - - )} - - - - {authenticatedMode === "ltc" ? ( - <> - - - - - {rawWallet?.ltcAddress?.slice(0, 6)}... - {rawWallet?.ltcAddress?.slice(-4)} - - - - {ltcBalanceLoading && ( - - )} - {!isNaN(+ltcBalance) && !ltcBalanceLoading && ( - - - {ltcBalance} LTC - - { + setAuthenticatedMode('ltc'); + }} + src={ltcLogo} + style={{ + cursor: 'pointer', + width: '20px', + height: 'auto', + }} + /> + + )} + {authenticatedMode === 'ltc' && ( + + QORTAL WALLET + + } + placement="left" + arrow + sx={{ fontSize: '24' }} + slotProps={{ + tooltip: { + sx: { + color: '#ffffff', + backgroundColor: '#444444', + }, + }, + arrow: { + sx: { + color: '#444444', + }, + }, }} + > + { + setAuthenticatedMode('qort'); + }} + src={qortLogo} + style={{ + cursor: 'pointer', + width: '20px', + height: 'auto', + }} + /> + + )} + + + + {authenticatedMode === 'ltc' ? ( + <> + + + + + {rawWallet?.ltcAddress?.slice(0, 6)}... + {rawWallet?.ltcAddress?.slice(-4)} + + + + {ltcBalanceLoading && ( + + )} + {!isNaN(+ltcBalance) && !ltcBalanceLoading && ( + + + {ltcBalance} LTC + + + + )} + + + ) : ( + <> + - + + + {userInfo?.name} + + + + + {rawWallet?.address0?.slice(0, 6)}... + {rawWallet?.address0?.slice(-4)} + + + + {qortBalanceLoading && ( + + )} + {!qortBalanceLoading && balance >= 0 && ( + + + {balance?.toFixed(2)} QORT + + + + )} + + + {userInfo && !userInfo?.name && ( + { + executeEvent('openRegisterName', {}); + }} + > + REGISTER NAME + + )} + + { + setIsOpenSendQort(true); + // setExtstate("send-qort"); + setIsOpenDrawerProfile(false); + }} + > + Transfer QORT + + + )} - - - ) : ( - <> - - { + executeEvent('addTab', { + data: { service: 'APP', name: 'q-trade' }, + }); + executeEvent('open-apps-mode', {}); }} > - {userInfo?.name} + Get QORT at Q-Trade - - - - {rawWallet?.address0?.slice(0, 6)}... - {rawWallet?.address0?.slice(-4)} - - - - {qortBalanceLoading && ( - - )} - {!qortBalanceLoading && balance >= 0 && ( - - - {balance?.toFixed(2)} QORT - - - - )} - - - {userInfo && !userInfo?.name && ( - { - executeEvent('openRegisterName', {}) - }} - > - REGISTER NAME - - )} - - { - setIsOpenSendQort(true); - // setExtstate("send-qort"); - setIsOpenDrawerProfile(false); - }} - > - Transfer QORT - - - - )} - { - executeEvent("addTab", { - data: { service: "APP", name: "q-trade" }, - }); - executeEvent("open-apps-mode", {}); - }} - > - Get QORT at Q-Trade - - - } + + ); + }; const renderProfile = () => { return ( {isMobile && ( )} - {desktopViewMode !== "apps" && - desktopViewMode !== "dev" && - desktopViewMode !== "chat" && ( - <> - {renderProfileLeft()} - - )} + {desktopViewMode !== 'apps' && + desktopViewMode !== 'dev' && + desktopViewMode !== 'chat' && <>{renderProfileLeft()}} - + {!isMobile && ( <> LOG OUT} + title={ + + LOG OUT + + } placement="left" arrow - sx={{ fontSize: "24" }} + sx={{ fontSize: '24' }} slotProps={{ tooltip: { sx: { - color: "#ffffff", - backgroundColor: "#444444", + color: '#ffffff', + backgroundColor: '#444444', }, }, arrow: { sx: { - color: "#444444", + color: '#444444', }, }, }} @@ -1630,9 +1659,9 @@ function App() { setIsOpenDrawerProfile(false); }} style={{ - cursor: "pointer", + cursor: 'pointer', width: '20px', - height: 'auto' + height: 'auto', }} /> @@ -1646,27 +1675,40 @@ function App() { }} > SETTINGS} + title={ + + SETTINGS + + } placement="left" arrow - sx={{ fontSize: "24" }} + sx={{ fontSize: '24' }} slotProps={{ tooltip: { sx: { - color: "#ffffff", - backgroundColor: "#444444", + color: '#ffffff', + backgroundColor: '#444444', }, }, arrow: { sx: { - color: "#444444", + color: '#444444', }, }, }} > @@ -1678,27 +1720,40 @@ function App() { }} > USER LOOKUP} + title={ + + USER LOOKUP + + } placement="left" arrow - sx={{ fontSize: "24" }} + sx={{ fontSize: '24' }} slotProps={{ tooltip: { sx: { - color: "#ffffff", - backgroundColor: "#444444", + color: '#ffffff', + backgroundColor: '#444444', }, }, arrow: { sx: { - color: "#444444", + color: '#444444', }, }, }} > @@ -1706,153 +1761,186 @@ function App() { { - executeEvent('openWalletsApp', {}) + executeEvent('openWalletsApp', {}); }} > WALLETS} + title={ + + WALLETS + + } placement="left" arrow - sx={{ fontSize: "24" }} + sx={{ fontSize: '24' }} slotProps={{ tooltip: { sx: { - color: "#ffffff", - backgroundColor: "#444444", + color: '#ffffff', + backgroundColor: '#444444', }, }, arrow: { sx: { - color: "#444444", + color: '#444444', }, }, }} > - - + {desktopViewMode !== 'home' && ( <> - - YOUR ACCOUNT} - placement="left" - arrow - sx={{ fontSize: "24" }} - slotProps={{ - tooltip: { - sx: { - color: "#ffffff", - backgroundColor: "#444444", - }, - }, - arrow: { - sx: { - color: "#444444", - }, - }, - }} - > - { - setIsOpenDrawerProfile(true); - }}> - - - - + + YOUR ACCOUNT + + } + placement="left" + arrow + sx={{ fontSize: '24' }} + slotProps={{ + tooltip: { + sx: { + color: '#ffffff', + backgroundColor: '#444444', + }, + }, + arrow: { + sx: { + color: '#444444', + }, + }, + }} + > + { + setIsOpenDrawerProfile(true); + }} + > + + + )} - - + - + {extState === 'authenticated' && ( )} - - {extState === "authenticated" && isMainWindow && ( - - - - - + {extState === 'authenticated' && isMainWindow && ( + + + + )} - - { - try { - const res = await isRunningGateway() - if(res) throw new Error('Cannot view minting details on the gateway') - setIsOpenMinting(true) - - } catch (error) { - setOpenSnack(true) - setInfoSnack({ - type: 'error', - message: error?.message - }) - } - }}> + + { + try { + const res = await isRunningGateway(); + if (res) + throw new Error( + 'Cannot view minting details on the gateway' + ); + setIsOpenMinting(true); + } catch (error) { + setOpenSnack(true); + setInfoSnack({ + type: 'error', + message: error?.message, + }); + } + }} + > MINTING STATUS} + title={ + + MINTING STATUS + + } placement="left" arrow - sx={{ fontSize: "24" }} + sx={{ fontSize: '24' }} slotProps={{ tooltip: { sx: { - color: "#ffffff", - backgroundColor: "#444444", + color: '#ffffff', + backgroundColor: '#444444', }, }, arrow: { sx: { - color: "#444444", + color: '#444444', }, }, }} @@ -1860,74 +1948,92 @@ function App() { - + - {(desktopViewMode === "apps" || desktopViewMode === "home") && ( - { - if(desktopViewMode === "apps"){ - showTutorial('qapps', true) - } else { - showTutorial('getting-started', true) - } - }} > - TUTORIAL} - placement="left" - arrow - sx={{ fontSize: "24" }} - slotProps={{ - tooltip: { - sx: { - color: "#ffffff", - backgroundColor: "#444444", - }, + {(desktopViewMode === 'apps' || desktopViewMode === 'home') && ( + { + if (desktopViewMode === 'apps') { + showTutorial('qapps', true); + } else { + showTutorial('getting-started', true); + } + }} + > + + TUTORIAL + + } + placement="left" + arrow + sx={{ fontSize: '24' }} + slotProps={{ + tooltip: { + sx: { + color: '#ffffff', + backgroundColor: '#444444', }, - arrow: { - sx: { - color: "#444444", - }, + }, + arrow: { + sx: { + color: '#444444', }, - }} - > - - - - )} - - - BACKUP WALLET} - placement="left" - arrow - sx={{ fontSize: "24" }} - slotProps={{ - tooltip: { - sx: { - color: "#ffffff", - backgroundColor: "#444444", - }, - }, - arrow: { - sx: { - color: "#444444", - }, - }, - }} - > - { - setExtstate("download-wallet"); - setIsOpenDrawerProfile(false); + }, + }} + > + + + + )} + + + + BACKUP WALLET + + } + placement="left" + arrow + sx={{ fontSize: '24' }} + slotProps={{ + tooltip: { + sx: { + color: '#ffffff', + backgroundColor: '#444444', + }, + }, + arrow: { + sx: { + color: '#444444', + }, + }, }} - src={Download} - style={{ - cursor: "pointer", - width: '20px' - }} - /> - + > + { + setExtstate('download-wallet'); + setIsOpenDrawerProfile(false); + }} + src={Download} + style={{ + cursor: 'pointer', + width: '20px', + }} + /> + - + ); @@ -1936,7 +2042,7 @@ function App() { return ( - - - {extState === "not-authenticated" && ( - - )} - {/* {extState !== "not-authenticated" && ( + + + {extState === 'not-authenticated' && ( + + )} + {/* {extState !== "not-authenticated" && ( )} */} - {extState === "authenticated" && isMainWindow && ( - - - - {!isMobile && renderProfile()} - - - - - )} - {isOpenSendQort && isMainWindow && ( - - - - - - - { - setIsOpenSendQort(false); - setIsOpenSendQortSuccess(true); - }} - defaultPaymentTo={paymentTo} - /> - - )} - - {isShowQortalRequest && !isMainWindow && ( - <> - - - - {messageQortalRequest?.text1} - - - {messageQortalRequest?.text2 && ( - <> - - - - {messageQortalRequest?.text2} - - - - - )} - {messageQortalRequest?.text3 && ( - <> - - - {messageQortalRequest?.text3} - - - - - )} - - {messageQortalRequest?.text4 && ( - - {messageQortalRequest?.text4} - + + {!isMobile && renderProfile()} - )} - - {messageQortalRequest?.html && ( -
- )} - - - + )} + {isOpenSendQort && isMainWindow && ( + - {messageQortalRequest?.highlightedText} - - - {messageQortalRequest?.fee && ( - <> - - - - {"Fee: "} - {messageQortalRequest?.fee} - {" QORT"} - - - - )} - {messageQortalRequest?.checkbox1 && ( + - { - qortalRequestCheckbox1Ref.current = e.target.checked; - }} - edge="start" - tabIndex={-1} - disableRipple - defaultChecked={messageQortalRequest?.checkbox1?.value} - sx={{ - "&.Mui-checked": { - color: "white", // Customize the color when checked - }, - "& .MuiSvgIcon-root": { - color: "white", - }, - }} - /> - - - {messageQortalRequest?.checkbox1?.label} - - - )} - - - - onOkQortalRequest("accepted")} - > - accept - - onCancelQortalRequest()} - > - decline - - - {sendPaymentError} - - )} - {extState === "web-app-request-buy-order" && !isMainWindow && ( - <> - - - - The Application

{" "} - {requestBuyOrder?.hostname}

- - is requesting {requestBuyOrder?.crosschainAtInfo?.length}{" "} - {`buy order${ - requestBuyOrder?.crosschainAtInfo.length === 1 ? "" : "s" - }`} - -
- - - {requestBuyOrder?.crosschainAtInfo?.reduce((latest, cur) => { - return latest + +cur?.qortAmount; - }, 0)}{" "} - QORT - - - - FOR - - - - {roundUpToDecimals( - requestBuyOrder?.crosschainAtInfo?.reduce((latest, cur) => { - return latest + +cur?.expectedForeignAmount; - }, 0) - )} - {` ${requestBuyOrder?.crosschainAtInfo?.[0]?.foreignBlockchain}`} - - {/* - - - Confirm Wallet Password - - - setPaymentPassword(e.target.value)} - /> */} - - - confirmBuyOrder(false)} - > - accept - - confirmBuyOrder(true)} - > - decline - - - {sendPaymentError} - - )} - - {extState === "web-app-request-payment" && !isMainWindow && ( - <> - - - - The Application

{" "} - {sendqortState?.hostname}

- is requesting a payment -
- - - {sendqortState?.description} - - - - {sendqortState?.amount} QORT - - {/* - - - Confirm Wallet Password - - - setPaymentPassword(e.target.value)} - /> */} - - - confirmPayment(false)} - > - accept - - confirmPayment(true)} - > - decline - - - {sendPaymentError} - - )} - {extState === "web-app-request-connection" && !isMainWindow && ( - <> - -
- -
- - - The Application

{" "} - {requestConnection?.hostname}

- is requestion a connection -
- - - - responseToConnectionRequest( - true, - requestConnection?.hostname, - requestConnection.interactionId - ) - } - > - accept - - - responseToConnectionRequest( - false, - requestConnection?.hostname, - requestConnection.interactionId - ) - } - > - decline - - - - )} - {extState === "web-app-request-authentication" && !isMainWindow && ( - <> - -
- -
- - - The Application

{" "} - {requestConnection?.hostname}

- requests authentication -
- - - - - - Authenticate - - - { - setExtstate("create-wallet"); - }} - > - Create account - - - )} - {extState === "wallets" && ( - <> - - - { - setRawWallet(null); - setExtstate("not-authenticated"); - logoutFunc(); - }} - src={Return} - /> - - - - - )} - {rawWallet && extState === "wallet-dropped" && ( - <> - - - { - setRawWallet(null); - setExtstate("wallets"); - logoutFunc(); - }} - src={Return} - /> - - -
- -
- - - {rawWallet?.name ? rawWallet?.name : rawWallet?.address0} - - - Authenticate - - - - - <> - - Wallet Password - - - setAuthenticatePassword(e.target.value)} - onKeyDown={(e) => { - if (e.key === "Enter") { - authenticateWallet(); - } - }} - ref={passwordRef} - /> - {useLocalNode ? ( - <> - - - {"Using node: "} {currentNode?.url} - - - ) : ( - <> - - - {"Using public node"} - - - )} - - - - Authenticate - - {walletToBeDecryptedError} - - - )} - {extState === "download-wallet" && ( - <> - - - - - -
- -
- - - - Download Account - - - - {!walletToBeDownloaded && ( - <> - - Confirm Wallet Password - - - - setWalletToBeDownloadedPassword(e.target.value) - - } - /> - - - Confirm password - - {walletToBeDownloadedError} - - )} - - {walletToBeDownloaded && ( - <> - { - await saveFileToDiskFunc(); - await showInfo({ - message: `Keep your account file secure.`, - }); - }} - > - Download account - - - )} - - )} - {extState === "create-wallet" && ( - <> - {!walletToBeDownloaded && ( - <> - - - { - if(creationStep === 2){ - setCreationStep(1) - return - } - setExtstate("not-authenticated"); - setShowSeed(false) - setCreationStep(1) - setWalletToBeDownloadedPasswordConfirm('') - setWalletToBeDownloadedPassword('') - }} - src={Return} - /> - - -
- -
- - */} + - Set up your Qortal account - - - - - - A ‘ { - setShowSeed(true) - }} style={{ - fontSize: '14px', - color: 'steelblue', - cursor: 'pointer' - }}>SEEDPHRASE ’ has been randomly generated in the background. - - - - - If you wish to VIEW THE SEEDPHRASE, click the word 'SEEDPHRASE' in this text. Seedphrases are used to generate the private key for your Qortal account. For security by default, seedphrases are NOT displayed unless specifically chosen. - - - Create your Qortal account by clicking NEXT below. - - - - { - setCreationStep(2) - }}> - Next - - -
- -
- - - - Your seedphrase - - - {generatorRef.current?.parsedString} - - - - Export Seedphrase - - - - - - - - - - -
- - - - Wallet Password - - - - setWalletToBeDownloadedPassword(e.target.value) - } + onClick={returnToMain} /> - - - Confirm Wallet Password - - - - setWalletToBeDownloadedPasswordConfirm(e.target.value) - } - /> - - There is no minimum length requirement - + + + { + setIsOpenSendQort(false); + setIsOpenSendQortSuccess(true); + }} + defaultPaymentTo={paymentTo} + /> + + )} - - Create Account - - - {walletToBeDownloadedError} - - )} - - {walletToBeDownloaded && ( - <> - - - - - Congrats, you’re all set up! - - - - - Save your account in a place where you will remember it! - - - { - await saveFileToDiskFunc(); - returnToMain(); - await showInfo({ - message: `Keep your wallet file secure.`, - }); - }} - > - Backup Account - - - )} - - )} - {isOpenSendQortSuccess && ( - - - - - - The transfer was succesful! - - - { - returnToMain(); - }} - > - Continue - - - )} - {extState === "transfer-success-request" && ( - <> - - - - - The transfer was succesful! - - - { - window.close(); - }} - > - Continue - - - )} - {extState === "buy-order-submitted" && ( - <> - - - - - Your buy order was submitted - - - { - window.close(); - }} - > - Close - - - )} - {countdown && ( - - {/* */} - { - window.close(); - }} - size={75} - strokeWidth={8} - > - {({ remainingTime }) => {remainingTime}} - - - )} - {isLoading && } - {isShow && ( - - {message.paymentFee ? "Payment" : "Publish"} - - - {message.message} - - {message?.paymentFee && ( - - payment fee: {message.paymentFee} - - )} - {message?.publishFee && ( - - publish fee: {message.publishFee} - - )} - - - - - - - - )} - {isShowInfo && ( - - {"Important Info"} - - - {messageInfo.message} - - - - - - - )} - {isShowUnsavedChanges && ( - - {"LOGOUT"} - - - {messageUnsavedChanges.message} - - - - - - - - )} - {isShowQortalRequestExtension && isMainWindow && ( - - { - onCancelQortalRequestExtension(); - }} - size={50} - strokeWidth={5} - > - {({ remainingTime }) => {remainingTime}} - - + {isShowQortalRequest && !isMainWindow && ( + <> + - {messageQortalRequestExtension?.text1} + {messageQortalRequest?.text1} - {messageQortalRequestExtension?.text2 && ( + {messageQortalRequest?.text2 && ( <> - {messageQortalRequestExtension?.text2} + {messageQortalRequest?.text2} )} - {messageQortalRequestExtension?.text3 && ( + {messageQortalRequest?.text3 && ( <> - {messageQortalRequestExtension?.text3} + {messageQortalRequest?.text3} )} - {messageQortalRequestExtension?.text4 && ( + {messageQortalRequest?.text4 && ( - {messageQortalRequestExtension?.text4} + {messageQortalRequest?.text4} )} - {messageQortalRequestExtension?.html && ( + {messageQortalRequest?.html && (
)} - {messageQortalRequestExtension?.highlightedText} + {messageQortalRequest?.highlightedText} - {messageQortalRequestExtension?.json && ( - <> - - - - - - - )} - - {messageQortalRequestExtension?.fee && ( + {messageQortalRequest?.fee && ( <> - {"Fee: "} - {messageQortalRequestExtension?.fee} - {" QORT"} + {'Fee: '} + {messageQortalRequest?.fee} + {' QORT'} )} - {messageQortalRequestExtension?.appFee && ( - <> - - {"App Fee: "} - {messageQortalRequestExtension?.appFee} - {" QORT"} - - - - )} - {messageQortalRequestExtension?.foreignFee && ( - <> - - - - {"Foreign Fee: "} - {messageQortalRequestExtension?.foreignFee} - - - - )} - {messageQortalRequestExtension?.checkbox1 && ( + {messageQortalRequest?.checkbox1 && ( - {messageQortalRequestExtension?.checkbox1?.label} + {messageQortalRequest?.checkbox1?.label} )} -{messageQortalRequestExtension?.confirmCheckbox && ( - setConfirmRequestRead(e.target.checked)} - checked={confirmRequestRead} - edge="start" - tabIndex={-1} - disableRipple - sx={{ - "&.Mui-checked": { - color: "white", - }, - "& .MuiSvgIcon-root": { - color: "white", - }, - }} - /> - } - label={ - - - I have read this request - - - - } - /> - )} - - { - if(messageQortalRequestExtension?.confirmCheckbox && !confirmRequestRead) return - onOkQortalRequestExtension("accepted") + minWidth: '102px', }} + onClick={() => onOkQortalRequest('accepted')} > accept - - + onCancelQortalRequestExtension()} + onClick={() => onCancelQortalRequest()} > decline - + {sendPaymentError} + + )} + {extState === 'web-app-request-buy-order' && !isMainWindow && ( + <> + + + + The Application

{' '} + {requestBuyOrder?.hostname}

+ + is requesting {requestBuyOrder?.crosschainAtInfo?.length}{' '} + {`buy order${ + requestBuyOrder?.crosschainAtInfo.length === 1 ? '' : 's' + }`} + +
+ + + {requestBuyOrder?.crosschainAtInfo?.reduce((latest, cur) => { + return latest + +cur?.qortAmount; + }, 0)}{' '} + QORT + + + + FOR + + + + {roundUpToDecimals( + requestBuyOrder?.crosschainAtInfo?.reduce((latest, cur) => { + return latest + +cur?.expectedForeignAmount; + }, 0) + )} + {` ${requestBuyOrder?.crosschainAtInfo?.[0]?.foreignBlockchain}`} + + {/* + + + Confirm Wallet Password + + + setPaymentPassword(e.target.value)} + /> */} + + + confirmBuyOrder(false)} + > + accept + + confirmBuyOrder(true)} + > + decline + + + {sendPaymentError} + + )} + + {extState === 'web-app-request-payment' && !isMainWindow && ( + <> + + + + The Application

{' '} + {sendqortState?.hostname}

+ is requesting a payment +
+ + + {sendqortState?.description} + + + + {sendqortState?.amount} QORT + + {/* + + + Confirm Wallet Password + + + setPaymentPassword(e.target.value)} + /> */} + + + confirmPayment(false)} + > + accept + + confirmPayment(true)} + > + decline + + + {sendPaymentError} + + )} + {extState === 'web-app-request-connection' && !isMainWindow && ( + <> + +
+ +
+ + + The Application

{' '} + {requestConnection?.hostname}

+ is requestion a connection +
+ + + + responseToConnectionRequest( + true, + requestConnection?.hostname, + requestConnection.interactionId + ) + } + > + accept + + + responseToConnectionRequest( + false, + requestConnection?.hostname, + requestConnection.interactionId + ) + } + > + decline + + + + )} + {extState === 'web-app-request-authentication' && !isMainWindow && ( + <> + +
+ +
+ + + The Application

{' '} + {requestConnection?.hostname}

+ requests authentication +
+ + + + + + Authenticate + + + { + setExtstate('create-wallet'); + }} + > + Create account + + + )} + {extState === 'wallets' && ( + <> + + + { + setRawWallet(null); + setExtstate('not-authenticated'); + logoutFunc(); + }} + /> + + + + )} + {rawWallet && extState === 'wallet-dropped' && ( + <> + + + { + setRawWallet(null); + setExtstate('wallets'); + logoutFunc(); + }} + /> + + +
+ +
+ + + + {rawWallet?.name ? rawWallet?.name : rawWallet?.address0} + + + + Authenticate + + + + + <> + + Wallet Password + + + setAuthenticatePassword(e.target.value)} + onKeyDown={(e) => { + if (e.key === 'Enter') { + authenticateWallet(); + } + }} + ref={passwordRef} + /> + {useLocalNode ? ( + <> + + + {'Using node: '} {currentNode?.url} + + + ) : ( + <> + + + {'Using public node'} + + + )} + + + + Authenticate + + {walletToBeDecryptedError} + + + )} + {extState === 'download-wallet' && ( + <> + + + + + +
+ +
+ + + + Download Account + + + + {!walletToBeDownloaded && ( + <> + + Confirm Wallet Password + + + + setWalletToBeDownloadedPassword(e.target.value) + } + /> + + + Confirm password + + {walletToBeDownloadedError} + + )} + + {walletToBeDownloaded && ( + <> + { + await saveFileToDiskFunc(); + await showInfo({ + message: `Keep your account file secure.`, + }); + }} + > + Download account + + + )} + + )} + {extState === 'create-wallet' && ( + <> + {!walletToBeDownloaded && ( + <> + + + { + if (creationStep === 2) { + setCreationStep(1); + return; + } + setExtstate('not-authenticated'); + setShowSeed(false); + setCreationStep(1); + setWalletToBeDownloadedPasswordConfirm(''); + setWalletToBeDownloadedPassword(''); + }} + src={Return} + /> + + + + +
+ +
+ + + + + Set up your Qortal account + + + + + + + + A ‘{' '} + { + setShowSeed(true); + }} + style={{ + fontSize: '14px', + color: 'steelblue', + cursor: 'pointer', + }} + > + SEEDPHRASE + {' '} + ’ has been randomly generated in the background. + + + If you wish to VIEW THE SEEDPHRASE, click the word + 'SEEDPHRASE' in this text. Seedphrases are used to + generate the private key for your Qortal account. For + security by default, seedphrases are NOT displayed unless + specifically chosen. + + + Create your Qortal account by clicking{' '} + + NEXT + {' '} + below. + + + { + setCreationStep(2); + }} + > + Next + + +
+ +
+ + + + + Your seedphrase + + + + {generatorRef.current?.parsedString} + + + + Export Seedphrase + + + + + + + +
+ + + + Wallet Password + + + + setWalletToBeDownloadedPassword(e.target.value) + } + /> + + + Confirm Wallet Password + + + + setWalletToBeDownloadedPasswordConfirm(e.target.value) + } + /> + + + There is no minimum length requirement + + + + + Create Account + + + {walletToBeDownloadedError} + + )} + + {walletToBeDownloaded && ( + <> + + + + + Congrats, you’re all set up! + + + + + + Save your account in a place where you will remember it! + + + + { + await saveFileToDiskFunc(); + returnToMain(); + await showInfo({ + message: `Keep your wallet file secure.`, + }); + }} + > + Backup Account + + + )} + + )} + {isOpenSendQortSuccess && ( + + + + + + The transfer was succesful! + + + { + returnToMain(); + }} + > + Continue + -
- )} - {isSettingsOpen && ( - - )} - - - {renderProfileLeft()} - - - - + )} + {extState === 'transfer-success-request' && ( + <> + + + + + The transfer was succesful! + + + { + window.close(); + }} + > + Continue + + + )} + {extState === 'buy-order-submitted' && ( + <> + + + + + Your buy order was submitted + + + { + window.close(); + }} + > + Close + + + )} + {countdown && ( + + {/* */} + { + window.close(); + }} + size={75} + strokeWidth={8} + > + {({ remainingTime }) => {remainingTime}} + + + )} + {isLoading && } + {isShow && ( + + + {message.paymentFee ? 'Payment' : 'Publish'} + + + + {message.message} + + {message?.paymentFee && ( + + payment fee: {message.paymentFee} + + )} + {message?.publishFee && ( + + publish fee: {message.publishFee} + + )} + + + + + + + )} + {isShowInfo && ( + + + {'Important Info'} + + + + {messageInfo.message} + + + + + + + )} + {isShowUnsavedChanges && ( + + {'LOGOUT'} + + + {messageUnsavedChanges.message} + + + + + + + + )} + {isShowQortalRequestExtension && isMainWindow && ( + + { + onCancelQortalRequestExtension(); + }} + size={50} + strokeWidth={5} + > + {({ remainingTime }) => {remainingTime}} + + + + + {messageQortalRequestExtension?.text1} + + + {messageQortalRequestExtension?.text2 && ( + <> + + + + {messageQortalRequestExtension?.text2} + + + + + )} + {messageQortalRequestExtension?.text3 && ( + <> + + + {messageQortalRequestExtension?.text3} + + + + + )} + + {messageQortalRequestExtension?.text4 && ( + + + {messageQortalRequestExtension?.text4} + + + )} + + {messageQortalRequestExtension?.html && ( +
+ )} + + + + {messageQortalRequestExtension?.highlightedText} + + + {messageQortalRequestExtension?.json && ( + <> + + + + + + )} + + {messageQortalRequestExtension?.fee && ( + <> + + + + {'Fee: '} + {messageQortalRequestExtension?.fee} + {' QORT'} + + + + )} + {messageQortalRequestExtension?.appFee && ( + <> + + {'App Fee: '} + {messageQortalRequestExtension?.appFee} + {' QORT'} + + + + )} + {messageQortalRequestExtension?.foreignFee && ( + <> + + + + {'Foreign Fee: '} + {messageQortalRequestExtension?.foreignFee} + + + + )} + {messageQortalRequestExtension?.checkbox1 && ( + + { + qortalRequestCheckbox1Ref.current = e.target.checked; + }} + edge="start" + tabIndex={-1} + disableRipple + defaultChecked={ + messageQortalRequestExtension?.checkbox1?.value + } + sx={{ + '&.Mui-checked': { + color: 'white', // Customize the color when checked + }, + '& .MuiSvgIcon-root': { + color: 'white', + }, + }} + /> + + + {messageQortalRequestExtension?.checkbox1?.label} + + + )} + + {messageQortalRequestExtension?.confirmCheckbox && ( + setConfirmRequestRead(e.target.checked)} + checked={confirmRequestRead} + edge="start" + tabIndex={-1} + disableRipple + sx={{ + '&.Mui-checked': { + color: 'white', + }, + '& .MuiSvgIcon-root': { + color: 'white', + }, + }} + /> + } + label={ + + + I have read this request + + + + } + /> + )} + + + + { + if ( + messageQortalRequestExtension?.confirmCheckbox && + !confirmRequestRead + ) + return; + onOkQortalRequestExtension('accepted'); + }} + > + accept + + onCancelQortalRequestExtension()} + > + decline + + + {sendPaymentError} + +
+ )} + {isSettingsOpen && ( + + )} + + + {renderProfileLeft()} + + + + - {extState === "create-wallet" && walletToBeDownloaded && ( - { - showTutorial('important-information', true) - }} sx={{ - position: 'fixed', - bottom: '25px', - right: '25px' - }}> - - + {extState === 'create-wallet' && walletToBeDownloaded && ( + { + showTutorial('important-information', true); + }} + sx={{ + position: 'fixed', + bottom: '25px', + right: '25px', + }} + > + + )} - {isOpenMinting && ( - - )} + {isOpenMinting && ( + + )} + + ); } -export default App; \ No newline at end of file +export default App; diff --git a/src/Wallets.tsx b/src/Wallets.tsx index 4211fd0..fa65b5a 100644 --- a/src/Wallets.tsx +++ b/src/Wallets.tsx @@ -1,48 +1,58 @@ -import React, { useContext, useEffect, useRef, useState } from "react"; -import List from "@mui/material/List"; -import ListItem from "@mui/material/ListItem"; -import Divider from "@mui/material/Divider"; -import ListItemText from "@mui/material/ListItemText"; -import ListItemAvatar from "@mui/material/ListItemAvatar"; -import Avatar from "@mui/material/Avatar"; -import Typography from "@mui/material/Typography"; -import { Box, Button, ButtonBase, Dialog, DialogActions, DialogContent, DialogTitle, IconButton, Input } from "@mui/material"; -import { CustomButton } from "./App-styles"; -import { useDropzone } from "react-dropzone"; -import EditIcon from "@mui/icons-material/Edit"; -import { Label } from "./components/Group/AddGroup"; -import { Spacer } from "./common/Spacer"; -import { getWallets, storeWallets, walletVersion } from "./background"; -import { useModal } from "./common/useModal"; -import PhraseWallet from "./utils/generateWallet/phrase-wallet"; -import { decryptStoredWalletFromSeedPhrase } from "./utils/decryptWallet"; -import { crypto } from "./constants/decryptWallet"; -import { LoadingButton } from "@mui/lab"; -import { PasswordField } from "./components"; -import { HtmlTooltip } from "./ExtStates/NotAuthenticated"; -import { GlobalContext } from "./App"; +import React, { useContext, useEffect, useState } from 'react'; +import List from '@mui/material/List'; +import ListItem from '@mui/material/ListItem'; +import Divider from '@mui/material/Divider'; +import ListItemText from '@mui/material/ListItemText'; +import ListItemAvatar from '@mui/material/ListItemAvatar'; +import Avatar from '@mui/material/Avatar'; +import Typography from '@mui/material/Typography'; +import { + Box, + Button, + ButtonBase, + Dialog, + DialogActions, + DialogContent, + DialogTitle, + IconButton, + Input, +} from '@mui/material'; +import { CustomButton } from './App-styles'; +import { useDropzone } from 'react-dropzone'; +import EditIcon from '@mui/icons-material/Edit'; +import { Label } from './components/Group/AddGroup'; +import { Spacer } from './common/Spacer'; +import { getWallets, storeWallets, walletVersion } from './background'; +import { useModal } from './common/useModal'; +import PhraseWallet from './utils/generateWallet/phrase-wallet'; +import { decryptStoredWalletFromSeedPhrase } from './utils/decryptWallet'; +import { crypto } from './constants/decryptWallet'; +import { LoadingButton } from '@mui/lab'; +import { PasswordField } from './components'; +import { HtmlTooltip } from './ExtStates/NotAuthenticated'; +import { GlobalContext } from './App'; -const parsefilenameQortal = (filename)=> { - return filename.startsWith("qortal_backup_") ? filename.slice(14) : filename; - } +const parsefilenameQortal = (filename) => { + return filename.startsWith('qortal_backup_') ? filename.slice(14) : filename; +}; export const Wallets = ({ setExtState, setRawWallet, rawWallet }) => { const [wallets, setWallets] = useState([]); const [isLoading, setIsLoading] = useState(true); - const [seedValue, setSeedValue] = useState(""); - const [seedName, setSeedName] = useState(""); - const [seedError, setSeedError] = useState(""); - const { hasSeenGettingStarted } = useContext(GlobalContext); + const [seedValue, setSeedValue] = useState(''); + const [seedName, setSeedName] = useState(''); + const [seedError, setSeedError] = useState(''); + const { hasSeenGettingStarted } = useContext(GlobalContext); - const [password, setPassword] = useState(""); + const [password, setPassword] = useState(''); const [isOpenSeedModal, setIsOpenSeedModal] = useState(false); const [isLoadingEncryptSeed, setIsLoadingEncryptSeed] = useState(false); - const { isShow, onCancel, onOk, show, } = useModal(); + const { isShow, onCancel, onOk, show } = useModal(); const { getRootProps, getInputProps } = useDropzone({ accept: { - "application/json": [".json"], // Only accept JSON files + 'application/json': ['.json'], // Only accept JSON files }, onDrop: async (acceptedFiles) => { const files: any = acceptedFiles; @@ -53,8 +63,8 @@ export const Wallets = ({ setExtState, setRawWallet, rawWallet }) => { const fileContents = await new Promise((resolve, reject) => { const reader = new FileReader(); - reader.onabort = () => reject("File reading was aborted"); - reader.onerror = () => reject("File reading has failed"); + reader.onabort = () => reject('File reading was aborted'); + reader.onerror = () => reject('File reading has failed'); reader.onload = () => { // Resolve the promise with the reader result when reading completes resolve(reader.result); @@ -63,9 +73,9 @@ export const Wallets = ({ setExtState, setRawWallet, rawWallet }) => { // Read the file as text reader.readAsText(file); }); - if (typeof fileContents !== "string") continue; - const parsedData = JSON.parse(fileContents) - importedWallets.push({...parsedData, filename: file?.name}); + if (typeof fileContents !== 'string') continue; + const parsedData = JSON.parse(fileContents); + importedWallets.push({ ...parsedData, filename: file?.name }); } catch (error) { console.error(error); } @@ -108,83 +118,85 @@ export const Wallets = ({ setExtState, setRawWallet, rawWallet }) => { }); }; - const handleSetSeedValue = async ()=> { + const handleSetSeedValue = async () => { try { - setIsOpenSeedModal(true) - const {seedValue, seedName, password} = await show({ - message: "", - publishFee: "", + setIsOpenSeedModal(true); + const { seedValue, seedName, password } = await show({ + message: '', + publishFee: '', }); - setIsLoadingEncryptSeed(true) - const res = await decryptStoredWalletFromSeedPhrase(seedValue) + setIsLoadingEncryptSeed(true); + const res = await decryptStoredWalletFromSeedPhrase(seedValue); const wallet2 = new PhraseWallet(res, walletVersion); const wallet = await wallet2.generateSaveWalletData( password, crypto.kdfThreads, () => {} ); - if(wallet?.address0){ - setWallets([...wallets, { - ...wallet, - name: seedName - }]); - setIsOpenSeedModal(false) - setSeedValue('') - setSeedName('') - setPassword('') - setSeedError('') + if (wallet?.address0) { + setWallets([ + ...wallets, + { + ...wallet, + name: seedName, + }, + ]); + setIsOpenSeedModal(false); + setSeedValue(''); + setSeedName(''); + setPassword(''); + setSeedError(''); } else { - setSeedError('Could not create account.') + setSeedError('Could not create account.'); } - } catch (error) { - setSeedError(error?.message || 'Could not create account.') + setSeedError(error?.message || 'Could not create account.'); } finally { - setIsLoadingEncryptSeed(false) + setIsLoadingEncryptSeed(false); } - } + }; const selectedWalletFunc = (wallet) => { setRawWallet(wallet); - setExtState("wallet-dropped"); + setExtState('wallet-dropped'); }; - useEffect(()=> { - setIsLoading(true) - getWallets().then((res)=> { - - if(res && Array.isArray(res)){ - setWallets(res) + useEffect(() => { + setIsLoading(true); + getWallets() + .then((res) => { + if (res && Array.isArray(res)) { + setWallets(res); } - setIsLoading(false) - }).catch((error)=> { - console.error(error) - setIsLoading(false) - }) - }, []) + setIsLoading(false); + }) + .catch((error) => { + console.error(error); + setIsLoading(false); + }); + }, []); - useEffect(()=> { - if(!isLoading && wallets && Array.isArray(wallets)){ - storeWallets(wallets) + useEffect(() => { + if (!isLoading && wallets && Array.isArray(wallets)) { + storeWallets(wallets); } - }, [wallets, isLoading]) + }, [wallets, isLoading]); - if(isLoading) return null + if (isLoading) return null; return (
- {(wallets?.length === 0 || - !wallets) ? ( - <> - No accounts saved - - - ): ( - <> - Your saved accounts - - - )} + {wallets?.length === 0 || !wallets ? ( + <> + No accounts saved + + + ) : ( + <> + Your saved accounts + + + )} {rawWallet && ( @@ -196,174 +208,198 @@ export const Wallets = ({ setExtState, setRawWallet, rawWallet }) => { )} {wallets?.length > 0 && ( - - {wallets?.map((wallet, idx) => { - return ( - <> - - - - ); - })} - + + {wallets?.map((wallet, idx) => { + return ( + <> + + + + ); + })} + )} - + - - Already have a Qortal account? Enter your secret backup phrase here to access it. This phrase is one of the ways to recover your account. - - } - > - - - Add seed-phrase - - - Use this option to connect additional Qortal wallets you've already made, in order to login with them afterwards. You will need access to your backup JSON file in order to do so. - - } - > - - - Add account - + disableHoverListener={hasSeenGettingStarted === true} + title={ + + + Already have a Qortal account? Enter your secret backup phrase + here to access it. This phrase is one of the ways to recover + your account. + + + } + > + + Add seed-phrase + + + + + + Use this option to connect additional Qortal wallets you've + already made, in order to login with them afterwards. You will + need access to your backup JSON file in order to do so. + + + } + > + + + Add account + - { - if (e.key === 'Enter' && seedValue && seedName && password) { - onOk({seedValue, seedName, password}); - } - }} - > - - Type or paste in your seed-phrase - - - + { + if (e.key === 'Enter' && seedValue && seedName && password) { + onOk({ seedValue, seedName, password }); + } + }} + > + + Type or paste in your seed-phrase + + + + - setSeedName(e.target.value)} - /> - - - setSeedName(e.target.value)} + /> + + + + + setSeedValue(e.target.value)} autoComplete="off" sx={{ - width: '100%' + width: '100%', }} /> - - - + + + setPassword(e.target.value)} autoComplete="off" sx={{ - width: '100%' + width: '100%', }} /> - - - - - - - + + + + { - if(!seedValue || !seedName || !password) return - onOk({seedValue, seedName, password}); - }} - autoFocus - > - Add - - {seedError} - - - + disabled={!seedValue || !seedName || !password} + variant="contained" + onClick={() => { + if (!seedValue || !seedName || !password) return; + onOk({ seedValue, seedName, password }); + }} + autoFocus + > + Add + + + {seedError} + + +
- ); }; const WalletItem = ({ wallet, updateWalletItem, idx, setSelectedWallet }) => { - const [name, setName] = useState(""); - const [note, setNote] = useState(""); + const [name, setName] = useState(''); + const [note, setNote] = useState(''); const [isEdit, setIsEdit] = useState(false); useEffect(() => { @@ -382,71 +418,81 @@ const WalletItem = ({ wallet, updateWalletItem, idx, setSelectedWallet }) => { }} sx={{ width: '100%', - padding: '10px' + padding: '10px', }} - > {wallet?.address0} {wallet?.note} - Login + + Login + } /> { - e.stopPropagation(); - setIsEdit(true); - }} - edge="end" - aria-label="edit" - > - - + sx={{ + alignSelf: 'flex-start', + }} + onClick={(e) => { + e.stopPropagation(); + setIsEdit(true); + }} + edge="end" + aria-label="edit" + > + + {isEdit && ( @@ -455,10 +501,12 @@ const WalletItem = ({ wallet, updateWalletItem, idx, setSelectedWallet }) => { value={name} onChange={(e) => setName(e.target.value)} sx={{ - width: "100%", + width: '100%', }} /> + + { maxLength: 100, }} sx={{ - width: "100%", + width: '100%', }} /> + + - - + {combinedListTempAndReal.map((message, index, list) => { let fullMessage = message; if (hashMapMailMessages[message?.identifier]) { fullMessage = hashMapMailMessages[message.identifier]; - if (fullMessage?.error) { return ( {fullMessage?.error} - ); @@ -855,23 +865,23 @@ export const Thread = ({ return ( Downloading from QDN - ); })} - + {!hasLastPage && !isLoading && ( <> - - - - - + First + + + + - -
+ + + +
diff --git a/src/components/PasswordField/PasswordField.tsx b/src/components/PasswordField/PasswordField.tsx index 0c3e8b7..8a96b17 100644 --- a/src/components/PasswordField/PasswordField.tsx +++ b/src/components/PasswordField/PasswordField.tsx @@ -4,56 +4,59 @@ import { TextField, TextFieldProps, styled, -} from "@mui/material"; -import { forwardRef, useState } from "react"; -import VisibilityOffIcon from "@mui/icons-material/VisibilityOff"; -import VisibilityIcon from "@mui/icons-material/Visibility"; + useTheme, +} from '@mui/material'; +import { forwardRef, useState } from 'react'; +import VisibilityOffIcon from '@mui/icons-material/VisibilityOff'; +import VisibilityIcon from '@mui/icons-material/Visibility'; export const CustomInput = styled(TextField)(({ theme }) => ({ - width: "183px", - borderRadius: "5px", - backgroundColor: theme.palette.background.paper, - outline: "none", - input: { - fontSize: 10, - fontFamily: "Inter", - fontWeight: 400, - color: theme.palette.text.primary, - "&::placeholder": { - fontSize: 16, - color: theme.palette.text.disabled, - }, - outline: "none", - padding: "10px", + width: '183px', + borderRadius: '5px', + backgroundColor: theme.palette.background.paper, + outline: 'none', + input: { + fontSize: 10, + fontFamily: 'Inter', + fontWeight: 400, + color: theme.palette.text.primary, + '&::placeholder': { + fontSize: 16, + color: theme.palette.text.disabled, }, - "& .MuiOutlinedInput-root": { - "& fieldset": { - border: `0.5px solid ${theme.palette.divider}`, - }, - "&:hover fieldset": { - border: `0.5px solid ${theme.palette.divider}`, - }, - "&.Mui-focused fieldset": { - border: `0.5px solid ${theme.palette.divider}`, - }, + outline: 'none', + padding: '10px', + }, + '& .MuiOutlinedInput-root': { + '& fieldset': { + border: `0.5px solid ${theme.palette.divider}`, }, - "& .MuiInput-underline:before": { - borderBottom: "none", + '&:hover fieldset': { + border: `0.5px solid ${theme.palette.divider}`, }, - "& .MuiInput-underline:hover:not(.Mui-disabled):before": { - borderBottom: "none", + '&.Mui-focused fieldset': { + border: `0.5px solid ${theme.palette.divider}`, }, - "& .MuiInput-underline:after": { - borderBottom: "none", - }, - })); + }, + '& .MuiInput-underline:before': { + borderBottom: 'none', + }, + '& .MuiInput-underline:hover:not(.Mui-disabled):before': { + borderBottom: 'none', + }, + '& .MuiInput-underline:after': { + borderBottom: 'none', + }, +})); export const PasswordField = forwardRef( ({ ...props }, ref) => { const [canViewPassword, setCanViewPassword] = useState(false); + const theme = useTheme(); + return ( ( > @@ -81,7 +87,10 @@ export const PasswordField = forwardRef( > From efd3624e92ed05f6b1ddbd36f1057dc75ba97470 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 12 Apr 2025 15:44:03 +0200 Subject: [PATCH 12/31] Move some components into styles folder, delete duplicated themes --- src/App.tsx | 2 +- src/ExtStates/NotAuthenticated.tsx | 2 +- src/Wallets.tsx | 2 +- src/components/Apps/AppsHomeDesktop.tsx | 88 +++++++------- src/components/DesktopSideBar.tsx | 64 +++++----- src/main.tsx | 18 +-- .../Theme => styles}/ThemeContext.tsx | 25 ++-- .../Theme => styles}/ThemeSelector.tsx | 0 src/styles/theme.ts | 113 +++++++++--------- 9 files changed, 151 insertions(+), 163 deletions(-) rename src/{components/Theme => styles}/ThemeContext.tsx (51%) rename src/{components/Theme => styles}/ThemeSelector.tsx (100%) diff --git a/src/App.tsx b/src/App.tsx index 6365115..52d322c 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -149,7 +149,7 @@ import { BuyQortInformation } from './components/BuyQortInformation'; import { QortPayment } from './components/QortPayment'; import { GeneralNotifications } from './components/GeneralNotifications'; import { PdfViewer } from './common/PdfViewer'; -import ThemeSelector from './components/Theme/ThemeSelector'; +import ThemeSelector from './styles/ThemeSelector.tsx'; type extStates = | 'not-authenticated' diff --git a/src/ExtStates/NotAuthenticated.tsx b/src/ExtStates/NotAuthenticated.tsx index bfb33d4..049c282 100644 --- a/src/ExtStates/NotAuthenticated.tsx +++ b/src/ExtStates/NotAuthenticated.tsx @@ -29,7 +29,7 @@ import { CustomizedSnackbars } from '../components/Snackbar/Snackbar'; import { cleanUrl, gateways } from '../background'; import { GlobalContext } from '../App'; import Tooltip, { TooltipProps, tooltipClasses } from '@mui/material/Tooltip'; -import ThemeSelector from '../components/Theme/ThemeSelector'; +import ThemeSelector from '../styles/ThemeSelector'; const manifestData = { version: '0.5.3', diff --git a/src/Wallets.tsx b/src/Wallets.tsx index fa65b5a..5752639 100644 --- a/src/Wallets.tsx +++ b/src/Wallets.tsx @@ -423,7 +423,7 @@ const WalletItem = ({ wallet, updateWalletItem, idx, setSelectedWallet }) => { > { - const [qortalUrl, setQortalUrl] = useState(""); + const [qortalUrl, setQortalUrl] = useState(''); const openQortalUrl = () => { try { @@ -31,9 +31,9 @@ export const AppsHomeDesktop = ({ const res = extractComponents(qortalUrl); if (res) { const { service, name, identifier, path } = res; - executeEvent("addTab", { data: { service, name, identifier, path } }); - executeEvent("open-apps-mode", {}); - setQortalUrl("qortal://"); + executeEvent('addTab', { data: { service, name, identifier, path } }); + executeEvent('open-apps-mode', {}); + setQortalUrl('qortal://'); } } catch (error) {} }; @@ -41,12 +41,12 @@ export const AppsHomeDesktop = ({ <> Apps Dashboard @@ -57,19 +57,19 @@ export const AppsHomeDesktop = ({ { - if (e.key === "Enter" && qortalUrl) { + if (e.key === 'Enter' && qortalUrl) { openQortalUrl(); } }} @@ -108,7 +108,7 @@ export const AppsHomeDesktop = ({ openQortalUrl()}> @@ -119,18 +119,18 @@ export const AppsHomeDesktop = ({ { - setMode("library"); + setMode('library'); }} > @@ -150,7 +150,7 @@ export const AppsHomeDesktop = ({ /> - + ); }; diff --git a/src/components/DesktopSideBar.tsx b/src/components/DesktopSideBar.tsx index bd883be..08336e5 100644 --- a/src/components/DesktopSideBar.tsx +++ b/src/components/DesktopSideBar.tsx @@ -1,12 +1,12 @@ -import { Box, ButtonBase, useTheme } from "@mui/material"; -import { HomeIcon } from "../assets/Icons/HomeIcon"; -import { MessagingIcon } from "../assets/Icons/MessagingIcon"; -import { Save } from "./Save/Save"; -import { IconWrapper } from "./Desktop/DesktopFooter"; -import { useRecoilState } from "recoil"; -import { enabledDevModeAtom } from "../atoms/global"; -import { AppsIcon } from "../assets/Icons/AppsIcon"; -import ThemeSelector from "./Theme/ThemeSelector"; +import { Box, ButtonBase, useTheme } from '@mui/material'; +import { HomeIcon } from '../assets/Icons/HomeIcon'; +import { MessagingIcon } from '../assets/Icons/MessagingIcon'; +import { Save } from './Save/Save'; +import { IconWrapper } from './Desktop/DesktopFooter'; +import { useRecoilState } from 'recoil'; +import { enabledDevModeAtom } from '../atoms/global'; +import { AppsIcon } from '../assets/Icons/AppsIcon'; +import ThemeSelector from '../styles/ThemeSelector'; export const DesktopSideBar = ({ goToHome, @@ -24,25 +24,25 @@ export const DesktopSideBar = ({ }) => { const [isEnabledDevMode, setIsEnabledDevMode] = useRecoilState(enabledDevModeAtom); - + const theme = useTheme(); return ( { goToHome(); @@ -51,39 +51,39 @@ export const DesktopSideBar = ({ { - setDesktopViewMode("apps"); + setDesktopViewMode('apps'); // setIsOpenSideViewDirects(false) // setIsOpenSideViewGroups(false) }} > { - setDesktopViewMode("chat"); + setDesktopViewMode('chat'); }} > @@ -121,24 +121,22 @@ export const DesktopSideBar = ({ {isEnabledDevMode && ( { - setDesktopViewMode("dev"); + setDesktopViewMode('dev'); }} > - + )} - + ); }; diff --git a/src/main.tsx b/src/main.tsx index 881d4c1..791c63d 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -1,13 +1,13 @@ -import React from "react"; -import ReactDOM from "react-dom/client"; -import App from "./App.tsx"; -import "./index.css"; -import "./messaging/messagesToBackground"; -import { MessageQueueProvider } from "./MessageQueueContext.tsx"; -import { RecoilRoot } from "recoil"; -import { ThemeProvider } from "./components/Theme/ThemeContext.tsx"; +import React from 'react'; +import ReactDOM from 'react-dom/client'; +import App from './App.tsx'; +import './index.css'; +import './messaging/messagesToBackground'; +import { MessageQueueProvider } from './MessageQueueContext.tsx'; +import { RecoilRoot } from 'recoil'; +import { ThemeProvider } from './styles/ThemeContext.tsx'; -ReactDOM.createRoot(document.getElementById("root")!).render( +ReactDOM.createRoot(document.getElementById('root')!).render( <> diff --git a/src/components/Theme/ThemeContext.tsx b/src/styles/ThemeContext.tsx similarity index 51% rename from src/components/Theme/ThemeContext.tsx rename to src/styles/ThemeContext.tsx index e21ab9f..ed52fda 100644 --- a/src/components/Theme/ThemeContext.tsx +++ b/src/styles/ThemeContext.tsx @@ -1,24 +1,19 @@ import { createContext, useContext, useState, useMemo } from 'react'; -import { createTheme, ThemeProvider as MuiThemeProvider } from '@mui/material/styles'; +import { ThemeProvider as MuiThemeProvider } from '@mui/material/styles'; +import { darkTheme, lightTheme } from './theme'; -const darkTheme = createTheme({ - palette: { - mode: 'dark', - }, +const ThemeContext = createContext({ + themeMode: 'light', + toggleTheme: () => {}, }); -const lightTheme = createTheme({ - palette: { - mode: 'light', - }, -}); - -const ThemeContext = createContext({ themeMode: 'light', toggleTheme: () => {} }); - export const ThemeProvider = ({ children }: { children: React.ReactNode }) => { const [themeMode, setThemeMode] = useState('light'); - const theme = useMemo(() => (themeMode === 'light' ? lightTheme : darkTheme), [themeMode]); + const theme = useMemo( + () => (themeMode === 'light' ? lightTheme : darkTheme), + [themeMode] + ); const toggleTheme = () => { setThemeMode((prevMode) => (prevMode === 'light' ? 'dark' : 'light')); @@ -31,4 +26,4 @@ export const ThemeProvider = ({ children }: { children: React.ReactNode }) => { ); }; -export const useThemeContext = () => useContext(ThemeContext); \ No newline at end of file +export const useThemeContext = () => useContext(ThemeContext); diff --git a/src/components/Theme/ThemeSelector.tsx b/src/styles/ThemeSelector.tsx similarity index 100% rename from src/components/Theme/ThemeSelector.tsx rename to src/styles/ThemeSelector.tsx diff --git a/src/styles/theme.ts b/src/styles/theme.ts index feb0367..f036fe6 100644 --- a/src/styles/theme.ts +++ b/src/styles/theme.ts @@ -1,54 +1,50 @@ -import { createTheme } from '@mui/material/styles' - +import { createTheme } from '@mui/material/styles'; // Extend the Theme interface - const commonThemeOptions = { typography: { - fontFamily: [ - 'Roboto' - ].join(','), + fontFamily: ['Roboto'].join(','), h1: { fontSize: '2rem', - fontWeight: 600 + fontWeight: 600, }, h2: { fontSize: '1.75rem', - fontWeight: 500 + fontWeight: 500, }, h3: { fontSize: '1.5rem', - fontWeight: 500 + fontWeight: 500, }, h4: { fontSize: '1.25rem', - fontWeight: 500 + fontWeight: 500, }, h5: { fontSize: '1rem', - fontWeight: 500 + fontWeight: 500, }, h6: { fontSize: '0.875rem', - fontWeight: 500 + fontWeight: 500, }, body1: { fontSize: '23px', fontWeight: 400, lineHeight: 1.5, - letterSpacing: '0.5px' + letterSpacing: '0.5px', }, body2: { fontSize: '18px', fontWeight: 400, lineHeight: 1.4, - letterSpacing: '0.2px' - } + letterSpacing: '0.2px', + }, }, spacing: 8, shape: { - borderRadius: 4 + borderRadius: 4, }, breakpoints: { values: { @@ -56,8 +52,8 @@ const commonThemeOptions = { sm: 600, md: 900, lg: 1200, - xl: 1536 - } + xl: 1536, + }, }, components: { MuiButton: { @@ -66,25 +62,24 @@ const commonThemeOptions = { backgroundColor: 'inherit', transition: 'filter 0.3s ease-in-out', '&:hover': { - filter: 'brightness(1.1)' - } - } + filter: 'brightness(1.1)', + }, + }, }, defaultProps: { disableElevation: true, - disableRipple: true - } + disableRipple: true, + }, }, MuiModal: { styleOverrides: { root: { zIndex: 50000, }, - } - - } - } -} + }, + }, + }, +}; const lightTheme = createTheme({ ...commonThemeOptions, @@ -93,19 +88,19 @@ const lightTheme = createTheme({ primary: { main: '#f4f4fb', dark: '#eaecf4', - light: '#f9f9fd' + light: '#f9f9fd', }, secondary: { - main: '#1EAAF1' + main: '#1EAAF1', }, background: { default: '#fafafa', - paper: '#f0f0f0' + paper: '#f0f0f0', }, text: { primary: '#000000', - secondary: '#525252' - } + secondary: '#525252', + }, }, components: { @@ -119,20 +114,20 @@ const lightTheme = createTheme({ '&:hover': { cursor: 'pointer', boxShadow: - 'rgba(0, 0, 0, 0.1) 0px 4px 6px -1px, rgba(0, 0, 0, 0.06) 0px 2px 4px -1px;' - } - } - } + 'rgba(0, 0, 0, 0.1) 0px 4px 6px -1px, rgba(0, 0, 0, 0.06) 0px 2px 4px -1px;', + }, + }, + }, }, MuiIcon: { defaultProps: { style: { - color: '#000000' - } - } - } + color: '#000000', + }, + }, + }, }, -}) +}); const darkTheme = createTheme({ ...commonThemeOptions, @@ -140,45 +135,45 @@ const darkTheme = createTheme({ mode: 'dark', primary: { main: '#2e3d60', - dark: "#1a2744", - light: "#3f4b66", + dark: '#1a2744', + light: '#3f4b66', }, secondary: { - main: '#45adff' + main: '#45adff', }, background: { default: '#313338', - paper: "#1e1e20" + paper: '#1e1e20', }, text: { primary: '#ffffff', - secondary: '#b3b3b3' - } + secondary: '#b3b3b3', + }, }, components: { MuiCard: { styleOverrides: { root: { - boxShadow: "none", + boxShadow: 'none', borderRadius: '8px', transition: 'all 0.3s ease-in-out', '&:hover': { cursor: 'pointer', boxShadow: - ' 0px 3px 4px 0px hsla(0,0%,0%,0.14), 0px 3px 3px -2px hsla(0,0%,0%,0.12), 0px 1px 8px 0px hsla(0,0%,0%,0.2);' - } - } - } + ' 0px 3px 4px 0px hsla(0,0%,0%,0.14), 0px 3px 3px -2px hsla(0,0%,0%,0.12), 0px 1px 8px 0px hsla(0,0%,0%,0.2);', + }, + }, + }, }, MuiIcon: { defaultProps: { style: { - color: '#ffffff' - } - } - } + color: '#ffffff', + }, + }, + }, }, -}) +}); -export { lightTheme, darkTheme } +export { lightTheme, darkTheme }; From f440d5bd3fc652707aab5c2621bc6f1fad100315 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 12 Apr 2025 16:38:52 +0200 Subject: [PATCH 13/31] Move css into theme folder --- src/styles/App-styles.ts | 260 +++++++++++++++++++++++++++++++++++ src/styles/ThemeContext.tsx | 29 ---- src/styles/ThemeSelector.tsx | 77 ----------- src/styles/index.css | 137 ++++++++++++++++++ src/styles/theme.ts | 4 +- 5 files changed, 399 insertions(+), 108 deletions(-) create mode 100644 src/styles/App-styles.ts delete mode 100644 src/styles/ThemeContext.tsx delete mode 100644 src/styles/ThemeSelector.tsx create mode 100644 src/styles/index.css diff --git a/src/styles/App-styles.ts b/src/styles/App-styles.ts new file mode 100644 index 0000000..9774f06 --- /dev/null +++ b/src/styles/App-styles.ts @@ -0,0 +1,260 @@ +import { Typography, Box, TextField, InputLabel } from '@mui/material'; +import { styled } from '@mui/system'; + +export const AppContainer = styled(Box)(({ theme }) => ({ + display: 'flex', + alignItems: 'center', + flexDirection: 'column', + width: '100vw', + height: '100vh', + radius: '15px', + backgroundColor: theme.palette.background.default, + color: theme.palette.text.primary, + overflow: 'hidden', +})); + +export const AuthenticatedContainer = styled(Box)(({ theme }) => ({ + display: 'flex', + width: '100%', + height: '100%', + justifyContent: 'space-between', + backgroundColor: theme.palette.background.default, + color: theme.palette.text.primary, +})); + +export const AuthenticatedContainerInnerLeft = styled(Box)(({ theme }) => ({ + display: 'flex', + alignItems: 'center', + flexDirection: 'column', + height: '100%', + width: '100%', + backgroundColor: theme.palette.background.default, + color: theme.palette.text.primary, +})); + +export const AuthenticatedContainerInnerRight = styled(Box)(({ theme }) => ({ + display: 'flex', + alignItems: 'center', + flexDirection: 'column', + width: '60px', + height: '100%', + backgroundColor: theme.palette.background.default, + color: theme.palette.text.primary, +})); + +export const AuthenticatedContainerInnerTop = styled(Box)(({ theme }) => ({ + display: 'flex', + alignItems: 'center', + justifyContent: 'flex-start', + width: '100%px', + height: '60px', + padding: '20px', + backgroundColor: theme.palette.background.default, + color: theme.palette.text.primary, +})); + +export const TextP = styled(Typography)(({ theme }) => ({ + fontSize: '13px', + fontWeight: 600, + fontFamily: 'Inter', + backgroundColor: theme.palette.background.default, + color: theme.palette.text.primary, +})); + +export const TextItalic = styled('span')(({ theme }) => ({ + fontSize: '13px', + fontWeight: 600, + fontFamily: 'Inter', + fontStyle: 'italic', + backgroundColor: theme.palette.background.default, + color: theme.palette.text.primary, +})); + +export const TextSpan = styled('span')(({ theme }) => ({ + fontSize: '13px', + fontFamily: 'Inter', + fontWeight: 800, + backgroundColor: theme.palette.background.default, + color: theme.palette.text.primary, +})); + +export const AddressBox = styled(Box)(({ theme }) => ({ + display: 'flex', + border: `1px solid ${ + theme.palette.mode === 'dark' + ? 'rgba(255, 255, 255, 0.5)' + : 'rgba(0, 0, 0, 0.3)' + }`, + justifyContent: 'space-between', + alignItems: 'center', + width: 'auto', + height: '25px', + padding: '5px 15px', + gap: '5px', + borderRadius: '100px', + fontFamily: 'Inter', + fontSize: '12px', + fontWeight: 600, + lineHeight: '14.52px', + textAlign: 'left', + backgroundColor: theme.palette.background.default, + color: theme.palette.text.primary, + cursor: 'pointer', + transition: 'all 0.2s', + + '&:hover': { + backgroundColor: + theme.palette.mode === 'dark' + ? 'rgba(41, 41, 43, 1)' + : 'rgba(240, 240, 240, 1)', + color: theme.palette.mode === 'dark' ? '#fff' : '#000', + + 'svg path': { + fill: theme.palette.mode === 'dark' ? '#fff' : '#000', + }, + }, +})); + +export const CustomButton = styled(Box)(({ theme }) => ({ + boxSizing: 'border-box', + padding: '15px 20px', + gap: '10px', + + border: `0.5px solid ${ + theme.palette.mode === 'dark' + ? 'rgba(255, 255, 255, 0.5)' + : 'rgba(0, 0, 0, 0.3)' + }`, + filter: 'drop-shadow(1px 4px 10.5px rgba(0, 0, 0, 0.3))', + borderRadius: '5px', + + display: 'inline-flex', + justifyContent: 'center', + alignItems: 'center', + + width: 'fit-content', + minWidth: '160px', + cursor: 'pointer', + transition: 'all 0.2s', + + fontWeight: 600, + fontFamily: 'Inter', + textAlign: 'center', + backgroundColor: theme.palette.background.default, + color: theme.palette.text.primary, + + '&:hover': { + backgroundColor: + theme.palette.mode === 'dark' + ? 'rgba(41, 41, 43, 1)' + : 'rgba(230, 230, 230, 1)', + color: '#fff', + + 'svg path': { + fill: '#fff', + }, + }, +})); + +interface CustomButtonProps { + bgColor?: string; + color?: string; +} + +export const CustomButtonAccept = styled(Box)( + ({ bgColor, color, theme }) => ({ + boxSizing: 'border-box', + padding: '15px 20px', + gap: '10px', + border: `0.5px solid ${ + theme.palette.mode === 'dark' + ? 'rgba(255, 255, 255, 0.5)' + : 'rgba(0, 0, 0, 0.3)' + }`, + filter: 'drop-shadow(1px 4px 10.5px rgba(0,0,0,0.3))', + borderRadius: 5, + display: 'inline-flex', + justifyContent: 'center', + alignItems: 'center', + width: 'fit-content', + transition: 'all 0.2s', + minWidth: 160, + cursor: 'pointer', + fontWeight: 600, + fontFamily: 'Inter', + textAlign: 'center', + opacity: 0.7, + + // Color and backgroundColor with fallbacks + backgroundColor: + bgColor || (theme.palette.mode === 'dark' ? '#1d1d1d' : '#f5f5f5'), + color: color || (theme.palette.mode === 'dark' ? '#fff' : '#000'), + + '&:hover': { + opacity: 1, + backgroundColor: + bgColor || + (theme.palette.mode === 'dark' + ? 'rgba(41, 41, 43, 1)' + : 'rgba(230, 230, 230, 1)'), + color: color || '#fff', + svg: { + path: { + fill: color || '#fff', + }, + }, + }, + }) +); + +export const CustomInput = styled(TextField)(({ theme }) => ({ + width: '183px', // Adjust the width as needed + borderRadius: '5px', + // backgroundColor: "rgba(30, 30, 32, 1)", + outline: 'none', + backgroundColor: theme.palette.background.default, + color: theme.palette.text.primary, + input: { + fontSize: 10, + fontFamily: 'Inter', + fontWeight: 400, + color: 'white', + '&::placeholder': { + fontSize: 16, + color: 'rgba(255, 255, 255, 0.2)', + }, + outline: 'none', + padding: '10px', + }, + '& .MuiOutlinedInput-root': { + '& fieldset': { + border: '0.5px solid rgba(255, 255, 255, 0.5)', + }, + '&:hover fieldset': { + border: '0.5px solid rgba(255, 255, 255, 0.5)', + }, + '&.Mui-focused fieldset': { + border: '0.5px solid rgba(255, 255, 255, 0.5)', + }, + }, + '& .MuiInput-underline:before': { + borderBottom: 'none', + }, + '& .MuiInput-underline:hover:not(.Mui-disabled):before': { + borderBottom: 'none', + }, + '& .MuiInput-underline:after': { + borderBottom: 'none', + }, +})); + +export const CustomLabel = styled(InputLabel)(({ theme }) => ({ + fontWeight: 400, + fontFamily: 'Inter', + fontSize: '10px', + lineHeight: '12px', + color: + theme.palette.mode === 'dark' + ? 'rgba(255, 255, 255, 0.5)' + : 'rgba(0, 0, 0, 0.5)', +})); diff --git a/src/styles/ThemeContext.tsx b/src/styles/ThemeContext.tsx deleted file mode 100644 index ed52fda..0000000 --- a/src/styles/ThemeContext.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import { createContext, useContext, useState, useMemo } from 'react'; -import { ThemeProvider as MuiThemeProvider } from '@mui/material/styles'; -import { darkTheme, lightTheme } from './theme'; - -const ThemeContext = createContext({ - themeMode: 'light', - toggleTheme: () => {}, -}); - -export const ThemeProvider = ({ children }: { children: React.ReactNode }) => { - const [themeMode, setThemeMode] = useState('light'); - - const theme = useMemo( - () => (themeMode === 'light' ? lightTheme : darkTheme), - [themeMode] - ); - - const toggleTheme = () => { - setThemeMode((prevMode) => (prevMode === 'light' ? 'dark' : 'light')); - }; - - return ( - - {children} - - ); -}; - -export const useThemeContext = () => useContext(ThemeContext); diff --git a/src/styles/ThemeSelector.tsx b/src/styles/ThemeSelector.tsx deleted file mode 100644 index 5b55f53..0000000 --- a/src/styles/ThemeSelector.tsx +++ /dev/null @@ -1,77 +0,0 @@ -import { useThemeContext } from "./ThemeContext"; -import { styled, Switch } from "@mui/material"; - -const ThemeSwitch = styled(Switch)(({ theme }) => ({ - width: 62, - height: 34, - padding: 7, - "& .MuiSwitch-switchBase": { - margin: 1, - padding: 0, - transform: "translateX(6px)", - "&.Mui-checked": { - color: "#fff", - transform: "translateX(22px)", - "& .MuiSwitch-thumb:before": { - backgroundImage: `url('data:image/svg+xml;utf8,')`, - }, - "& + .MuiSwitch-track": { - opacity: 1, - backgroundColor: "#aab4be", - ...theme.applyStyles("dark", { - backgroundColor: "#8796A5", - }), - }, - }, - }, - "& .MuiSwitch-thumb": { - backgroundColor: "#001e3c", - width: 32, - height: 32, - "&::before": { - content: "''", - position: "absolute", - width: "100%", - height: "100%", - left: 0, - top: 0, - backgroundRepeat: "no-repeat", - backgroundPosition: "center", - backgroundImage: `url('data:image/svg+xml;utf8,')`, - }, - ...theme.applyStyles("dark", { - backgroundColor: "#003892", - }), - }, - "& .MuiSwitch-track": { - opacity: 1, - backgroundColor: "#aab4be", - borderRadius: 20 / 2, - ...theme.applyStyles("dark", { - backgroundColor: "#8796A5", - }), - }, -})); - -const ThemeSelector = ({ style }) => { - const { themeMode, toggleTheme } = useThemeContext(); - return ( -
- -
- ); -}; - -export default ThemeSelector; diff --git a/src/styles/index.css b/src/styles/index.css new file mode 100644 index 0000000..3fd5ed7 --- /dev/null +++ b/src/styles/index.css @@ -0,0 +1,137 @@ +@font-face { + font-family: 'Inter'; + src: url('./styles/fonts/Inter-SemiBold.ttf') format('truetype'); + font-weight: 600; +} +@font-face { + font-family: 'Inter'; + src: url('./styles/fonts/Inter-ExtraBold.ttf') format('truetype'); + font-weight: 800; +} +@font-face { + font-family: 'Inter'; + src: url('./styles/fonts/Inter-Bold.ttf') format('truetype'); + font-weight: 700; +} +@font-face { + font-family: 'Inter'; + src: url('./styles/fonts/Inter-Regular.ttf') format('truetype'); + font-weight: 400; +} + +:root { + padding: 0px; + margin: 0px; + box-sizing: border-box !important; + word-break: break-word; + --color-instance: #1e1e20; + --color-instance-popover-bg: #222222; + --Mail-Background: rgba(49, 51, 56, 1); + --new-message-text: black; + --bg-primary: rgba(31, 32, 35, 1); + --bg-2: #27282c; + --bg-3: rgba(0, 0, 0, 0.1); + --unread: #4297e2; + --danger: #b14646; + --apps-circle: #1f2023; + --green: #5eb049; +} + +body { + margin: 0px; + overflow: hidden; +} + +.image-container { + position: relative; +} + +.image-container img { + position: absolute; + left: 0; + top: 0; + transition: opacity 0.5s ease; /* Optional: adds a fade effect */ +} + +.image-container .hover-image { + opacity: 0; +} + +.image-container:hover .hover-image { + opacity: 1; +} + +.image-container:hover .base-image { + opacity: 0.6; +} + +::-webkit-scrollbar-track { + background-color: transparent; +} +::-webkit-scrollbar-track:hover { + background-color: transparent; +} + +::-webkit-scrollbar { + width: 16px; + height: 10px; +} + +::-webkit-scrollbar-thumb { + background-color: #444444; + border-radius: 8px; + background-clip: content-box; + border: 4px solid transparent; + transition: 0.3s background-color; +} +::-webkit-scrollbar-thumb:hover { + background-color: #363636; +} + +@property --var1 { + syntax: ''; + inherits: true; + initial-value: transparent; +} + +.scrollable-container { + transition: --var1 0.4s; +} + +.scrollable-container:hover { + --var1: #444444; +} + +.scrollable-container::-webkit-scrollbar-thumb { + background-color: var(--var1); + border-radius: 8px; + background-clip: content-box; + border: 4px solid transparent; + opacity: 0; +} + +/* Mobile-specific scrollbar styles */ +@media only screen and (max-width: 600px) { + ::-webkit-scrollbar { + width: 8px; /* Narrower scrollbar width on mobile */ + height: 6px; /* Narrower scrollbar height on mobile */ + } + + ::-webkit-scrollbar-thumb { + border-radius: 4px; /* Adjust the radius for a narrower thumb */ + border: 2px solid transparent; /* Narrower thumb border */ + } +} + +.group-list::-webkit-scrollbar-thumb:hover { + background-color: whitesmoke; +} + +html, +body { + overscroll-behavior: none !important; +} + +.swiper { + width: 100%; +} diff --git a/src/styles/theme.ts b/src/styles/theme.ts index f036fe6..1ae2ea8 100644 --- a/src/styles/theme.ts +++ b/src/styles/theme.ts @@ -91,7 +91,7 @@ const lightTheme = createTheme({ light: '#f9f9fd', }, secondary: { - main: '#1EAAF1', + main: '#c2deec', }, background: { default: '#fafafa', @@ -108,7 +108,7 @@ const lightTheme = createTheme({ styleOverrides: { root: { boxShadow: - 'rgba(0, 0, 0, 0.1) 0px 1px 3px 0px, rgba(0, 0, 0, 0.06) 0px 1px 2px 0px;', + 'rgba(0, 0, 0, 0.1) 0px 1px 3px 0px, rgba(230, 200, 200, 0.06) 0px 1px 2px 0px;', borderRadius: '8px', transition: 'all 0.3s ease-in-out', '&:hover': { From 4f9b8fe6cc01a6069d200ca85f255d235b4d9489 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 12 Apr 2025 16:39:53 +0200 Subject: [PATCH 14/31] Transform svg into tsx --- src/Wallets.tsx | 2 +- src/assets/svgs/Download.tsx | 32 ++++++++++++++++++++++++++++++++ src/assets/svgs/Logout.tsx | 28 ++++++++++++++++++++++++++++ 3 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 src/assets/svgs/Download.tsx create mode 100644 src/assets/svgs/Logout.tsx diff --git a/src/Wallets.tsx b/src/Wallets.tsx index 5752639..8faf005 100644 --- a/src/Wallets.tsx +++ b/src/Wallets.tsx @@ -17,7 +17,7 @@ import { IconButton, Input, } from '@mui/material'; -import { CustomButton } from './App-styles'; +import { CustomButton } from './styles/App-styles'; import { useDropzone } from 'react-dropzone'; import EditIcon from '@mui/icons-material/Edit'; import { Label } from './components/Group/AddGroup'; diff --git a/src/assets/svgs/Download.tsx b/src/assets/svgs/Download.tsx new file mode 100644 index 0000000..577f1b4 --- /dev/null +++ b/src/assets/svgs/Download.tsx @@ -0,0 +1,32 @@ +import { useTheme } from '@mui/material'; +import { SVGProps } from './interfaces'; + +export const Download: React.FC = ({ + color, + opacity, + ...children +}) => { + const theme = useTheme(); + + const setColor = color ? color : theme.palette.text.primary; + const setOpacity = opacity ? opacity : 0.5; + + return ( + + + + ); +}; diff --git a/src/assets/svgs/Logout.tsx b/src/assets/svgs/Logout.tsx new file mode 100644 index 0000000..3cdf9bb --- /dev/null +++ b/src/assets/svgs/Logout.tsx @@ -0,0 +1,28 @@ +import { useTheme } from '@mui/material'; +import { SVGProps } from './interfaces'; + +export const Logout: React.FC = ({ color, opacity, ...children }) => { + const theme = useTheme(); + + const setColor = color ? color : theme.palette.text.primary; + const setOpacity = opacity ? opacity : 0.3; + + return ( + + + + ); +}; From 2a41667ef87e7fcfa68dd482fb831e1904442873 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 12 Apr 2025 16:40:32 +0200 Subject: [PATCH 15/31] Format code, rmove unused --- src/ExtStates/NotAuthenticated.tsx | 4 +- src/common/BoundedNumericTextField.tsx | 60 +- src/components/AddressQRCode.tsx | 52 +- src/components/Apps/AppsHomeDesktop.tsx | 3 +- .../Chat/AnnouncementDiscussion.tsx | 414 ++-- src/components/Chat/AnnouncementList.tsx | 88 +- src/components/Chat/ChatDirect.tsx | 1265 +++++----- src/components/Chat/ChatGroup.tsx | 1880 ++++++++------ src/components/Chat/GroupAnnouncements.tsx | 419 ++-- src/components/DesktopSideBar.tsx | 2 +- src/components/GlobalActions/JoinGroup.tsx | 148 +- src/components/Group/Group.tsx | 2206 +++++++++-------- src/components/Minting/Minting.tsx | 314 ++- src/components/QortPayment.tsx | 303 +-- src/components/Theme/ThemeContext.tsx | 29 + src/components/Theme/ThemeSelector.tsx | 77 + 16 files changed, 3969 insertions(+), 3295 deletions(-) create mode 100644 src/components/Theme/ThemeContext.tsx create mode 100644 src/components/Theme/ThemeSelector.tsx diff --git a/src/ExtStates/NotAuthenticated.tsx b/src/ExtStates/NotAuthenticated.tsx index 049c282..f7729de 100644 --- a/src/ExtStates/NotAuthenticated.tsx +++ b/src/ExtStates/NotAuthenticated.tsx @@ -6,7 +6,7 @@ import React, { useState, } from 'react'; import { Spacer } from '../common/Spacer'; -import { CustomButton, TextP, TextSpan } from '../App-styles'; +import { CustomButton, TextP, TextSpan } from '../styles/App-styles'; import { Box, Button, @@ -29,7 +29,7 @@ import { CustomizedSnackbars } from '../components/Snackbar/Snackbar'; import { cleanUrl, gateways } from '../background'; import { GlobalContext } from '../App'; import Tooltip, { TooltipProps, tooltipClasses } from '@mui/material/Tooltip'; -import ThemeSelector from '../styles/ThemeSelector'; +import ThemeSelector from '../components/Theme/ThemeSelector'; const manifestData = { version: '0.5.3', diff --git a/src/common/BoundedNumericTextField.tsx b/src/common/BoundedNumericTextField.tsx index c997f8a..9951d7e 100644 --- a/src/common/BoundedNumericTextField.tsx +++ b/src/common/BoundedNumericTextField.tsx @@ -3,15 +3,15 @@ import { InputAdornment, TextField, TextFieldProps, -} from "@mui/material"; -import React, { useRef, useState } from "react"; -import AddIcon from "@mui/icons-material/Add"; -import RemoveIcon from "@mui/icons-material/Remove"; +} from '@mui/material'; +import React, { useRef, useState } from 'react'; +import AddIcon from '@mui/icons-material/Add'; +import RemoveIcon from '@mui/icons-material/Remove'; import { removeTrailingZeros, setNumberWithinBounds, -} from "./numberFunctions.ts"; -import { CustomInput } from "../App-styles.ts"; +} from './numberFunctions.ts'; +import { CustomInput } from '../styles/App-styles.ts'; type eventType = React.ChangeEvent; type BoundedNumericTextFieldProps = { @@ -37,18 +37,18 @@ export const BoundedNumericTextField = ({ ...props }: BoundedNumericTextFieldProps) => { const [textFieldValue, setTextFieldValue] = useState( - initialValue || "" + initialValue || '' ); const ref = useRef(null); const stringIsEmpty = (value: string) => { - return value === ""; + return value === ''; }; const isAllZerosNum = /^0*\.?0*$/; const isFloatNum = /^-?[0-9]*\.?[0-9]*$/; const isIntegerNum = /^-?[0-9]+$/; const skipMinMaxCheck = (value: string) => { - const lastIndexIsDecimal = value.charAt(value.length - 1) === "."; + const lastIndexIsDecimal = value.charAt(value.length - 1) === '.'; const isEmpty = stringIsEmpty(value); const isAllZeros = isAllZerosNum.test(value); const isInteger = isIntegerNum.test(value); @@ -69,7 +69,7 @@ export const BoundedNumericTextField = ({ const getSigDigits = (number: string) => { if (isIntegerNum.test(number)) return 0; - const decimalSplit = number.split("."); + const decimalSplit = number.split('.'); return decimalSplit[decimalSplit.length - 1].length; }; @@ -78,15 +78,15 @@ export const BoundedNumericTextField = ({ }; const filterTypes = (value: string) => { - if (allowDecimals === false) value = value.replace(".", ""); - if (allowNegatives === false) value = value.replace("-", ""); + if (allowDecimals === false) value = value.replace('.', ''); + if (allowNegatives === false) value = value.replace('-', ''); if (sigDigitsExceeded(value, maxSigDigits)) { value = value.substring(0, value.length - 1); } return value; }; const filterValue = (value: string) => { - if (stringIsEmpty(value)) return ""; + if (stringIsEmpty(value)) return ''; value = filterTypes(value); if (isFloatNum.test(value)) { return setMinMaxValue(value); @@ -109,8 +109,8 @@ export const BoundedNumericTextField = ({ const formatValueOnBlur = (e: eventType) => { let value = e.target.value; - if (stringIsEmpty(value) || value === ".") { - setTextFieldValue(""); + if (stringIsEmpty(value) || value === '.') { + setTextFieldValue(''); return; } @@ -129,23 +129,33 @@ export const BoundedNumericTextField = ({ ...props?.InputProps, endAdornment: addIconButtons ? ( - changeValueWithIncDecButton(1)}> - {" "} + changeValueWithIncDecButton(1)} + > + {' '} - changeValueWithIncDecButton(-1)}> - {" "} + changeValueWithIncDecButton(-1)} + > + {' '} ) : ( <> ), }} - onChange={e => listeners(e as eventType)} - onBlur={e => { + onChange={(e) => listeners(e as eventType)} + onBlur={(e) => { formatValueOnBlur(e as eventType); }} autoComplete="off" diff --git a/src/components/AddressQRCode.tsx b/src/components/AddressQRCode.tsx index 77fa252..ebe2036 100644 --- a/src/components/AddressQRCode.tsx +++ b/src/components/AddressQRCode.tsx @@ -1,57 +1,57 @@ -import React, { useState } from "react"; -import QRCode from "react-qr-code"; -import { TextP } from "../App-styles"; -import { Box, Typography } from "@mui/material"; +import React, { useState } from 'react'; +import QRCode from 'react-qr-code'; +import { TextP } from '../styles/App-styles'; +import { Box, Typography } from '@mui/material'; export const AddressQRCode = ({ targetAddress }) => { const [open, setOpen] = useState(false); return ( { - setOpen((prev)=> !prev); + setOpen((prev) => !prev); }} > - {open ? 'Hide QR code' :'See QR code'} + {open ? 'Hide QR code' : 'See QR code'} {open && ( diff --git a/src/components/Apps/AppsHomeDesktop.tsx b/src/components/Apps/AppsHomeDesktop.tsx index 7d65edb..b8167ca 100644 --- a/src/components/Apps/AppsHomeDesktop.tsx +++ b/src/components/Apps/AppsHomeDesktop.tsx @@ -15,7 +15,8 @@ import { SortablePinnedApps } from './SortablePinnedApps'; import { extractComponents } from '../Chat/MessageDisplay'; import ArrowOutwardIcon from '@mui/icons-material/ArrowOutward'; import { AppsPrivate } from './AppsPrivate'; -import ThemeSelector from '../../styles/ThemeSelector'; +import ThemeSelector from '../Theme/ThemeSelector'; + export const AppsHomeDesktop = ({ setMode, myApp, diff --git a/src/components/Chat/AnnouncementDiscussion.tsx b/src/components/Chat/AnnouncementDiscussion.tsx index 5603de0..5ebe885 100644 --- a/src/components/Chat/AnnouncementDiscussion.tsx +++ b/src/components/Chat/AnnouncementDiscussion.tsx @@ -1,18 +1,32 @@ -import React, { useMemo, useRef, useState } from "react"; -import TipTap from "./TipTap"; -import { AuthenticatedContainerInnerTop, CustomButton } from "../../App-styles"; -import { Box, CircularProgress } from "@mui/material"; -import { objectToBase64 } from "../../qdn/encryption/group-encryption"; -import ShortUniqueId from "short-unique-id"; -import { LoadingSnackbar } from "../Snackbar/LoadingSnackbar"; -import { getBaseApi, getFee } from "../../background"; -import { decryptPublishes, getTempPublish, handleUnencryptedPublishes, saveTempPublish } from "./GroupAnnouncements"; -import { AnnouncementList } from "./AnnouncementList"; -import { Spacer } from "../../common/Spacer"; +import React, { useMemo, useRef, useState } from 'react'; +import TipTap from './TipTap'; +import { + AuthenticatedContainerInnerTop, + CustomButton, +} from '../../styles/App-styles'; +import { Box, CircularProgress } from '@mui/material'; +import { objectToBase64 } from '../../qdn/encryption/group-encryption'; +import ShortUniqueId from 'short-unique-id'; +import { LoadingSnackbar } from '../Snackbar/LoadingSnackbar'; +import { getBaseApi, getFee } from '../../background'; +import { + decryptPublishes, + getTempPublish, + handleUnencryptedPublishes, + saveTempPublish, +} from './GroupAnnouncements'; +import { AnnouncementList } from './AnnouncementList'; +import { Spacer } from '../../common/Spacer'; import ArrowBackIcon from '@mui/icons-material/ArrowBack'; -import { getArbitraryEndpointReact, getBaseApiReact, isMobile, pauseAllQueues, resumeAllQueues } from "../../App"; +import { + getArbitraryEndpointReact, + getBaseApiReact, + isMobile, + pauseAllQueues, + resumeAllQueues, +} from '../../App'; -const tempKey = 'accouncement-comment' +const tempKey = 'accouncement-comment'; const uid = new ShortUniqueId({ length: 8 }); export const AnnouncementDiscussion = ({ @@ -23,28 +37,28 @@ export const AnnouncementDiscussion = ({ setSelectedAnnouncement, show, myName, - isPrivate + isPrivate, }) => { const [isSending, setIsSending] = useState(false); const [isLoading, setIsLoading] = useState(false); const [isFocusedParent, setIsFocusedParent] = useState(false); - const [comments, setComments] = useState([]) - const [tempPublishedList, setTempPublishedList] = useState([]) - const firstMountRef = useRef(false) - const [data, setData] = useState({}) + const [comments, setComments] = useState([]); + const [tempPublishedList, setTempPublishedList] = useState([]); + const firstMountRef = useRef(false); + const [data, setData] = useState({}); const editorRef = useRef(null); const setEditorRef = (editorInstance) => { editorRef.current = editorInstance; }; - + const clearEditorContent = () => { if (editorRef.current) { editorRef.current.chain().focus().clearContent().run(); - if(isMobile){ + if (isMobile) { setTimeout(() => { - editorRef.current?.chain().blur().run(); - setIsFocusedParent(false) + editorRef.current?.chain().blur().run(); + setIsFocusedParent(false); }, 200); } } @@ -52,14 +66,16 @@ export const AnnouncementDiscussion = ({ const getData = async ({ identifier, name }, isPrivate) => { try { - const res = await fetch( `${getBaseApiReact()}/arbitrary/DOCUMENT/${name}/${identifier}?encoding=base64` ); - if(!res?.ok) return + if (!res?.ok) return; const data = await res.text(); - const response = isPrivate === false ? handleUnencryptedPublishes([data]) : await decryptPublishes([{ data }], secretKey); - + const response = + isPrivate === false + ? handleUnencryptedPublishes([data]) + : await decryptPublishes([{ data }], secretKey); + const messageData = response[0]; setData((prev) => { return { @@ -67,19 +83,19 @@ export const AnnouncementDiscussion = ({ [`${identifier}-${name}`]: messageData, }; }); - } catch (error) {} }; const publishAnc = async ({ encryptedData, identifier }: any) => { try { if (!selectedAnnouncement) return; - + return new Promise((res, rej) => { - window.sendMessage("publishGroupEncryptedResource", { - encryptedData, - identifier, - }) + window + .sendMessage('publishGroupEncryptedResource', { + encryptedData, + identifier, + }) .then((response) => { if (!response?.error) { res(response); @@ -88,63 +104,60 @@ export const AnnouncementDiscussion = ({ rej(response.error); }) .catch((error) => { - rej(error.message || "An error occurred"); + rej(error.message || 'An error occurred'); }); - }); } catch (error) {} }; - const setTempData = async ()=> { + const setTempData = async () => { try { - const getTempAnnouncements = await getTempPublish() - if(getTempAnnouncements[tempKey]){ - let tempData = [] - Object.keys(getTempAnnouncements[tempKey] || {}).map((key)=> { - const value = getTempAnnouncements[tempKey][key] - if(value.data?.announcementId === selectedAnnouncement.identifier){ - tempData.push(value.data) + const getTempAnnouncements = await getTempPublish(); + if (getTempAnnouncements[tempKey]) { + let tempData = []; + Object.keys(getTempAnnouncements[tempKey] || {}).map((key) => { + const value = getTempAnnouncements[tempKey][key]; + if (value.data?.announcementId === selectedAnnouncement.identifier) { + tempData.push(value.data); + } + }); + setTempPublishedList(tempData); } - }) - setTempPublishedList(tempData) - } - } catch (error) { - - } - - } + } catch (error) {} + }; const publishComment = async () => { try { - pauseAllQueues() - const fee = await getFee('ARBITRARY') + pauseAllQueues(); + const fee = await getFee('ARBITRARY'); await show({ - message: "Would you like to perform a ARBITRARY transaction?" , - publishFee: fee.fee + ' QORT' - }) + message: 'Would you like to perform a ARBITRARY transaction?', + publishFee: fee.fee + ' QORT', + }); if (isSending) return; if (editorRef.current) { const htmlContent = editorRef.current.getHTML(); - - if (!htmlContent?.trim() || htmlContent?.trim() === "

") return; + + if (!htmlContent?.trim() || htmlContent?.trim() === '

') return; setIsSending(true); const message = { version: 1, extra: {}, message: htmlContent, }; - const secretKeyObject = isPrivate === false ? null : await getSecretKey(false, true); - const message64: any = await objectToBase64(message); - - const encryptSingle = isPrivate === false ? message64 : await encryptChatMessage( - message64, - secretKeyObject - ); + const secretKeyObject = + isPrivate === false ? null : await getSecretKey(false, true); + const message64: any = await objectToBase64(message); + + const encryptSingle = + isPrivate === false + ? message64 + : await encryptChatMessage(message64, secretKeyObject); const randomUid = uid.rnd(); const identifier = `cm-${selectedAnnouncement.identifier}-${randomUid}`; const res = await publishAnc({ encryptedData: encryptSingle, - identifier + identifier, }); const dataToSaveToStorage = { @@ -153,18 +166,18 @@ export const AnnouncementDiscussion = ({ service: 'DOCUMENT', tempData: message, created: Date.now(), - announcementId: selectedAnnouncement.identifier - } - await saveTempPublish({data: dataToSaveToStorage, key: tempKey}) - setTempData() - + announcementId: selectedAnnouncement.identifier, + }; + await saveTempPublish({ data: dataToSaveToStorage, key: tempKey }); + setTempData(); + clearEditorContent(); } // send chat message } catch (error) { console.error(error); } finally { - resumeAllQueues() + resumeAllQueues(); setIsSending(false); } }; @@ -172,7 +185,6 @@ export const AnnouncementDiscussion = ({ const getComments = React.useCallback( async (selectedAnnouncement, isPrivate) => { try { - setIsLoading(true); const offset = 0; @@ -181,13 +193,13 @@ export const AnnouncementDiscussion = ({ const identifier = `cm-${selectedAnnouncement.identifier}`; const url = `${getBaseApiReact()}${getArbitraryEndpointReact()}?mode=ALL&service=DOCUMENT&identifier=${identifier}&limit=20&includemetadata=false&offset=${offset}&reverse=true&prefix=true`; const response = await fetch(url, { - method: "GET", + method: 'GET', headers: { - "Content-Type": "application/json", + 'Content-Type': 'application/json', }, }); const responseData = await response.json(); - setTempData() + setTempData(); setComments(responseData); setIsLoading(false); for (const data of responseData) { @@ -203,119 +215,122 @@ export const AnnouncementDiscussion = ({ [secretKey] ); - const loadMore = async()=> { + const loadMore = async () => { try { setIsLoading(true); - const offset = comments.length + const offset = comments.length; const identifier = `cm-${selectedAnnouncement.identifier}`; - const url = `${getBaseApiReact()}${getArbitraryEndpointReact()}?mode=ALL&service=DOCUMENT&identifier=${identifier}&limit=20&includemetadata=false&offset=${offset}&reverse=true&prefix=true`; - const response = await fetch(url, { - method: "GET", - headers: { - "Content-Type": "application/json", - }, - }); - const responseData = await response.json(); + const url = `${getBaseApiReact()}${getArbitraryEndpointReact()}?mode=ALL&service=DOCUMENT&identifier=${identifier}&limit=20&includemetadata=false&offset=${offset}&reverse=true&prefix=true`; + const response = await fetch(url, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, + }); + const responseData = await response.json(); - setComments((prev)=> [...prev, ...responseData]); - setIsLoading(false); - for (const data of responseData) { - getData({ name: data.name, identifier: data.identifier }, isPrivate); - } - } catch (error) { - - } - - } + setComments((prev) => [...prev, ...responseData]); + setIsLoading(false); + for (const data of responseData) { + getData({ name: data.name, identifier: data.identifier }, isPrivate); + } + } catch (error) {} + }; const combinedListTempAndReal = useMemo(() => { // Combine the two lists const combined = [...tempPublishedList, ...comments]; - + // Remove duplicates based on the "identifier" const uniqueItems = new Map(); - combined.forEach(item => { - uniqueItems.set(item.identifier, item); // This will overwrite duplicates, keeping the last occurrence + combined.forEach((item) => { + uniqueItems.set(item.identifier, item); // This will overwrite duplicates, keeping the last occurrence }); - + // Convert the map back to an array and sort by "created" timestamp in descending order - const sortedList = Array.from(uniqueItems.values()).sort((a, b) => b.created - a.created); - + const sortedList = Array.from(uniqueItems.values()).sort( + (a, b) => b.created - a.created + ); + return sortedList; }, [tempPublishedList, comments]); React.useEffect(() => { - if(!secretKey && isPrivate) return + if (!secretKey && isPrivate) return; if (selectedAnnouncement && !firstMountRef.current && isPrivate !== null) { getComments(selectedAnnouncement, isPrivate); - firstMountRef.current = true + firstMountRef.current = true; } }, [selectedAnnouncement, secretKey, isPrivate]); return (
-
- - - setSelectedAnnouncement(null)} sx={{ - cursor: 'pointer' - }} /> - +
+ + setSelectedAnnouncement(null)} + sx={{ + cursor: 'pointer', + }} + /> + -
{}} + setSelectedAnnouncement={() => {}} disableComment showLoadMore={comments.length > 0 && comments.length % 20 === 0} loadMore={loadMore} myName={myName} - />
- - {isFocusedParent && ( - { - if(isSending) return - setIsFocusedParent(false) - clearEditorContent() - // Unfocus the editor - }} - style={{ - marginTop: 'auto', - alignSelf: 'center', - cursor: isSending ? 'default' : 'pointer', - flexShrink: 0, - padding: isMobile && '5px', - fontSize: isMobile && '14px', - background: 'red', - }} - > - - {` Close`} - - - )} - { - if (isSending) return; - publishComment(); - }} - style={{ - marginTop: "auto", - alignSelf: "center", - cursor: isSending ? "default" : "pointer", - background: isSending && "rgba(0, 0, 0, 0.8)", + - {isSending && ( - { + if (isSending) return; + setIsFocusedParent(false); + clearEditorContent(); + // Unfocus the editor }} - /> + style={{ + marginTop: 'auto', + alignSelf: 'center', + cursor: isSending ? 'default' : 'pointer', + flexShrink: 0, + padding: isMobile && '5px', + fontSize: isMobile && '14px', + background: 'red', + }} + > + {` Close`} + )} - {` Publish Comment`} - - - + { + if (isSending) return; + publishComment(); + }} + style={{ + marginTop: 'auto', + alignSelf: 'center', + cursor: isSending ? 'default' : 'pointer', + background: isSending && 'rgba(0, 0, 0, 0.8)', + flexShrink: 0, + padding: isMobile && '5px', + fontSize: isMobile && '14px', + }} + > + {isSending && ( + + )} + {` Publish Comment`} + +
- +
diff --git a/src/components/Chat/AnnouncementList.tsx b/src/components/Chat/AnnouncementList.tsx index b55ebb5..efaf1fa 100644 --- a/src/components/Chat/AnnouncementList.tsx +++ b/src/components/Chat/AnnouncementList.tsx @@ -1,13 +1,13 @@ -import React, { useCallback, useState, useEffect, useRef } from "react"; +import React, { useCallback, useState, useEffect, useRef } from 'react'; import { List, AutoSizer, CellMeasurerCache, CellMeasurer, -} from "react-virtualized"; -import { AnnouncementItem } from "./AnnouncementItem"; -import { Box } from "@mui/material"; -import { CustomButton } from "../../App-styles"; +} from 'react-virtualized'; +import { AnnouncementItem } from './AnnouncementItem'; +import { Box } from '@mui/material'; +import { CustomButton } from '../../styles/App-styles'; const cache = new CellMeasurerCache({ fixedWidth: true, @@ -21,9 +21,8 @@ export const AnnouncementList = ({ disableComment, showLoadMore, loadMore, - myName + myName, }) => { - const listRef = useRef(); const [messages, setMessages] = useState(initialMessages); @@ -35,39 +34,44 @@ export const AnnouncementList = ({ setMessages(initialMessages); }, [initialMessages]); - return (
{messages.map((message) => { - const messageData = message?.tempData ? { - decryptedData: message?.tempData - } : announcementData[`${message.identifier}-${message.name}`]; + const messageData = message?.tempData + ? { + decryptedData: message?.tempData, + } + : announcementData[`${message.identifier}-${message.name}`]; return ( - -
- -
- +
+ +
); })} {/* @@ -83,16 +87,20 @@ export const AnnouncementList = ({ /> )} */} - - {showLoadMore && ( - Load older announcements - )} - + + {showLoadMore && ( + + Load older announcements + + )} +
); }; diff --git a/src/components/Chat/ChatDirect.tsx b/src/components/Chat/ChatDirect.tsx index d23f026..1af3f8e 100644 --- a/src/components/Chat/ChatDirect.tsx +++ b/src/components/Chat/ChatDirect.tsx @@ -1,52 +1,78 @@ -import React, { useCallback, useEffect, useMemo, useReducer, useRef, useState } from 'react' +import React, { + useCallback, + useEffect, + useMemo, + useReducer, + useRef, + useState, +} from 'react'; -import { objectToBase64 } from '../../qdn/encryption/group-encryption' -import { ChatList } from './ChatList' -import "@chatscope/chat-ui-kit-styles/dist/default/styles.min.css"; -import Tiptap from './TipTap' -import { CustomButton } from '../../App-styles' +import { objectToBase64 } from '../../qdn/encryption/group-encryption'; +import { ChatList } from './ChatList'; +import '@chatscope/chat-ui-kit-styles/dist/default/styles.min.css'; +import Tiptap from './TipTap'; +import { CustomButton } from '../../styles/App-styles'; import CircularProgress from '@mui/material/CircularProgress'; import { Box, ButtonBase, Input, Typography } from '@mui/material'; import { LoadingSnackbar } from '../Snackbar/LoadingSnackbar'; import { getNameInfo } from '../Group/Group'; import { Spacer } from '../../common/Spacer'; import { CustomizedSnackbars } from '../Snackbar/Snackbar'; -import { getBaseApiReact, getBaseApiReactSocket, isMobile, pauseAllQueues, resumeAllQueues } from '../../App'; +import { + getBaseApiReact, + getBaseApiReactSocket, + isMobile, + pauseAllQueues, + resumeAllQueues, +} from '../../App'; import { getPublicKey } from '../../background'; import { useMessageQueue } from '../../MessageQueueContext'; -import { executeEvent, subscribeToEvent, unsubscribeFromEvent } from '../../utils/events'; +import { + executeEvent, + subscribeToEvent, + unsubscribeFromEvent, +} from '../../utils/events'; 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 { ExitIcon } from '../../assets/Icons/ExitIcon'; import { MessageItem, ReplyPreview } from './MessageItem'; - const uid = new ShortUniqueId({ length: 5 }); +export const ChatDirect = ({ + myAddress, + isNewChat, + selectedDirect, + setSelectedDirect, + setNewChat, + getTimestampEnterChat, + myName, + balance, + close, + setMobileViewModeKeepOpen, +}) => { + const { queueChats, addToQueue, processWithNewMessages } = useMessageQueue(); + const [isFocusedParent, setIsFocusedParent] = useState(false); + const [onEditMessage, setOnEditMessage] = useState(null); -export const ChatDirect = ({ myAddress, isNewChat, selectedDirect, setSelectedDirect, setNewChat, getTimestampEnterChat, myName, balance, close, setMobileViewModeKeepOpen}) => { - const { queueChats, addToQueue, processWithNewMessages} = useMessageQueue(); - const [isFocusedParent, setIsFocusedParent] = useState(false); - const [onEditMessage, setOnEditMessage] = useState(null) - - const [messages, setMessages] = useState([]) - const [isSending, setIsSending] = useState(false) - const [directToValue, setDirectToValue] = useState('') - const hasInitialized = useRef(false) - const [isLoading, setIsLoading] = useState(false) + const [messages, setMessages] = useState([]); + const [isSending, setIsSending] = useState(false); + const [directToValue, setDirectToValue] = useState(''); + const hasInitialized = useRef(false); + const [isLoading, setIsLoading] = useState(false); const [openSnack, setOpenSnack] = React.useState(false); const [infoSnack, setInfoSnack] = React.useState(null); - const [publicKeyOfRecipient, setPublicKeyOfRecipient] = React.useState("") - const hasInitializedWebsocket = useRef(false) - const [chatReferences, setChatReferences] = useState({}) + const [publicKeyOfRecipient, setPublicKeyOfRecipient] = React.useState(''); + const hasInitializedWebsocket = useRef(false); + const [chatReferences, setChatReferences] = useState({}); const editorRef = useRef(null); const socketRef = useRef(null); const timeoutIdRef = useRef(null); - const [messageSize, setMessageSize] = useState(0) + const [messageSize, setMessageSize] = useState(0); const groupSocketTimeoutRef = useRef(null); - const [replyMessage, setReplyMessage] = useState(null) + const [replyMessage, setReplyMessage] = useState(null); const setEditorRef = (editorInstance) => { editorRef.current = editorInstance; }; @@ -55,42 +81,47 @@ export const ChatDirect = ({ myAddress, isNewChat, selectedDirect, setSelectedDi const triggerRerender = () => { forceUpdate(); // Trigger re-render by updating the state }; - const publicKeyOfRecipientRef = useRef(null) - const getPublicKeyFunc = async (address)=> { + const publicKeyOfRecipientRef = useRef(null); + const getPublicKeyFunc = async (address) => { try { - const publicKey = await getPublicKey(address) - if(publicKeyOfRecipientRef.current !== selectedDirect?.address) return - setPublicKeyOfRecipient(publicKey) - } catch (error) { - - } - } + const publicKey = await getPublicKey(address); + if (publicKeyOfRecipientRef.current !== selectedDirect?.address) return; + setPublicKeyOfRecipient(publicKey); + } catch (error) {} + }; - const tempMessages = useMemo(()=> { - if(!selectedDirect?.address) return [] - if(queueChats[selectedDirect?.address]){ - return queueChats[selectedDirect?.address]?.filter((item)=> !item?.chatReference) + const tempMessages = useMemo(() => { + if (!selectedDirect?.address) return []; + if (queueChats[selectedDirect?.address]) { + return queueChats[selectedDirect?.address]?.filter( + (item) => !item?.chatReference + ); } - return [] - }, [selectedDirect?.address, queueChats]) + return []; + }, [selectedDirect?.address, queueChats]); - const tempChatReferences = useMemo(()=> { - if(!selectedDirect?.address) return [] - if(queueChats[selectedDirect?.address]){ - return queueChats[selectedDirect?.address]?.filter((item)=> !!item?.chatReference) + const tempChatReferences = useMemo(() => { + if (!selectedDirect?.address) return []; + if (queueChats[selectedDirect?.address]) { + return queueChats[selectedDirect?.address]?.filter( + (item) => !!item?.chatReference + ); } - return [] - }, [selectedDirect?.address, queueChats]) + return []; + }, [selectedDirect?.address, queueChats]); - useEffect(()=> { - if(selectedDirect?.address){ - publicKeyOfRecipientRef.current = selectedDirect?.address - getPublicKeyFunc(publicKeyOfRecipientRef.current) + useEffect(() => { + if (selectedDirect?.address) { + publicKeyOfRecipientRef.current = selectedDirect?.address; + getPublicKeyFunc(publicKeyOfRecipientRef.current); } - }, [selectedDirect?.address]) - + }, [selectedDirect?.address]); - const middletierFunc = async (data: any, selectedDirectAddress: string, myAddress: string) => { + const middletierFunc = async ( + data: any, + selectedDirectAddress: string, + myAddress: string + ) => { try { if (hasInitialized.current) { decryptMessages(data, true); @@ -99,9 +130,9 @@ export const ChatDirect = ({ myAddress, isNewChat, selectedDirect, setSelectedDi hasInitialized.current = true; const url = `${getBaseApiReact()}/chat/messages?involving=${selectedDirectAddress}&involving=${myAddress}&encoding=BASE64&limit=0&reverse=false`; const response = await fetch(url, { - method: "GET", + method: 'GET', headers: { - "Content-Type": "application/json", + 'Content-Type': 'application/json', }, }); const responseData = await response.json(); @@ -109,589 +140,666 @@ export const ChatDirect = ({ myAddress, isNewChat, selectedDirect, setSelectedDi } catch (error) { console.error(error); } - } + }; - const decryptMessages = (encryptedMessages: any[], isInitiated: boolean)=> { - try { - return new Promise((res, rej)=> { - window.sendMessage("decryptDirect", { + const decryptMessages = (encryptedMessages: any[], isInitiated: boolean) => { + try { + return new Promise((res, rej) => { + window + .sendMessage('decryptDirect', { data: encryptedMessages, involvingAddress: selectedDirect?.address, }) - .then((decryptResponse) => { - if (!decryptResponse?.error) { - const response = processWithNewMessages(decryptResponse, selectedDirect?.address); - res(response); - - if (isInitiated) { - const formatted = response.filter((rawItem) => !rawItem?.chatReference).map((item) => ({ + .then((decryptResponse) => { + if (!decryptResponse?.error) { + const response = processWithNewMessages( + decryptResponse, + selectedDirect?.address + ); + res(response); + + if (isInitiated) { + const formatted = response + .filter((rawItem) => !rawItem?.chatReference) + .map((item) => ({ ...item, id: item.signature, text: item.message, unread: item?.sender === myAddress ? false : true, })); - setMessages((prev) => [...prev, ...formatted]); - setChatReferences((prev) => { - const organizedChatReferences = { ...prev }; + setMessages((prev) => [...prev, ...formatted]); + setChatReferences((prev) => { + const organizedChatReferences = { ...prev }; - response.filter((rawItem) => !!rawItem?.chatReference && rawItem?.type === 'edit').forEach((item) => { - try { - organizedChatReferences[item.chatReference] = { - ...(organizedChatReferences[item.chatReference] || {}), - edit: item - }; - } catch(error){ - - } - }) - return organizedChatReferences - }) - } else { - hasInitialized.current = true; - const formatted = response.filter((rawItem) => !rawItem?.chatReference) + response + .filter( + (rawItem) => + !!rawItem?.chatReference && rawItem?.type === 'edit' + ) + .forEach((item) => { + try { + organizedChatReferences[item.chatReference] = { + ...(organizedChatReferences[item.chatReference] || + {}), + edit: item, + }; + } catch (error) {} + }); + return organizedChatReferences; + }); + } else { + hasInitialized.current = true; + const formatted = response + .filter((rawItem) => !rawItem?.chatReference) .map((item) => ({ ...item, id: item.signature, text: item.message, unread: false, })); - setMessages(formatted); + setMessages(formatted); - setChatReferences((prev) => { - const organizedChatReferences = { ...prev }; + setChatReferences((prev) => { + const organizedChatReferences = { ...prev }; - response.filter((rawItem) => !!rawItem?.chatReference && rawItem?.type === 'edit').forEach((item) => { - try { - organizedChatReferences[item.chatReference] = { - ...(organizedChatReferences[item.chatReference] || {}), - edit: item - }; - } catch(error){ - - } - }) - return organizedChatReferences - }) - } - return; + response + .filter( + (rawItem) => + !!rawItem?.chatReference && rawItem?.type === 'edit' + ) + .forEach((item) => { + try { + organizedChatReferences[item.chatReference] = { + ...(organizedChatReferences[item.chatReference] || + {}), + edit: item, + }; + } catch (error) {} + }); + return organizedChatReferences; + }); } - rej(response.error); - }) - .catch((error) => { - rej(error.message || "An error occurred"); - }); - - }) - } catch (error) { - - } - } + return; + } + rej(response.error); + }) + .catch((error) => { + rej(error.message || 'An error occurred'); + }); + }); + } catch (error) {} + }; - const forceCloseWebSocket = () => { + const forceCloseWebSocket = () => { + if (socketRef.current) { + clearTimeout(timeoutIdRef.current); + clearTimeout(groupSocketTimeoutRef.current); + socketRef.current.close(1000, 'forced'); + socketRef.current = null; + } + }; + + const pingWebSocket = () => { + try { + if (socketRef.current?.readyState === WebSocket.OPEN) { + socketRef.current.send('ping'); + timeoutIdRef.current = setTimeout(() => { + if (socketRef.current) { + socketRef.current.close(); + clearTimeout(groupSocketTimeoutRef.current); + } + }, 5000); // Close if no pong in 5 seconds + } + } catch (error) { + console.error('Error during ping:', error); + } + }; + + const initWebsocketMessageGroup = () => { + forceCloseWebSocket(); // Close any existing connection + + if (!selectedDirect?.address || !myAddress) return; + + const socketLink = `${getBaseApiReactSocket()}/websockets/chat/messages?involving=${selectedDirect?.address}&involving=${myAddress}&encoding=BASE64&limit=100`; + socketRef.current = new WebSocket(socketLink); + + socketRef.current.onopen = () => { + setTimeout(pingWebSocket, 50); // Initial ping + }; + + socketRef.current.onmessage = (e) => { + try { + if (e.data === 'pong') { + clearTimeout(timeoutIdRef.current); + groupSocketTimeoutRef.current = setTimeout(pingWebSocket, 45000); // Ping every 45 seconds + } else { + middletierFunc( + JSON.parse(e.data), + selectedDirect?.address, + myAddress + ); + + setIsLoading(false); + } + } catch (error) { + console.error('Error handling WebSocket message:', error); + } + }; + + socketRef.current.onclose = (event) => { + clearTimeout(groupSocketTimeoutRef.current); + clearTimeout(timeoutIdRef.current); + console.warn(`WebSocket closed: ${event.reason || 'unknown reason'}`); + if (event.reason !== 'forced' && event.code !== 1000) { + setTimeout(() => initWebsocketMessageGroup(), 10000); // Retry after 10 seconds + } + }; + + socketRef.current.onerror = (error) => { + console.error('WebSocket error:', error); + clearTimeout(groupSocketTimeoutRef.current); + clearTimeout(timeoutIdRef.current); if (socketRef.current) { - clearTimeout(timeoutIdRef.current); - clearTimeout(groupSocketTimeoutRef.current); - socketRef.current.close(1000, 'forced'); - socketRef.current = null; + socketRef.current.close(); } }; - - const pingWebSocket = () => { - try { - if (socketRef.current?.readyState === WebSocket.OPEN) { - socketRef.current.send('ping'); - timeoutIdRef.current = setTimeout(() => { - if (socketRef.current) { - socketRef.current.close(); - clearTimeout(groupSocketTimeoutRef.current); - } - }, 5000); // Close if no pong in 5 seconds - } - } catch (error) { - console.error('Error during ping:', error); - } + }; + + const setDirectChatValueFunc = async (e) => { + setDirectToValue(e.detail.directToValue); + }; + useEffect(() => { + subscribeToEvent('setDirectToValueNewChat', setDirectChatValueFunc); + + return () => { + unsubscribeFromEvent('setDirectToValueNewChat', setDirectChatValueFunc); }; - + }, []); - const initWebsocketMessageGroup = () => { - forceCloseWebSocket(); // Close any existing connection - - if (!selectedDirect?.address || !myAddress) return; - - const socketLink = `${getBaseApiReactSocket()}/websockets/chat/messages?involving=${selectedDirect?.address}&involving=${myAddress}&encoding=BASE64&limit=100`; - socketRef.current = new WebSocket(socketLink); - - socketRef.current.onopen = () => { - setTimeout(pingWebSocket, 50); // Initial ping - }; - - socketRef.current.onmessage = (e) => { - try { - if (e.data === 'pong') { - clearTimeout(timeoutIdRef.current); - groupSocketTimeoutRef.current = setTimeout(pingWebSocket, 45000); // Ping every 45 seconds - } else { - middletierFunc(JSON.parse(e.data), selectedDirect?.address, myAddress) + useEffect(() => { + if (hasInitializedWebsocket.current || isNewChat) return; + setIsLoading(true); + initWebsocketMessageGroup(); + hasInitializedWebsocket.current = true; - setIsLoading(false); - } - } catch (error) { - console.error('Error handling WebSocket message:', error); - } - }; - - socketRef.current.onclose = (event) => { - clearTimeout(groupSocketTimeoutRef.current); - clearTimeout(timeoutIdRef.current); - console.warn(`WebSocket closed: ${event.reason || 'unknown reason'}`); - if (event.reason !== 'forced' && event.code !== 1000) { - setTimeout(() => initWebsocketMessageGroup(), 10000); // Retry after 10 seconds - } - }; - - socketRef.current.onerror = (error) => { - console.error('WebSocket error:', error); - clearTimeout(groupSocketTimeoutRef.current); - clearTimeout(timeoutIdRef.current); - if (socketRef.current) { - socketRef.current.close(); - } - }; + return () => { + forceCloseWebSocket(); // Clean up WebSocket on component unmount }; + }, [selectedDirect?.address, myAddress, isNewChat]); - const setDirectChatValueFunc = async (e)=> { - setDirectToValue(e.detail.directToValue) - } - useEffect(() => { - subscribeToEvent("setDirectToValueNewChat", setDirectChatValueFunc); - - return () => { - unsubscribeFromEvent("setDirectToValueNewChat", setDirectChatValueFunc); - }; - }, []); - - useEffect(() => { - if (hasInitializedWebsocket.current || isNewChat) return; - setIsLoading(true); - initWebsocketMessageGroup(); - hasInitializedWebsocket.current = true; - - return () => { - forceCloseWebSocket(); // Clean up WebSocket on component unmount - }; - }, [selectedDirect?.address, myAddress, isNewChat]); + const sendChatDirect = async ( + { chatReference = undefined, messageText, otherData }: any, + address, + publicKeyOfRecipient, + isNewChatVar + ) => { + try { + const directTo = isNewChatVar ? directToValue : address; + if (!directTo) return; + return new Promise((res, rej) => { + window + .sendMessage( + 'sendChatDirect', + { + directTo, + chatReference, + messageText, + otherData, + publicKeyOfRecipient, + address: directTo, + }, + 120000 + ) + .then(async (response) => { + if (!response?.error) { + if (isNewChatVar) { + let getRecipientName = null; + try { + getRecipientName = await getNameInfo(response.recipient); + } catch (error) { + console.error('Error fetching recipient name:', error); + } + setSelectedDirect({ + address: response.recipient, + name: getRecipientName, + timestamp: Date.now(), + sender: myAddress, + senderName: myName, + }); + setNewChat(null); + window + .sendMessage('addTimestampEnterChat', { + timestamp: Date.now(), + groupId: response.recipient, + }) + .catch((error) => { + console.error( + 'Failed to add timestamp:', + error.message || 'An error occurred' + ); + }); -const sendChatDirect = async ({ chatReference = undefined, messageText, otherData}: any, address, publicKeyOfRecipient, isNewChatVar)=> { - try { - const directTo = isNewChatVar ? directToValue : address - - if(!directTo) return - return new Promise((res, rej)=> { - window.sendMessage("sendChatDirect", { - directTo, - chatReference, - messageText, - otherData, - publicKeyOfRecipient, - address: directTo, - }, 120000) - .then(async (response) => { - if (!response?.error) { - if (isNewChatVar) { - let getRecipientName = null; - try { - getRecipientName = await getNameInfo(response.recipient); - } catch (error) { - console.error("Error fetching recipient name:", error); + setTimeout(() => { + getTimestampEnterChat(); + }, 400); } - setSelectedDirect({ - address: response.recipient, - name: getRecipientName, - timestamp: Date.now(), - sender: myAddress, - senderName: myName, - }); - setNewChat(null); - - window.sendMessage("addTimestampEnterChat", { - timestamp: Date.now(), - groupId: response.recipient, - }).catch((error) => { - console.error("Failed to add timestamp:", error.message || "An error occurred"); - }); - - setTimeout(() => { - getTimestampEnterChat(); - }, 400); + res(response); + return; } - res(response); - return; - } - rej(response.error); - }) - .catch((error) => { - rej(error.message || "An error occurred"); - }); - - }) - } catch (error) { - throw new Error(error) - } finally { - } -} -const clearEditorContent = () => { - if (editorRef.current) { - setMessageSize(0) - editorRef.current.chain().focus().clearContent().run(); - if(isMobile){ - setTimeout(() => { - editorRef.current?.chain().blur().run(); - setIsFocusedParent(false) - executeEvent("sent-new-message-group", {}) - setTimeout(() => { - triggerRerender(); - }, 300); - }, 200); + rej(response.error); + }) + .catch((error) => { + rej(error.message || 'An error occurred'); + }); + }); + } catch (error) { + throw new Error(error); + } finally { } - } -}; -useEffect(() => { - if (!editorRef?.current) return; - const handleUpdate = () => { - const htmlContent = editorRef?.current.getHTML(); - const stringified = JSON.stringify(htmlContent); - const size = new Blob([stringified]).size; - setMessageSize(size + 200); }; - - // Add a listener for the editorRef?.current's content updates - editorRef?.current.on('update', handleUpdate); - - // Cleanup the listener on unmount - return () => { - editorRef?.current.off('update', handleUpdate); + const clearEditorContent = () => { + if (editorRef.current) { + setMessageSize(0); + editorRef.current.chain().focus().clearContent().run(); + if (isMobile) { + setTimeout(() => { + editorRef.current?.chain().blur().run(); + setIsFocusedParent(false); + executeEvent('sent-new-message-group', {}); + setTimeout(() => { + triggerRerender(); + }, 300); + }, 200); + } + } }; -}, [editorRef?.current]); + useEffect(() => { + if (!editorRef?.current) return; + const handleUpdate = () => { + const htmlContent = editorRef?.current.getHTML(); + const stringified = JSON.stringify(htmlContent); + const size = new Blob([stringified]).size; + setMessageSize(size + 200); + }; - const sendMessage = async ()=> { - try { - if(messageSize > 4000) return + // Add a listener for the editorRef?.current's content updates + editorRef?.current.on('update', handleUpdate); - - if(+balance < 4) throw new Error('You need at least 4 QORT to send a message') - if(isSending) return - if (editorRef.current) { - const htmlContent = editorRef.current.getHTML(); - - if(!htmlContent?.trim() || htmlContent?.trim() === '

') return - setIsSending(true) - pauseAllQueues() - const message = JSON.stringify(htmlContent) - - - if(isNewChat){ - await sendChatDirect({ messageText: htmlContent}, null, null, true) - return + // Cleanup the listener on unmount + return () => { + editorRef?.current.off('update', handleUpdate); + }; + }, [editorRef?.current]); + + const sendMessage = async () => { + try { + if (messageSize > 4000) return; + + if (+balance < 4) + throw new Error('You need at least 4 QORT to send a message'); + if (isSending) return; + if (editorRef.current) { + const htmlContent = editorRef.current.getHTML(); + + if (!htmlContent?.trim() || htmlContent?.trim() === '

') return; + setIsSending(true); + pauseAllQueues(); + const message = JSON.stringify(htmlContent); + + if (isNewChat) { + await sendChatDirect({ messageText: htmlContent }, null, null, true); + return; } - let repliedTo = replyMessage?.signature + let repliedTo = replyMessage?.signature; - if (replyMessage?.chatReference) { - repliedTo = replyMessage?.chatReference - } - let chatReference = onEditMessage?.signature + if (replyMessage?.chatReference) { + repliedTo = replyMessage?.chatReference; + } + let chatReference = onEditMessage?.signature; const otherData = { ...(onEditMessage?.decryptedData || {}), specialId: uid.rnd(), repliedTo: onEditMessage ? onEditMessage?.repliedTo : repliedTo, - type: chatReference ? 'edit' : '' - } + type: chatReference ? 'edit' : '', + }; const sendMessageFunc = async () => { - return await sendChatDirect({ chatReference, messageText: htmlContent, otherData}, selectedDirect?.address, publicKeyOfRecipient, false) + return await sendChatDirect( + { chatReference, messageText: htmlContent, otherData }, + selectedDirect?.address, + publicKeyOfRecipient, + false + ); }; - - // Add the function to the queue const messageObj = { message: { timestamp: Date.now(), - senderName: myName, - sender: myAddress, - ...(otherData || {}), - text: htmlContent, + senderName: myName, + sender: myAddress, + ...(otherData || {}), + text: htmlContent, }, - chatReference - } - addToQueue(sendMessageFunc, messageObj, 'chat-direct', - selectedDirect?.address ); + chatReference, + }; + addToQueue( + sendMessageFunc, + messageObj, + 'chat-direct', + selectedDirect?.address + ); setTimeout(() => { - executeEvent("sent-new-message-group", {}) + executeEvent('sent-new-message-group', {}); }, 150); - clearEditorContent() - setReplyMessage(null) - setOnEditMessage(null) - - } - // send chat message - } catch (error) { - const errorMsg = error?.message || error - setInfoSnack({ - type: "error", - message: errorMsg === 'invalid signature' ? 'You need at least 4 QORT to send a message' : errorMsg, - }); - setOpenSnack(true); - console.error(error) - } finally { - setIsSending(false) - resumeAllQueues() + clearEditorContent(); + setReplyMessage(null); + setOnEditMessage(null); } + // send chat message + } catch (error) { + const errorMsg = error?.message || error; + setInfoSnack({ + type: 'error', + message: + errorMsg === 'invalid signature' + ? 'You need at least 4 QORT to send a message' + : errorMsg, + }); + setOpenSnack(true); + console.error(error); + } finally { + setIsSending(false); + resumeAllQueues(); } + }; - const onReply = useCallback((message)=> { - if(onEditMessage){ - clearEditorContent() + const onReply = useCallback( + (message) => { + if (onEditMessage) { + clearEditorContent(); } - setReplyMessage(message) - setOnEditMessage(null) - editorRef?.current?.chain().focus() - }, [onEditMessage]) - - - const onEdit = useCallback((message)=> { - setOnEditMessage(message) - setReplyMessage(null) - editorRef.current.chain().focus().setContent(message?.text).run(); - - }, []) - + setReplyMessage(message); + setOnEditMessage(null); + editorRef?.current?.chain().focus(); + }, + [onEditMessage] + ); + + const onEdit = useCallback((message) => { + setOnEditMessage(message); + setReplyMessage(null); + editorRef.current.chain().focus().setContent(message?.text).run(); + }, []); + return ( -
+
{!isMobile && ( - - - Close Direct Chat + + + + Close Direct Chat + + + )} + {isMobile && ( + + + + { + close(); + }} + > + + + + + {isNewChat + ? '' + : selectedDirect?.name || + selectedDirect?.address?.slice(0, 10) + '...'} + + + { + setSelectedDirect(null); + setMobileViewModeKeepOpen(''); + setNewChat(false); + }} + > + + + + )} - {isMobile && ( - - - - { - close() - }} - > - - - - - {isNewChat ? '' : selectedDirect?.name || (selectedDirect?.address?.slice(0,10) + '...')} - - - { - setSelectedDirect(null) - setMobileViewModeKeepOpen('') - setNewChat(false) - }} - > - - - - - - )} {isNewChat && ( <> - - setDirectToValue(e.target.value)} /> - + + setDirectToValue(e.target.value)} + /> )} - - - -
-
+ +
+
- {replyMessage && ( - - - - { - setReplyMessage(null) - setOnEditMessage(null) - - }} - > - - - - )} - {onEditMessage && ( - - - - { - setReplyMessage(null) - setOnEditMessage(null) - - clearEditorContent() - - - }} - > - - - - )} - - - {messageSize > 750 && ( - - 4000 ? 'var(--danger)' : 'unset' - }}>{`Your message size is of ${messageSize} bytes out of a maximum of 4000`} - - - )} -
- - - - { - - if(isSending) return - sendMessage() - }} - style={{ - marginTop: 'auto', - alignSelf: 'center', - cursor: isSending ? 'default' : 'pointer', - background: isSending && 'rgba(0, 0, 0, 0.8)', - flexShrink: 0, - padding: '5px', - width: '100px', - minWidth: 'auto' + justifyContent: 'flex-end', + }} + > + {replyMessage && ( + - {isSending && ( - + + { + setReplyMessage(null); + setOnEditMessage(null); + }} + > + + + + )} + {onEditMessage && ( + + + + { + setReplyMessage(null); + setOnEditMessage(null); + + clearEditorContent(); + }} + > + + + + )} + + + {messageSize > 750 && ( + + 4000 ? 'var(--danger)' : 'unset', + }} + >{`Your message size is of ${messageSize} bytes out of a maximum of 4000`} + + )} +
+ + + { + if (isSending) return; + sendMessage(); + }} + style={{ + marginTop: 'auto', + alignSelf: 'center', + cursor: isSending ? 'default' : 'pointer', + background: isSending && 'rgba(0, 0, 0, 0.8)', + flexShrink: 0, + padding: '5px', + width: '100px', + minWidth: 'auto', + }} + > + {isSending && ( + { left: '50%', marginTop: '-12px', marginLeft: '-12px', - color: 'white' + color: 'white', }} /> - )} - {` Send`} - - - + )} + {` Send`} + +
- - + +
- ) -} + ); +}; diff --git a/src/components/Chat/ChatGroup.tsx b/src/components/Chat/ChatGroup.tsx index e054bd9..4cf5a7a 100644 --- a/src/components/Chat/ChatGroup.tsx +++ b/src/components/Chat/ChatGroup.tsx @@ -1,100 +1,140 @@ -import React, { useCallback, useContext, useEffect, useMemo, useReducer, useRef, useState } from 'react' -import { CreateCommonSecret } from './CreateCommonSecret' -import { reusableGet } from '../../qdn/publish/pubish' -import { uint8ArrayToObject } from '../../backgroundFunctions/encryption' -import { base64ToUint8Array, decodeBase64ForUIChatMessages, objectToBase64 } from '../../qdn/encryption/group-encryption' -import { ChatContainerComp } from './ChatContainer' -import { ChatList } from './ChatList' -import "@chatscope/chat-ui-kit-styles/dist/default/styles.min.css"; -import Tiptap from './TipTap' -import { CustomButton } from '../../App-styles' +import React, { + useCallback, + useContext, + useEffect, + useMemo, + useReducer, + useRef, + useState, +} from 'react'; +import { CreateCommonSecret } from './CreateCommonSecret'; +import { reusableGet } from '../../qdn/publish/pubish'; +import { uint8ArrayToObject } from '../../backgroundFunctions/encryption'; +import { + base64ToUint8Array, + decodeBase64ForUIChatMessages, + objectToBase64, +} from '../../qdn/encryption/group-encryption'; +import { ChatContainerComp } from './ChatContainer'; +import { ChatList } from './ChatList'; +import '@chatscope/chat-ui-kit-styles/dist/default/styles.min.css'; +import Tiptap from './TipTap'; +import { CustomButton } from '../../styles/App-styles'; import CircularProgress from '@mui/material/CircularProgress'; -import { LoadingSnackbar } from '../Snackbar/LoadingSnackbar' -import { getBaseApiReact, getBaseApiReactSocket, isMobile, MyContext, pauseAllQueues, resumeAllQueues } from '../../App' -import { CustomizedSnackbars } from '../Snackbar/Snackbar' -import { PUBLIC_NOTIFICATION_CODE_FIRST_SECRET_KEY } from '../../constants/codes' -import { useMessageQueue } from '../../MessageQueueContext' -import { executeEvent, subscribeToEvent, unsubscribeFromEvent } from '../../utils/events' -import { Box, ButtonBase, Divider, Typography } from '@mui/material' -import ShortUniqueId from "short-unique-id"; -import { ReplyPreview } from './MessageItem' -import { ExitIcon } from '../../assets/Icons/ExitIcon' -import { RESOURCE_TYPE_NUMBER_GROUP_CHAT_REACTIONS } from '../../constants/resourceTypes' -import { isExtMsg } from '../../background' -import AppViewerContainer from '../Apps/AppViewerContainer' -import CloseIcon from "@mui/icons-material/Close"; -import { throttle } from 'lodash' +import { LoadingSnackbar } from '../Snackbar/LoadingSnackbar'; +import { + getBaseApiReact, + getBaseApiReactSocket, + isMobile, + MyContext, + pauseAllQueues, + resumeAllQueues, +} from '../../App'; +import { CustomizedSnackbars } from '../Snackbar/Snackbar'; +import { PUBLIC_NOTIFICATION_CODE_FIRST_SECRET_KEY } from '../../constants/codes'; +import { useMessageQueue } from '../../MessageQueueContext'; +import { + executeEvent, + subscribeToEvent, + unsubscribeFromEvent, +} from '../../utils/events'; +import { Box, ButtonBase, Divider, Typography } from '@mui/material'; +import ShortUniqueId from 'short-unique-id'; +import { ReplyPreview } from './MessageItem'; +import { ExitIcon } from '../../assets/Icons/ExitIcon'; +import { RESOURCE_TYPE_NUMBER_GROUP_CHAT_REACTIONS } from '../../constants/resourceTypes'; +import { isExtMsg } from '../../background'; +import AppViewerContainer from '../Apps/AppViewerContainer'; +import CloseIcon from '@mui/icons-material/Close'; +import { throttle } from 'lodash'; const uid = new ShortUniqueId({ length: 5 }); -export const ChatGroup = ({selectedGroup, secretKey, setSecretKey, getSecretKey, myAddress, handleNewEncryptionNotification, hide, handleSecretKeyCreationInProgress, triedToFetchSecretKey, myName, balance, getTimestampEnterChatParent, hideView, isPrivate}) => { - const {isUserBlocked} = useContext(MyContext) - const [messages, setMessages] = useState([]) - const [chatReferences, setChatReferences] = useState({}) - const [isSending, setIsSending] = useState(false) - const [isLoading, setIsLoading] = useState(false) +export const ChatGroup = ({ + selectedGroup, + secretKey, + setSecretKey, + getSecretKey, + myAddress, + handleNewEncryptionNotification, + hide, + handleSecretKeyCreationInProgress, + triedToFetchSecretKey, + myName, + balance, + getTimestampEnterChatParent, + hideView, + isPrivate, +}) => { + const { isUserBlocked } = useContext(MyContext); + const [messages, setMessages] = useState([]); + const [chatReferences, setChatReferences] = useState({}); + const [isSending, setIsSending] = useState(false); + const [isLoading, setIsLoading] = useState(false); const [isMoved, setIsMoved] = useState(false); const [openSnack, setOpenSnack] = React.useState(false); const [infoSnack, setInfoSnack] = React.useState(null); - const hasInitialized = useRef(false) + const hasInitialized = useRef(false); const [isFocusedParent, setIsFocusedParent] = useState(false); - const [replyMessage, setReplyMessage] = useState(null) - const [onEditMessage, setOnEditMessage] = useState(null) - const [isOpenQManager, setIsOpenQManager] = useState(null) + const [replyMessage, setReplyMessage] = useState(null); + const [onEditMessage, setOnEditMessage] = useState(null); + const [isOpenQManager, setIsOpenQManager] = useState(null); -const [messageSize, setMessageSize] = useState(0) - const hasInitializedWebsocket = useRef(false) + const [messageSize, setMessageSize] = useState(0); + const hasInitializedWebsocket = useRef(false); const socketRef = useRef(null); // WebSocket reference const timeoutIdRef = useRef(null); // Timeout ID reference const groupSocketTimeoutRef = useRef(null); // Group Socket Timeout reference const editorRef = useRef(null); const { queueChats, addToQueue, processWithNewMessages } = useMessageQueue(); const [, forceUpdate] = useReducer((x) => x + 1, 0); - const lastReadTimestamp = useRef(null) + const lastReadTimestamp = useRef(null); const handleUpdateRef = useRef(null); - const getTimestampEnterChat = async (selectedGroup) => { try { return new Promise((res, rej) => { - window.sendMessage("getTimestampEnterChat") - .then((response) => { - if (!response?.error) { - if(response && selectedGroup){ - lastReadTimestamp.current = response[selectedGroup] || undefined - window.sendMessage("addTimestampEnterChat", { - timestamp: Date.now(), - groupId: selectedGroup - }).catch((error) => { - console.error("Failed to add timestamp:", error.message || "An error occurred"); + window + .sendMessage('getTimestampEnterChat') + .then((response) => { + if (!response?.error) { + if (response && selectedGroup) { + lastReadTimestamp.current = + response[selectedGroup] || undefined; + window + .sendMessage('addTimestampEnterChat', { + timestamp: Date.now(), + groupId: selectedGroup, + }) + .catch((error) => { + console.error( + 'Failed to add timestamp:', + error.message || 'An error occurred' + ); + }); + + setTimeout(() => { + getTimestampEnterChatParent(); + }, 600); + } + + res(response); + return; + } + rej(response.error); + }) + .catch((error) => { + rej(error.message || 'An error occurred'); }); - - - setTimeout(() => { - getTimestampEnterChatParent(); - }, 600); - } - - res(response); - return; - } - rej(response.error); - }) - .catch((error) => { - rej(error.message || "An error occurred"); - }); - }); } catch (error) {} }; - useEffect(()=> { - if(!selectedGroup) return - getTimestampEnterChat(selectedGroup) - }, [selectedGroup]) - - + useEffect(() => { + if (!selectedGroup) return; + getTimestampEnterChat(selectedGroup); + }, [selectedGroup]); const members = useMemo(() => { const uniqueMembers = new Set(); @@ -115,70 +155,77 @@ const [messageSize, setMessageSize] = useState(0) editorRef.current = editorInstance; }; - const tempMessages = useMemo(()=> { - if(!selectedGroup) return [] - if(queueChats[selectedGroup]){ - return queueChats[selectedGroup]?.filter((item)=> !item?.chatReference) + const tempMessages = useMemo(() => { + if (!selectedGroup) return []; + if (queueChats[selectedGroup]) { + return queueChats[selectedGroup]?.filter((item) => !item?.chatReference); } - return [] - }, [selectedGroup, queueChats]) - const tempChatReferences = useMemo(()=> { - if(!selectedGroup) return [] - if(queueChats[selectedGroup]){ - return queueChats[selectedGroup]?.filter((item)=> !!item?.chatReference) + return []; + }, [selectedGroup, queueChats]); + const tempChatReferences = useMemo(() => { + if (!selectedGroup) return []; + if (queueChats[selectedGroup]) { + return queueChats[selectedGroup]?.filter((item) => !!item?.chatReference); } - return [] - }, [selectedGroup, queueChats]) + return []; + }, [selectedGroup, queueChats]); - const secretKeyRef = useRef(null) + const secretKeyRef = useRef(null); - useEffect(()=> { - if(secretKey){ - secretKeyRef.current = secretKey + useEffect(() => { + if (secretKey) { + secretKeyRef.current = secretKey; } - }, [secretKey]) + }, [secretKey]); - // const getEncryptedSecretKey = useCallback(()=> { - // const response = getResource() - // const decryptResponse = decryptResource() - // return - // }, []) + // const getEncryptedSecretKey = useCallback(()=> { + // const response = getResource() + // const decryptResponse = decryptResource() + // return + // }, []) - - const checkForFirstSecretKeyNotification = (messages)=> { - messages?.forEach((message)=> { + const checkForFirstSecretKeyNotification = (messages) => { + messages?.forEach((message) => { try { - const decodeMsg = atob(message.data); - if(decodeMsg === PUBLIC_NOTIFICATION_CODE_FIRST_SECRET_KEY){ - handleSecretKeyCreationInProgress() - return + const decodeMsg = atob(message.data); + if (decodeMsg === PUBLIC_NOTIFICATION_CODE_FIRST_SECRET_KEY) { + handleSecretKeyCreationInProgress(); + return; } - } catch (error) { - - } - }) - } + } catch (error) {} + }); + }; - const updateChatMessagesWithBlocksFunc = (e) => { - if(e.detail){ - setMessages((prev)=> prev?.filter((item)=> { - return !isUserBlocked(item?.sender, item?.senderName) - })) - } - }; - - useEffect(() => { - subscribeToEvent("updateChatMessagesWithBlocks", updateChatMessagesWithBlocksFunc); - - return () => { - unsubscribeFromEvent("updateChatMessagesWithBlocks", updateChatMessagesWithBlocksFunc); - }; - }, []); + const updateChatMessagesWithBlocksFunc = (e) => { + if (e.detail) { + setMessages((prev) => + prev?.filter((item) => { + return !isUserBlocked(item?.sender, item?.senderName); + }) + ); + } + }; - const middletierFunc = async (data: any, groupId: string) => { + useEffect(() => { + subscribeToEvent( + 'updateChatMessagesWithBlocks', + updateChatMessagesWithBlocksFunc + ); + + return () => { + unsubscribeFromEvent( + 'updateChatMessagesWithBlocks', + updateChatMessagesWithBlocksFunc + ); + }; + }, []); + + const middletierFunc = async (data: any, groupId: string) => { try { if (hasInitialized.current) { - const dataRemovedBlock = data?.filter((item)=> !isUserBlocked(item?.sender, item?.senderName)) + const dataRemovedBlock = data?.filter( + (item) => !isUserBlocked(item?.sender, item?.senderName) + ); decryptMessages(dataRemovedBlock, true); return; @@ -186,351 +233,462 @@ const [messageSize, setMessageSize] = useState(0) hasInitialized.current = true; const url = `${getBaseApiReact()}/chat/messages?txGroupId=${groupId}&encoding=BASE64&limit=0&reverse=false`; const response = await fetch(url, { - method: "GET", + method: 'GET', headers: { - "Content-Type": "application/json", + 'Content-Type': 'application/json', }, }); const responseData = await response.json(); - const dataRemovedBlock = responseData?.filter((item)=> { - return !isUserBlocked(item?.sender, item?.senderName) - }) + const dataRemovedBlock = responseData?.filter((item) => { + return !isUserBlocked(item?.sender, item?.senderName); + }); decryptMessages(dataRemovedBlock, false); } catch (error) { console.error(error); } - } - - const decryptMessages = (encryptedMessages: any[], isInitiated: boolean )=> { - try { - if(!secretKeyRef.current){ - checkForFirstSecretKeyNotification(encryptedMessages) - } - return new Promise((res, rej)=> { - window.sendMessage("decryptSingle", { + }; + + const decryptMessages = (encryptedMessages: any[], isInitiated: boolean) => { + try { + if (!secretKeyRef.current) { + checkForFirstSecretKeyNotification(encryptedMessages); + } + return new Promise((res, rej) => { + window + .sendMessage('decryptSingle', { data: encryptedMessages, secretKeyObject: secretKey, }) - .then((response) => { - if (!response?.error) { - const filterUIMessages = encryptedMessages.filter((item) => !isExtMsg(item.data)); - const decodedUIMessages = decodeBase64ForUIChatMessages(filterUIMessages); - - const combineUIAndExtensionMsgsBefore = [...decodedUIMessages, ...response]; - const combineUIAndExtensionMsgs = processWithNewMessages( - combineUIAndExtensionMsgsBefore.map((item) => ({ - ...item, - ...(item?.decryptedData || {}), - })), - selectedGroup - ); - res(combineUIAndExtensionMsgs); - - if (isInitiated) { + .then((response) => { + if (!response?.error) { + const filterUIMessages = encryptedMessages.filter( + (item) => !isExtMsg(item.data) + ); + const decodedUIMessages = + decodeBase64ForUIChatMessages(filterUIMessages); - const formatted = combineUIAndExtensionMsgs - .filter((rawItem) => !rawItem?.chatReference) - .map((item) => { - const additionalFields = item?.data === 'NDAwMQ==' ? { - text: "

First group key created.

" - } : {} - return { - ...item, - id: item.signature, - text: item?.decryptedData?.message || "", - repliedTo: item?.repliedTo || item?.decryptedData?.repliedTo, - unread: item?.sender === myAddress ? false : !!item?.chatReference ? false : true, - isNotEncrypted: !!item?.messageText, - ...additionalFields - } - }); - setMessages((prev) => [...prev, ...formatted]); - - setChatReferences((prev) => { - const organizedChatReferences = { ...prev }; - combineUIAndExtensionMsgs - .filter((rawItem) => rawItem && rawItem.chatReference && (rawItem?.decryptedData?.type === "reaction" || rawItem?.decryptedData?.type === "edit" || rawItem?.type === "edit" || rawItem?.isEdited || rawItem?.type === "reaction")) - .forEach((item) => { - try { - if(item?.decryptedData?.type === "edit"){ - organizedChatReferences[item.chatReference] = { - ...(organizedChatReferences[item.chatReference] || {}), - edit: item.decryptedData, - }; - } else if(item?.type === "edit" || item?.isEdited){ - organizedChatReferences[item.chatReference] = { - ...(organizedChatReferences[item.chatReference] || {}), - edit: item, - }; - } else { - const content = item?.content || item.decryptedData?.content; - const sender = item.sender; - const newTimestamp = item.timestamp; - const contentState = item?.contentState !== undefined ? item?.contentState : item.decryptedData?.contentState; - - if (!content || typeof content !== "string" || !sender || typeof sender !== "string" || !newTimestamp) { - console.warn("Invalid content, sender, or timestamp in reaction data", item); - return; - } - - organizedChatReferences[item.chatReference] = { - ...(organizedChatReferences[item.chatReference] || {}), - reactions: organizedChatReferences[item.chatReference]?.reactions || {}, - }; - - organizedChatReferences[item.chatReference].reactions[content] = - organizedChatReferences[item.chatReference].reactions[content] || []; - - let latestTimestampForSender = null; - - organizedChatReferences[item.chatReference].reactions[content] = - organizedChatReferences[item.chatReference].reactions[content].filter((reaction) => { - if (reaction.sender === sender) { - latestTimestampForSender = Math.max(latestTimestampForSender || 0, reaction.timestamp); - } - return reaction.sender !== sender; - }); - - if (latestTimestampForSender && newTimestamp < latestTimestampForSender) { - return; - } - - if (contentState !== false) { - organizedChatReferences[item.chatReference].reactions[content].push(item); - } - - if (organizedChatReferences[item.chatReference].reactions[content].length === 0) { - delete organizedChatReferences[item.chatReference].reactions[content]; - } + const combineUIAndExtensionMsgsBefore = [ + ...decodedUIMessages, + ...response, + ]; + const combineUIAndExtensionMsgs = processWithNewMessages( + combineUIAndExtensionMsgsBefore.map((item) => ({ + ...item, + ...(item?.decryptedData || {}), + })), + selectedGroup + ); + res(combineUIAndExtensionMsgs); + + if (isInitiated) { + const formatted = combineUIAndExtensionMsgs + .filter((rawItem) => !rawItem?.chatReference) + .map((item) => { + const additionalFields = + item?.data === 'NDAwMQ==' + ? { + text: '

First group key created.

', } - - } catch (error) { - console.error("Error processing reaction/edit item:", error, item); - } - }); - - return organizedChatReferences; + : {}; + return { + ...item, + id: item.signature, + text: item?.decryptedData?.message || '', + repliedTo: + item?.repliedTo || item?.decryptedData?.repliedTo, + unread: + item?.sender === myAddress + ? false + : !!item?.chatReference + ? false + : true, + isNotEncrypted: !!item?.messageText, + ...additionalFields, + }; }); - } else { - let firstUnreadFound = false; - const formatted = combineUIAndExtensionMsgs - .filter((rawItem) => !rawItem?.chatReference) - .map((item) => { - const additionalFields = item?.data === 'NDAwMQ==' ? { - text: "

First group key created.

" - } : {} - const divide = lastReadTimestamp.current && !firstUnreadFound && item.timestamp > lastReadTimestamp.current && myAddress !== item?.sender; - - if(divide){ - firstUnreadFound = true - } - return { - ...item, - id: item.signature, - text: item?.decryptedData?.message || "", - repliedTo: item?.repliedTo || item?.decryptedData?.repliedTo, - isNotEncrypted: !!item?.messageText, - unread: false, - divide, - ...additionalFields - } - }); - setMessages(formatted); - - setChatReferences((prev) => { - const organizedChatReferences = { ...prev }; - - combineUIAndExtensionMsgs - .filter((rawItem) => rawItem && rawItem.chatReference && (rawItem?.decryptedData?.type === "reaction" || rawItem?.decryptedData?.type === "edit" || rawItem?.type === "edit" || rawItem?.isEdited || rawItem?.type === "reaction")) - .forEach((item) => { - try { - if(item?.decryptedData?.type === "edit"){ - organizedChatReferences[item.chatReference] = { - ...(organizedChatReferences[item.chatReference] || {}), - edit: item.decryptedData, - }; - } else if(item?.type === "edit" || item?.isEdited){ - organizedChatReferences[item.chatReference] = { - ...(organizedChatReferences[item.chatReference] || {}), - edit: item, - }; - } else { - const content = item?.content || item.decryptedData?.content; + setMessages((prev) => [...prev, ...formatted]); + + setChatReferences((prev) => { + const organizedChatReferences = { ...prev }; + combineUIAndExtensionMsgs + .filter( + (rawItem) => + rawItem && + rawItem.chatReference && + (rawItem?.decryptedData?.type === 'reaction' || + rawItem?.decryptedData?.type === 'edit' || + rawItem?.type === 'edit' || + rawItem?.isEdited || + rawItem?.type === 'reaction') + ) + .forEach((item) => { + try { + if (item?.decryptedData?.type === 'edit') { + organizedChatReferences[item.chatReference] = { + ...(organizedChatReferences[item.chatReference] || + {}), + edit: item.decryptedData, + }; + } else if (item?.type === 'edit' || item?.isEdited) { + organizedChatReferences[item.chatReference] = { + ...(organizedChatReferences[item.chatReference] || + {}), + edit: item, + }; + } else { + const content = + item?.content || item.decryptedData?.content; const sender = item.sender; const newTimestamp = item.timestamp; - const contentState = item?.contentState !== undefined ? item?.contentState : item.decryptedData?.contentState; - - if (!content || typeof content !== "string" || !sender || typeof sender !== "string" || !newTimestamp) { - console.warn("Invalid content, sender, or timestamp in reaction data", item); + const contentState = + item?.contentState !== undefined + ? item?.contentState + : item.decryptedData?.contentState; + + if ( + !content || + typeof content !== 'string' || + !sender || + typeof sender !== 'string' || + !newTimestamp + ) { + console.warn( + 'Invalid content, sender, or timestamp in reaction data', + item + ); return; } - + organizedChatReferences[item.chatReference] = { - ...(organizedChatReferences[item.chatReference] || {}), - reactions: organizedChatReferences[item.chatReference]?.reactions || {}, + ...(organizedChatReferences[item.chatReference] || + {}), + reactions: + organizedChatReferences[item.chatReference] + ?.reactions || {}, }; - - organizedChatReferences[item.chatReference].reactions[content] = - organizedChatReferences[item.chatReference].reactions[content] || []; - + + organizedChatReferences[item.chatReference].reactions[ + content + ] = + organizedChatReferences[item.chatReference] + .reactions[content] || []; + let latestTimestampForSender = null; - - organizedChatReferences[item.chatReference].reactions[content] = - organizedChatReferences[item.chatReference].reactions[content].filter((reaction) => { - if (reaction.sender === sender) { - latestTimestampForSender = Math.max(latestTimestampForSender || 0, reaction.timestamp); - } - return reaction.sender !== sender; - }); - - if (latestTimestampForSender && newTimestamp < latestTimestampForSender) { + + organizedChatReferences[item.chatReference].reactions[ + content + ] = organizedChatReferences[ + item.chatReference + ].reactions[content].filter((reaction) => { + if (reaction.sender === sender) { + latestTimestampForSender = Math.max( + latestTimestampForSender || 0, + reaction.timestamp + ); + } + return reaction.sender !== sender; + }); + + if ( + latestTimestampForSender && + newTimestamp < latestTimestampForSender + ) { return; } - + if (contentState !== false) { - organizedChatReferences[item.chatReference].reactions[content].push(item); + organizedChatReferences[ + item.chatReference + ].reactions[content].push(item); } - - if (organizedChatReferences[item.chatReference].reactions[content].length === 0) { - delete organizedChatReferences[item.chatReference].reactions[content]; + + if ( + organizedChatReferences[item.chatReference] + .reactions[content].length === 0 + ) { + delete organizedChatReferences[item.chatReference] + .reactions[content]; } } - } catch (error) { - console.error("Error processing reaction item:", error, item); - } - }); - - return organizedChatReferences; + } catch (error) { + console.error( + 'Error processing reaction/edit item:', + error, + item + ); + } + }); + + return organizedChatReferences; + }); + } else { + let firstUnreadFound = false; + const formatted = combineUIAndExtensionMsgs + .filter((rawItem) => !rawItem?.chatReference) + .map((item) => { + const additionalFields = + item?.data === 'NDAwMQ==' + ? { + text: '

First group key created.

', + } + : {}; + const divide = + lastReadTimestamp.current && + !firstUnreadFound && + item.timestamp > lastReadTimestamp.current && + myAddress !== item?.sender; + + if (divide) { + firstUnreadFound = true; + } + return { + ...item, + id: item.signature, + text: item?.decryptedData?.message || '', + repliedTo: + item?.repliedTo || item?.decryptedData?.repliedTo, + isNotEncrypted: !!item?.messageText, + unread: false, + divide, + ...additionalFields, + }; }); - } + setMessages(formatted); + + setChatReferences((prev) => { + const organizedChatReferences = { ...prev }; + + combineUIAndExtensionMsgs + .filter( + (rawItem) => + rawItem && + rawItem.chatReference && + (rawItem?.decryptedData?.type === 'reaction' || + rawItem?.decryptedData?.type === 'edit' || + rawItem?.type === 'edit' || + rawItem?.isEdited || + rawItem?.type === 'reaction') + ) + .forEach((item) => { + try { + if (item?.decryptedData?.type === 'edit') { + organizedChatReferences[item.chatReference] = { + ...(organizedChatReferences[item.chatReference] || + {}), + edit: item.decryptedData, + }; + } else if (item?.type === 'edit' || item?.isEdited) { + organizedChatReferences[item.chatReference] = { + ...(organizedChatReferences[item.chatReference] || + {}), + edit: item, + }; + } else { + const content = + item?.content || item.decryptedData?.content; + const sender = item.sender; + const newTimestamp = item.timestamp; + const contentState = + item?.contentState !== undefined + ? item?.contentState + : item.decryptedData?.contentState; + + if ( + !content || + typeof content !== 'string' || + !sender || + typeof sender !== 'string' || + !newTimestamp + ) { + console.warn( + 'Invalid content, sender, or timestamp in reaction data', + item + ); + return; + } + + organizedChatReferences[item.chatReference] = { + ...(organizedChatReferences[item.chatReference] || + {}), + reactions: + organizedChatReferences[item.chatReference] + ?.reactions || {}, + }; + + organizedChatReferences[item.chatReference].reactions[ + content + ] = + organizedChatReferences[item.chatReference] + .reactions[content] || []; + + let latestTimestampForSender = null; + + organizedChatReferences[item.chatReference].reactions[ + content + ] = organizedChatReferences[ + item.chatReference + ].reactions[content].filter((reaction) => { + if (reaction.sender === sender) { + latestTimestampForSender = Math.max( + latestTimestampForSender || 0, + reaction.timestamp + ); + } + return reaction.sender !== sender; + }); + + if ( + latestTimestampForSender && + newTimestamp < latestTimestampForSender + ) { + return; + } + + if (contentState !== false) { + organizedChatReferences[ + item.chatReference + ].reactions[content].push(item); + } + + if ( + organizedChatReferences[item.chatReference] + .reactions[content].length === 0 + ) { + delete organizedChatReferences[item.chatReference] + .reactions[content]; + } + } + } catch (error) { + console.error( + 'Error processing reaction item:', + error, + item + ); + } + }); + + return organizedChatReferences; + }); } - rej(response.error); - }) - .catch((error) => { - rej(error.message || "An error occurred"); - }); - - }) - } catch (error) { - - } + } + rej(response.error); + }) + .catch((error) => { + rej(error.message || 'An error occurred'); + }); + }); + } catch (error) {} + }; + + const forceCloseWebSocket = () => { + if (socketRef.current) { + clearTimeout(timeoutIdRef.current); + clearTimeout(groupSocketTimeoutRef.current); + socketRef.current.close(1000, 'forced'); + socketRef.current = null; } + }; - + const pingGroupSocket = () => { + try { + if (socketRef.current?.readyState === WebSocket.OPEN) { + socketRef.current.send('ping'); + timeoutIdRef.current = setTimeout(() => { + if (socketRef.current) { + socketRef.current.close(); + clearTimeout(groupSocketTimeoutRef.current); + } + }, 5000); // Close if no pong in 5 seconds + } + } catch (error) { + console.error('Error during ping:', error); + } + }; + const initWebsocketMessageGroup = () => { + let socketLink = `${getBaseApiReactSocket()}/websockets/chat/messages?txGroupId=${selectedGroup}&encoding=BASE64&limit=100`; + socketRef.current = new WebSocket(socketLink); - const forceCloseWebSocket = () => { - if (socketRef.current) { - - clearTimeout(timeoutIdRef.current); - clearTimeout(groupSocketTimeoutRef.current); - socketRef.current.close(1000, 'forced'); - socketRef.current = null; + socketRef.current.onopen = () => { + setTimeout(pingGroupSocket, 50); + }; + socketRef.current.onmessage = (e) => { + try { + if (e.data === 'pong') { + clearTimeout(timeoutIdRef.current); + groupSocketTimeoutRef.current = setTimeout(pingGroupSocket, 45000); // Ping every 45 seconds + } else { + middletierFunc(JSON.parse(e.data), selectedGroup); + setIsLoading(false); + } + } catch (error) {} + }; + socketRef.current.onclose = () => { + clearTimeout(groupSocketTimeoutRef.current); + clearTimeout(timeoutIdRef.current); + console.warn(`WebSocket closed: ${event.reason || 'unknown reason'}`); + if (event.reason !== 'forced' && event.code !== 1000) { + setTimeout(() => initWebsocketMessageGroup(), 1000); // Retry after 10 seconds } }; - - const pingGroupSocket = () => { - try { - if (socketRef.current?.readyState === WebSocket.OPEN) { - socketRef.current.send('ping'); - timeoutIdRef.current = setTimeout(() => { - if (socketRef.current) { - socketRef.current.close(); - clearTimeout(groupSocketTimeoutRef.current); - } - }, 5000); // Close if no pong in 5 seconds - } - } catch (error) { - console.error('Error during ping:', error); + socketRef.current.onerror = (e) => { + clearTimeout(groupSocketTimeoutRef.current); + clearTimeout(timeoutIdRef.current); + if (socketRef.current) { + socketRef.current.close(); + } + }; + }; + + useEffect(() => { + if (hasInitializedWebsocket.current) return; + if (triedToFetchSecretKey && !secretKey) { + forceCloseWebSocket(); + setMessages([]); + setIsLoading(true); + initWebsocketMessageGroup(); } - } - const initWebsocketMessageGroup = () => { - + }, [triedToFetchSecretKey, secretKey, isPrivate]); - let socketLink = `${getBaseApiReactSocket()}/websockets/chat/messages?txGroupId=${selectedGroup}&encoding=BASE64&limit=100` - socketRef.current = new WebSocket(socketLink) + useEffect(() => { + if (isPrivate === null) return; + if (isPrivate === false || !secretKey || hasInitializedWebsocket.current) + return; + forceCloseWebSocket(); + setMessages([]); + setIsLoading(true); + pauseAllQueues(); + setTimeout(() => { + resumeAllQueues(); + }, 6000); + initWebsocketMessageGroup(); + hasInitializedWebsocket.current = true; + }, [secretKey, isPrivate]); - - socketRef.current.onopen = () => { - setTimeout(pingGroupSocket, 50) - } - socketRef.current.onmessage = (e) => { - try { - if (e.data === 'pong') { - clearTimeout(timeoutIdRef.current); - groupSocketTimeoutRef.current = setTimeout(pingGroupSocket, 45000); // Ping every 45 seconds - } else { - middletierFunc(JSON.parse(e.data), selectedGroup) - setIsLoading(false) - } - } catch (error) { - - } - - - } - socketRef.current.onclose = () => { - clearTimeout(groupSocketTimeoutRef.current); - clearTimeout(timeoutIdRef.current); - console.warn(`WebSocket closed: ${event.reason || 'unknown reason'}`); - if (event.reason !== 'forced' && event.code !== 1000) { - setTimeout(() => initWebsocketMessageGroup(), 1000); // Retry after 10 seconds - } - } - socketRef.current.onerror = (e) => { - clearTimeout(groupSocketTimeoutRef.current); - clearTimeout(timeoutIdRef.current); - if (socketRef.current) { - socketRef.current.close(); - } - } - } + useEffect(() => { + const notifications = messages.filter( + (message) => message?.decryptedData?.type === 'notification' + ); + if (notifications.length === 0) return; + const latestNotification = notifications.reduce((latest, current) => { + return current.timestamp > latest.timestamp ? current : latest; + }, notifications[0]); + handleNewEncryptionNotification(latestNotification); + }, [messages]); - useEffect(()=> { - if(hasInitializedWebsocket.current) return - if(triedToFetchSecretKey && !secretKey){ - forceCloseWebSocket() - setMessages([]) - setIsLoading(true) - initWebsocketMessageGroup() - } - }, [triedToFetchSecretKey, secretKey, isPrivate]) - - useEffect(()=> { - if(isPrivate === null) return - if(isPrivate === false || !secretKey || hasInitializedWebsocket.current) return - forceCloseWebSocket() - setMessages([]) - setIsLoading(true) - pauseAllQueues() - setTimeout(() => { - resumeAllQueues() - }, 6000); - initWebsocketMessageGroup() - hasInitializedWebsocket.current = true - }, [secretKey, isPrivate]) - - - useEffect(()=> { - const notifications = messages.filter((message)=> message?.decryptedData - ?.type === 'notification') - if(notifications.length === 0) return - const latestNotification = notifications.reduce((latest, current) => { - return current.timestamp > latest.timestamp ? current : latest; - }, notifications[0]); - handleNewEncryptionNotification(latestNotification) - - }, [messages]) - - - const encryptChatMessage = async (data: string, secretKeyObject: any, reactiontypeNumber?: number)=> { + const encryptChatMessage = async ( + data: string, + secretKeyObject: any, + reactiontypeNumber?: number + ) => { try { - return new Promise((res, rej)=> { - window.sendMessage("encryptSingle", { - data, - secretKeyObject, - typeNumber: reactiontypeNumber, - }) + return new Promise((res, rej) => { + window + .sendMessage('encryptSingle', { + data, + secretKeyObject, + typeNumber: reactiontypeNumber, + }) .then((response) => { if (!response?.error) { res(response); @@ -539,172 +697,189 @@ const [messageSize, setMessageSize] = useState(0) rej(response.error); }) .catch((error) => { - rej(error.message || "An error occurred"); + rej(error.message || 'An error occurred'); }); - - }) + }); + } catch (error) {} + }; + + const sendChatGroup = async ({ + groupId, + typeMessage = undefined, + chatReference = undefined, + messageText, + }: any) => { + try { + return new Promise((res, rej) => { + window + .sendMessage( + 'sendChatGroup', + { + groupId, + typeMessage, + chatReference, + messageText, + }, + 120000 + ) + .then((response) => { + if (!response?.error) { + res(response); + return; + } + rej(response.error); + }) + .catch((error) => { + rej(error.message || 'An error occurred'); + }); + }); } catch (error) { - + throw new Error(error); } -} - -const sendChatGroup = async ({groupId, typeMessage = undefined, chatReference = undefined, messageText}: any)=> { - try { - return new Promise((res, rej)=> { - window.sendMessage("sendChatGroup", { - groupId, - typeMessage, - chatReference, - messageText, - }, 120000) - .then((response) => { - if (!response?.error) { - res(response); - return; - } - rej(response.error); - }) - .catch((error) => { - rej(error.message || "An error occurred"); - }); - - }) - } catch (error) { - throw new Error(error) - } -} -const clearEditorContent = () => { - if (editorRef.current) { - setMessageSize(0) - editorRef.current.chain().focus().clearContent().run(); - if(isMobile){ - setTimeout(() => { - editorRef.current?.chain().blur().run(); - setIsFocusedParent(false) - executeEvent("sent-new-message-group", {}) + }; + const clearEditorContent = () => { + if (editorRef.current) { + setMessageSize(0); + editorRef.current.chain().focus().clearContent().run(); + if (isMobile) { setTimeout(() => { - triggerRerender(); - }, 300); - }, 200); + editorRef.current?.chain().blur().run(); + setIsFocusedParent(false); + executeEvent('sent-new-message-group', {}); + setTimeout(() => { + triggerRerender(); + }, 300); + }, 200); + } } - } -}; + }; + const sendMessage = async () => { + try { + if (messageSize > 4000) return; + if (isPrivate === null) + throw new Error('Unable to determine if group is private'); + if (isSending) return; + if (+balance < 4) + throw new Error('You need at least 4 QORT to send a message'); + pauseAllQueues(); + if (editorRef.current) { + const htmlContent = editorRef.current.getHTML(); - const sendMessage = async ()=> { - try { - if(messageSize > 4000) return - if(isPrivate === null) throw new Error('Unable to determine if group is private') - if(isSending) return - if(+balance < 4) throw new Error('You need at least 4 QORT to send a message') - pauseAllQueues() - if (editorRef.current) { - const htmlContent = editorRef.current.getHTML(); - - if(!htmlContent?.trim() || htmlContent?.trim() === '

') return - + if (!htmlContent?.trim() || htmlContent?.trim() === '

') return; - setIsSending(true) - const message = isPrivate === false ? editorRef.current.getJSON() : htmlContent - const secretKeyObject = await getSecretKey(false, true) + setIsSending(true); + const message = + isPrivate === false ? editorRef.current.getJSON() : htmlContent; + const secretKeyObject = await getSecretKey(false, true); - let repliedTo = replyMessage?.signature + let repliedTo = replyMessage?.signature; - if (replyMessage?.chatReference) { - repliedTo = replyMessage?.chatReference - } - let chatReference = onEditMessage?.signature - - const publicData = isPrivate ? {} : { - isEdited : chatReference ? true : false, + if (replyMessage?.chatReference) { + repliedTo = replyMessage?.chatReference; } + let chatReference = onEditMessage?.signature; + + const publicData = isPrivate + ? {} + : { + isEdited: chatReference ? true : false, + }; const otherData = { repliedTo, ...(onEditMessage?.decryptedData || {}), type: chatReference ? 'edit' : '', specialId: uid.rnd(), - ...publicData - } + ...publicData, + }; const objectMessage = { ...(otherData || {}), [isPrivate ? 'message' : 'messageText']: message, - version: 3 - } - const message64: any = await objectToBase64(objectMessage) - - const encryptSingle = isPrivate === false ? JSON.stringify(objectMessage) : await encryptChatMessage(message64, secretKeyObject) - // const res = await sendChatGroup({groupId: selectedGroup,messageText: encryptSingle}) - - const sendMessageFunc = async () => { - return await sendChatGroup({groupId: selectedGroup,messageText: encryptSingle, chatReference}) + version: 3, }; - + const message64: any = await objectToBase64(objectMessage); + + const encryptSingle = + isPrivate === false + ? JSON.stringify(objectMessage) + : await encryptChatMessage(message64, secretKeyObject); + // const res = await sendChatGroup({groupId: selectedGroup,messageText: encryptSingle}) + + const sendMessageFunc = async () => { + return await sendChatGroup({ + groupId: selectedGroup, + messageText: encryptSingle, + chatReference, + }); + }; + // Add the function to the queue const messageObj = { message: { text: htmlContent, timestamp: Date.now(), - senderName: myName, - sender: myAddress, - ...(otherData || {}) + senderName: myName, + sender: myAddress, + ...(otherData || {}), }, - chatReference - } - addToQueue(sendMessageFunc, messageObj, 'chat', - selectedGroup ); + chatReference, + }; + addToQueue(sendMessageFunc, messageObj, 'chat', selectedGroup); setTimeout(() => { - executeEvent("sent-new-message-group", {}) + executeEvent('sent-new-message-group', {}); }, 150); - clearEditorContent() - setReplyMessage(null) - setOnEditMessage(null) - } - // send chat message - } catch (error) { - const errorMsg = error?.message || error - setInfoSnack({ - type: "error", - message: errorMsg, - }); - setOpenSnack(true); - console.error(error) - } finally { - setIsSending(false) - resumeAllQueues() + clearEditorContent(); + setReplyMessage(null); + setOnEditMessage(null); } + // send chat message + } catch (error) { + const errorMsg = error?.message || error; + setInfoSnack({ + type: 'error', + message: errorMsg, + }); + setOpenSnack(true); + console.error(error); + } finally { + setIsSending(false); + resumeAllQueues(); } + }; - useEffect(() => { - if (!editorRef?.current) return; - - handleUpdateRef.current = throttle(async () => { - try { - if(isPrivate){ + useEffect(() => { + if (!editorRef?.current) return; + + handleUpdateRef.current = throttle(async () => { + try { + if (isPrivate) { const htmlContent = editorRef.current.getHTML(); - const message64 = await objectToBase64(JSON.stringify(htmlContent)) - const secretKeyObject = await getSecretKey(false, true) - const encryptSingle = await encryptChatMessage(message64, secretKeyObject) + const message64 = await objectToBase64(JSON.stringify(htmlContent)); + const secretKeyObject = await getSecretKey(false, true); + const encryptSingle = await encryptChatMessage( + message64, + secretKeyObject + ); setMessageSize((encryptSingle?.length || 0) + 200); } else { const htmlContent = editorRef.current.getJSON(); - const message = JSON.stringify(htmlContent) - const size = new Blob([message]).size + const message = JSON.stringify(htmlContent); + const size = new Blob([message]).size; setMessageSize(size + 300); } - - } catch (error) { + } catch (error) { // calc size error - } - }, 1200); - - const currentEditor = editorRef.current; - - currentEditor.on("update", handleUpdateRef.current); - - return () => { - currentEditor.off("update", handleUpdateRef.current); - }; - }, [editorRef, setMessageSize, isPrivate]); + } + }, 1200); + + const currentEditor = editorRef.current; + + currentEditor.on('update', handleUpdateRef.current); + + return () => { + currentEditor.off('update', handleUpdateRef.current); + }; + }, [editorRef, setMessageSize, isPrivate]); useEffect(() => { if (hide) { @@ -713,210 +888,260 @@ const clearEditorContent = () => { setIsMoved(false); // Reset the position immediately when showing } }, [hide]); - - const onReply = useCallback((message)=> { - if(onEditMessage){ - clearEditorContent() - } - setReplyMessage(message) - setOnEditMessage(null) - editorRef?.current?.chain().focus() - }, [onEditMessage]) - - const onEdit = useCallback((message)=> { - setOnEditMessage(message) - setReplyMessage(null) - editorRef.current.chain().focus().setContent(message?.messageText || message?.text).run(); - - }, []) - const handleReaction = useCallback(async (reaction, chatMessage, reactionState = true)=> { - try { - - if(isSending) return - if(+balance < 4) throw new Error('You need at least 4 QORT to send a message') - pauseAllQueues() - - - setIsSending(true) - const message = '' - const secretKeyObject = await getSecretKey(false, true) - - - const otherData = { - specialId: uid.rnd(), - type: 'reaction', - content: reaction, - contentState: reactionState + const onReply = useCallback( + (message) => { + if (onEditMessage) { + clearEditorContent(); } - const objectMessage = { - message, - ...(otherData || {}) + setReplyMessage(message); + setOnEditMessage(null); + editorRef?.current?.chain().focus(); + }, + [onEditMessage] + ); + + const onEdit = useCallback((message) => { + setOnEditMessage(message); + setReplyMessage(null); + editorRef.current + .chain() + .focus() + .setContent(message?.messageText || message?.text) + .run(); + }, []); + const handleReaction = useCallback( + async (reaction, chatMessage, reactionState = true) => { + try { + if (isSending) return; + if (+balance < 4) + throw new Error('You need at least 4 QORT to send a message'); + pauseAllQueues(); + + setIsSending(true); + const message = ''; + const secretKeyObject = await getSecretKey(false, true); + + const otherData = { + specialId: uid.rnd(), + type: 'reaction', + content: reaction, + contentState: reactionState, + }; + const objectMessage = { + message, + ...(otherData || {}), + }; + const message64: any = await objectToBase64(objectMessage); + const reactiontypeNumber = RESOURCE_TYPE_NUMBER_GROUP_CHAT_REACTIONS; + const encryptSingle = + isPrivate === false + ? JSON.stringify(objectMessage) + : await encryptChatMessage( + message64, + secretKeyObject, + reactiontypeNumber + ); + // const res = await sendChatGroup({groupId: selectedGroup,messageText: encryptSingle}) + + const sendMessageFunc = async () => { + return await sendChatGroup({ + groupId: selectedGroup, + messageText: encryptSingle, + chatReference: chatMessage.signature, + }); + }; + + // Add the function to the queue + const messageObj = { + message: { + text: message, + timestamp: Date.now(), + senderName: myName, + sender: myAddress, + ...(otherData || {}), + }, + chatReference: chatMessage.signature, + }; + addToQueue(sendMessageFunc, messageObj, 'chat-reaction', selectedGroup); + // setTimeout(() => { + // executeEvent("sent-new-message-group", {}) + // }, 150); + // clearEditorContent() + // setReplyMessage(null) + + // send chat message + } catch (error) { + const errorMsg = error?.message || error; + setInfoSnack({ + type: 'error', + message: errorMsg, + }); + setOpenSnack(true); + console.error(error); + } finally { + setIsSending(false); + resumeAllQueues(); } - const message64: any = await objectToBase64(objectMessage) - const reactiontypeNumber = RESOURCE_TYPE_NUMBER_GROUP_CHAT_REACTIONS - const encryptSingle = isPrivate === false ? JSON.stringify(objectMessage) : await encryptChatMessage(message64, secretKeyObject, reactiontypeNumber) - // const res = await sendChatGroup({groupId: selectedGroup,messageText: encryptSingle}) - - const sendMessageFunc = async () => { - return await sendChatGroup({groupId: selectedGroup,messageText: encryptSingle, chatReference: chatMessage.signature}) - }; + }, + [isPrivate] + ); - // Add the function to the queue - const messageObj = { - message: { - text: message, - timestamp: Date.now(), - senderName: myName, - sender: myAddress, - ...(otherData || {}) - }, - chatReference: chatMessage.signature - } - addToQueue(sendMessageFunc, messageObj, 'chat-reaction', - selectedGroup ); - // setTimeout(() => { - // executeEvent("sent-new-message-group", {}) - // }, 150); - // clearEditorContent() - // setReplyMessage(null) + const openQManager = useCallback(() => { + setIsOpenQManager(true); + }, []); - // send chat message - } catch (error) { - const errorMsg = error?.message || error - setInfoSnack({ - type: "error", - message: errorMsg, - }); - setOpenSnack(true); - console.error(error) - } finally { - setIsSending(false) - resumeAllQueues() - } - }, [isPrivate]) - - const openQManager = useCallback(()=> { - setIsOpenQManager(true) - }, []) - return ( -
- - - - {(!!secretKey || isPrivate === false) && ( -
- -
+ + + {(!!secretKey || isPrivate === false) && ( +
- {replyMessage && ( - - + }} + > +
+ {replyMessage && ( + + - { - setReplyMessage(null) - - setOnEditMessage(null) + { + setReplyMessage(null); - }} - > - - - - )} - {onEditMessage && ( - - + setOnEditMessage(null); + }} + > + + + + )} + {onEditMessage && ( + + - { - setReplyMessage(null) - setOnEditMessage(null) - - clearEditorContent() - - }} - > - - - - )} - - - - {messageSize > 750 && ( - - 4000 ? 'var(--danger)' : 'unset' - }}>{`Your message size is of ${messageSize} bytes out of a maximum of 4000`} + { + setReplyMessage(null); + setOnEditMessage(null); - - )} -
- - + clearEditorContent(); + }} + > + + + + )} - { - - if(isSending) return - sendMessage() + + {messageSize > 750 && ( + + 4000 ? 'var(--danger)' : 'unset', + }} + >{`Your message size is of ${messageSize} bytes out of a maximum of 4000`} + + )} +
+ + + { + if (isSending) return; + sendMessage(); }} style={{ marginTop: 'auto', @@ -926,85 +1151,108 @@ const clearEditorContent = () => { flexShrink: 0, padding: '5px', width: '100px', - minWidth: 'auto' - + minWidth: 'auto', }} > {isSending && ( + size={18} + sx={{ + position: 'absolute', + top: '50%', + left: '50%', + marginTop: '-12px', + marginLeft: '-12px', + color: 'white', + }} + /> )} {` Send`} - - -
- )} - {isOpenQManager !== null && ( - - - - Q-Manager - { - setIsOpenQManager(false) - }}> - - - - - + +
)} - - {/* */} - - + {isOpenQManager !== null && ( + + + + Q-Manager + { + setIsOpenQManager(false); + }} + > + + + + + + + + )} + + {/* */} + +
- ) -} + ); +}; diff --git a/src/components/Chat/GroupAnnouncements.tsx b/src/components/Chat/GroupAnnouncements.tsx index 9ba7668..98dd48b 100644 --- a/src/components/Chat/GroupAnnouncements.tsx +++ b/src/components/Chat/GroupAnnouncements.tsx @@ -4,30 +4,33 @@ import React, { useMemo, useRef, useState, -} from "react"; -import { CreateCommonSecret } from "./CreateCommonSecret"; -import { reusableGet } from "../../qdn/publish/pubish"; -import { uint8ArrayToObject } from "../../backgroundFunctions/encryption"; +} from 'react'; +import { CreateCommonSecret } from './CreateCommonSecret'; +import { reusableGet } from '../../qdn/publish/pubish'; +import { uint8ArrayToObject } from '../../backgroundFunctions/encryption'; import { base64ToUint8Array, objectToBase64, -} from "../../qdn/encryption/group-encryption"; -import { ChatContainerComp } from "./ChatContainer"; -import { ChatList } from "./ChatList"; -import "@chatscope/chat-ui-kit-styles/dist/default/styles.min.css"; -import Tiptap from "./TipTap"; -import { AuthenticatedContainerInnerTop, CustomButton } from "../../App-styles"; -import CircularProgress from "@mui/material/CircularProgress"; -import { getBaseApi, getFee } from "../../background"; -import { LoadingSnackbar } from "../Snackbar/LoadingSnackbar"; -import { Box, Typography } from "@mui/material"; -import { Spacer } from "../../common/Spacer"; -import ShortUniqueId from "short-unique-id"; -import { AnnouncementList } from "./AnnouncementList"; +} from '../../qdn/encryption/group-encryption'; +import { ChatContainerComp } from './ChatContainer'; +import { ChatList } from './ChatList'; +import '@chatscope/chat-ui-kit-styles/dist/default/styles.min.css'; +import Tiptap from './TipTap'; +import { + AuthenticatedContainerInnerTop, + CustomButton, +} from '../../styles/App-styles'; +import CircularProgress from '@mui/material/CircularProgress'; +import { getBaseApi, getFee } from '../../background'; +import { LoadingSnackbar } from '../Snackbar/LoadingSnackbar'; +import { Box, Typography } from '@mui/material'; +import { Spacer } from '../../common/Spacer'; +import ShortUniqueId from 'short-unique-id'; +import { AnnouncementList } from './AnnouncementList'; const uid = new ShortUniqueId({ length: 8 }); -import CampaignIcon from "@mui/icons-material/Campaign"; -import ArrowBackIcon from "@mui/icons-material/ArrowBack"; -import { AnnouncementDiscussion } from "./AnnouncementDiscussion"; +import CampaignIcon from '@mui/icons-material/Campaign'; +import ArrowBackIcon from '@mui/icons-material/ArrowBack'; +import { AnnouncementDiscussion } from './AnnouncementDiscussion'; import { MyContext, getArbitraryEndpointReact, @@ -35,11 +38,11 @@ import { isMobile, pauseAllQueues, resumeAllQueues, -} from "../../App"; -import { RequestQueueWithPromise } from "../../utils/queue/queue"; -import { CustomizedSnackbars } from "../Snackbar/Snackbar"; -import { addDataPublishesFunc, getDataPublishesFunc } from "../Group/Group"; -import { getRootHeight } from "../../utils/mobile/mobileUtils"; +} from '../../App'; +import { RequestQueueWithPromise } from '../../utils/queue/queue'; +import { CustomizedSnackbars } from '../Snackbar/Snackbar'; +import { addDataPublishesFunc, getDataPublishesFunc } from '../Group/Group'; +import { getRootHeight } from '../../utils/mobile/mobileUtils'; export const requestQueueCommentCount = new RequestQueueWithPromise(3); export const requestQueuePublishedAccouncements = new RequestQueueWithPromise( @@ -48,10 +51,11 @@ export const requestQueuePublishedAccouncements = new RequestQueueWithPromise( export const saveTempPublish = async ({ data, key }: any) => { return new Promise((res, rej) => { - window.sendMessage("saveTempPublish", { - data, - key, - }) + window + .sendMessage('saveTempPublish', { + data, + key, + }) .then((response) => { if (!response?.error) { res(response); @@ -60,37 +64,37 @@ export const saveTempPublish = async ({ data, key }: any) => { rej(response.error); }) .catch((error) => { - rej(error.message || "An error occurred"); + rej(error.message || 'An error occurred'); }); - }); }; export const getTempPublish = async () => { return new Promise((res, rej) => { - window.sendMessage("getTempPublish", {}) - .then((response) => { - if (!response?.error) { - res(response); - return; - } - rej(response.error); - }) - .catch((error) => { - rej(error.message || "An error occurred"); - }); - + window + .sendMessage('getTempPublish', {}) + .then((response) => { + if (!response?.error) { + res(response); + return; + } + rej(response.error); + }) + .catch((error) => { + rej(error.message || 'An error occurred'); + }); }); }; export const decryptPublishes = async (encryptedMessages: any[], secretKey) => { try { return await new Promise((res, rej) => { - window.sendMessage("decryptSingleForPublishes", { - data: encryptedMessages, - secretKeyObject: secretKey, - skipDecodeBase64: true, - }) + window + .sendMessage('decryptSingleForPublishes', { + data: encryptedMessages, + secretKeyObject: secretKey, + skipDecodeBase64: true, + }) .then((response) => { if (!response?.error) { res(response); @@ -99,26 +103,23 @@ export const decryptPublishes = async (encryptedMessages: any[], secretKey) => { rej(response.error); }) .catch((error) => { - rej(error.message || "An error occurred"); + rej(error.message || 'An error occurred'); }); - }); } catch (error) {} }; -export const handleUnencryptedPublishes = (publishes) => { - let publishesData = [] - publishes.forEach((pub)=> { +export const handleUnencryptedPublishes = (publishes) => { + let publishesData = []; + publishes.forEach((pub) => { try { const decryptToUnit8Array = base64ToUint8Array(pub); const decodedData = uint8ArrayToObject(decryptToUnit8Array); - if(decodedData){ - publishesData.push({decryptedData: decodedData}) + if (decodedData) { + publishesData.push({ decryptedData: decodedData }); } - } catch (error) { - - } - }) - return publishesData + } catch (error) {} + }); + return publishesData; }; export const GroupAnnouncements = ({ selectedGroup, @@ -130,7 +131,7 @@ export const GroupAnnouncements = ({ isAdmin, hide, myName, - isPrivate + isPrivate, }) => { const [messages, setMessages] = useState([]); const [isSending, setIsSending] = useState(false); @@ -159,12 +160,15 @@ export const GroupAnnouncements = ({ useEffect(() => { if (!selectedGroup) return; (async () => { - const res = await getDataPublishesFunc(selectedGroup, "anc"); + const res = await getDataPublishesFunc(selectedGroup, 'anc'); dataPublishes.current = res || {}; })(); }, [selectedGroup]); - const getAnnouncementData = async ({ identifier, name, resource }, isPrivate) => { + const getAnnouncementData = async ( + { identifier, name, resource }, + isPrivate + ) => { try { let data = dataPublishes.current[`${name}-${identifier}`]; if ( @@ -179,14 +183,17 @@ export const GroupAnnouncements = ({ }); if (!res?.ok) return; data = await res.text(); - await addDataPublishesFunc({ ...resource, data }, selectedGroup, "anc"); + await addDataPublishesFunc({ ...resource, data }, selectedGroup, 'anc'); } else { 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]; - if(!messageData) return + if (!messageData) return; setAnnouncementData((prev) => { return { ...prev, @@ -194,12 +201,17 @@ export const GroupAnnouncements = ({ }; }); } catch (error) { - console.error("error", error); + console.error('error', error); } }; useEffect(() => { - if ((!secretKey && isPrivate) || hasInitializedWebsocket.current || isPrivate === null) return; + if ( + (!secretKey && isPrivate) || + hasInitializedWebsocket.current || + isPrivate === null + ) + return; setIsLoading(true); // initWebsocketMessageGroup() hasInitializedWebsocket.current = true; @@ -208,10 +220,11 @@ export const GroupAnnouncements = ({ const encryptChatMessage = async (data: string, secretKeyObject: any) => { try { return new Promise((res, rej) => { - window.sendMessage("encryptSingle", { - data, - secretKeyObject, - }) + window + .sendMessage('encryptSingle', { + data, + secretKeyObject, + }) .then((response) => { if (!response?.error) { res(response); @@ -220,19 +233,19 @@ export const GroupAnnouncements = ({ rej(response.error); }) .catch((error) => { - rej(error.message || "An error occurred"); + rej(error.message || 'An error occurred'); }); - }); } catch (error) {} }; const publishAnc = async ({ encryptedData, identifier }: any) => { return new Promise((res, rej) => { - window.sendMessage("publishGroupEncryptedResource", { - encryptedData, - identifier, - }) + window + .sendMessage('publishGroupEncryptedResource', { + encryptedData, + identifier, + }) .then((response) => { if (!response?.error) { res(response); @@ -241,9 +254,8 @@ export const GroupAnnouncements = ({ rej(response.error); }) .catch((error) => { - rej(error.message || "An error occurred"); + rej(error.message || 'An error occurred'); }); - }); }; const clearEditorContent = () => { @@ -255,7 +267,7 @@ export const GroupAnnouncements = ({ setIsFocusedParent(false); setTimeout(() => { triggerRerender(); - }, 300); + }, 300); }, 200); } } @@ -266,10 +278,12 @@ export const GroupAnnouncements = ({ const getTempAnnouncements = await getTempPublish(); if (getTempAnnouncements?.announcement) { let tempData = []; - Object.keys(getTempAnnouncements?.announcement || {}).filter((annKey)=> annKey?.startsWith(`grp-${selectedGroup}-anc`)).map((key) => { - const value = getTempAnnouncements?.announcement[key]; - tempData.push(value.data); - }); + Object.keys(getTempAnnouncements?.announcement || {}) + .filter((annKey) => annKey?.startsWith(`grp-${selectedGroup}-anc`)) + .map((key) => { + const value = getTempAnnouncements?.announcement[key]; + tempData.push(value.data); + }); setTempPublishedList(tempData); } } catch (error) {} @@ -278,27 +292,28 @@ export const GroupAnnouncements = ({ const publishAnnouncement = async () => { try { pauseAllQueues(); - const fee = await getFee("ARBITRARY"); + const fee = await getFee('ARBITRARY'); await show({ - message: "Would you like to perform a ARBITRARY transaction?", - publishFee: fee.fee + " QORT", + message: 'Would you like to perform a ARBITRARY transaction?', + publishFee: fee.fee + ' QORT', }); if (isSending) return; if (editorRef.current) { const htmlContent = editorRef.current.getHTML(); - if (!htmlContent?.trim() || htmlContent?.trim() === "

") return; + if (!htmlContent?.trim() || htmlContent?.trim() === '

') return; setIsSending(true); const message = { version: 1, extra: {}, message: htmlContent, }; - const secretKeyObject = isPrivate === false ? null : await getSecretKey(false, true); - const message64: any = await objectToBase64(message); - const encryptSingle = isPrivate === false ? message64 : await encryptChatMessage( - message64, - secretKeyObject - ); + const secretKeyObject = + isPrivate === false ? null : await getSecretKey(false, true); + const message64: any = await objectToBase64(message); + const encryptSingle = + isPrivate === false + ? message64 + : await encryptChatMessage(message64, secretKeyObject); const randomUid = uid.rnd(); const identifier = `grp-${selectedGroup}-anc-${randomUid}`; const res = await publishAnc({ @@ -309,13 +324,13 @@ export const GroupAnnouncements = ({ const dataToSaveToStorage = { name: myName, identifier, - service: "DOCUMENT", + service: 'DOCUMENT', tempData: message, created: Date.now(), }; await saveTempPublish({ data: dataToSaveToStorage, - key: "announcement", + key: 'announcement', }); setTempData(selectedGroup); clearEditorContent(); @@ -324,7 +339,7 @@ export const GroupAnnouncements = ({ } catch (error) { if (!error) return; setInfoSnack({ - type: "error", + type: 'error', message: error, }); setOpenSnack(true); @@ -343,9 +358,9 @@ export const GroupAnnouncements = ({ const identifier = `grp-${selectedGroup}-anc-`; const url = `${getBaseApiReact()}${getArbitraryEndpointReact()}?mode=ALL&service=DOCUMENT&identifier=${identifier}&limit=20&includemetadata=false&offset=${offset}&reverse=true&prefix=true`; const response = await fetch(url, { - method: "GET", + method: 'GET', headers: { - "Content-Type": "application/json", + 'Content-Type': 'application/json', }, }); const responseData = await response.json(); @@ -354,11 +369,14 @@ export const GroupAnnouncements = ({ setAnnouncements(responseData); setIsLoading(false); for (const data of responseData) { - getAnnouncementData({ - name: data.name, - identifier: data.identifier, - resource: data, - }, isPrivate); + getAnnouncementData( + { + name: data.name, + identifier: data.identifier, + resource: data, + }, + isPrivate + ); } } catch (error) { } finally { @@ -369,8 +387,13 @@ export const GroupAnnouncements = ({ ); React.useEffect(() => { - if(!secretKey && isPrivate) return - if (selectedGroup && !hasInitialized.current && !hide && isPrivate !== null) { + if (!secretKey && isPrivate) return; + if ( + selectedGroup && + !hasInitialized.current && + !hide && + isPrivate !== null + ) { getAnnouncements(selectedGroup, isPrivate); hasInitialized.current = true; } @@ -384,9 +407,9 @@ export const GroupAnnouncements = ({ const identifier = `grp-${selectedGroup}-anc-`; const url = `${getBaseApiReact()}${getArbitraryEndpointReact()}?mode=ALL&service=DOCUMENT&identifier=${identifier}&limit=20&includemetadata=false&offset=${offset}&reverse=true&prefix=true`; const response = await fetch(url, { - method: "GET", + method: 'GET', headers: { - "Content-Type": "application/json", + 'Content-Type': 'application/json', }, }); const responseData = await response.json(); @@ -394,7 +417,10 @@ export const GroupAnnouncements = ({ setAnnouncements((prev) => [...prev, ...responseData]); setIsLoading(false); for (const data of responseData) { - getAnnouncementData({ name: data.name, identifier: data.identifier }, isPrivate); + getAnnouncementData( + { name: data.name, identifier: data.identifier }, + isPrivate + ); } } catch (error) {} }; @@ -406,9 +432,9 @@ export const GroupAnnouncements = ({ const identifier = `grp-${selectedGroup}-anc-`; const url = `${getBaseApiReact()}${getArbitraryEndpointReact()}?mode=ALL&service=DOCUMENT&identifier=${identifier}&limit=20&includemetadata=false&offset=${0}&reverse=true&prefix=true`; const response = await fetch(url, { - method: "GET", + method: 'GET', headers: { - "Content-Type": "application/json", + 'Content-Type': 'application/json', }, }); const responseData = await response.json(); @@ -416,10 +442,13 @@ export const GroupAnnouncements = ({ if (!latestMessage) { for (const data of responseData) { try { - getAnnouncementData({ - name: data.name, - identifier: data.identifier, - }, isPrivate); + getAnnouncementData( + { + name: data.name, + identifier: data.identifier, + }, + isPrivate + ); } catch (error) {} } setAnnouncements(responseData); @@ -434,7 +463,10 @@ export const GroupAnnouncements = ({ for (const data of newArray) { try { - getAnnouncementData({ name: data.name, identifier: data.identifier }, isPrivate); + getAnnouncementData( + { name: data.name, identifier: data.identifier }, + isPrivate + ); } catch (error) {} } setAnnouncements((prev) => [...newArray, ...prev]); @@ -486,13 +518,15 @@ export const GroupAnnouncements = ({
{!isMobile && ( Group Announcements )} - +
{!isLoading && combinedListTempAndReal?.length === 0 && ( No announcements @@ -589,28 +622,28 @@ export const GroupAnnouncements = ({ style={{ // position: 'fixed', // bottom: '0px', - backgroundColor: "#232428", - minHeight: isMobile ? "0px" : "150px", - maxHeight: isMobile ? "auto" : "400px", - display: "flex", - flexDirection: "column", - overflow: "hidden", - width: "100%", - boxSizing: "border-box", - padding: isMobile ? "10px" : "20px", - position: isFocusedParent ? "fixed" : "relative", - bottom: isFocusedParent ? "0px" : "unset", - top: isFocusedParent ? "0px" : "unset", - zIndex: isFocusedParent ? 5 : "unset", + backgroundColor: '#232428', + minHeight: isMobile ? '0px' : '150px', + maxHeight: isMobile ? 'auto' : '400px', + display: 'flex', + flexDirection: 'column', + overflow: 'hidden', + width: '100%', + boxSizing: 'border-box', + padding: isMobile ? '10px' : '20px', + position: isFocusedParent ? 'fixed' : 'relative', + bottom: isFocusedParent ? '0px' : 'unset', + top: isFocusedParent ? '0px' : 'unset', + zIndex: isFocusedParent ? 5 : 'unset', flexShrink: 0, }} >
@@ -625,12 +658,12 @@ export const GroupAnnouncements = ({
{isFocusedParent && ( @@ -639,19 +672,19 @@ export const GroupAnnouncements = ({ if (isSending) return; setIsFocusedParent(false); clearEditorContent(); - setTimeout(() => { - triggerRerender(); - }, 300); + setTimeout(() => { + triggerRerender(); + }, 300); // Unfocus the editor }} style={{ - marginTop: "auto", - alignSelf: "center", - cursor: isSending ? "default" : "pointer", - background: "var(--danger)", + marginTop: 'auto', + alignSelf: 'center', + cursor: isSending ? 'default' : 'pointer', + background: 'var(--danger)', flexShrink: 0, - padding: isMobile && "5px", - fontSize: isMobile && "14px", + padding: isMobile && '5px', + fontSize: isMobile && '14px', }} > {` Close`} @@ -663,25 +696,25 @@ export const GroupAnnouncements = ({ publishAnnouncement(); }} style={{ - marginTop: "auto", - alignSelf: "center", - cursor: isSending ? "default" : "pointer", - background: isSending && "rgba(0, 0, 0, 0.8)", + marginTop: 'auto', + alignSelf: 'center', + cursor: isSending ? 'default' : 'pointer', + background: isSending && 'rgba(0, 0, 0, 0.8)', flexShrink: 0, - padding: isMobile && "5px", - fontSize: isMobile && "14px", + padding: isMobile && '5px', + fontSize: isMobile && '14px', }} > {isSending && ( )} @@ -701,7 +734,7 @@ export const GroupAnnouncements = ({
diff --git a/src/components/DesktopSideBar.tsx b/src/components/DesktopSideBar.tsx index 08336e5..dce577b 100644 --- a/src/components/DesktopSideBar.tsx +++ b/src/components/DesktopSideBar.tsx @@ -6,7 +6,7 @@ import { IconWrapper } from './Desktop/DesktopFooter'; import { useRecoilState } from 'recoil'; import { enabledDevModeAtom } from '../atoms/global'; import { AppsIcon } from '../assets/Icons/AppsIcon'; -import ThemeSelector from '../styles/ThemeSelector'; +import ThemeSelector from './Theme/ThemeSelector'; export const DesktopSideBar = ({ goToHome, diff --git a/src/components/GlobalActions/JoinGroup.tsx b/src/components/GlobalActions/JoinGroup.tsx index f03430b..5085bac 100644 --- a/src/components/GlobalActions/JoinGroup.tsx +++ b/src/components/GlobalActions/JoinGroup.tsx @@ -1,5 +1,5 @@ -import React, { useContext, useEffect, useMemo, useState } from "react"; -import { subscribeToEvent, unsubscribeFromEvent } from "../../utils/events"; +import React, { useContext, useEffect, useMemo, useState } from 'react'; +import { subscribeToEvent, unsubscribeFromEvent } from '../../utils/events'; import { Box, Button, @@ -9,12 +9,12 @@ import { DialogActions, DialogContent, Typography, -} from "@mui/material"; -import { CustomButton, CustomButtonAccept } from "../../App-styles"; -import { getBaseApiReact, MyContext } from "../../App"; -import { getFee } from "../../background"; -import { CustomizedSnackbars } from "../Snackbar/Snackbar"; -import { FidgetSpinner } from "react-loader-spinner"; +} from '@mui/material'; +import { CustomButton, CustomButtonAccept } from '../../styles/App-styles'; +import { getBaseApiReact, MyContext } from '../../App'; +import { getFee } from '../../background'; +import { CustomizedSnackbars } from '../Snackbar/Snackbar'; +import { FidgetSpinner } from 'react-loader-spinner'; export const JoinGroup = ({ memberGroups }) => { const { show, setTxList } = useContext(MyContext); @@ -42,43 +42,45 @@ export const JoinGroup = ({ memberGroups }) => { }; useEffect(() => { - subscribeToEvent("globalActionJoinGroup", handleJoinGroup); + subscribeToEvent('globalActionJoinGroup', handleJoinGroup); return () => { - unsubscribeFromEvent("globalActionJoinGroup", handleJoinGroup); + unsubscribeFromEvent('globalActionJoinGroup', handleJoinGroup); }; }, []); - const isInGroup = useMemo(()=> { - return !!memberGroups.find((item)=> +item?.groupId === +groupInfo?.groupId) - }, [memberGroups, groupInfo]) + const isInGroup = useMemo(() => { + return !!memberGroups.find( + (item) => +item?.groupId === +groupInfo?.groupId + ); + }, [memberGroups, groupInfo]); const joinGroup = async (group, isOpen) => { try { const groupId = group.groupId; - const fee = await getFee("JOIN_GROUP"); + const fee = await getFee('JOIN_GROUP'); await show({ - message: "Would you like to perform an JOIN_GROUP transaction?", - publishFee: fee.fee + " QORT", + message: 'Would you like to perform an JOIN_GROUP transaction?', + publishFee: fee.fee + ' QORT', }); setIsLoadingJoinGroup(true); await new Promise((res, rej) => { window - .sendMessage("joinGroup", { + .sendMessage('joinGroup', { groupId, }) .then((response) => { if (!response?.error) { setInfoSnack({ - type: "success", + type: 'success', 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) { setTxList((prev) => [ { ...response, - type: "joined-group", + type: 'joined-group', label: `Joined Group ${group?.groupName}: awaiting confirmation`, labelDone: `Joined Group ${group?.groupName}: success!`, done: false, @@ -90,7 +92,7 @@ export const JoinGroup = ({ memberGroups }) => { setTxList((prev) => [ { ...response, - type: "joined-group-request", + type: 'joined-group-request', label: `Requested to join Group ${group?.groupName}: awaiting confirmation`, labelDone: `Requested to join Group ${group?.groupName}: success!`, done: false, @@ -105,7 +107,7 @@ export const JoinGroup = ({ memberGroups }) => { return; } else { setInfoSnack({ - type: "error", + type: 'error', message: response?.error, }); setOpenSnack(true); @@ -114,8 +116,8 @@ export const JoinGroup = ({ memberGroups }) => { }) .catch((error) => { setInfoSnack({ - type: "error", - message: error.message || "An error occurred", + type: 'error', + message: error.message || 'An error occurred', }); setOpenSnack(true); rej(error); @@ -138,37 +140,37 @@ export const JoinGroup = ({ memberGroups }) => { {!groupInfo && ( - {" "} + {' '} {" "} + />{' '} )} @@ -176,7 +178,7 @@ export const JoinGroup = ({ memberGroups }) => { @@ -185,7 +187,7 @@ export const JoinGroup = ({ memberGroups }) => { {groupInfo?.description && ( @@ -193,19 +195,19 @@ export const JoinGroup = ({ memberGroups }) => { )} {isInGroup && ( - - *You are already in this group! - + + *You are already in this group! + )} {!isInGroup && groupInfo?.isOpen === false && ( @@ -216,32 +218,34 @@ export const JoinGroup = ({ memberGroups }) => { - { + { joinGroup(groupInfo, groupInfo?.isOpen); setIsOpen(false); - }} disabled={isInGroup}> - - Join - + + Join + - + setIsOpen(false)} > @@ -259,14 +263,14 @@ export const JoinGroup = ({ memberGroups }) => { {isLoadingJoinGroup && ( { - 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=symmetric-qchat-group-${ groupId }&exactmatchnames=true&limit=0&reverse=true&${queryString}&prefix=true`; const response = await fetch(url); if (!response.ok) { - throw new Error("network error"); + throw new Error('network error'); } const adminData = await response.json(); const filterId = adminData.filter( - (data: any) => - data.identifier === `symmetric-qchat-group-${groupId}` + (data: any) => data.identifier === `symmetric-qchat-group-${groupId}` ); if (filterId?.length === 0) { return false; @@ -111,7 +111,6 @@ export const getPublishesFromAdmins = async (admins: string[], groupId) => { return dateB.getTime() - dateA.getTime(); }); - return sortedData[0]; }; interface GroupProps { @@ -149,7 +148,7 @@ export const getGroupAdminsAddress = async (groupNumber: number) => { export function validateSecretKey(obj) { // Check if the input is an object - if (typeof obj !== "object" || obj === null) { + if (typeof obj !== 'object' || obj === null) { return false; } @@ -164,19 +163,19 @@ export function validateSecretKey(obj) { const value = obj[key]; // Check that value is an object and not null - if (typeof value !== "object" || value === null) { + if (typeof value !== 'object' || value === null) { return false; } - // Check for messageKey - if (!value.hasOwnProperty("messageKey")) { + // Check for messageKey + if (!value.hasOwnProperty('messageKey')) { return false; } // Ensure messageKey and nonce are non-empty strings if ( - typeof value.messageKey !== "string" || - value.messageKey.trim() === "" + typeof value.messageKey !== 'string' || + value.messageKey.trim() === '' ) { return false; } @@ -196,33 +195,34 @@ export const getGroupMembers = async (groupNumber: number) => { return groupData; }; - export const decryptResource = async (data: string, fromQortalRequest) => { try { return new Promise((res, rej) => { - window.sendMessage("decryptGroupEncryption", { - data, - }) + window + .sendMessage('decryptGroupEncryption', { + data, + }) .then((response) => { if (!response?.error) { res(response); return; } - if(fromQortalRequest){ - rej({error: response.error, message: response?.error}); + if (fromQortalRequest) { + rej({ error: response.error, message: response?.error }); } else { rej(response.error); - } }) .catch((error) => { - if(fromQortalRequest){ - rej({message: error.message || "An error occurred", error: error.message || "An error occurred"}); + if (fromQortalRequest) { + rej({ + message: error.message || 'An error occurred', + error: error.message || 'An error occurred', + }); } else { - rej(error.message || "An error occurred",); + rej(error.message || 'An error occurred'); } }); - }); } catch (error) {} }; @@ -230,11 +230,12 @@ export const decryptResource = async (data: string, fromQortalRequest) => { export const addDataPublishesFunc = async (data: string, groupId, type) => { try { return new Promise((res, rej) => { - window.sendMessage("addDataPublishes", { - data, - groupId, - type, - }) + window + .sendMessage('addDataPublishes', { + data, + groupId, + type, + }) .then((response) => { if (!response?.error) { res(response); @@ -243,9 +244,8 @@ export const addDataPublishesFunc = async (data: string, groupId, type) => { rej(response.error); }) .catch((error) => { - rej(error.message || "An error occurred"); + rej(error.message || 'An error occurred'); }); - }); } catch (error) {} }; @@ -253,10 +253,11 @@ export const addDataPublishesFunc = async (data: string, groupId, type) => { export const getDataPublishesFunc = async (groupId, type) => { try { return new Promise((res, rej) => { - window.sendMessage("getDataPublishes", { - groupId, - type, - }) + window + .sendMessage('getDataPublishes', { + groupId, + type, + }) .then((response) => { if (!response?.error) { res(response); @@ -265,9 +266,8 @@ export const getDataPublishesFunc = async (groupId, type) => { rej(response.error); }) .catch((error) => { - rej(error.message || "An error occurred"); + rej(error.message || 'An error occurred'); }); - }); } catch (error) {} }; @@ -279,7 +279,7 @@ export async function getNameInfo(address: string) { if (nameData?.length > 0) { return nameData[0]?.name; } else { - return ""; + return ''; } } @@ -294,7 +294,6 @@ export const getGroupAdmins = async (groupNumber: number) => { let membersAddresses = []; let both = []; - const getMemNames = groupData?.members?.map(async (member) => { if (member?.member) { const name = await requestQueueAdminMemberNames.enqueue(() => { @@ -327,7 +326,7 @@ export const getNames = async (listOfMembers) => { if (name) { members.push({ ...member, name }); } else { - members.push({ ...member, name: "" }); + members.push({ ...member, name: '' }); } } @@ -339,7 +338,6 @@ export const getNames = async (listOfMembers) => { return members; }; export const getNamesForAdmins = async (admins) => { - let members: any = []; const getMemNames = admins?.map(async (admin) => { @@ -379,9 +377,9 @@ export const Group = ({ balance, setIsOpenDrawerProfile, setDesktopViewMode, - desktopViewMode + desktopViewMode, }: GroupProps) => { - const [desktopSideView, setDesktopSideView] = useState('groups') + const [desktopSideView, setDesktopSideView] = useState('groups'); const [secretKey, setSecretKey] = useState(null); const [secretKeyPublishDate, setSecretKeyPublishDate] = useState(null); @@ -405,10 +403,11 @@ export const Group = ({ const [openAddGroup, setOpenAddGroup] = useState(false); const [isInitialGroups, setIsInitialGroups] = useState(false); const [openManageMembers, setOpenManageMembers] = useState(false); - const { setMemberGroups, rootHeight, isRunningPublicNode } = useContext(MyContext); + const { setMemberGroups, rootHeight, isRunningPublicNode } = + useContext(MyContext); const lastGroupNotification = useRef(null); const [timestampEnterData, setTimestampEnterData] = useState({}); - const [chatMode, setChatMode] = useState("groups"); + const [chatMode, setChatMode] = useState('groups'); const [newChat, setNewChat] = useState(false); const [openSnack, setOpenSnack] = React.useState(false); const [infoSnack, setInfoSnack] = React.useState(null); @@ -417,18 +416,18 @@ export const Group = ({ const [isLoadingGroup, setIsLoadingGroup] = React.useState(false); const [firstSecretKeyInCreation, setFirstSecretKeyInCreation] = React.useState(false); - const [groupSection, setGroupSection] = React.useState("home"); + const [groupSection, setGroupSection] = React.useState('home'); const [groupAnnouncements, setGroupAnnouncements] = React.useState({}); const [defaultThread, setDefaultThread] = React.useState(null); const [isOpenDrawer, setIsOpenDrawer] = React.useState(false); - const setIsOpenBlockedUserModal = useSetRecoilState(isOpenBlockedModalAtom) + const setIsOpenBlockedUserModal = useSetRecoilState(isOpenBlockedModalAtom); const [hideCommonKeyPopup, setHideCommonKeyPopup] = React.useState(false); - const [isLoadingGroupMessage, setIsLoadingGroupMessage] = React.useState(""); - const [drawerMode, setDrawerMode] = React.useState("groups"); + const [isLoadingGroupMessage, setIsLoadingGroupMessage] = React.useState(''); + const [drawerMode, setDrawerMode] = React.useState('groups'); const [mutedGroups, setMutedGroups] = useState([]); - const [mobileViewMode, setMobileViewMode] = useState("home"); - const [mobileViewModeKeepOpen, setMobileViewModeKeepOpen] = useState(""); + const [mobileViewMode, setMobileViewMode] = useState('home'); + const [mobileViewModeKeepOpen, setMobileViewModeKeepOpen] = useState(''); const isFocusedRef = useRef(true); const timestampEnterDataRef = useRef({}); const selectedGroupRef = useRef(null); @@ -441,42 +440,42 @@ export const Group = ({ const { clearStatesMessageQueueProvider } = useMessageQueue(); const initiatedGetMembers = useRef(false); const [groupChatTimestamps, setGroupChatTimestamps] = React.useState({}); - const [appsMode, setAppsMode] = useState('home') - const [appsModeDev, setAppsModeDev] = useState('home') - const [isOpenSideViewDirects, setIsOpenSideViewDirects] = useState(false) - const [isOpenSideViewGroups, setIsOpenSideViewGroups] = useState(false) - const [isForceShowCreationKeyPopup, setIsForceShowCreationKeyPopup] = useState(false) + const [appsMode, setAppsMode] = useState('home'); + const [appsModeDev, setAppsModeDev] = useState('home'); + const [isOpenSideViewDirects, setIsOpenSideViewDirects] = useState(false); + const [isOpenSideViewGroups, setIsOpenSideViewGroups] = useState(false); + const [isForceShowCreationKeyPopup, setIsForceShowCreationKeyPopup] = + useState(false); - const [groupsProperties, setGroupsProperties] = useRecoilState(groupsPropertiesAtom) + const [groupsProperties, setGroupsProperties] = + useRecoilState(groupsPropertiesAtom); const setUserInfoForLevels = useSetRecoilState(addressInfoControllerAtom); - const isPrivate = useMemo(()=> { - if(selectedGroup?.groupId === '0') return false - if(!selectedGroup?.groupId || !groupsProperties[selectedGroup?.groupId]) return null - if(groupsProperties[selectedGroup?.groupId]?.isOpen === true) return false - if(groupsProperties[selectedGroup?.groupId]?.isOpen === false) return true - return null - }, [selectedGroup]) + const isPrivate = useMemo(() => { + if (selectedGroup?.groupId === '0') return false; + if (!selectedGroup?.groupId || !groupsProperties[selectedGroup?.groupId]) + return null; + if (groupsProperties[selectedGroup?.groupId]?.isOpen === true) return false; + if (groupsProperties[selectedGroup?.groupId]?.isOpen === false) return true; + return null; + }, [selectedGroup]); - - - - const setSelectedGroupId = useSetRecoilState(selectedGroupIdAtom) - const toggleSideViewDirects = ()=> { - if(isOpenSideViewGroups){ - setIsOpenSideViewGroups(false) + const setSelectedGroupId = useSetRecoilState(selectedGroupIdAtom); + const toggleSideViewDirects = () => { + if (isOpenSideViewGroups) { + setIsOpenSideViewGroups(false); } - setIsOpenSideViewDirects((prev)=> !prev) - } - const toggleSideViewGroups = ()=> { - if(isOpenSideViewDirects){ - setIsOpenSideViewDirects(false) + setIsOpenSideViewDirects((prev) => !prev); + }; + const toggleSideViewGroups = () => { + if (isOpenSideViewDirects) { + setIsOpenSideViewDirects(false); } - setIsOpenSideViewGroups((prev)=> !prev) - } - useEffect(()=> { - timestampEnterDataRef.current = timestampEnterData - }, [timestampEnterData]) + setIsOpenSideViewGroups((prev) => !prev); + }; + useEffect(() => { + timestampEnterDataRef.current = timestampEnterData; + }, [timestampEnterData]); useEffect(() => { isFocusedRef.current = isFocused; @@ -487,21 +486,20 @@ export const Group = ({ useEffect(() => { selectedGroupRef.current = selectedGroup; - setSelectedGroupId(selectedGroup?.groupId) + setSelectedGroupId(selectedGroup?.groupId); }, [selectedGroup]); useEffect(() => { selectedDirectRef.current = selectedDirect; }, [selectedDirect]); - - const getUserSettings = async () => { try { return new Promise((res, rej) => { - window.sendMessage("getUserSettings", { - key: "mutedGroups", - }) + window + .sendMessage('getUserSettings', { + key: 'mutedGroups', + }) .then((response) => { if (!response?.error) { setMutedGroups(response || []); @@ -511,12 +509,11 @@ export const Group = ({ rej(response.error); }) .catch((error) => { - rej(error.message || "An error occurred"); + rej(error.message || 'An error occurred'); }); - }); } catch (error) { - console.log("error", error); + console.log('error', error); } }; @@ -527,56 +524,56 @@ export const Group = ({ const getTimestampEnterChat = async () => { try { return new Promise((res, rej) => { - window.sendMessage("getTimestampEnterChat") - .then((response) => { - if (!response?.error) { - setTimestampEnterData(response); - res(response); - return; - } - rej(response.error); - }) - .catch((error) => { - rej(error.message || "An error occurred"); - }); - + window + .sendMessage('getTimestampEnterChat') + .then((response) => { + if (!response?.error) { + setTimestampEnterData(response); + res(response); + return; + } + rej(response.error); + }) + .catch((error) => { + rej(error.message || 'An error occurred'); + }); }); } catch (error) {} }; const refreshHomeDataFunc = () => { - setGroupSection("default"); + setGroupSection('default'); setTimeout(() => { - setGroupSection("home"); + setGroupSection('home'); }, 300); }; const getGroupAnnouncements = async () => { try { return new Promise((res, rej) => { - window.sendMessage("getGroupNotificationTimestamp") - .then((response) => { - if (!response?.error) { - setGroupAnnouncements(response); - res(response); - return; - } - rej(response.error); - }) - .catch((error) => { - rej(error.message || "An error occurred"); - }); - + window + .sendMessage('getGroupNotificationTimestamp') + .then((response) => { + if (!response?.error) { + setGroupAnnouncements(response); + res(response); + return; + } + rej(response.error); + }) + .catch((error) => { + rej(error.message || 'An error occurred'); + }); }); } catch (error) {} }; - useEffect(()=> { - if(myAddress){ - getGroupAnnouncements() - getTimestampEnterChat() + useEffect(() => { + if (myAddress) { + getGroupAnnouncements(); + getTimestampEnterChat(); } - }, [myAddress]) + }, [myAddress]); const getGroupOwner = async (groupId) => { try { @@ -592,9 +589,6 @@ export const Group = ({ } catch (error) {} }; - - - const directChatHasUnread = useMemo(() => { let hasUnread = false; directs.forEach((direct) => { @@ -615,13 +609,12 @@ export const Group = ({ const groupChatHasUnread = useMemo(() => { let hasUnread = false; groups.forEach((group) => { - - if ( group?.data && group?.sender !== myAddress && - group?.timestamp && groupChatTimestamps[group?.groupId] && - ((!timestampEnterData[group?.groupId] && + group?.timestamp && + groupChatTimestamps[group?.groupId] && + ((!timestampEnterData[group?.groupId] && Date.now() - group?.timestamp < timeDifferenceForNotificationChats) || timestampEnterData[group?.groupId] < group?.timestamp) ) { @@ -644,15 +637,12 @@ export const Group = ({ return hasUnread; }, [groupAnnouncements, groups]); - - - const getSecretKey = async ( loadingGroupParam?: boolean, secretKeyToPublish?: boolean ) => { try { - setIsLoadingGroupMessage("Locating encryption keys"); + setIsLoadingGroupMessage('Locating encryption keys'); pauseAllQueues(); let dataFromStorage; let publishFromStorage; @@ -660,8 +650,7 @@ export const Group = ({ if ( secretKeyToPublish && secretKey && - lastFetchedSecretKey.current - && + lastFetchedSecretKey.current && Date.now() - lastFetchedSecretKey.current < 600000 ) return secretKey; @@ -681,10 +670,11 @@ export const Group = ({ setAdmins(addresses); setAdminsWithNames(both); if (!names.length) { - throw new Error("Network error"); + throw new Error('Network error'); } const publish = - publishFromStorage || (await getPublishesFromAdmins(names, selectedGroup?.groupId)); + publishFromStorage || + (await getPublishesFromAdmins(names, selectedGroup?.groupId)); if (prevGroupId !== selectedGroupRef.current.groupId) { if (settimeoutForRefetchSecretKey.current) { @@ -705,7 +695,7 @@ export const Group = ({ data = dataFromStorage; } else { // const shouldRebuild = !secretKeyPublishDate || (publish?.update && publish?.updated > secretKeyPublishDate) - setIsLoadingGroupMessage("Downloading encryption keys"); + setIsLoadingGroupMessage('Downloading encryption keys'); const res = await fetch( `${getBaseApiReact()}/arbitrary/DOCUMENT_PRIVATE/${publish.name}/${ publish.identifier @@ -717,20 +707,25 @@ export const Group = ({ const dataint8Array = base64ToUint8Array(decryptedKey.data); const decryptedKeyToObject = uint8ArrayToObject(dataint8Array); if (!validateSecretKey(decryptedKeyToObject)) - throw new Error("SecretKey is not valid"); + throw new Error('SecretKey is not valid'); setSecretKeyDetails(publish); setSecretKey(decryptedKeyToObject); lastFetchedSecretKey.current = Date.now(); setMemberCountFromSecretKeyData(decryptedKey.count); - window.sendMessage("setGroupData", { - groupId: selectedGroup?.groupId, - secretKeyData: data, - secretKeyResource: publish, - admins: { names, addresses, both }, - }).catch((error) => { - console.error("Failed to set group data:", error.message || "An error occurred"); + window + .sendMessage('setGroupData', { + groupId: selectedGroup?.groupId, + secretKeyData: data, + secretKeyResource: publish, + admins: { names, addresses, both }, + }) + .catch((error) => { + console.error( + 'Failed to set group data:', + error.message || 'An error occurred' + ); }); - + if (decryptedKeyToObject) { setTriedToFetchSecretKey(true); setFirstSecretKeyInCreation(false); @@ -739,7 +734,7 @@ export const Group = ({ setTriedToFetchSecretKey(true); } } catch (error) { - if (error === "Unable to decrypt data") { + if (error === 'Unable to decrypt data') { setTriedToFetchSecretKey(true); settimeoutForRefetchSecretKey.current = setTimeout(() => { getSecretKey(); @@ -747,182 +742,197 @@ export const Group = ({ } } finally { setIsLoadingGroup(false); - setIsLoadingGroupMessage(""); + setIsLoadingGroupMessage(''); resumeAllQueues(); } }; - - const getAdminsForPublic = async(selectedGroup)=> { + const getAdminsForPublic = async (selectedGroup) => { try { - const { names, addresses, both } = - await getGroupAdmins(selectedGroup?.groupId) - setAdmins(addresses); - setAdminsWithNames(both); + const { names, addresses, both } = await getGroupAdmins( + selectedGroup?.groupId + ); + setAdmins(addresses); + setAdminsWithNames(both); } catch (error) { //error } - } + }; useEffect(() => { if (selectedGroup && isPrivate !== null) { - if(isPrivate){ + if (isPrivate) { setTriedToFetchSecretKey(false); getSecretKey(true); } - + getGroupOwner(selectedGroup?.groupId); } - if(isPrivate === false){ + if (isPrivate === false) { setTriedToFetchSecretKey(true); - if(selectedGroup?.groupId !== '0'){ - getAdminsForPublic(selectedGroup) + if (selectedGroup?.groupId !== '0') { + getAdminsForPublic(selectedGroup); } - - } }, [selectedGroup, isPrivate]); - - - - - const getCountNewMesg = async (groupId, after)=> { + const getCountNewMesg = async (groupId, after) => { try { const response = await fetch( `${getBaseApiReact()}/chat/messages?after=${after}&txGroupId=${groupId}&haschatreference=false&encoding=BASE64&limit=1` ); const data = await response.json(); - if(data && data[0]) return data[0].timestamp - } catch (error) { - - } - } + if (data && data[0]) return data[0].timestamp; + } catch (error) {} + }; - const getLatestRegularChat = async (groups)=> { + const getLatestRegularChat = async (groups) => { try { - - const groupData = {} + const groupData = {}; - const getGroupData = groups.map(async(group)=> { - if(!group.groupId || !group?.timestamp) return null - if((!groupData[group.groupId] || groupData[group.groupId] < group.timestamp)){ - const hasMoreRecentMsg = await getCountNewMesg(group.groupId, timestampEnterDataRef.current[group?.groupId] || Date.now() - 24 * 60 * 60 * 1000) - if(hasMoreRecentMsg){ - groupData[group.groupId] = hasMoreRecentMsg + const getGroupData = groups.map(async (group) => { + if (!group.groupId || !group?.timestamp) return null; + if ( + !groupData[group.groupId] || + groupData[group.groupId] < group.timestamp + ) { + const hasMoreRecentMsg = await getCountNewMesg( + group.groupId, + timestampEnterDataRef.current[group?.groupId] || + Date.now() - 24 * 60 * 60 * 1000 + ); + if (hasMoreRecentMsg) { + groupData[group.groupId] = hasMoreRecentMsg; } } else { - return null + return null; } - }) + }); - await Promise.all(getGroupData) - setGroupChatTimestamps(groupData) - } catch (error) { - - } - } + await Promise.all(getGroupData); + setGroupChatTimestamps(groupData); + } catch (error) {} + }; - const getGroupsProperties = useCallback(async(address)=> { + const getGroupsProperties = useCallback(async (address) => { try { const url = `${getBaseApiReact()}/groups/member/${address}`; const response = await fetch(url); - if(!response.ok) throw new Error('Cannot get group properties') + if (!response.ok) throw new Error('Cannot get group properties'); let data = await response.json(); - const transformToObject = data.reduce((result, item) => { - - result[item.groupId] = item - return result; - }, {}); - setGroupsProperties(transformToObject) + const transformToObject = data.reduce((result, item) => { + result[item.groupId] = item; + return result; + }, {}); + setGroupsProperties(transformToObject); } catch (error) { // error } - }, []) + }, []); - - useEffect(()=> { - if(!myAddress) return - if(areKeysEqual(groups?.map((grp)=> grp?.groupId), Object.keys(groupsProperties))){ + useEffect(() => { + if (!myAddress) return; + if ( + areKeysEqual( + groups?.map((grp) => grp?.groupId), + Object.keys(groupsProperties) + ) + ) { } else { - getGroupsProperties(myAddress) + getGroupsProperties(myAddress); } - }, [groups, myAddress]) - - + }, [groups, myAddress]); useEffect(() => { // Handler function for incoming messages const messageHandler = (event) => { if (event.origin !== window.location.origin) { - return; + return; } const message = event.data; - if (message?.action === "SET_GROUPS") { - + if (message?.action === 'SET_GROUPS') { // Update the component state with the received 'sendqort' state setGroups(sortArrayByTimestampAndGroupName(message.payload)); getLatestRegularChat(message.payload); - setMemberGroups(message.payload?.filter((item)=> item?.groupId !== '0')); - - if (selectedGroupRef.current && groupSectionRef.current === "chat") { - window.sendMessage("addTimestampEnterChat", { - timestamp: Date.now(), - groupId: selectedGroupRef.current.groupId, - }).catch((error) => { - console.error("Failed to add timestamp:", error.message || "An error occurred"); - }); + setMemberGroups( + message.payload?.filter((item) => item?.groupId !== '0') + ); + + if (selectedGroupRef.current && groupSectionRef.current === 'chat') { + window + .sendMessage('addTimestampEnterChat', { + timestamp: Date.now(), + groupId: selectedGroupRef.current.groupId, + }) + .catch((error) => { + console.error( + 'Failed to add timestamp:', + error.message || 'An error occurred' + ); + }); } - + if (selectedDirectRef.current) { - window.sendMessage("addTimestampEnterChat", { - timestamp: Date.now(), - groupId: selectedDirectRef.current.address, - }).catch((error) => { - console.error("Failed to add timestamp:", error.message || "An error occurred"); - }); + window + .sendMessage('addTimestampEnterChat', { + timestamp: Date.now(), + groupId: selectedDirectRef.current.address, + }) + .catch((error) => { + console.error( + 'Failed to add timestamp:', + error.message || 'An error occurred' + ); + }); } - + setTimeout(() => { getTimestampEnterChat(); }, 600); } - - if (message?.action === "SET_GROUP_ANNOUNCEMENTS") { + + if (message?.action === 'SET_GROUP_ANNOUNCEMENTS') { // Update the component state with the received 'sendqort' state setGroupAnnouncements(message.payload); - - if (selectedGroupRef.current && groupSectionRef.current === "announcement") { - window.sendMessage("addGroupNotificationTimestamp", { - timestamp: Date.now(), - groupId: selectedGroupRef.current.groupId, - }).catch((error) => { - console.error("Failed to add group notification timestamp:", error.message || "An error occurred"); - }); - + + if ( + selectedGroupRef.current && + groupSectionRef.current === 'announcement' + ) { + window + .sendMessage('addGroupNotificationTimestamp', { + timestamp: Date.now(), + groupId: selectedGroupRef.current.groupId, + }) + .catch((error) => { + console.error( + 'Failed to add group notification timestamp:', + error.message || 'An error occurred' + ); + }); + setTimeout(() => { getGroupAnnouncements(); }, 200); } } - - if (message?.action === "SET_DIRECTS") { + + if (message?.action === 'SET_DIRECTS') { // Update the component state with the received 'sendqort' state setDirects(message.payload); - } else if (message?.action === "PLAY_NOTIFICATION_SOUND") { + } else if (message?.action === 'PLAY_NOTIFICATION_SOUND') { // audio.play(); } }; - + // Attach the event listener - window.addEventListener("message", messageHandler); - + window.addEventListener('message', messageHandler); + // Clean up the event listener on component unmount return () => { - window.removeEventListener("message", messageHandler); + window.removeEventListener('message', messageHandler); }; }, []); - useEffect(() => { if ( @@ -933,11 +943,12 @@ export const Group = ({ ) return; - window.sendMessage("setupGroupWebsocket", {}) - .catch((error) => { - console.error("Failed to setup group websocket:", error.message || "An error occurred"); - }); - + window.sendMessage('setupGroupWebsocket', {}).catch((error) => { + console.error( + 'Failed to setup group websocket:', + error.message || 'An error occurred' + ); + }); hasInitializedWebsocket.current = true; }, [myAddress, groups]); @@ -954,7 +965,8 @@ export const Group = ({ !initiatedGetMembers.current && selectedGroup?.groupId && secretKey && - admins.includes(myAddress) && selectedGroup?.groupId !== '0' + admins.includes(myAddress) && + selectedGroup?.groupId !== '0' ) { // getAdmins(selectedGroup?.groupId); getMembers(selectedGroup?.groupId); @@ -1000,10 +1012,11 @@ export const Group = ({ try { setIsLoadingNotifyAdmin(true); await new Promise((res, rej) => { - window.sendMessage("notifyAdminRegenerateSecretKey", { - adminAddress: admin.address, - groupName: selectedGroup?.groupName, - }) + window + .sendMessage('notifyAdminRegenerateSecretKey', { + adminAddress: admin.address, + groupName: selectedGroup?.groupName, + }) .then((response) => { if (!response?.error) { res(response); @@ -1012,19 +1025,18 @@ export const Group = ({ rej(response.error); }) .catch((error) => { - rej(error.message || "An error occurred"); + rej(error.message || 'An error occurred'); }); - }); setInfoSnack({ - type: "success", - message: "Successfully sent notification.", + type: 'success', + message: 'Successfully sent notification.', }); setOpenSnack(true); } catch (error) { setInfoSnack({ - type: "error", - message: "Unable to send notification", + type: 'error', + message: 'Unable to send notification', }); } finally { setIsLoadingNotifyAdmin(false); @@ -1038,7 +1050,8 @@ export const Group = ({ if (!findGroup) return false; if (!findGroup?.data) return false; return ( - findGroup?.timestamp && groupChatTimestamps[findGroup?.groupId] && + findGroup?.timestamp && + groupChatTimestamps[findGroup?.groupId] && ((!timestampEnterData[selectedGroup?.groupId] && Date.now() - findGroup?.timestamp < timeDifferenceForNotificationChats) || @@ -1066,23 +1079,27 @@ export const Group = ({ return; } if (findDirect) { - if(!isMobile){ - setDesktopSideView("directs"); - setDesktopViewMode('home') + if (!isMobile) { + setDesktopSideView('directs'); + setDesktopViewMode('home'); } else { - setMobileViewModeKeepOpen("messaging"); + setMobileViewModeKeepOpen('messaging'); } setSelectedDirect(null); setNewChat(false); - window.sendMessage("addTimestampEnterChat", { - timestamp: Date.now(), - groupId: findDirect.address, - }).catch((error) => { - console.error("Failed to add timestamp:", error.message || "An error occurred"); + window + .sendMessage('addTimestampEnterChat', { + timestamp: Date.now(), + groupId: findDirect.address, + }) + .catch((error) => { + console.error( + 'Failed to add timestamp:', + error.message || 'An error occurred' + ); }); - setTimeout(() => { setSelectedDirect(findDirect); @@ -1102,36 +1119,40 @@ export const Group = ({ ); if (findDirect) { - if(!isMobile){ - setDesktopSideView("directs"); + if (!isMobile) { + setDesktopSideView('directs'); } else { - setMobileViewModeKeepOpen("messaging"); + setMobileViewModeKeepOpen('messaging'); } setSelectedDirect(null); setNewChat(false); - window.sendMessage("addTimestampEnterChat", { - timestamp: Date.now(), - groupId: findDirect.address, - }).catch((error) => { - console.error("Failed to add timestamp:", error.message || "An error occurred"); + window + .sendMessage('addTimestampEnterChat', { + timestamp: Date.now(), + groupId: findDirect.address, + }) + .catch((error) => { + console.error( + 'Failed to add timestamp:', + error.message || 'An error occurred' + ); }); - setTimeout(() => { setSelectedDirect(findDirect); getTimestampEnterChat(); }, 200); } else { - if(!isMobile){ - setDesktopSideView("directs"); + if (!isMobile) { + setDesktopSideView('directs'); } else { - setMobileViewModeKeepOpen("messaging"); + setMobileViewModeKeepOpen('messaging'); } setNewChat(true); setTimeout(() => { - executeEvent("setDirectToValueNewChat", { + executeEvent('setDirectToValueNewChat', { directToValue: name || directAddress, }); }, 500); @@ -1139,41 +1160,50 @@ export const Group = ({ }; useEffect(() => { - subscribeToEvent("openDirectMessageInternal", openDirectChatFromInternal); + subscribeToEvent('openDirectMessageInternal', openDirectChatFromInternal); return () => { unsubscribeFromEvent( - "openDirectMessageInternal", + 'openDirectMessageInternal', openDirectChatFromInternal ); }; }, [directs, selectedDirect]); useEffect(() => { - subscribeToEvent("openDirectMessage", openDirectChatFromNotification); + subscribeToEvent('openDirectMessage', openDirectChatFromNotification); return () => { - unsubscribeFromEvent("openDirectMessage", openDirectChatFromNotification); + unsubscribeFromEvent('openDirectMessage', openDirectChatFromNotification); }; }, [directs, selectedDirect]); const handleMarkAsRead = (e) => { const { groupId } = e.detail; - window.sendMessage("addTimestampEnterChat", { - timestamp: Date.now(), - groupId, - }).catch((error) => { - console.error("Failed to add timestamp:", error.message || "An error occurred"); - }); - - - window.sendMessage("addGroupNotificationTimestamp", { + window + .sendMessage('addTimestampEnterChat', { timestamp: Date.now(), groupId, - }).catch((error) => { - console.error("Failed to add group notification timestamp:", error.message || "An error occurred"); - }); - + }) + .catch((error) => { + console.error( + 'Failed to add timestamp:', + error.message || 'An error occurred' + ); + }); + + window + .sendMessage('addGroupNotificationTimestamp', { + timestamp: Date.now(), + groupId, + }) + .catch((error) => { + console.error( + 'Failed to add group notification timestamp:', + error.message || 'An error occurred' + ); + }); + setTimeout(() => { getGroupAnnouncements(); getTimestampEnterChat(); @@ -1181,10 +1211,10 @@ export const Group = ({ }; useEffect(() => { - subscribeToEvent("markAsRead", handleMarkAsRead); + subscribeToEvent('markAsRead', handleMarkAsRead); return () => { - unsubscribeFromEvent("markAsRead", handleMarkAsRead); + unsubscribeFromEvent('markAsRead', handleMarkAsRead); }; }, []); @@ -1196,7 +1226,7 @@ export const Group = ({ setSecretKeyDetails(null); setNewEncryptionNotification(null); setMemberCountFromSecretKeyData(null); - setIsForceShowCreationKeyPopup(false) + setIsForceShowCreationKeyPopup(false); setSelectedGroup(null); setSelectedDirect(null); setGroups([]); @@ -1212,7 +1242,7 @@ export const Group = ({ setOpenManageMembers(false); setMemberGroups([]); // Assuming you're clearing the context here as well setTimestampEnterData({}); - setChatMode("groups"); + setChatMode('groups'); setNewChat(false); setOpenSnack(false); setInfoSnack(null); @@ -1220,10 +1250,10 @@ export const Group = ({ setIsLoadingGroups(false); setIsLoadingGroup(false); setFirstSecretKeyInCreation(false); - setGroupSection("home"); + setGroupSection('home'); setGroupAnnouncements({}); setDefaultThread(null); - setMobileViewMode("home"); + setMobileViewMode('home'); // Reset all useRef values to their initial states hasInitialized.current = false; hasInitializedWebsocket.current = false; @@ -1237,8 +1267,8 @@ export const Group = ({ setupGroupWebsocketInterval.current = null; settimeoutForRefetchSecretKey.current = null; initiatedGetMembers.current = false; - if(!isMobile){ - setDesktopViewMode('home') + if (!isMobile) { + setDesktopViewMode('home'); } }; @@ -1248,30 +1278,29 @@ export const Group = ({ }; useEffect(() => { - subscribeToEvent("logout-event", logoutEventFunc); + subscribeToEvent('logout-event', logoutEventFunc); return () => { - unsubscribeFromEvent("logout-event", logoutEventFunc); + unsubscribeFromEvent('logout-event', logoutEventFunc); }; }, []); const openAppsMode = () => { if (isMobile) { - setMobileViewMode("apps"); + setMobileViewMode('apps'); } if (!isMobile) { - setDesktopViewMode('apps') - + setDesktopViewMode('apps'); } - if(isMobile){ - setIsOpenSideViewDirects(false) - setIsOpenSideViewGroups(false) - setGroupSection("default"); + if (isMobile) { + setIsOpenSideViewDirects(false); + setIsOpenSideViewGroups(false); + setGroupSection('default'); setSelectedGroup(null); setNewChat(false); setSelectedDirect(null); setSecretKey(null); - setGroupOwner(null) + setGroupOwner(null); lastFetchedSecretKey.current = null; initiatedGetMembers.current = false; setSecretKeyPublishDate(null); @@ -1282,23 +1311,19 @@ export const Group = ({ setMemberCountFromSecretKeyData(null); setTriedToFetchSecretKey(false); setFirstSecretKeyInCreation(false); - setIsOpenSideViewDirects(false) - setIsOpenSideViewGroups(false) + setIsOpenSideViewDirects(false); + setIsOpenSideViewGroups(false); } - - }; useEffect(() => { - subscribeToEvent("open-apps-mode", openAppsMode); + subscribeToEvent('open-apps-mode', openAppsMode); return () => { - unsubscribeFromEvent("open-apps-mode", openAppsMode); + unsubscribeFromEvent('open-apps-mode', openAppsMode); }; }, []); - - const openGroupChatFromNotification = (e) => { if (isLoadingOpenSectionFromNotification.current) return; @@ -1306,18 +1331,18 @@ export const Group = ({ const findGroup = groups?.find((group) => +group?.groupId === +groupId); if (findGroup?.groupId === selectedGroup?.groupId) { isLoadingOpenSectionFromNotification.current = false; - setChatMode("groups"); - setDesktopViewMode('chat') + setChatMode('groups'); + setDesktopViewMode('chat'); return; } if (findGroup) { - setChatMode("groups"); + setChatMode('groups'); setSelectedGroup(null); setSelectedDirect(null); setNewChat(false); setSecretKey(null); - setGroupOwner(null) + setGroupOwner(null); lastFetchedSecretKey.current = null; initiatedGetMembers.current = false; setSecretKeyPublishDate(null); @@ -1326,26 +1351,30 @@ export const Group = ({ setAdminsWithNames([]); setMembers([]); setMemberCountFromSecretKeyData(null); - setIsForceShowCreationKeyPopup(false) + setIsForceShowCreationKeyPopup(false); setTriedToFetchSecretKey(false); setFirstSecretKeyInCreation(false); - setGroupSection("chat"); - if(!isMobile){ - setDesktopViewMode('chat') + setGroupSection('chat'); + if (!isMobile) { + setDesktopViewMode('chat'); } - window.sendMessage("addTimestampEnterChat", { - timestamp: Date.now(), - groupId: findGroup.groupId, - }).catch((error) => { - console.error("Failed to add timestamp:", error.message || "An error occurred"); + window + .sendMessage('addTimestampEnterChat', { + timestamp: Date.now(), + groupId: findGroup.groupId, + }) + .catch((error) => { + console.error( + 'Failed to add timestamp:', + error.message || 'An error occurred' + ); }); - setTimeout(() => { setSelectedGroup(findGroup); - setMobileViewMode("group"); - setDesktopSideView('groups') + setMobileViewMode('group'); + setDesktopSideView('groups'); getTimestampEnterChat(); isLoadingOpenSectionFromNotification.current = false; }, 350); @@ -1355,10 +1384,10 @@ export const Group = ({ }; useEffect(() => { - subscribeToEvent("openGroupMessage", openGroupChatFromNotification); + subscribeToEvent('openGroupMessage', openGroupChatFromNotification); return () => { - unsubscribeFromEvent("openGroupMessage", openGroupChatFromNotification); + unsubscribeFromEvent('openGroupMessage', openGroupChatFromNotification); }; }, [groups, selectedGroup]); @@ -1368,10 +1397,10 @@ export const Group = ({ const findGroup = groups?.find((group) => +group?.groupId === +groupId); if (findGroup?.groupId === selectedGroup?.groupId) return; if (findGroup) { - setChatMode("groups"); + setChatMode('groups'); setSelectedGroup(null); setSecretKey(null); - setGroupOwner(null) + setGroupOwner(null); lastFetchedSecretKey.current = null; initiatedGetMembers.current = false; setSecretKeyPublishDate(null); @@ -1380,24 +1409,29 @@ export const Group = ({ setAdminsWithNames([]); setMembers([]); setMemberCountFromSecretKeyData(null); - setIsForceShowCreationKeyPopup(false) + setIsForceShowCreationKeyPopup(false); setTriedToFetchSecretKey(false); setFirstSecretKeyInCreation(false); - setGroupSection("announcement"); - if(!isMobile){ - setDesktopViewMode('chat') + setGroupSection('announcement'); + if (!isMobile) { + setDesktopViewMode('chat'); } - window.sendMessage("addGroupNotificationTimestamp", { - timestamp: Date.now(), - groupId: findGroup.groupId, - }).catch((error) => { - console.error("Failed to add group notification timestamp:", error.message || "An error occurred"); + window + .sendMessage('addGroupNotificationTimestamp', { + timestamp: Date.now(), + groupId: findGroup.groupId, + }) + .catch((error) => { + console.error( + 'Failed to add group notification timestamp:', + error.message || 'An error occurred' + ); }); - + setTimeout(() => { setSelectedGroup(findGroup); - setMobileViewMode("group"); - setDesktopSideView('groups') + setMobileViewMode('group'); + setDesktopSideView('groups'); getGroupAnnouncements(); }, 350); } @@ -1405,13 +1439,13 @@ export const Group = ({ useEffect(() => { subscribeToEvent( - "openGroupAnnouncement", + 'openGroupAnnouncement', openGroupAnnouncementFromNotification ); return () => { unsubscribeFromEvent( - "openGroupAnnouncement", + 'openGroupAnnouncement', openGroupAnnouncementFromNotification ); }; @@ -1422,16 +1456,16 @@ export const Group = ({ const { groupId } = data; const findGroup = groups?.find((group) => +group?.groupId === +groupId); if (findGroup?.groupId === selectedGroup?.groupId) { - setGroupSection("forum"); + setGroupSection('forum'); setDefaultThread(data); return; } if (findGroup) { - setChatMode("groups"); + setChatMode('groups'); setSelectedGroup(null); setSecretKey(null); - setGroupOwner(null) + setGroupOwner(null); lastFetchedSecretKey.current = null; initiatedGetMembers.current = false; setSecretKeyPublishDate(null); @@ -1440,28 +1474,28 @@ export const Group = ({ setAdminsWithNames([]); setMembers([]); setMemberCountFromSecretKeyData(null); - setIsForceShowCreationKeyPopup(false) + setIsForceShowCreationKeyPopup(false); setTriedToFetchSecretKey(false); setFirstSecretKeyInCreation(false); - setGroupSection("forum"); + setGroupSection('forum'); setDefaultThread(data); - if(!isMobile){ - setDesktopViewMode('chat') + if (!isMobile) { + setDesktopViewMode('chat'); } setTimeout(() => { setSelectedGroup(findGroup); - setMobileViewMode("group"); - setDesktopSideView('groups') + setMobileViewMode('group'); + setDesktopSideView('groups'); getGroupAnnouncements(); }, 350); } }; useEffect(() => { - subscribeToEvent("openThreadNewPost", openThreadNewPostFunc); + subscribeToEvent('openThreadNewPost', openThreadNewPostFunc); return () => { - unsubscribeFromEvent("openThreadNewPost", openThreadNewPostFunc); + unsubscribeFromEvent('openThreadNewPost', openThreadNewPostFunc); }; }, [groups, selectedGroup]); @@ -1471,23 +1505,21 @@ export const Group = ({ const goToHome = async () => { if (isMobile) { - setMobileViewMode("home"); + setMobileViewMode('home'); } if (!isMobile) { } - setDesktopViewMode('home') - + setDesktopViewMode('home'); await new Promise((res) => { setTimeout(() => { res(null); }, 200); }); - }; const goToAnnouncements = async () => { - setGroupSection("default"); + setGroupSection('default'); await new Promise((res) => { setTimeout(() => { res(null); @@ -1495,14 +1527,19 @@ export const Group = ({ }); setSelectedDirect(null); setNewChat(false); - setGroupSection("announcement"); - window.sendMessage("addGroupNotificationTimestamp", { - timestamp: Date.now(), - groupId: selectedGroupRef.current.groupId, - }).catch((error) => { - console.error("Failed to add group notification timestamp:", error.message || "An error occurred"); + setGroupSection('announcement'); + window + .sendMessage('addGroupNotificationTimestamp', { + timestamp: Date.now(), + groupId: selectedGroupRef.current.groupId, + }) + .catch((error) => { + console.error( + 'Failed to add group notification timestamp:', + error.message || 'An error occurred' + ); }); - + setTimeout(() => { getGroupAnnouncements(); }, 200); @@ -1510,33 +1547,37 @@ export const Group = ({ const openDrawerGroups = () => { setIsOpenDrawer(true); - setDrawerMode("groups"); + setDrawerMode('groups'); }; const goToThreads = () => { setSelectedDirect(null); setNewChat(false); - setGroupSection("forum"); + setGroupSection('forum'); }; const goToChat = async () => { - setGroupSection("default"); + setGroupSection('default'); await new Promise((res) => { setTimeout(() => { res(null); }, 200); }); - setGroupSection("chat"); + setGroupSection('chat'); setNewChat(false); setSelectedDirect(null); if (selectedGroupRef.current) { - window.sendMessage("addTimestampEnterChat", { - timestamp: Date.now(), - groupId: selectedGroupRef.current.groupId, - }).catch((error) => { - console.error("Failed to add timestamp:", error.message || "An error occurred"); + window + .sendMessage('addTimestampEnterChat', { + timestamp: Date.now(), + groupId: selectedGroupRef.current.groupId, + }) + .catch((error) => { + console.error( + 'Failed to add timestamp:', + error.message || 'An error occurred' + ); }); - setTimeout(() => { getTimestampEnterChat(); @@ -1544,39 +1585,42 @@ export const Group = ({ } }; - - const renderDirects = () => { return (
{!isMobile && ( - - + { - setDesktopSideView("groups"); + setDesktopSideView('groups'); }} > { - setDesktopSideView("directs"); + setDesktopSideView('directs'); }} > @@ -1609,31 +1658,31 @@ export const Group = ({ height={24} color={ directChatHasUnread - ? "var(--unread)" + ? 'var(--unread)' : desktopSideView === 'directs' - ? "white" - : "rgba(250, 250, 250, 0.5)" + ? 'white' + : 'rgba(250, 250, 250, 0.5)' } /> - - + + )} - +
{directs.map((direct: any) => ( { - console.error("Failed to add timestamp:", error.message || "An error occurred"); + window + .sendMessage('addTimestampEnterChat', { + timestamp: Date.now(), + groupId: direct.address, + }) + .catch((error) => { + console.error( + 'Failed to add timestamp:', + error.message || 'An error occurred' + ); }); - + setTimeout(() => { setSelectedDirect(direct); @@ -1657,29 +1711,29 @@ export const Group = ({ }, 200); }} sx={{ - display: "flex", - width: "100%", - flexDirection: "column", - cursor: "pointer", - border: "1px #232428 solid", - padding: "2px", - borderRadius: "2px", + display: 'flex', + width: '100%', + flexDirection: 'column', + cursor: 'pointer', + border: '1px #232428 solid', + padding: '2px', + borderRadius: '2px', background: - direct?.address === selectedDirect?.address && "white", + direct?.address === selectedDirect?.address && 'white', }} > @@ -1688,28 +1742,32 @@ export const Group = ({ {direct?.sender !== myAddress && @@ -1721,7 +1779,7 @@ export const Group = ({ direct?.timestamp) && ( )} @@ -1732,10 +1790,10 @@ export const Group = ({
New Chat @@ -1757,142 +1815,150 @@ export const Group = ({ ); }; - const renderGroups = () => { return (
{!isMobile && ( - - { - setDesktopSideView("groups"); + - { + setDesktopSideView('groups'); + }} > - - - - { - setDesktopSideView("directs"); - }} - > - + + + + { + setDesktopSideView('directs'); + }} > - - + label="Messaging" + selected={desktopSideView === 'directs'} + > + + )} - +
{groups.map((group: any) => ( { - setMobileViewMode("group"); - setDesktopSideView('groups') + setMobileViewMode('group'); + setDesktopSideView('groups'); initiatedGetMembers.current = false; clearAllQueues(); setSelectedDirect(null); setTriedToFetchSecretKey(false); setNewChat(false); setSelectedGroup(null); - setUserInfoForLevels({}) + setUserInfoForLevels({}); setSecretKey(null); lastFetchedSecretKey.current = null; setSecretKeyPublishDate(null); setAdmins([]); setSecretKeyDetails(null); setAdminsWithNames([]); - setGroupOwner(null) + setGroupOwner(null); setMembers([]); setMemberCountFromSecretKeyData(null); setHideCommonKeyPopup(false); setFirstSecretKeyInCreation(false); - setGroupSection("chat"); + setGroupSection('chat'); setIsOpenDrawer(false); - setIsForceShowCreationKeyPopup(false) + setIsForceShowCreationKeyPopup(false); setTimeout(() => { setSelectedGroup(group); - }, 200); }} sx={{ - display: "flex", - width: "100%", - flexDirection: "column", - cursor: "pointer", - border: "1px #232428 solid", - padding: "2px", - borderRadius: "2px", + display: 'flex', + width: '100%', + flexDirection: 'column', + cursor: 'pointer', + border: '1px #232428 solid', + padding: '2px', + borderRadius: '2px', background: - group?.groupId === selectedGroup?.groupId && "white", + group?.groupId === selectedGroup?.groupId && 'white', }} > {groupsProperties[group?.groupId]?.isOpen === false ? ( - - + + - ): ( - - + ) : ( + + - // - // {group.groupName?.charAt(0)} - // + // + // {group.groupName?.charAt(0)} + // )} - {groupAnnouncements[group?.groupId] && !groupAnnouncements[group?.groupId]?.seentimestamp && ( )} {group?.data && - groupChatTimestamps[group?.groupId] && + groupChatTimestamps[group?.groupId] && group?.sender !== myAddress && group?.timestamp && ((!timestampEnterData[group?.groupId] && @@ -1992,7 +2071,7 @@ export const Group = ({ group?.timestamp) && ( )} @@ -2004,48 +2083,47 @@ export const Group = ({
- {chatMode === "groups" && ( + {chatMode === 'groups' && ( <> - { - setOpenAddGroup(true); - }} - > - { + setOpenAddGroup(true); }} - /> - Group Mgmt - - {!isRunningPublicNode && ( - { - setIsOpenBlockedUserModal(true); - }} - sx={{ - minWidth: 'unset', - padding: '10px' - }} - > - - - )} - + > + + Group Mgmt + + {!isRunningPublicNode && ( + { + setIsOpenBlockedUserModal(true); + }} + sx={{ + minWidth: 'unset', + padding: '10px', + }} + > + + + )} )} - {chatMode === "directs" && ( + {chatMode === 'directs' && ( { setNewChat(true); @@ -2055,7 +2133,7 @@ export const Group = ({ > New Chat @@ -2065,7 +2143,7 @@ export const Group = ({
); }; - + return ( <> - - -
- {!isMobile && ((desktopViewMode !== 'apps' && desktopViewMode !== 'dev') || isOpenSideViewGroups) && ( - - )} + {!isMobile && + ((desktopViewMode !== 'apps' && desktopViewMode !== 'dev') || + isOpenSideViewGroups) && ( + + )} - {!isMobile && desktopViewMode === 'chat' && desktopSideView !== 'directs' && renderGroups()} - {!isMobile && desktopViewMode === 'chat' && desktopSideView === 'directs' && renderDirects()} + {!isMobile && + desktopViewMode === 'chat' && + desktopSideView !== 'directs' && + renderGroups()} + {!isMobile && + desktopViewMode === 'chat' && + desktopSideView === 'directs' && + renderDirects()} - {newChat && ( <> - {isMobile && ( - { - close() + close(); }} > - + - { - setSelectedDirect(null) - setMobileViewModeKeepOpen('') + setSelectedDirect(null); + setMobileViewModeKeepOpen(''); }} > - + - )} + )} - - No group selected - - - )} - -
- {!isMobile && ( - - - - )} - - - - - {triedToFetchSecretKey && ( - + + )} + +
+ {!isMobile && ( + + )} + + + {triedToFetchSecretKey && ( + + )} + {isPrivate && + firstSecretKeyInCreation && + triedToFetchSecretKey && + !secretKeyPublishDate && ( +
+ {' '} + + The group's first common encryption key is in the process + of creation. Please wait a few minutes for it to be + retrieved by the network. Checking every 2 minutes... + +
+ )} + {isPrivate && + !admins.includes(myAddress) && + !secretKey && + triedToFetchSecretKey ? ( + <> + {secretKeyPublishDate || + (!secretKeyPublishDate && !firstSecretKeyInCreation) ? ( +
+ {' '} + + You are not part of the encrypted group of members. Wait + until an admin re-encrypts the keys. + + + + + Only unencrypted messages will be displayed. + + + + + Try notifying an admin from the list of admins below: + + + {adminsWithNames.map((admin) => { + return ( + + {admin?.name} + notifyAdmin(admin)} + > + Notify + + + ); + })} +
+ ) : null} + + ) : admins.includes(myAddress) && + !secretKey && + isPrivate && + triedToFetchSecretKey ? null : !triedToFetchSecretKey ? null : ( + <> + + + {groupSection === 'adminSpace' && ( + + )} + + )} + + + {((isPrivate && + admins.includes(myAddress) && + shouldReEncrypt && + triedToFetchSecretKey && + !firstSecretKeyInCreation && + !hideCommonKeyPopup) || + isForceShowCreationKeyPopup) && ( + )} - {isPrivate && firstSecretKeyInCreation && - triedToFetchSecretKey && - !secretKeyPublishDate && ( -
- {" "} - - The group's first common encryption key is in the - process of creation. Please wait a few minutes for it to - be retrieved by the network. Checking every 2 minutes... - -
- )} - {isPrivate && !admins.includes(myAddress) && - !secretKey && - triedToFetchSecretKey ? ( - <> - {secretKeyPublishDate || - (!secretKeyPublishDate && !firstSecretKeyInCreation) ? ( -
- {" "} - - You are not part of the encrypted group of members. - Wait until an admin re-encrypts the keys. - - - - Only unencrypted messages will be displayed. - - - - Try notifying an admin from the list of admins below: - - - {adminsWithNames.map((admin) => { - return ( - - {admin?.name} - notifyAdmin(admin)} - > - Notify - - - ); - })} -
- ) : null} - - ) : admins.includes(myAddress) && - (!secretKey && isPrivate) && - triedToFetchSecretKey ? null : !triedToFetchSecretKey ? null : ( - <> - - - {groupSection === "adminSpace" && ( - - )} - - - )} - - - {((isPrivate && admins.includes(myAddress) && - shouldReEncrypt && - triedToFetchSecretKey && - !firstSecretKeyInCreation && - !hideCommonKeyPopup) || isForceShowCreationKeyPopup) && ( - - )} -
- {openManageMembers && ( - - )} -
- - + + {openManageMembers && ( + + )} +
+ {selectedDirect && !newChat && ( <> )} - - {!isMobile && ( - - )} - {!isMobile && ( - - )} - - - {!isMobile && ( - - - - )} + {!isMobile && ( + + )} + {!isMobile && ( + + )} - + {!isMobile && ( + + )} - + - - + > @@ -2583,5 +2719,3 @@ export const Group = ({ ); }; - - diff --git a/src/components/Minting/Minting.tsx b/src/components/Minting/Minting.tsx index af63ece..4a9eeb3 100644 --- a/src/components/Minting/Minting.tsx +++ b/src/components/Minting/Minting.tsx @@ -13,29 +13,29 @@ import { InputLabel, Snackbar, Typography, -} from "@mui/material"; +} from '@mui/material'; import React, { useCallback, useContext, useEffect, useMemo, useState, -} from "react"; -import CloseIcon from "@mui/icons-material/Close"; -import { MyContext, getBaseApiReact } from "../../App"; +} from 'react'; +import CloseIcon from '@mui/icons-material/Close'; +import { MyContext, getBaseApiReact } from '../../App'; import { executeEvent, subscribeToEvent, unsubscribeFromEvent, -} from "../../utils/events"; -import { getFee, getNameOrAddress } from "../../background"; -import CopyToClipboard from "react-copy-to-clipboard"; -import { AddressBox } from "../../App-styles"; -import { Spacer } from "../../common/Spacer"; -import Copy from "../../assets/svgs/Copy.svg"; -import { Loader } from "../Loader"; -import { FidgetSpinner } from "react-loader-spinner"; -import { useModal } from "../../common/useModal"; +} from '../../utils/events'; +import { getFee, getNameOrAddress } from '../../background'; +import CopyToClipboard from 'react-copy-to-clipboard'; +import { AddressBox } from '../../styles/App-styles'; +import { Spacer } from '../../common/Spacer'; +import Copy from '../../assets/svgs/Copy.svg'; +import { Loader } from '../Loader'; +import { FidgetSpinner } from 'react-loader-spinner'; +import { useModal } from '../../common/useModal'; export const Minting = ({ setIsOpenMinting, @@ -47,30 +47,30 @@ export const Minting = ({ }) => { const [mintingAccounts, setMintingAccounts] = useState([]); const [accountInfo, setAccountInfo] = useState(null); - const [rewardSharePublicKey, setRewardSharePublicKey] = useState(""); - const [mintingKey, setMintingKey] = useState(""); - const [rewardsharekey, setRewardsharekey] = useState(""); + const [rewardSharePublicKey, setRewardSharePublicKey] = useState(''); + const [mintingKey, setMintingKey] = useState(''); + const [rewardsharekey, setRewardsharekey] = useState(''); const [rewardShares, setRewardShares] = useState([]); const [nodeInfos, setNodeInfos] = useState({}); const [openSnack, setOpenSnack] = useState(false); const [isLoading, setIsLoading] = useState(false); - const { show: showKey, message } = useModal(); - const { isShow: isShowNext, onOk, show: showNext } = useModal(); + const { show: showKey, message } = useModal(); + const { isShow: isShowNext, onOk, show: showNext } = useModal(); const [info, setInfo] = useState(null); const [names, setNames] = useState({}); const [accountInfos, setAccountInfos] = useState({}); - const [showWaitDialog, setShowWaitDialog] = useState(false) + const [showWaitDialog, setShowWaitDialog] = useState(false); const isPartOfMintingGroup = useMemo(() => { if (groups?.length === 0) return false; - return !!groups?.find((item) => item?.groupId?.toString() === "694"); + return !!groups?.find((item) => item?.groupId?.toString() === '694'); }, [groups]); const getMintingAccounts = useCallback(async () => { try { const url = `${getBaseApiReact()}/admin/mintingaccounts`; const response = await fetch(url); if (!response.ok) { - throw new Error("network error"); + throw new Error('network error'); } const data = await response.json(); setMintingAccounts(data); @@ -117,7 +117,7 @@ export const Minting = ({ const url = `${getBaseApiReact()}/addresses/${address}`; const response = await fetch(url); if (!response.ok) { - throw new Error("network error"); + throw new Error('network error'); } const data = await response.json(); if (others) { @@ -144,10 +144,10 @@ export const Minting = ({ }; useEffect(() => { - subscribeToEvent("refresh-rewardshare-list", refreshRewardShare); + subscribeToEvent('refresh-rewardshare-list', refreshRewardShare); return () => { - unsubscribeFromEvent("refresh-rewardshare-list", refreshRewardShare); + unsubscribeFromEvent('refresh-rewardshare-list', refreshRewardShare); }; }, [myAddress]); @@ -177,15 +177,15 @@ export const Minting = ({ try { const url = `${getBaseApiReact()}/admin/status`; const response = await fetch(url, { - method: "GET", + method: 'GET', headers: { - "Content-Type": "application/json", + 'Content-Type': 'application/json', }, }); const data = await response.json(); setNodeInfos(data); } catch (error) { - console.error("Request failed", error); + console.error('Request failed', error); } }; @@ -194,11 +194,11 @@ export const Minting = ({ const url = `${getBaseApiReact()}/addresses/rewardshares?involving=${address}`; const response = await fetch(url); if (!response.ok) { - throw new Error("network error"); + throw new Error('network error'); } const data = await response.json(); setRewardShares(data); - return data + return data; } catch (error) {} }, []); @@ -208,10 +208,10 @@ export const Minting = ({ return await new Promise((res, rej) => { window .sendMessage( - "ADMIN_ACTION", + 'ADMIN_ACTION', { - type: "addmintingaccount", + type: 'addmintingaccount', value: val, }, 180000, @@ -220,7 +220,7 @@ export const Minting = ({ .then((response) => { if (!response?.error) { res(response); - setMintingKey(""); + setMintingKey(''); setTimeout(() => { getMintingAccounts(); }, 300); @@ -229,13 +229,13 @@ export const Minting = ({ rej({ message: response.error }); }) .catch((error) => { - rej({ message: error.message || "An error occurred" }); + rej({ message: error.message || 'An error occurred' }); }); }); } catch (error) { setInfo({ - type: "error", - message: error?.message || "Unable to add minting account", + type: 'error', + message: error?.message || 'Unable to add minting account', }); setOpenSnack(true); } finally { @@ -249,10 +249,10 @@ export const Minting = ({ return await new Promise((res, rej) => { window .sendMessage( - "ADMIN_ACTION", + 'ADMIN_ACTION', { - type: "removemintingaccount", + type: 'removemintingaccount', value: val, }, 180000, @@ -270,13 +270,13 @@ export const Minting = ({ rej({ message: response.error }); }) .catch((error) => { - rej({ message: error.message || "An error occurred" }); + rej({ message: error.message || 'An error occurred' }); }); }); } catch (error) { setInfo({ - type: "error", - message: error?.message || "Unable to remove minting account", + type: 'error', + message: error?.message || 'Unable to remove minting account', }); setOpenSnack(true); } finally { @@ -285,14 +285,14 @@ export const Minting = ({ }, []); const createRewardShare = useCallback(async (publicKey, recipient) => { - const fee = await getFee("REWARD_SHARE"); + const fee = await getFee('REWARD_SHARE'); await show({ - message: "Would you like to perform an REWARD_SHARE transaction?", - publishFee: fee.fee + " QORT", + message: 'Would you like to perform an REWARD_SHARE transaction?', + publishFee: fee.fee + ' QORT', }); return await new Promise((res, rej) => { window - .sendMessage("createRewardShare", { + .sendMessage('createRewardShare', { recipientPublicKey: publicKey, }) .then((response) => { @@ -301,7 +301,7 @@ export const Minting = ({ { recipient, ...response, - type: "add-rewardShare", + type: 'add-rewardShare', label: `Add rewardshare: awaiting confirmation`, labelDone: `Add rewardshare: success!`, done: false, @@ -314,7 +314,7 @@ export const Minting = ({ rej({ message: response.error }); }) .catch((error) => { - rej({ message: error.message || "An error occurred" }); + rej({ message: error.message || 'An error occurred' }); }); }); }, []); @@ -322,7 +322,7 @@ export const Minting = ({ const getRewardSharePrivateKey = useCallback(async (publicKey) => { return await new Promise((res, rej) => { window - .sendMessage("getRewardSharePrivateKey", { + .sendMessage('getRewardSharePrivateKey', { recipientPublicKey: publicKey, }) .then((response) => { @@ -333,7 +333,7 @@ export const Minting = ({ rej({ message: response.error }); }) .catch((error) => { - rej({ message: error.message || "An error occurred" }); + rej({ message: error.message || 'An error occurred' }); }); }); }, []); @@ -341,26 +341,24 @@ export const Minting = ({ const waitUntilRewardShareIsConfirmed = async (timeoutMs = 600000) => { const pollingInterval = 30000; const startTime = Date.now(); - + const sleep = (ms) => new Promise((res) => setTimeout(res, ms)); - + while (Date.now() - startTime < timeoutMs) { - - const rewardShares = await getRewardShares(myAddress); - const findRewardShare = rewardShares?.find( - (item) => - item?.recipient === myAddress && item?.mintingAccount === myAddress - ); - - if (findRewardShare) { - return true; // Exit early if found - } - - + const rewardShares = await getRewardShares(myAddress); + const findRewardShare = rewardShares?.find( + (item) => + item?.recipient === myAddress && item?.mintingAccount === myAddress + ); + + if (findRewardShare) { + return true; // Exit early if found + } + await sleep(pollingInterval); // Wait before the next poll } - - throw new Error("Timeout waiting for reward share confirmation"); + + throw new Error('Timeout waiting for reward share confirmation'); }; const startMinting = async () => { @@ -377,23 +375,22 @@ export const Minting = ({ addMintingAccount(privateRewardShare); } else { await createRewardShare(accountInfo?.publicKey, myAddress); - setShowWaitDialog(true) - await waitUntilRewardShareIsConfirmed() + setShowWaitDialog(true); + await waitUntilRewardShareIsConfirmed(); await showNext({ - message: '' - }) + message: '', + }); const privateRewardShare = await getRewardSharePrivateKey( accountInfo?.publicKey ); - setShowWaitDialog(false) + setShowWaitDialog(false); addMintingAccount(privateRewardShare); - } } catch (error) { - setShowWaitDialog(false) + setShowWaitDialog(false); setInfo({ - type: "error", - message: error?.message || "Unable to start minting", + type: 'error', + message: error?.message || 'Unable to start minting', }); setOpenSnack(true); } finally { @@ -412,13 +409,13 @@ export const Minting = ({ const url = `${getBaseApiReact()}/groups/member/${address}`; const response = await fetch(url); const data = await response.json(); - return !!data?.find((grp) => grp?.groupId?.toString() === "694"); + return !!data?.find((grp) => grp?.groupId?.toString() === '694'); }; const removeRewardShare = useCallback(async (rewardShare) => { return await new Promise((res, rej) => { window - .sendMessage("removeRewardShare", { + .sendMessage('removeRewardShare', { rewardShareKeyPairPublicKey: rewardShare.rewardSharePublicKey, recipient: rewardShare.recipient, percentageShare: -1, @@ -430,7 +427,7 @@ export const Minting = ({ { ...rewardShare, ...response, - type: "remove-rewardShare", + type: 'remove-rewardShare', label: `Remove rewardshare: awaiting confirmation`, labelDone: `Remove rewardshare: success!`, done: false, @@ -442,7 +439,7 @@ export const Minting = ({ rej({ message: response.error }); }) .catch((error) => { - rej({ message: error.message || "An error occurred" }); + rej({ message: error.message || 'An error occurred' }); }); }); }, []); @@ -454,8 +451,8 @@ export const Minting = ({ const privateRewardShare = await removeRewardShare(rewardShare); } catch (error) { setInfo({ - type: "error", - message: error?.message || "Unable to remove reward share", + type: 'error', + message: error?.message || 'Unable to remove reward share', }); setOpenSnack(true); } finally { @@ -468,9 +465,9 @@ export const Minting = ({ setIsLoading(true); const confirmReceiver = await getNameOrAddress(receiver); if (confirmReceiver.error) - throw new Error("Invalid receiver address or name"); + throw new Error('Invalid receiver address or name'); const isInMinterGroup = await checkIfMinterGroup(confirmReceiver); - if (!isInMinterGroup) throw new Error("Account not in Minter Group"); + if (!isInMinterGroup) throw new Error('Account not in Minter Group'); const publicKey = await getPublicKeyFromAddress(confirmReceiver); const findRewardShare = rewardShares?.find( (item) => @@ -487,8 +484,8 @@ export const Minting = ({ } } catch (error) { setInfo({ - type: "error", - message: error?.message || "Unable to create reward share", + type: 'error', + message: error?.message || 'Unable to create reward share', }); setOpenSnack(true); } finally { @@ -550,11 +547,9 @@ export const Minting = ({ (accountInfo?.blocksMinted + accountInfo?.blocksMintedAdjustment); let countBlocksString = countBlocks.toString(); - return "" + countBlocksString; + return '' + countBlocksString; }; - - return ( - {"Manage your minting"} + {'Manage your minting'} {isLoading && ( Account: {handleNames(accountInfo?.address)} @@ -631,11 +626,11 @@ export const Minting = ({ {isPartOfMintingGroup && !accountIsMinting && ( - - )} @@ -837,7 +835,7 @@ export const Minting = ({ {info?.message} diff --git a/src/components/QortPayment.tsx b/src/components/QortPayment.tsx index 6add9b2..ec68533 100644 --- a/src/components/QortPayment.tsx +++ b/src/components/QortPayment.tsx @@ -1,167 +1,170 @@ import { Box, CircularProgress } from '@mui/material'; -import React, { useEffect, useState } from 'react' -import { CustomButton, CustomInput, CustomLabel, TextP } from '../App-styles'; +import React, { useEffect, useState } from 'react'; +import { + CustomButton, + CustomInput, + CustomLabel, + TextP, +} from '../styles/App-styles'; import { Spacer } from '../common/Spacer'; import BoundedNumericTextField from '../common/BoundedNumericTextField'; import { PasswordField } from './PasswordField/PasswordField'; import { ErrorText } from './ErrorText/ErrorText'; import { getFee } from '../background'; -export const QortPayment = ({balance, show, onSuccess, defaultPaymentTo}) => { - const [paymentTo, setPaymentTo] = useState(defaultPaymentTo); - const [paymentAmount, setPaymentAmount] = useState(0); - const [paymentPassword, setPaymentPassword] = useState(""); - const [sendPaymentError, setSendPaymentError] = useState(""); - const [sendPaymentSuccess, setSendPaymentSuccess] = useState(""); - const [isLoadingSendCoin, setIsLoadingSendCoin] = useState(false); +export const QortPayment = ({ balance, show, onSuccess, defaultPaymentTo }) => { + const [paymentTo, setPaymentTo] = useState(defaultPaymentTo); + const [paymentAmount, setPaymentAmount] = useState(0); + const [paymentPassword, setPaymentPassword] = useState(''); + const [sendPaymentError, setSendPaymentError] = useState(''); + const [sendPaymentSuccess, setSendPaymentSuccess] = useState(''); + const [isLoadingSendCoin, setIsLoadingSendCoin] = useState(false); + const sendCoinFunc = async () => { + try { + setSendPaymentError(''); + setSendPaymentSuccess(''); + if (!paymentTo) { + setSendPaymentError('Please enter a recipient'); + return; + } + if (!paymentAmount) { + setSendPaymentError('Please enter an amount greater than 0'); + return; + } + if (!paymentPassword) { + setSendPaymentError('Please enter your wallet password'); + return; + } + const fee = await getFee('PAYMENT'); - - const sendCoinFunc = async() => { - try { - setSendPaymentError(""); - setSendPaymentSuccess(""); - if (!paymentTo) { - setSendPaymentError("Please enter a recipient"); - return; + await show({ + message: `Would you like to transfer ${Number(paymentAmount)} QORT?`, + paymentFee: fee.fee + ' QORT', + }); + setIsLoadingSendCoin(true); + window + .sendMessage('sendCoin', { + amount: Number(paymentAmount), + receiver: paymentTo.trim(), + password: paymentPassword, + }) + .then((response) => { + if (response?.error) { + setSendPaymentError(response.error); + } else { + onSuccess(); } - if (!paymentAmount) { - setSendPaymentError("Please enter an amount greater than 0"); - return; - } - if (!paymentPassword) { - setSendPaymentError("Please enter your wallet password"); - return; - } - const fee = await getFee('PAYMENT') - - await show({ - message: `Would you like to transfer ${Number(paymentAmount)} QORT?` , - paymentFee: fee.fee + ' QORT' - }) - setIsLoadingSendCoin(true); - window - .sendMessage("sendCoin", { - amount: Number(paymentAmount), - receiver: paymentTo.trim(), - password: paymentPassword, - }) - .then((response) => { - if (response?.error) { - setSendPaymentError(response.error); - } else { - onSuccess() - - } - setIsLoadingSendCoin(false); - }) - .catch((error) => { - console.error("Failed to send coin:", error); - setIsLoadingSendCoin(false); - }); - } catch (error) { - // error - } - }; + setIsLoadingSendCoin(false); + }) + .catch((error) => { + console.error('Failed to send coin:', error); + setIsLoadingSendCoin(false); + }); + } catch (error) { + // error + } + }; return ( <> - - Transfer QORT - - - - Balance: - - - {balance?.toFixed(2)} QORT - - - + sx={{ + display: 'flex', + flexDirection: 'column', + alignItems: 'flex-start', + }} + > + + Transfer QORT + + + + Balance: + + + {balance?.toFixed(2)} QORT + + + - - To - - setPaymentTo(e.target.value)} - autoComplete="off" - /> - - - Amount - - - setPaymentAmount(+e)} - /> - - - Confirm Wallet Password - - - setPaymentPassword(e.target.value)} - autoComplete="off" - /> - - - {sendPaymentError} - {/* {sendPaymentSuccess} */} - - + To + + setPaymentTo(e.target.value)} + autoComplete="off" + /> + + Amount + + setPaymentAmount(+e)} + /> + + + Confirm Wallet Password + + + setPaymentPassword(e.target.value)} + autoComplete="off" + /> +
+ + {sendPaymentError} + {/* {sendPaymentSuccess} */} + + { + if (isLoadingSendCoin) return; + sendCoinFunc(); + }} + > + {isLoadingSendCoin && ( + { - if(isLoadingSendCoin) return - sendCoinFunc(); - }} - > - {isLoadingSendCoin && ( - - )} - Send - + /> + )} + Send + - ) -} + ); +}; diff --git a/src/components/Theme/ThemeContext.tsx b/src/components/Theme/ThemeContext.tsx new file mode 100644 index 0000000..e64c183 --- /dev/null +++ b/src/components/Theme/ThemeContext.tsx @@ -0,0 +1,29 @@ +import { createContext, useContext, useState, useMemo } from 'react'; +import { ThemeProvider as MuiThemeProvider } from '@mui/material/styles'; +import { darkTheme, lightTheme } from '../../styles/theme'; + +const ThemeContext = createContext({ + themeMode: 'light', + toggleTheme: () => {}, +}); + +export const ThemeProvider = ({ children }: { children: React.ReactNode }) => { + const [themeMode, setThemeMode] = useState('light'); + + const theme = useMemo( + () => (themeMode === 'light' ? lightTheme : darkTheme), + [themeMode] + ); + + const toggleTheme = () => { + setThemeMode((prevMode) => (prevMode === 'light' ? 'dark' : 'light')); + }; + + return ( + + {children} + + ); +}; + +export const useThemeContext = () => useContext(ThemeContext); diff --git a/src/components/Theme/ThemeSelector.tsx b/src/components/Theme/ThemeSelector.tsx new file mode 100644 index 0000000..5b55f53 --- /dev/null +++ b/src/components/Theme/ThemeSelector.tsx @@ -0,0 +1,77 @@ +import { useThemeContext } from "./ThemeContext"; +import { styled, Switch } from "@mui/material"; + +const ThemeSwitch = styled(Switch)(({ theme }) => ({ + width: 62, + height: 34, + padding: 7, + "& .MuiSwitch-switchBase": { + margin: 1, + padding: 0, + transform: "translateX(6px)", + "&.Mui-checked": { + color: "#fff", + transform: "translateX(22px)", + "& .MuiSwitch-thumb:before": { + backgroundImage: `url('data:image/svg+xml;utf8,')`, + }, + "& + .MuiSwitch-track": { + opacity: 1, + backgroundColor: "#aab4be", + ...theme.applyStyles("dark", { + backgroundColor: "#8796A5", + }), + }, + }, + }, + "& .MuiSwitch-thumb": { + backgroundColor: "#001e3c", + width: 32, + height: 32, + "&::before": { + content: "''", + position: "absolute", + width: "100%", + height: "100%", + left: 0, + top: 0, + backgroundRepeat: "no-repeat", + backgroundPosition: "center", + backgroundImage: `url('data:image/svg+xml;utf8,')`, + }, + ...theme.applyStyles("dark", { + backgroundColor: "#003892", + }), + }, + "& .MuiSwitch-track": { + opacity: 1, + backgroundColor: "#aab4be", + borderRadius: 20 / 2, + ...theme.applyStyles("dark", { + backgroundColor: "#8796A5", + }), + }, +})); + +const ThemeSelector = ({ style }) => { + const { themeMode, toggleTheme } = useThemeContext(); + return ( +
+ +
+ ); +}; + +export default ThemeSelector; From 6323820732b7a28687371deac5827e93473f6026 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 12 Apr 2025 16:40:45 +0200 Subject: [PATCH 16/31] Refactor theme --- src/App-styles.ts | 260 -------------------------------------------- src/App.tsx | 48 +++----- src/index.css | 137 ----------------------- src/main.tsx | 5 +- src/styles/theme.ts | 1 - 5 files changed, 19 insertions(+), 432 deletions(-) delete mode 100644 src/App-styles.ts delete mode 100644 src/index.css diff --git a/src/App-styles.ts b/src/App-styles.ts deleted file mode 100644 index 9774f06..0000000 --- a/src/App-styles.ts +++ /dev/null @@ -1,260 +0,0 @@ -import { Typography, Box, TextField, InputLabel } from '@mui/material'; -import { styled } from '@mui/system'; - -export const AppContainer = styled(Box)(({ theme }) => ({ - display: 'flex', - alignItems: 'center', - flexDirection: 'column', - width: '100vw', - height: '100vh', - radius: '15px', - backgroundColor: theme.palette.background.default, - color: theme.palette.text.primary, - overflow: 'hidden', -})); - -export const AuthenticatedContainer = styled(Box)(({ theme }) => ({ - display: 'flex', - width: '100%', - height: '100%', - justifyContent: 'space-between', - backgroundColor: theme.palette.background.default, - color: theme.palette.text.primary, -})); - -export const AuthenticatedContainerInnerLeft = styled(Box)(({ theme }) => ({ - display: 'flex', - alignItems: 'center', - flexDirection: 'column', - height: '100%', - width: '100%', - backgroundColor: theme.palette.background.default, - color: theme.palette.text.primary, -})); - -export const AuthenticatedContainerInnerRight = styled(Box)(({ theme }) => ({ - display: 'flex', - alignItems: 'center', - flexDirection: 'column', - width: '60px', - height: '100%', - backgroundColor: theme.palette.background.default, - color: theme.palette.text.primary, -})); - -export const AuthenticatedContainerInnerTop = styled(Box)(({ theme }) => ({ - display: 'flex', - alignItems: 'center', - justifyContent: 'flex-start', - width: '100%px', - height: '60px', - padding: '20px', - backgroundColor: theme.palette.background.default, - color: theme.palette.text.primary, -})); - -export const TextP = styled(Typography)(({ theme }) => ({ - fontSize: '13px', - fontWeight: 600, - fontFamily: 'Inter', - backgroundColor: theme.palette.background.default, - color: theme.palette.text.primary, -})); - -export const TextItalic = styled('span')(({ theme }) => ({ - fontSize: '13px', - fontWeight: 600, - fontFamily: 'Inter', - fontStyle: 'italic', - backgroundColor: theme.palette.background.default, - color: theme.palette.text.primary, -})); - -export const TextSpan = styled('span')(({ theme }) => ({ - fontSize: '13px', - fontFamily: 'Inter', - fontWeight: 800, - backgroundColor: theme.palette.background.default, - color: theme.palette.text.primary, -})); - -export const AddressBox = styled(Box)(({ theme }) => ({ - display: 'flex', - border: `1px solid ${ - theme.palette.mode === 'dark' - ? 'rgba(255, 255, 255, 0.5)' - : 'rgba(0, 0, 0, 0.3)' - }`, - justifyContent: 'space-between', - alignItems: 'center', - width: 'auto', - height: '25px', - padding: '5px 15px', - gap: '5px', - borderRadius: '100px', - fontFamily: 'Inter', - fontSize: '12px', - fontWeight: 600, - lineHeight: '14.52px', - textAlign: 'left', - backgroundColor: theme.palette.background.default, - color: theme.palette.text.primary, - cursor: 'pointer', - transition: 'all 0.2s', - - '&:hover': { - backgroundColor: - theme.palette.mode === 'dark' - ? 'rgba(41, 41, 43, 1)' - : 'rgba(240, 240, 240, 1)', - color: theme.palette.mode === 'dark' ? '#fff' : '#000', - - 'svg path': { - fill: theme.palette.mode === 'dark' ? '#fff' : '#000', - }, - }, -})); - -export const CustomButton = styled(Box)(({ theme }) => ({ - boxSizing: 'border-box', - padding: '15px 20px', - gap: '10px', - - border: `0.5px solid ${ - theme.palette.mode === 'dark' - ? 'rgba(255, 255, 255, 0.5)' - : 'rgba(0, 0, 0, 0.3)' - }`, - filter: 'drop-shadow(1px 4px 10.5px rgba(0, 0, 0, 0.3))', - borderRadius: '5px', - - display: 'inline-flex', - justifyContent: 'center', - alignItems: 'center', - - width: 'fit-content', - minWidth: '160px', - cursor: 'pointer', - transition: 'all 0.2s', - - fontWeight: 600, - fontFamily: 'Inter', - textAlign: 'center', - backgroundColor: theme.palette.background.default, - color: theme.palette.text.primary, - - '&:hover': { - backgroundColor: - theme.palette.mode === 'dark' - ? 'rgba(41, 41, 43, 1)' - : 'rgba(230, 230, 230, 1)', - color: '#fff', - - 'svg path': { - fill: '#fff', - }, - }, -})); - -interface CustomButtonProps { - bgColor?: string; - color?: string; -} - -export const CustomButtonAccept = styled(Box)( - ({ bgColor, color, theme }) => ({ - boxSizing: 'border-box', - padding: '15px 20px', - gap: '10px', - border: `0.5px solid ${ - theme.palette.mode === 'dark' - ? 'rgba(255, 255, 255, 0.5)' - : 'rgba(0, 0, 0, 0.3)' - }`, - filter: 'drop-shadow(1px 4px 10.5px rgba(0,0,0,0.3))', - borderRadius: 5, - display: 'inline-flex', - justifyContent: 'center', - alignItems: 'center', - width: 'fit-content', - transition: 'all 0.2s', - minWidth: 160, - cursor: 'pointer', - fontWeight: 600, - fontFamily: 'Inter', - textAlign: 'center', - opacity: 0.7, - - // Color and backgroundColor with fallbacks - backgroundColor: - bgColor || (theme.palette.mode === 'dark' ? '#1d1d1d' : '#f5f5f5'), - color: color || (theme.palette.mode === 'dark' ? '#fff' : '#000'), - - '&:hover': { - opacity: 1, - backgroundColor: - bgColor || - (theme.palette.mode === 'dark' - ? 'rgba(41, 41, 43, 1)' - : 'rgba(230, 230, 230, 1)'), - color: color || '#fff', - svg: { - path: { - fill: color || '#fff', - }, - }, - }, - }) -); - -export const CustomInput = styled(TextField)(({ theme }) => ({ - width: '183px', // Adjust the width as needed - borderRadius: '5px', - // backgroundColor: "rgba(30, 30, 32, 1)", - outline: 'none', - backgroundColor: theme.palette.background.default, - color: theme.palette.text.primary, - input: { - fontSize: 10, - fontFamily: 'Inter', - fontWeight: 400, - color: 'white', - '&::placeholder': { - fontSize: 16, - color: 'rgba(255, 255, 255, 0.2)', - }, - outline: 'none', - padding: '10px', - }, - '& .MuiOutlinedInput-root': { - '& fieldset': { - border: '0.5px solid rgba(255, 255, 255, 0.5)', - }, - '&:hover fieldset': { - border: '0.5px solid rgba(255, 255, 255, 0.5)', - }, - '&.Mui-focused fieldset': { - border: '0.5px solid rgba(255, 255, 255, 0.5)', - }, - }, - '& .MuiInput-underline:before': { - borderBottom: 'none', - }, - '& .MuiInput-underline:hover:not(.Mui-disabled):before': { - borderBottom: 'none', - }, - '& .MuiInput-underline:after': { - borderBottom: 'none', - }, -})); - -export const CustomLabel = styled(InputLabel)(({ theme }) => ({ - fontWeight: 400, - fontFamily: 'Inter', - fontSize: '10px', - lineHeight: '12px', - color: - theme.palette.mode === 'dark' - ? 'rgba(255, 255, 255, 0.5)' - : 'rgba(0, 0, 0, 0.5)', -})); diff --git a/src/App.tsx b/src/App.tsx index 52d322c..aa4c016 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -34,8 +34,8 @@ import ltcLogo from './assets/ltc.png'; import PersonSearchIcon from '@mui/icons-material/PersonSearch'; import qortLogo from './assets/qort.png'; import { CopyToClipboard } from 'react-copy-to-clipboard'; -import Download from './assets/svgs/Download.svg'; -import Logout from './assets/svgs/Logout.svg'; +import { Download } from './assets/svgs/Download.tsx'; +import { Logout } from './assets/svgs/Logout.tsx'; import { Return } from './assets/svgs/Return.tsx'; import WarningIcon from '@mui/icons-material/Warning'; import Success from './assets/svgs/Success.svg'; @@ -45,7 +45,6 @@ import AccountBalanceWalletIcon from '@mui/icons-material/AccountBalanceWallet'; import PriorityHighIcon from '@mui/icons-material/PriorityHigh'; import { createAccount, - generateRandomSentence, saveFileToDisk, saveSeedPhraseToDisk, } from './utils/generateWallet/generateWallet'; @@ -61,16 +60,14 @@ import { AuthenticatedContainerInnerRight, CustomButton, CustomButtonAccept, - CustomInput, CustomLabel, TextItalic, TextP, TextSpan, -} from './App-styles'; +} from './styles/App-styles.ts'; import { Spacer } from './common/Spacer'; import { Loader } from './components/Loader'; import { PasswordField, ErrorText } from './components'; -import { ChatGroup } from './components/Chat/ChatGroup'; import { Group, requestQueueMemberNames } from './components/Group/Group'; import { TaskManager } from './components/TaskManager/TaskManger'; import { useModal } from './common/useModal'; @@ -149,7 +146,7 @@ import { BuyQortInformation } from './components/BuyQortInformation'; import { QortPayment } from './components/QortPayment'; import { GeneralNotifications } from './components/GeneralNotifications'; import { PdfViewer } from './common/PdfViewer'; -import ThemeSelector from './styles/ThemeSelector.tsx'; +import ThemeSelector from './components/Theme/ThemeSelector.tsx'; type extStates = | 'not-authenticated' @@ -1652,16 +1649,16 @@ function App() { }, }} > - { - logoutFunc(); - setIsOpenDrawerProfile(false); - }} + { + logoutFunc(); + setIsOpenDrawerProfile(false); }} /> @@ -2020,15 +2017,16 @@ function App() { }, }} > - { - setExtstate('download-wallet'); - setIsOpenDrawerProfile(false); - }} - src={Download} + { + setExtstate('download-wallet'); + setIsOpenDrawerProfile(false); }} /> @@ -2155,18 +2153,6 @@ function App() { maxWidth: '700px', }} > - {/* */} '; - inherits: true; - initial-value: transparent; -} - -.scrollable-container { - transition: --var1 0.4s; -} - -.scrollable-container:hover { - --var1: #444444; -} - -.scrollable-container::-webkit-scrollbar-thumb { - background-color: var(--var1); - border-radius: 8px; - background-clip: content-box; - border: 4px solid transparent; - opacity: 0; -} - -/* Mobile-specific scrollbar styles */ -@media only screen and (max-width: 600px) { - ::-webkit-scrollbar { - width: 8px; /* Narrower scrollbar width on mobile */ - height: 6px; /* Narrower scrollbar height on mobile */ - } - - ::-webkit-scrollbar-thumb { - border-radius: 4px; /* Adjust the radius for a narrower thumb */ - border: 2px solid transparent; /* Narrower thumb border */ - } -} - -.group-list::-webkit-scrollbar-thumb:hover { - background-color: whitesmoke; -} - -html, -body { - overscroll-behavior: none !important; -} - -.swiper { - width: 100%; -} diff --git a/src/main.tsx b/src/main.tsx index 791c63d..3f04c89 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -1,11 +1,10 @@ -import React from 'react'; import ReactDOM from 'react-dom/client'; import App from './App.tsx'; -import './index.css'; +import '../src/styles/index.css'; import './messaging/messagesToBackground'; import { MessageQueueProvider } from './MessageQueueContext.tsx'; import { RecoilRoot } from 'recoil'; -import { ThemeProvider } from './styles/ThemeContext.tsx'; +import { ThemeProvider } from './components/Theme/ThemeContext.tsx'; ReactDOM.createRoot(document.getElementById('root')!).render( <> diff --git a/src/styles/theme.ts b/src/styles/theme.ts index 1ae2ea8..ef414c5 100644 --- a/src/styles/theme.ts +++ b/src/styles/theme.ts @@ -102,7 +102,6 @@ const lightTheme = createTheme({ secondary: '#525252', }, }, - components: { MuiCard: { styleOverrides: { From 8bb55b74f5b93fe47063fe94043d6b67c7a6af11 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 12 Apr 2025 17:05:17 +0200 Subject: [PATCH 17/31] Change sun color/background --- src/components/Theme/ThemeSelector.tsx | 66 +++++++++++++------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/src/components/Theme/ThemeSelector.tsx b/src/components/Theme/ThemeSelector.tsx index 5b55f53..dbf9d90 100644 --- a/src/components/Theme/ThemeSelector.tsx +++ b/src/components/Theme/ThemeSelector.tsx @@ -1,58 +1,58 @@ -import { useThemeContext } from "./ThemeContext"; -import { styled, Switch } from "@mui/material"; +import { useThemeContext } from './ThemeContext'; +import { styled, Switch } from '@mui/material'; const ThemeSwitch = styled(Switch)(({ theme }) => ({ width: 62, height: 34, padding: 7, - "& .MuiSwitch-switchBase": { + '& .MuiSwitch-switchBase': { margin: 1, padding: 0, - transform: "translateX(6px)", - "&.Mui-checked": { - color: "#fff", - transform: "translateX(22px)", - "& .MuiSwitch-thumb:before": { + transform: 'translateX(6px)', + '&.Mui-checked': { + color: '#fff', + transform: 'translateX(22px)', + '& .MuiSwitch-thumb:before': { backgroundImage: `url('data:image/svg+xml;utf8,')`, }, - "& + .MuiSwitch-track": { + '& + .MuiSwitch-track': { opacity: 1, - backgroundColor: "#aab4be", - ...theme.applyStyles("dark", { - backgroundColor: "#8796A5", + backgroundColor: '#aab4be', + ...theme.applyStyles('dark', { + backgroundColor: '#8796A5', }), }, }, }, - "& .MuiSwitch-thumb": { - backgroundColor: "#001e3c", + '& .MuiSwitch-thumb': { + backgroundColor: '#fde402', width: 32, height: 32, - "&::before": { + '&::before': { content: "''", - position: "absolute", - width: "100%", - height: "100%", + position: 'absolute', + width: '100%', + height: '100%', left: 0, top: 0, - backgroundRepeat: "no-repeat", - backgroundPosition: "center", + backgroundRepeat: 'no-repeat', + backgroundPosition: 'center', backgroundImage: `url('data:image/svg+xml;utf8,')`, }, - ...theme.applyStyles("dark", { - backgroundColor: "#003892", + ...theme.applyStyles('dark', { + backgroundColor: '#003892', }), }, - "& .MuiSwitch-track": { + '& .MuiSwitch-track': { opacity: 1, - backgroundColor: "#aab4be", + backgroundColor: '#aab4be', borderRadius: 20 / 2, - ...theme.applyStyles("dark", { - backgroundColor: "#8796A5", + ...theme.applyStyles('dark', { + backgroundColor: '#8796A5', }), }, })); @@ -62,14 +62,14 @@ const ThemeSelector = ({ style }) => { return (
- +
); }; From d7356be44383a937f5e4d05710fb3739a638b8a4 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 12 Apr 2025 17:42:30 +0200 Subject: [PATCH 18/31] Move css into theme folder, add dynamic class depending on selected theme --- src/components/CoreSyncStatus.tsx | 129 +++++++++++------- src/{components => styles}/CoreSyncStatus.css | 49 ++++--- 2 files changed, 108 insertions(+), 70 deletions(-) rename src/{components => styles}/CoreSyncStatus.css (87%) diff --git a/src/components/CoreSyncStatus.tsx b/src/components/CoreSyncStatus.tsx index 2334bef..44fed2c 100644 --- a/src/components/CoreSyncStatus.tsx +++ b/src/components/CoreSyncStatus.tsx @@ -1,28 +1,31 @@ -import React, { useEffect, useState } from 'react'; -import syncedImg from '../assets/syncStatus/synced.png' -import syncedMintingImg from '../assets/syncStatus/synced_minting.png' -import syncingImg from '../assets/syncStatus/syncing.png' +import { useEffect, useState } from 'react'; +import syncedImg from '../assets/syncStatus/synced.png'; +import syncedMintingImg from '../assets/syncStatus/synced_minting.png'; +import syncingImg from '../assets/syncStatus/syncing.png'; import { getBaseApiReact } from '../App'; -import './CoreSyncStatus.css' -export const CoreSyncStatus = ({imageSize, position}) => { +import '../styles/CoreSyncStatus.css'; +import { useTheme } from '@mui/material'; + +export const CoreSyncStatus = ({ imageSize, position }) => { const [nodeInfos, setNodeInfos] = useState({}); const [coreInfos, setCoreInfos] = useState({}); const [isUsingGateway, setIsUsingGateway] = useState(false); + const theme = useTheme(); useEffect(() => { const getNodeInfos = async () => { - - try { - setIsUsingGateway(!!getBaseApiReact()?.includes('ext-node.qortal.link')) - const url = `${getBaseApiReact()}/admin/status`; - const response = await fetch(url, { - method: "GET", - headers: { - "Content-Type": "application/json", - }, - }); - const data = await response.json(); + setIsUsingGateway( + !!getBaseApiReact()?.includes('ext-node.qortal.link') + ); + const url = `${getBaseApiReact()}/admin/status`; + const response = await fetch(url, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, + }); + const data = await response.json(); setNodeInfos(data); } catch (error) { console.error('Request failed', error); @@ -30,14 +33,12 @@ export const CoreSyncStatus = ({imageSize, position}) => { }; const getCoreInfos = async () => { - - try { const url = `${getBaseApiReact()}/admin/info`; const response = await fetch(url, { - method: "GET", + method: 'GET', headers: { - "Content-Type": "application/json", + 'Content-Type': 'application/json', }, }); const data = await response.json(); @@ -59,55 +60,85 @@ export const CoreSyncStatus = ({imageSize, position}) => { }, []); const renderSyncStatusIcon = () => { - const { isSynchronizing = false, syncPercent = 0, isMintingPossible = false, height = 0, numberOfConnections = 0 } = nodeInfos; - const buildVersion = coreInfos?.buildVersion ? coreInfos?.buildVersion.substring(0, 12) : ''; + const { + isSynchronizing = false, + syncPercent = 0, + isMintingPossible = false, + height = 0, + numberOfConnections = 0, + } = nodeInfos; + const buildVersion = coreInfos?.buildVersion + ? coreInfos?.buildVersion.substring(0, 12) + : ''; let imagePath = syncingImg; - let message = `Synchronizing` + let message = `Synchronizing`; if (isMintingPossible && !isUsingGateway) { imagePath = syncedMintingImg; - message = `${isSynchronizing ? 'Synchronizing' : 'Synchronized'} ${'(Minting)'}` + message = `${isSynchronizing ? 'Synchronizing' : 'Synchronized'} ${'(Minting)'}`; } else if (isSynchronizing === true && syncPercent === 99) { - imagePath = syncingImg + imagePath = syncingImg; } else if (isSynchronizing && !isMintingPossible && syncPercent === 100) { imagePath = syncingImg; - message = `Synchronizing ${isUsingGateway ? '' :'(Not Minting)'}` + message = `Synchronizing ${isUsingGateway ? '' : '(Not Minting)'}`; } else if (!isSynchronizing && !isMintingPossible && syncPercent === 100) { - imagePath = syncedImg - message = `Synchronized ${isUsingGateway ? '' :'(Not Minting)'}` + imagePath = syncedImg; + message = `Synchronized ${isUsingGateway ? '' : '(Not Minting)'}`; } else if (isSynchronizing && isMintingPossible && syncPercent === 100) { imagePath = syncingImg; - message = `Synchronizing ${isUsingGateway ? '' :'(Minting)'}` + message = `Synchronizing ${isUsingGateway ? '' : '(Minting)'}`; } else if (!isSynchronizing && isMintingPossible && syncPercent === 100) { imagePath = syncedMintingImg; - message = `Synchronized ${isUsingGateway ? '' :'(Minting)'}` + message = `Synchronized ${isUsingGateway ? '' : '(Minting)'}`; } - - return ( -
- sync status -
+
+ + sync status + +

Core Information

-

Core Version: {buildVersion}

+

+ Core Version:{' '} + {buildVersion} +

{message}

-

Block Height: {height || ''}

-

Connected Peers: {numberOfConnections || ''}

-

Using public node: {isUsingGateway?.toString()}

+

+ Block Height:{' '} + {height || ''} +

+

+ Connected Peers:{' '} + + {numberOfConnections || ''} + +

+

+ Using public node:{' '} + + {isUsingGateway?.toString()} + +

); }; - return ( -
- {renderSyncStatusIcon()} -
- ); + return
{renderSyncStatusIcon()}
; }; - diff --git a/src/components/CoreSyncStatus.css b/src/styles/CoreSyncStatus.css similarity index 87% rename from src/components/CoreSyncStatus.css rename to src/styles/CoreSyncStatus.css index 6624845..0f60c2b 100644 --- a/src/components/CoreSyncStatus.css +++ b/src/styles/CoreSyncStatus.css @@ -9,25 +9,32 @@ } .tooltip .bottom { - min-width: 225px; - max-width: 250px; - top: 35px; - right: 0px; - /* transform: translate(-50%, 0); */ - padding: 10px 10px; - color: var(--black); - background-color: var(--bg-2); - font-weight: normal; - font-size: 13px; border-radius: 8px; - position: absolute; - z-index: 99999999; - box-sizing: border-box; - box-shadow: 0 1px 8px rgba(0, 0, 0, 0.5); border: 1px solid var(--black); - visibility: hidden; + box-shadow: 0 1px 8px rgba(0, 0, 0, 0.5); + box-sizing: border-box; + font-size: 13px; + font-weight: normal; + max-width: 250px; + min-width: 225px; opacity: 0; + padding: 10px 10px; + position: absolute; + right: 0px; + top: 35px; transition: opacity 0.2s; + visibility: hidden; + z-index: 99999999; +} + +.tooltip[data-theme='light'] .bottom { + background-color: #f1f1f1; + color: #000000; +} + +.tooltip[data-theme='dark'] .bottom { + background-color: var(--bg-2); + color: var(--black); } .tooltip:hover .bottom { @@ -47,13 +54,13 @@ } .tooltip .bottom i::after { - content: ""; - position: absolute; - width: 12px; - height: 12px; - left: 50%; - transform: translate(-50%, 50%) rotate(45deg); background-color: var(--white); border: 1px solid var(--black); box-shadow: 0 1px 8px rgba(0, 0, 0, 0.5); + content: ''; + height: 12px; + left: 50%; + position: absolute; + transform: translate(-50%, 50%) rotate(45deg); + width: 12px; } From 28ab30dcdf903c00c2f06161ef446418b9ef73d8 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 12 Apr 2025 17:43:24 +0200 Subject: [PATCH 19/31] Remove color --- src/App.tsx | 59 +++---------------- .../PasswordField/PasswordField.tsx | 18 +----- 2 files changed, 9 insertions(+), 68 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index aa4c016..92aa923 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1654,7 +1654,6 @@ function App() { cursor: 'pointer', width: '20px', height: 'auto', - color: 'rgba(255, 255, 255, 0.5)', }} onClick={() => { logoutFunc(); @@ -1700,14 +1699,7 @@ function App() { }, }} > - + @@ -1745,14 +1737,7 @@ function App() { }, }} > - + @@ -1790,14 +1775,7 @@ function App() { }, }} > - + @@ -1942,7 +1920,7 @@ function App() { }, }} > - + @@ -1986,7 +1964,7 @@ function App() { }, }} > - + )} @@ -2022,7 +2000,6 @@ function App() { cursor: 'pointer', width: '20px', height: 'auto', - color: 'rgba(255, 255, 255, 0.5)', }} onClick={() => { setExtstate('download-wallet'); @@ -2157,10 +2134,6 @@ function App() { style={{ cursor: 'pointer', height: '24px', - color: - theme.palette.mode === 'dark' - ? 'rgba(255, 255, 255, 0.5)' - : 'rgba(0, 0, 0, 0.3)', }} onClick={returnToMain} /> @@ -2668,10 +2641,6 @@ function App() { style={{ cursor: 'pointer', height: '24px', - color: - theme.palette.mode === 'dark' - ? 'rgba(255, 255, 255, 0.5)' - : 'rgba(0, 0, 0, 0.3)', }} onClick={() => { setRawWallet(null); @@ -2704,10 +2673,6 @@ function App() { style={{ cursor: 'pointer', height: '24px', - color: - theme.palette.mode === 'dark' - ? 'rgba(255, 255, 255, 0.5)' - : 'rgba(0, 0, 0, 0.3)', }} onClick={() => { setRawWallet(null); @@ -2812,17 +2777,12 @@ function App() { maxWidth: '700px', }} > - @@ -2907,14 +2867,10 @@ function App() { maxWidth: '700px', }} > - { if (creationStep === 2) { @@ -2927,7 +2883,6 @@ function App() { setWalletToBeDownloadedPasswordConfirm(''); setWalletToBeDownloadedPassword(''); }} - src={Return} /> diff --git a/src/components/PasswordField/PasswordField.tsx b/src/components/PasswordField/PasswordField.tsx index 8a96b17..31f0a49 100644 --- a/src/components/PasswordField/PasswordField.tsx +++ b/src/components/PasswordField/PasswordField.tsx @@ -71,28 +71,14 @@ export const PasswordField = forwardRef( data-testid="plain-text-indicator" sx={{ minWidth: 0, p: 0 }} > - + ) : ( - + )} From a099788fe75b364545ae5f399cb12ce607cf06aa Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 12 Apr 2025 17:43:30 +0200 Subject: [PATCH 20/31] Set opacity --- src/assets/svgs/Download.tsx | 2 +- src/assets/svgs/Logout.tsx | 2 +- src/styles/theme.ts | 2 ++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/assets/svgs/Download.tsx b/src/assets/svgs/Download.tsx index 577f1b4..7e19e1c 100644 --- a/src/assets/svgs/Download.tsx +++ b/src/assets/svgs/Download.tsx @@ -9,7 +9,7 @@ export const Download: React.FC = ({ const theme = useTheme(); const setColor = color ? color : theme.palette.text.primary; - const setOpacity = opacity ? opacity : 0.5; + const setOpacity = opacity ? opacity : 1; return ( = ({ color, opacity, ...children }) => { const theme = useTheme(); const setColor = color ? color : theme.palette.text.primary; - const setOpacity = opacity ? opacity : 0.3; + const setOpacity = opacity ? opacity : 1; return ( Date: Sat, 12 Apr 2025 17:51:03 +0200 Subject: [PATCH 21/31] Format file --- src/components/QMailStatus.tsx | 87 ++++++++++++++++++++-------------- 1 file changed, 52 insertions(+), 35 deletions(-) diff --git a/src/components/QMailStatus.tsx b/src/components/QMailStatus.tsx index cb1642a..0485240 100644 --- a/src/components/QMailStatus.tsx +++ b/src/components/QMailStatus.tsx @@ -1,31 +1,43 @@ -import React, { useMemo } from 'react' -import QMailLogo from '../assets/QMailLogo.png' -import { useRecoilState } from 'recoil' -import { mailsAtom, qMailLastEnteredTimestampAtom } from '../atoms/global' -import { isLessThanOneWeekOld } from './Group/QMailMessages' -import { ButtonBase, Tooltip } from '@mui/material' -import { executeEvent } from '../utils/events' -export const QMailStatus = () => { - const [lastEnteredTimestamp, setLastEnteredTimestamp] = useRecoilState(qMailLastEnteredTimestampAtom) - const [mails, setMails] = useRecoilState(mailsAtom) +import { useMemo } from 'react'; +import QMailLogo from '../assets/QMailLogo.png'; +import { useRecoilState } from 'recoil'; +import { mailsAtom, qMailLastEnteredTimestampAtom } from '../atoms/global'; +import { isLessThanOneWeekOld } from './Group/QMailMessages'; +import { ButtonBase, Tooltip } from '@mui/material'; +import { executeEvent } from '../utils/events'; - const hasNewMail = useMemo(()=> { - if(mails?.length === 0) return false - const latestMail = mails[0] - if(!lastEnteredTimestamp && isLessThanOneWeekOld(latestMail?.created)) return true - if((lastEnteredTimestamp < latestMail?.created) && isLessThanOneWeekOld(latestMail?.created)) return true - return false - }, [lastEnteredTimestamp, mails]) +export const QMailStatus = () => { + const [lastEnteredTimestamp, setLastEnteredTimestamp] = useRecoilState( + qMailLastEnteredTimestampAtom + ); + const [mails, setMails] = useRecoilState(mailsAtom); + + const hasNewMail = useMemo(() => { + if (mails?.length === 0) return false; + const latestMail = mails[0]; + if (!lastEnteredTimestamp && isLessThanOneWeekOld(latestMail?.created)) + return true; + if ( + lastEnteredTimestamp < latestMail?.created && + isLessThanOneWeekOld(latestMail?.created) + ) + return true; + return false; + }, [lastEnteredTimestamp, mails]); return ( - { - executeEvent("addTab", { data: { service: 'APP', name: 'q-mail' } }); - executeEvent("open-apps-mode", { }); - setLastEnteredTimestamp(Date.now()) - }} style={{ - position: 'relative' - }}> + { + executeEvent('addTab', { data: { service: 'APP', name: 'q-mail' } }); + executeEvent('open-apps-mode', {}); + setLastEnteredTimestamp(Date.now()); + }} + style={{ + position: 'relative', + }} + > {hasNewMail && ( -
{ height: '15px', width: '15px', borderRadius: '50%', - outline: '1px solid white' - }} /> + outline: '1px solid white', + }} + /> )} Q-MAIL} + title={ + + Q-MAIL + + } placement="left" arrow - sx={{ fontSize: "24" }} + sx={{ fontSize: '24' }} slotProps={{ tooltip: { sx: { - color: "#ffffff", - backgroundColor: "#444444", + color: '#ffffff', + backgroundColor: '#444444', }, }, arrow: { sx: { - color: "#444444", + color: '#444444', }, }, }} > - - ) -} + + ); +}; From 72e47ab38e7aeb4dca500e8a91f6e6d6b80f527d Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 12 Apr 2025 17:51:10 +0200 Subject: [PATCH 22/31] Extend interface --- src/assets/svgs/SaveIcon.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/assets/svgs/SaveIcon.tsx b/src/assets/svgs/SaveIcon.tsx index a7a3990..ff39072 100644 --- a/src/assets/svgs/SaveIcon.tsx +++ b/src/assets/svgs/SaveIcon.tsx @@ -1,13 +1,14 @@ import { useTheme } from '@mui/material'; +import { SVGProps } from './interfaces'; -// TODO: extend interface -export const SaveIcon = ({ color }) => { +export const SaveIcon: React.FC = ({ color, ...children }) => { const theme = useTheme(); const setColor = color ? color : theme.palette.text.primary; return ( Date: Sat, 12 Apr 2025 17:56:04 +0200 Subject: [PATCH 23/31] Remove unused parameters --- src/components/Apps/AppsDevMode.tsx | 334 +++++++++++++++------------- src/components/CoreSyncStatus.tsx | 2 +- 2 files changed, 185 insertions(+), 151 deletions(-) diff --git a/src/components/Apps/AppsDevMode.tsx b/src/components/Apps/AppsDevMode.tsx index d4edc0c..a01f6d7 100644 --- a/src/components/Apps/AppsDevMode.tsx +++ b/src/components/Apps/AppsDevMode.tsx @@ -1,46 +1,68 @@ -import React, { useCallback, useContext, useEffect, useMemo, useRef, useState } from "react"; -import { AppsDevModeHome } from "./AppsDevModeHome"; -import { Spacer } from "../../common/Spacer"; -import { MyContext, getBaseApiReact } from "../../App"; -import { AppInfo } from "./AppInfo"; +import React, { + useCallback, + useContext, + useEffect, + useMemo, + useRef, + useState, +} from 'react'; +import { AppsDevModeHome } from './AppsDevModeHome'; +import { Spacer } from '../../common/Spacer'; +import { MyContext, getBaseApiReact } from '../../App'; +import { AppInfo } from './AppInfo'; import { executeEvent, subscribeToEvent, unsubscribeFromEvent, -} from "../../utils/events"; -import { AppsParent } from "./Apps-styles"; -import AppViewerContainer from "./AppViewerContainer"; -import ShortUniqueId from "short-unique-id"; -import { AppPublish } from "./AppPublish"; -import { AppsLibraryDesktop } from "./AppsLibraryDesktop"; -import { AppsCategoryDesktop } from "./AppsCategoryDesktop"; -import { AppsNavBarDesktop } from "./AppsNavBarDesktop"; -import { Box, ButtonBase } from "@mui/material"; -import { HomeIcon } from "../../assets/Icons/HomeIcon"; -import { MessagingIcon } from "../../assets/Icons/MessagingIcon"; -import { Save } from "../Save/Save"; -import { HubsIcon } from "../../assets/Icons/HubsIcon"; -import { AppsDevModeNavBar } from "./AppsDevModeNavBar"; -import { CoreSyncStatus } from "../CoreSyncStatus"; -import { AppsIcon } from "../../assets/Icons/AppsIcon"; -import { IconWrapper } from "../Desktop/DesktopFooter"; +} from '../../utils/events'; +import { AppsParent } from './Apps-styles'; +import AppViewerContainer from './AppViewerContainer'; +import ShortUniqueId from 'short-unique-id'; +import { AppPublish } from './AppPublish'; +import { AppsLibraryDesktop } from './AppsLibraryDesktop'; +import { AppsCategoryDesktop } from './AppsCategoryDesktop'; +import { AppsNavBarDesktop } from './AppsNavBarDesktop'; +import { Box, ButtonBase } from '@mui/material'; +import { HomeIcon } from '../../assets/Icons/HomeIcon'; +import { MessagingIcon } from '../../assets/Icons/MessagingIcon'; +import { Save } from '../Save/Save'; +import { HubsIcon } from '../../assets/Icons/HubsIcon'; +import { AppsDevModeNavBar } from './AppsDevModeNavBar'; +import { AppsIcon } from '../../assets/Icons/AppsIcon'; +import { IconWrapper } from '../Desktop/DesktopFooter'; const uid = new ShortUniqueId({ length: 8 }); -export const AppsDevMode = ({ mode, setMode, show , myName, goToHome, setDesktopSideView, hasUnreadDirects, isDirects, isGroups, hasUnreadGroups, toggleSideViewGroups, toggleSideViewDirects, setDesktopViewMode, desktopViewMode, isApps}) => { +export const AppsDevMode = ({ + mode, + setMode, + show, + myName, + goToHome, + setDesktopSideView, + hasUnreadDirects, + isDirects, + isGroups, + hasUnreadGroups, + toggleSideViewGroups, + toggleSideViewDirects, + setDesktopViewMode, + desktopViewMode, + isApps, +}) => { const [availableQapps, setAvailableQapps] = useState([]); const [selectedAppInfo, setSelectedAppInfo] = useState(null); - const [selectedCategory, setSelectedCategory] = useState(null) + const [selectedCategory, setSelectedCategory] = useState(null); const [tabs, setTabs] = useState([]); const [selectedTab, setSelectedTab] = useState(null); const [isNewTabWindow, setIsNewTabWindow] = useState(false); - const [categories, setCategories] = useState([]) + const [categories, setCategories] = useState([]); const iframeRefs = useRef({}); - + useEffect(() => { setTimeout(() => { - executeEvent("appsDevModeSetTabsToNav", { + executeEvent('appsDevModeSetTabsToNav', { data: { tabs: tabs, selectedTab: selectedTab, @@ -50,17 +72,16 @@ export const AppsDevMode = ({ mode, setMode, show , myName, goToHome, setDesktop }, 100); }, [show, tabs, selectedTab, isNewTabWindow]); - - - - - - - - - const navigateBackFunc = (e) => { - if (['category', 'appInfo-from-category', 'appInfo', 'library', 'publish'].includes(mode)) { + if ( + [ + 'category', + 'appInfo-from-category', + 'appInfo', + 'library', + 'publish', + ].includes(mode) + ) { // Handle the various modes as needed if (mode === 'category') { setMode('library'); @@ -78,17 +99,16 @@ export const AppsDevMode = ({ mode, setMode, show , myName, goToHome, setDesktop } else if (mode === 'publish') { setMode('library'); } - } else if(selectedTab?.tabId) { - executeEvent(`navigateBackApp-${selectedTab?.tabId}`, {}) + } else if (selectedTab?.tabId) { + executeEvent(`navigateBackApp-${selectedTab?.tabId}`, {}); } }; - useEffect(() => { - subscribeToEvent("devModeNavigateBack", navigateBackFunc); + subscribeToEvent('devModeNavigateBack', navigateBackFunc); return () => { - unsubscribeFromEvent("devModeNavigateBack", navigateBackFunc); + unsubscribeFromEvent('devModeNavigateBack', navigateBackFunc); }; }, [mode, selectedTab]); @@ -100,58 +120,52 @@ export const AppsDevMode = ({ mode, setMode, show , myName, goToHome, setDesktop }; setTabs((prev) => [...prev, newTab]); setSelectedTab(newTab); - setMode("viewer"); + setMode('viewer'); setIsNewTabWindow(false); }; - - useEffect(() => { - subscribeToEvent("appsDevModeAddTab", addTabFunc); + subscribeToEvent('appsDevModeAddTab', addTabFunc); return () => { - unsubscribeFromEvent("appsDevModeAddTab", addTabFunc); + unsubscribeFromEvent('appsDevModeAddTab', addTabFunc); }; }, [tabs]); const updateTabFunc = (e) => { const data = e.detail?.data; - if(!data.tabId) return - const findIndexTab = tabs.findIndex((tab)=> tab?.tabId === data?.tabId) - if(findIndexTab === -1) return - const copyTabs = [...tabs] - const newTab ={ + if (!data.tabId) return; + const findIndexTab = tabs.findIndex((tab) => tab?.tabId === data?.tabId); + if (findIndexTab === -1) return; + const copyTabs = [...tabs]; + const newTab = { ...copyTabs[findIndexTab], - url: data.url + url: data.url, + }; + copyTabs[findIndexTab] = newTab; - } - copyTabs[findIndexTab] = newTab - setTabs(copyTabs); setSelectedTab(newTab); - setMode("viewer"); + setMode('viewer'); setIsNewTabWindow(false); }; - - useEffect(() => { - subscribeToEvent("appsDevModeUpdateTab", updateTabFunc); + subscribeToEvent('appsDevModeUpdateTab', updateTabFunc); return () => { - unsubscribeFromEvent("appsDevModeUpdateTab", updateTabFunc); + unsubscribeFromEvent('appsDevModeUpdateTab', updateTabFunc); }; }, [tabs]); - const setSelectedTabFunc = (e) => { const data = e.detail?.data; - if(!e.detail?.isDevMode) return + if (!e.detail?.isDevMode) return; setSelectedTab(data); setTimeout(() => { - executeEvent("appsDevModeSetTabsToNav", { + executeEvent('appsDevModeSetTabsToNav', { data: { tabs: tabs, selectedTab: data, @@ -161,13 +175,12 @@ export const AppsDevMode = ({ mode, setMode, show , myName, goToHome, setDesktop }, 100); setIsNewTabWindow(false); }; - useEffect(() => { - subscribeToEvent("setSelectedTabDevMode", setSelectedTabFunc); + subscribeToEvent('setSelectedTabDevMode', setSelectedTabFunc); return () => { - unsubscribeFromEvent("setSelectedTabDevMode", setSelectedTabFunc); + unsubscribeFromEvent('setSelectedTabDevMode', setSelectedTabFunc); }; }, [tabs, isNewTabWindow]); @@ -175,14 +188,14 @@ export const AppsDevMode = ({ mode, setMode, show , myName, goToHome, setDesktop const data = e.detail?.data; const copyTabs = [...tabs].filter((tab) => tab?.tabId !== data?.tabId); if (copyTabs?.length === 0) { - setMode("home"); + setMode('home'); } else { setSelectedTab(copyTabs[0]); } setTabs(copyTabs); setSelectedTab(copyTabs[0]); setTimeout(() => { - executeEvent("appsDevModeSetTabsToNav", { + executeEvent('appsDevModeSetTabsToNav', { data: { tabs: copyTabs, selectedTab: copyTabs[0], @@ -192,143 +205,157 @@ export const AppsDevMode = ({ mode, setMode, show , myName, goToHome, setDesktop }; useEffect(() => { - subscribeToEvent("removeTabDevMode", removeTabFunc); + subscribeToEvent('removeTabDevMode', removeTabFunc); return () => { - unsubscribeFromEvent("removeTabDevMode", removeTabFunc); + unsubscribeFromEvent('removeTabDevMode', removeTabFunc); }; }, [tabs]); const setNewTabWindowFunc = (e) => { setIsNewTabWindow(true); - setSelectedTab(null) + setSelectedTab(null); }; useEffect(() => { - subscribeToEvent("devModeNewTabWindow", setNewTabWindowFunc); + subscribeToEvent('devModeNewTabWindow', setNewTabWindowFunc); return () => { - unsubscribeFromEvent("devModeNewTabWindow", setNewTabWindowFunc); + unsubscribeFromEvent('devModeNewTabWindow', setNewTabWindowFunc); }; }, [tabs]); - return ( - - + { goToHome(); - }} > - - - + { - setDesktopViewMode('apps') + setDesktopViewMode('apps'); }} > - + { - setDesktopViewMode('chat') + setDesktopViewMode('chat'); }} > - - + { - setDesktopViewMode('dev') - }} - > - - - - - {mode !== 'home' && ( - + onClick={() => { + setDesktopViewMode('dev'); + }} + > + + + + + {mode !== 'home' && } + - )} - - - - - {mode === "home" && ( - - - - + {mode === 'home' && ( + + + )} - - - - + {tabs.map((tab) => { if (!iframeRefs.current[tab.tabId]) { iframeRefs.current[tab.tabId] = React.createRef(); } return ( - - - - + + + )} diff --git a/src/components/CoreSyncStatus.tsx b/src/components/CoreSyncStatus.tsx index 44fed2c..5223fee 100644 --- a/src/components/CoreSyncStatus.tsx +++ b/src/components/CoreSyncStatus.tsx @@ -6,7 +6,7 @@ import { getBaseApiReact } from '../App'; import '../styles/CoreSyncStatus.css'; import { useTheme } from '@mui/material'; -export const CoreSyncStatus = ({ imageSize, position }) => { +export const CoreSyncStatus = () => { const [nodeInfos, setNodeInfos] = useState({}); const [coreInfos, setCoreInfos] = useState({}); const [isUsingGateway, setIsUsingGateway] = useState(false); From 7fee249c65d44b2bf27e8e91db8d594bb6829d8e Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 12 Apr 2025 17:56:15 +0200 Subject: [PATCH 24/31] Remove unused parameters --- src/components/CoreSyncStatus.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/CoreSyncStatus.tsx b/src/components/CoreSyncStatus.tsx index 5223fee..e0e4692 100644 --- a/src/components/CoreSyncStatus.tsx +++ b/src/components/CoreSyncStatus.tsx @@ -101,15 +101,15 @@ export const CoreSyncStatus = () => { sync status

Core Information

From 7745b3da195fc327f84157b00cdda3f8bfde9201 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 12 Apr 2025 18:05:59 +0200 Subject: [PATCH 25/31] Set background to qortal search bar --- src/components/Apps/AppsHomeDesktop.tsx | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/components/Apps/AppsHomeDesktop.tsx b/src/components/Apps/AppsHomeDesktop.tsx index b8167ca..185950e 100644 --- a/src/components/Apps/AppsHomeDesktop.tsx +++ b/src/components/Apps/AppsHomeDesktop.tsx @@ -6,7 +6,7 @@ import { AppLibrarySubTitle, AppsContainer, } from './Apps-styles'; -import { Box, ButtonBase, Input } from '@mui/material'; +import { Box, ButtonBase, Input, useTheme } from '@mui/material'; import { Add } from '@mui/icons-material'; import { isMobile } from '../../App'; import { executeEvent } from '../../utils/events'; @@ -25,6 +25,7 @@ export const AppsHomeDesktop = ({ myName, }) => { const [qortalUrl, setQortalUrl] = useState(''); + const theme = useTheme(); const openQortalUrl = () => { try { @@ -36,7 +37,9 @@ export const AppsHomeDesktop = ({ executeEvent('open-apps-mode', {}); setQortalUrl('qortal://'); } - } catch (error) {} + } catch (error) { + console.log(error); + } }; return ( <> @@ -66,7 +69,10 @@ export const AppsHomeDesktop = ({ display: 'flex', gap: '20px', alignItems: 'center', - backgroundColor: '#1f2023', + backgroundColor: + theme.palette.mode === 'dark' + ? 'rgba(41, 41, 43, 1)' + : 'rgb(209, 209, 209)', padding: '7px', borderRadius: '20px', width: '100%', @@ -85,7 +91,7 @@ export const AppsHomeDesktop = ({ placeholder="qortal://" sx={{ width: '100%', - color: 'white', + color: theme.palette.mode === 'dark' ? 'white' : 'black', '& .MuiInput-input::placeholder': { color: 'rgba(84, 84, 84, 0.70) !important', fontSize: '20px', From ec2b1116ab52702b9af89797c5a76bd520607019 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 12 Apr 2025 18:11:56 +0200 Subject: [PATCH 26/31] Refactor walletIcon with theme --- src/App.tsx | 2 +- src/assets/Icons/WalletIcon.tsx | 18 ++++++++++++++---- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 92aa923..4cb416a 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1817,7 +1817,7 @@ function App() { setIsOpenDrawerProfile(true); }} > - + diff --git a/src/assets/Icons/WalletIcon.tsx b/src/assets/Icons/WalletIcon.tsx index 92223bf..9bb606b 100644 --- a/src/assets/Icons/WalletIcon.tsx +++ b/src/assets/Icons/WalletIcon.tsx @@ -1,8 +1,18 @@ -import React from "react"; +import { useTheme } from '@mui/material'; +import { SVGProps } from '../svgs/interfaces'; + +export const WalletIcon: React.FC = ({ + color, + width, + ...children +}) => { + const theme = useTheme(); + + const setColor = color ? color : theme.palette.text.primary; -export const WalletIcon = ({ color, height, width }) => { return ( { > From 9b675f470d5543e6d0868d1baf7f411da6576d4b Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 12 Apr 2025 18:17:57 +0200 Subject: [PATCH 27/31] Set background to notification icon --- src/components/Desktop/DesktopHeader.tsx | 172 ++++++++++--------- src/components/GeneralNotifications.tsx | 209 +++++++++++++---------- 2 files changed, 206 insertions(+), 175 deletions(-) diff --git a/src/components/Desktop/DesktopHeader.tsx b/src/components/Desktop/DesktopHeader.tsx index 4820054..9dbe111 100644 --- a/src/components/Desktop/DesktopHeader.tsx +++ b/src/components/Desktop/DesktopHeader.tsx @@ -1,47 +1,55 @@ -import * as React from "react"; +import * as React from 'react'; import { BottomNavigation, BottomNavigationAction, ButtonBase, Typography, -} from "@mui/material"; -import { Home, Groups, Message, ShowChart } from "@mui/icons-material"; -import Box from "@mui/material/Box"; -import BottomLogo from "../../assets/svgs/BottomLogo5.svg"; -import { CustomSvg } from "../../common/CustomSvg"; -import { WalletIcon } from "../../assets/Icons/WalletIcon"; -import { HubsIcon } from "../../assets/Icons/HubsIcon"; -import { TradingIcon } from "../../assets/Icons/TradingIcon"; -import { MessagingIcon } from "../../assets/Icons/MessagingIcon"; -import { HomeIcon } from "../../assets/Icons/HomeIcon"; -import { NotificationIcon2 } from "../../assets/Icons/NotificationIcon2"; -import { ChatIcon } from "../../assets/Icons/ChatIcon"; -import { ThreadsIcon } from "../../assets/Icons/ThreadsIcon"; -import { MembersIcon } from "../../assets/Icons/MembersIcon"; -import { AdminsIcon } from "../../assets/Icons/AdminsIcon"; +} from '@mui/material'; +import { Home, Groups, Message, ShowChart } from '@mui/icons-material'; +import Box from '@mui/material/Box'; +import BottomLogo from '../../assets/svgs/BottomLogo5.svg'; +import { CustomSvg } from '../../common/CustomSvg'; +import { HubsIcon } from '../../assets/Icons/HubsIcon'; +import { TradingIcon } from '../../assets/Icons/TradingIcon'; +import { MessagingIcon } from '../../assets/Icons/MessagingIcon'; +import { HomeIcon } from '../../assets/Icons/HomeIcon'; +import { NotificationIcon2 } from '../../assets/Icons/NotificationIcon2'; +import { ChatIcon } from '../../assets/Icons/ChatIcon'; +import { ThreadsIcon } from '../../assets/Icons/ThreadsIcon'; +import { MembersIcon } from '../../assets/Icons/MembersIcon'; +import { AdminsIcon } from '../../assets/Icons/AdminsIcon'; import LockIcon from '@mui/icons-material/Lock'; import NoEncryptionGmailerrorredIcon from '@mui/icons-material/NoEncryptionGmailerrorred'; -const IconWrapper = ({ children, label, color, selected, selectColor, customHeight }) => { +const IconWrapper = ({ + children, + label, + color, + selected, + selectColor, + customHeight, +}) => { return ( {children} { const [value, setValue] = React.useState(0); return ( - + {isPrivate && ( - + )} {isPrivate === false && ( - + )} - {selectedGroup?.groupId === '0' ? 'General' :selectedGroup?.groupName} + {selectedGroup?.groupId === '0' + ? 'General' + : selectedGroup?.groupName} - { - goToAnnouncements() + goToAnnouncements(); }} > @@ -158,11 +173,11 @@ export const DesktopHeader = ({ { - goToChat() + goToChat(); }} > @@ -184,12 +199,11 @@ export const DesktopHeader = ({ { - setGroupSection("forum"); - + setGroupSection('forum'); }} > { - setOpenManageMembers(true) - + setOpenManageMembers(true); }} > { - setGroupSection("adminSpace"); - + setGroupSection('adminSpace'); }} > diff --git a/src/components/GeneralNotifications.tsx b/src/components/GeneralNotifications.tsx index c7336dc..3235fbb 100644 --- a/src/components/GeneralNotifications.tsx +++ b/src/components/GeneralNotifications.tsx @@ -1,5 +1,4 @@ -import React, { useCallback, useEffect, useMemo, useRef, useState } from "react"; - +import { useState } from 'react'; import { Box, ButtonBase, @@ -8,58 +7,72 @@ import { Popover, Tooltip, Typography, -} from "@mui/material"; -import NotificationsIcon from "@mui/icons-material/Notifications"; -import AccountBalanceWalletIcon from "@mui/icons-material/AccountBalanceWallet"; -import { formatDate } from "../utils/time"; -import { useHandlePaymentNotification } from "../hooks/useHandlePaymentNotification"; -import { executeEvent } from "../utils/events"; + useTheme, +} from '@mui/material'; +import NotificationsIcon from '@mui/icons-material/Notifications'; +import AccountBalanceWalletIcon from '@mui/icons-material/AccountBalanceWallet'; +import { formatDate } from '../utils/time'; +import { useHandlePaymentNotification } from '../hooks/useHandlePaymentNotification'; +import { executeEvent } from '../utils/events'; export const GeneralNotifications = ({ address }) => { const [anchorEl, setAnchorEl] = useState(null); - const {latestTx, + + const { + latestTx, getNameOrAddressOfSenderMiddle, - hasNewPayment, setLastEnteredTimestampPayment, nameAddressOfSender} = useHandlePaymentNotification(address) - + hasNewPayment, + setLastEnteredTimestampPayment, + nameAddressOfSender, + } = useHandlePaymentNotification(address); + const handlePopupClick = (event) => { event.stopPropagation(); // Prevent parent onClick from firing setAnchorEl(event.currentTarget); }; + const theme = useTheme(); + return ( <> { handlePopupClick(e); - - }} style={{}} > - PAYMENT NOTIFICATION} - placement="left" - arrow - sx={{ fontSize: "24" }} - slotProps={{ - tooltip: { - sx: { - color: "#ffffff", - backgroundColor: "#444444", - }, - }, - arrow: { - sx: { - color: "#444444", - }, - }, - }} - > - + PAYMENT NOTIFICATION + + } + placement="left" + arrow + sx={{ fontSize: '24' }} + slotProps={{ + tooltip: { + sx: { + color: '#ffffff', + backgroundColor: '#444444', + }, + }, + arrow: { + sx: { + color: '#444444', + }, + }, }} - /> + > + @@ -67,81 +80,91 @@ export const GeneralNotifications = ({ address }) => { open={!!anchorEl} anchorEl={anchorEl} onClose={() => { - if(hasNewPayment){ - setLastEnteredTimestampPayment(Date.now()) + if (hasNewPayment) { + setLastEnteredTimestampPayment(Date.now()); } - setAnchorEl(null) - + setAnchorEl(null); }} // Close popover on click outside > - {!hasNewPayment && No new notifications} + {!hasNewPayment && ( + + No new notifications + + )} {hasNewPayment && ( { - setAnchorEl(null) - executeEvent('openWalletsApp', {}) + onClick={() => { + setAnchorEl(null); + executeEvent('openWalletsApp', {}); }} > - - - {" "} - {formatDate(latestTx?.timestamp)} - - - - {latestTx?.amount} - - {nameAddressOfSender.current[latestTx?.creatorAddress] || getNameOrAddressOfSenderMiddle(latestTx?.creatorAddress)} - + > + {' '} + {formatDate(latestTx?.timestamp)} + + + {latestTx?.amount} + + + {nameAddressOfSender.current[latestTx?.creatorAddress] || + getNameOrAddressOfSenderMiddle(latestTx?.creatorAddress)} + )} From 52c733f48a5925ca1d2185287b200bbaa6fd85aa Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 12 Apr 2025 18:19:21 +0200 Subject: [PATCH 28/31] Format file --- src/atoms/global.ts | 222 +++++++++++++++++++++++--------------------- 1 file changed, 116 insertions(+), 106 deletions(-) diff --git a/src/atoms/global.ts b/src/atoms/global.ts index 443d833..970708a 100644 --- a/src/atoms/global.ts +++ b/src/atoms/global.ts @@ -1,185 +1,195 @@ import { atom, selectorFamily } from 'recoil'; - export const sortablePinnedAppsAtom = atom({ - key: 'sortablePinnedAppsFromAtom', - default: [{ - name: 'Q-Tube', - service: 'APP' - }, { - name: 'Q-Mail', - service: 'APP' - }, { - name: 'Q-Share', - service: 'APP' - }, { - name: 'Q-Fund', - service: 'APP' - }, { - name: 'Q-Shop', - service: 'APP' - }, - { - name: 'Q-Trade', - service: 'APP' - }, - { - name: 'Q-Support', - service: 'APP' - }, - { - name: 'Q-Manager', - service: 'APP' - }, - { - name: 'Q-Blog', - service: 'APP' - }, - { - name: 'Q-Mintership', - service: 'APP' - }, - { - name: 'Q-Wallets', - service: 'APP' - }, - { - name: 'Q-Search', - service: 'APP' - }, -], + key: 'sortablePinnedAppsFromAtom', + default: [ + { + name: 'Q-Tube', + service: 'APP', + }, + { + name: 'Q-Mail', + service: 'APP', + }, + { + name: 'Q-Share', + service: 'APP', + }, + { + name: 'Q-Fund', + service: 'APP', + }, + { + name: 'Q-Shop', + service: 'APP', + }, + { + name: 'Q-Trade', + service: 'APP', + }, + { + name: 'Q-Support', + service: 'APP', + }, + { + name: 'Q-Manager', + service: 'APP', + }, + { + name: 'Q-Blog', + service: 'APP', + }, + { + name: 'Q-Mintership', + service: 'APP', + }, + { + name: 'Q-Wallets', + service: 'APP', + }, + { + name: 'Q-Search', + service: 'APP', + }, + ], }); export const canSaveSettingToQdnAtom = atom({ - key: 'canSaveSettingToQdnAtom', - default: false, + key: 'canSaveSettingToQdnAtom', + default: false, }); export const settingsQDNLastUpdatedAtom = atom({ - key: 'settingsQDNLastUpdatedAtom', - default: -100, + key: 'settingsQDNLastUpdatedAtom', + default: -100, }); export const settingsLocalLastUpdatedAtom = atom({ - key: 'settingsLocalLastUpdatedAtom', - default: 0, + key: 'settingsLocalLastUpdatedAtom', + default: 0, }); export const oldPinnedAppsAtom = atom({ - key: 'oldPinnedAppsAtom', - default: [], -}); -export const isUsingImportExportSettingsAtom = atom({ - key: 'isUsingImportExportSettingsAtom', - default: null, + key: 'oldPinnedAppsAtom', + default: [], }); +export const isUsingImportExportSettingsAtom = atom({ + key: 'isUsingImportExportSettingsAtom', + default: null, +}); export const fullScreenAtom = atom({ - key: 'fullScreenAtom', - default: false, + key: 'fullScreenAtom', + default: false, }); export const hasSettingsChangedAtom = atom({ - key: 'hasSettingsChangedAtom', - default: false, + key: 'hasSettingsChangedAtom', + default: false, }); export const navigationControllerAtom = atom({ - key: 'navigationControllerAtom', - default: {}, + key: 'navigationControllerAtom', + default: {}, }); export const enabledDevModeAtom = atom({ - key: 'enabledDevModeAtom', - default: false, + key: 'enabledDevModeAtom', + default: false, }); export const myGroupsWhereIAmAdminAtom = atom({ - key: 'myGroupsWhereIAmAdminAtom', - default: [], + key: 'myGroupsWhereIAmAdminAtom', + default: [], }); export const promotionTimeIntervalAtom = atom({ - key: 'promotionTimeIntervalAtom', - default: 0, + key: 'promotionTimeIntervalAtom', + default: 0, }); export const promotionsAtom = atom({ - key: 'promotionsAtom', - default: [], + key: 'promotionsAtom', + default: [], }); export const resourceDownloadControllerAtom = atom({ - key: 'resourceDownloadControllerAtom', - default: {}, + key: 'resourceDownloadControllerAtom', + default: {}, }); export const resourceKeySelector = selectorFamily({ key: 'resourceKeySelector', - get: (key) => ({ get }) => { - const resources = get(resourceDownloadControllerAtom); - return resources[key] || null; // Return the value for the key or null if not found - }, + get: + (key) => + ({ get }) => { + const resources = get(resourceDownloadControllerAtom); + return resources[key] || null; // Return the value for the key or null if not found + }, }); export const blobControllerAtom = atom({ - key: 'blobControllerAtom', - default: {}, + key: 'blobControllerAtom', + default: {}, }); export const blobKeySelector = selectorFamily({ key: 'blobKeySelector', - get: (key) => ({ get }) => { - const blobs = get(blobControllerAtom); - return blobs[key] || null; // Return the value for the key or null if not found - }, + get: + (key) => + ({ get }) => { + const blobs = get(blobControllerAtom); + return blobs[key] || null; // Return the value for the key or null if not found + }, }); export const selectedGroupIdAtom = atom({ - key: 'selectedGroupIdAtom', - default: null, + key: 'selectedGroupIdAtom', + default: null, }); export const addressInfoControllerAtom = atom({ - key: 'addressInfoControllerAtom', - default: {}, + key: 'addressInfoControllerAtom', + default: {}, }); export const addressInfoKeySelector = selectorFamily({ key: 'addressInfoKeySelector', - get: (key) => ({ get }) => { - const userInfo = get(addressInfoControllerAtom); - return userInfo[key] || null; // Return the value for the key or null if not found - }, + get: + (key) => + ({ get }) => { + const userInfo = get(addressInfoControllerAtom); + return userInfo[key] || null; // Return the value for the key or null if not found + }, }); export const isDisabledEditorEnterAtom = atom({ - key: 'isDisabledEditorEnterAtom', - default: false, + key: 'isDisabledEditorEnterAtom', + default: false, }); export const qMailLastEnteredTimestampAtom = atom({ - key: 'qMailLastEnteredTimestampAtom', - default: null, + key: 'qMailLastEnteredTimestampAtom', + default: null, }); export const lastPaymentSeenTimestampAtom = atom({ - key: 'lastPaymentSeenTimestampAtom', - default: null, + key: 'lastPaymentSeenTimestampAtom', + default: null, }); export const mailsAtom = atom({ - key: 'mailsAtom', - default: [], + key: 'mailsAtom', + default: [], }); export const groupsPropertiesAtom = atom({ - key: 'groupsPropertiesAtom', - default: {}, + key: 'groupsPropertiesAtom', + default: {}, }); export const isOpenBlockedModalAtom = atom({ - key: 'isOpenBlockedModalAtom', - default: false, -}); \ No newline at end of file + key: 'isOpenBlockedModalAtom', + default: false, +}); From 21d7eadd688826b70f67bf9de590066fbb48fdb1 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 12 Apr 2025 19:14:20 +0200 Subject: [PATCH 29/31] Set normal letterSpacing --- src/styles/theme.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/styles/theme.ts b/src/styles/theme.ts index 1bd2974..6ea83cc 100644 --- a/src/styles/theme.ts +++ b/src/styles/theme.ts @@ -32,7 +32,7 @@ const commonThemeOptions = { fontSize: '23px', fontWeight: 400, lineHeight: 1.5, - letterSpacing: '0.5px', + letterSpacing: 'normal', }, body2: { From 429afec64ab7e96c6e90246aeb0c672d25a2280f Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 12 Apr 2025 19:14:33 +0200 Subject: [PATCH 30/31] Format code --- public/pdfjs/web/viewer.css | 10473 +++++++++++++++++++--------------- 1 file changed, 5859 insertions(+), 4614 deletions(-) diff --git a/public/pdfjs/web/viewer.css b/public/pdfjs/web/viewer.css index 4639f99..96df5d0 100644 --- a/public/pdfjs/web/viewer.css +++ b/public/pdfjs/web/viewer.css @@ -13,5181 +13,6426 @@ * limitations under the License. */ -.messageBar{ - --closing-button-icon:url(images/messageBar_closingButton.svg); - --message-bar-close-button-color:var(--text-primary-color); - --message-bar-close-button-color-hover:var(--text-primary-color); - --message-bar-close-button-border-radius:4px; - --message-bar-close-button-border:none; - --message-bar-close-button-hover-bg-color:rgb(21 20 26 / 0.14); - --message-bar-close-button-active-bg-color:rgb(21 20 26 / 0.21); - --message-bar-close-button-focus-bg-color:rgb(21 20 26 / 0.07); +.messageBar { + --closing-button-icon: url(images/messageBar_closingButton.svg); + --message-bar-close-button-color: var(--text-primary-color); + --message-bar-close-button-color-hover: var(--text-primary-color); + --message-bar-close-button-border-radius: 4px; + --message-bar-close-button-border: none; + --message-bar-close-button-hover-bg-color: rgb(21 20 26 / 0.14); + --message-bar-close-button-active-bg-color: rgb(21 20 26 / 0.21); + --message-bar-close-button-focus-bg-color: rgb(21 20 26 / 0.07); } -@media (prefers-color-scheme: dark){ - -:where(html:not(.is-light)) .messageBar{ - --message-bar-close-button-hover-bg-color:rgb(251 251 254 / 0.14); - --message-bar-close-button-active-bg-color:rgb(251 251 254 / 0.21); - --message-bar-close-button-focus-bg-color:rgb(251 251 254 / 0.07); -} - } - -:where(html.is-dark) .messageBar{ - --message-bar-close-button-hover-bg-color:rgb(251 251 254 / 0.14); - --message-bar-close-button-active-bg-color:rgb(251 251 254 / 0.21); - --message-bar-close-button-focus-bg-color:rgb(251 251 254 / 0.07); -} - -@media screen and (forced-colors: active){ - -.messageBar{ - --message-bar-close-button-color:ButtonText; - --message-bar-close-button-border:1px solid ButtonText; - --message-bar-close-button-hover-bg-color:ButtonText; - --message-bar-close-button-active-bg-color:ButtonText; - --message-bar-close-button-focus-bg-color:ButtonText; - --message-bar-close-button-color-hover:HighlightText; -} - } - -.messageBar{ - - display:flex; - position:relative; - padding:8px 8px 8px 16px; - flex-direction:column; - justify-content:center; - align-items:center; - gap:8px; - -webkit-user-select:none; - -moz-user-select:none; - user-select:none; - - border-radius:4px; - - border:1px solid var(--message-bar-border-color); - background:var(--message-bar-bg-color); - color:var(--message-bar-fg-color); -} - -.messageBar > div{ - display:flex; - align-items:flex-start; - gap:8px; - align-self:stretch; - } - -:is(.messageBar > div)::before{ - content:""; - display:inline-block; - width:16px; - height:16px; - -webkit-mask-image:var(--message-bar-icon); - mask-image:var(--message-bar-icon); - -webkit-mask-size:cover; - mask-size:cover; - background-color:var(--message-bar-icon-color); - flex-shrink:0; - } - -.messageBar button{ - cursor:pointer; - } - -:is(.messageBar button):focus-visible{ - outline:var(--focus-ring-outline); - outline-offset:2px; - } - -.messageBar .closeButton{ - width:32px; - height:32px; - background:none; - border-radius:var(--message-bar-close-button-border-radius); - border:var(--message-bar-close-button-border); - - display:flex; - align-items:center; - justify-content:center; - } - -:is(.messageBar .closeButton)::before{ - content:""; - display:inline-block; - width:16px; - height:16px; - -webkit-mask-image:var(--closing-button-icon); - mask-image:var(--closing-button-icon); - -webkit-mask-size:cover; - mask-size:cover; - background-color:var(--message-bar-close-button-color); - } - -:is(.messageBar .closeButton):is(:hover,:active,:focus)::before{ - background-color:var(--message-bar-close-button-color-hover); - } - -:is(.messageBar .closeButton):hover{ - background-color:var(--message-bar-close-button-hover-bg-color); - } - -:is(.messageBar .closeButton):active{ - background-color:var(--message-bar-close-button-active-bg-color); - } - -:is(.messageBar .closeButton):focus{ - background-color:var(--message-bar-close-button-focus-bg-color); - } - -:is(.messageBar .closeButton) > span{ - display:inline-block; - width:0; - height:0; - overflow:hidden; - } - -#editorUndoBar{ - --text-primary-color:#15141a; - - --message-bar-icon:url(images/secondaryToolbarButton-documentProperties.svg); - --message-bar-icon-color:#0060df; - --message-bar-bg-color:#deeafc; - --message-bar-fg-color:var(--text-primary-color); - --message-bar-border-color:rgb(0 0 0 / 0.08); - - --undo-button-bg-color:rgb(21 20 26 / 0.07); - --undo-button-bg-color-hover:rgb(21 20 26 / 0.14); - --undo-button-bg-color-active:rgb(21 20 26 / 0.21); - - --undo-button-fg-color:var(--message-bar-fg-color); - --undo-button-fg-color-hover:var(--undo-button-fg-color); - --undo-button-fg-color-active:var(--undo-button-fg-color); - - --focus-ring-color:#0060df; - --focus-ring-outline:2px solid var(--focus-ring-color); -} - -@media (prefers-color-scheme: dark){ - -:where(html:not(.is-light)) #editorUndoBar{ - --text-primary-color:#fbfbfe; - - --message-bar-icon-color:#73a7f3; - --message-bar-bg-color:#003070; - --message-bar-border-color:rgb(255 255 255 / 0.08); - - --undo-button-bg-color:rgb(255 255 255 / 0.08); - --undo-button-bg-color-hover:rgb(255 255 255 / 0.14); - --undo-button-bg-color-active:rgb(255 255 255 / 0.21); -} - } - -:where(html.is-dark) #editorUndoBar{ - --text-primary-color:#fbfbfe; - - --message-bar-icon-color:#73a7f3; - --message-bar-bg-color:#003070; - --message-bar-border-color:rgb(255 255 255 / 0.08); - - --undo-button-bg-color:rgb(255 255 255 / 0.08); - --undo-button-bg-color-hover:rgb(255 255 255 / 0.14); - --undo-button-bg-color-active:rgb(255 255 255 / 0.21); -} - -@media screen and (forced-colors: active){ - -#editorUndoBar{ - --text-primary-color:CanvasText; - - --message-bar-icon-color:CanvasText; - --message-bar-bg-color:Canvas; - --message-bar-border-color:CanvasText; - - --undo-button-bg-color:ButtonText; - --undo-button-bg-color-hover:SelectedItem; - --undo-button-bg-color-active:SelectedItem; - - --undo-button-fg-color:ButtonFace; - --undo-button-fg-color-hover:SelectedItemText; - --undo-button-fg-color-active:SelectedItemText; - - --focus-ring-color:CanvasText; -} - } - -#editorUndoBar{ - - position:fixed; - top:50px; - left:50%; - transform:translateX(-50%); - z-index:10; - - padding-block:8px; - padding-inline:16px 8px; - - font:menu; - font-size:15px; - - cursor:default; -} - -#editorUndoBar button{ - cursor:pointer; - } - -#editorUndoBar #editorUndoBarUndoButton{ - border-radius:4px; - font-weight:590; - line-height:19.5px; - color:var(--undo-button-fg-color); - border:none; - padding:4px 16px; - margin-inline-start:8px; - height:32px; - - background-color:var(--undo-button-bg-color); - } - -:is(#editorUndoBar #editorUndoBarUndoButton):hover{ - background-color:var(--undo-button-bg-color-hover); - color:var(--undo-button-fg-color-hover); - } - -:is(#editorUndoBar #editorUndoBarUndoButton):active{ - background-color:var(--undo-button-bg-color-active); - color:var(--undo-button-fg-color-active); - } - -#editorUndoBar > div{ - align-items:center; - } - -.dialog{ - --dialog-bg-color:white; - --dialog-border-color:white; - --dialog-shadow:0 2px 14px 0 rgb(58 57 68 / 0.2); - --text-primary-color:#15141a; - --text-secondary-color:#5b5b66; - --hover-filter:brightness(0.9); - --focus-ring-color:#0060df; - --focus-ring-outline:2px solid var(--focus-ring-color); - --link-fg-color:#0060df; - --link-hover-fg-color:#0250bb; - --separator-color:#f0f0f4; - - --textarea-border-color:#8f8f9d; - --textarea-bg-color:white; - --textarea-fg-color:var(--text-secondary-color); - - --radio-bg-color:#f0f0f4; - --radio-checked-bg-color:#fbfbfe; - --radio-border-color:#8f8f9d; - --radio-checked-border-color:#0060df; - - --button-secondary-bg-color:#f0f0f4; - --button-secondary-fg-color:var(--text-primary-color); - --button-secondary-border-color:var(--button-secondary-bg-color); - --button-secondary-hover-bg-color:var(--button-secondary-bg-color); - --button-secondary-hover-fg-color:var(--button-secondary-fg-color); - --button-secondary-hover-border-color:var(--button-secondary-hover-bg-color); - - --button-primary-bg-color:#0060df; - --button-primary-fg-color:#fbfbfe; - --button-primary-border-color:var(--button-primary-bg-color); - --button-primary-hover-bg-color:var(--button-primary-bg-color); - --button-primary-hover-fg-color:var(--button-primary-fg-color); - --button-primary-hover-border-color:var(--button-primary-hover-bg-color); -} - -@media (prefers-color-scheme: dark){ - -:where(html:not(.is-light)) .dialog{ - --dialog-bg-color:#1c1b22; - --dialog-border-color:#1c1b22; - --dialog-shadow:0 2px 14px 0 #15141a; - --text-primary-color:#fbfbfe; - --text-secondary-color:#cfcfd8; - --focus-ring-color:#0df; - --hover-filter:brightness(1.4); - --link-fg-color:#0df; - --link-hover-fg-color:#80ebff; - --separator-color:#52525e; - - --textarea-bg-color:#42414d; - - --radio-bg-color:#2b2a33; - --radio-checked-bg-color:#15141a; - --radio-checked-border-color:#0df; - - --button-secondary-bg-color:#2b2a33; - --button-primary-bg-color:#0df; - --button-primary-fg-color:#15141a; -} - } - -:where(html.is-dark) .dialog{ - --dialog-bg-color:#1c1b22; - --dialog-border-color:#1c1b22; - --dialog-shadow:0 2px 14px 0 #15141a; - --text-primary-color:#fbfbfe; - --text-secondary-color:#cfcfd8; - --focus-ring-color:#0df; - --hover-filter:brightness(1.4); - --link-fg-color:#0df; - --link-hover-fg-color:#80ebff; - --separator-color:#52525e; - - --textarea-bg-color:#42414d; - - --radio-bg-color:#2b2a33; - --radio-checked-bg-color:#15141a; - --radio-checked-border-color:#0df; - - --button-secondary-bg-color:#2b2a33; - --button-primary-bg-color:#0df; - --button-primary-fg-color:#15141a; -} - -@media screen and (forced-colors: active){ - -.dialog{ - --dialog-bg-color:Canvas; - --dialog-border-color:CanvasText; - --dialog-shadow:none; - --text-primary-color:CanvasText; - --text-secondary-color:CanvasText; - --hover-filter:none; - --focus-ring-color:ButtonBorder; - --link-fg-color:LinkText; - --link-hover-fg-color:LinkText; - --separator-color:CanvasText; - - --textarea-border-color:ButtonBorder; - --textarea-bg-color:Field; - --textarea-fg-color:ButtonText; - - --radio-bg-color:ButtonFace; - --radio-checked-bg-color:ButtonFace; - --radio-border-color:ButtonText; - --radio-checked-border-color:ButtonText; - - --button-secondary-bg-color:ButtonFace; - --button-secondary-fg-color:ButtonText; - --button-secondary-border-color:ButtonText; - --button-secondary-hover-bg-color:AccentColor; - --button-secondary-hover-fg-color:AccentColorText; - - --button-primary-bg-color:ButtonText; - --button-primary-fg-color:ButtonFace; - --button-primary-hover-bg-color:AccentColor; - --button-primary-hover-fg-color:AccentColorText; -} - } - -.dialog{ - - font:message-box; - font-size:13px; - font-weight:400; - line-height:150%; - border-radius:4px; - padding:12px 16px; - border:1px solid var(--dialog-border-color); - background:var(--dialog-bg-color); - color:var(--text-primary-color); - box-shadow:var(--dialog-shadow); -} - -:is(.dialog .mainContainer) *:focus-visible{ - outline:var(--focus-ring-outline); - outline-offset:2px; - } - -:is(.dialog .mainContainer) .title{ - display:flex; - width:auto; - flex-direction:column; - justify-content:flex-end; - align-items:flex-start; - gap:12px; - } - -:is(:is(.dialog .mainContainer) .title) > span{ - font-size:13px; - font-style:normal; - font-weight:590; - line-height:150%; - } - -:is(.dialog .mainContainer) .dialogSeparator{ - width:100%; - height:0; - margin-block:4px; - border-top:1px solid var(--separator-color); - border-bottom:none; - } - -:is(.dialog .mainContainer) .dialogButtonsGroup{ - display:flex; - gap:12px; - align-self:flex-end; - } - -:is(.dialog .mainContainer) .radio{ - display:flex; - flex-direction:column; - align-items:flex-start; - gap:4px; - } - -:is(:is(.dialog .mainContainer) .radio) > .radioButton{ - display:flex; - gap:8px; - align-self:stretch; - align-items:center; - } - -:is(:is(:is(.dialog .mainContainer) .radio) > .radioButton) input{ - -webkit-appearance:none; - -moz-appearance:none; - appearance:none; - box-sizing:border-box; - width:16px; - height:16px; - border-radius:50%; - background-color:var(--radio-bg-color); - border:1px solid var(--radio-border-color); - } - -:is(:is(:is(:is(.dialog .mainContainer) .radio) > .radioButton) input):hover{ - filter:var(--hover-filter); - } - -:is(:is(:is(:is(.dialog .mainContainer) .radio) > .radioButton) input):checked{ - background-color:var(--radio-checked-bg-color); - border:4px solid var(--radio-checked-border-color); - } - -:is(:is(.dialog .mainContainer) .radio) > .radioLabel{ - display:flex; - padding-inline-start:24px; - align-items:flex-start; - gap:10px; - align-self:stretch; - } - -:is(:is(:is(.dialog .mainContainer) .radio) > .radioLabel) > span{ - flex:1 0 0; - font-size:11px; - color:var(--text-secondary-color); - } - -:is(.dialog .mainContainer) button:not(:is(.toggle-button,.closeButton)){ - border-radius:4px; - border:1px solid; - font:menu; - font-weight:600; - padding:4px 16px; - width:auto; - height:32px; - } - -:is(:is(.dialog .mainContainer) button:not(:is(.toggle-button,.closeButton))):hover{ - cursor:pointer; - filter:var(--hover-filter); - } - -.secondaryButton:is(:is(.dialog .mainContainer) button:not(:is(.toggle-button,.closeButton))){ - color:var(--button-secondary-fg-color); - background-color:var(--button-secondary-bg-color); - border-color:var(--button-secondary-border-color); - } - -.secondaryButton:is(:is(.dialog .mainContainer) button:not(:is(.toggle-button,.closeButton))):hover{ - color:var(--button-secondary-hover-fg-color); - background-color:var(--button-secondary-hover-bg-color); - border-color:var(--button-secondary-hover-border-color); - } - -.primaryButton:is(:is(.dialog .mainContainer) button:not(:is(.toggle-button,.closeButton))){ - color:var(--button-primary-fg-color); - background-color:var(--button-primary-bg-color); - border-color:var(--button-primary-border-color); - opacity:1; - } - -.primaryButton:is(:is(.dialog .mainContainer) button:not(:is(.toggle-button,.closeButton))):hover{ - color:var(--button-primary-hover-fg-color); - background-color:var(--button-primary-hover-bg-color); - border-color:var(--button-primary-hover-border-color); - } - -:is(.dialog .mainContainer) a{ - color:var(--link-fg-color); - } - -:is(:is(.dialog .mainContainer) a):hover{ - color:var(--link-hover-fg-color); - } - -:is(.dialog .mainContainer) textarea{ - font:inherit; - padding:8px; - resize:none; - margin:0; - box-sizing:border-box; - border-radius:4px; - border:1px solid var(--textarea-border-color); - background:var(--textarea-bg-color); - color:var(--textarea-fg-color); - } - -:is(:is(.dialog .mainContainer) textarea):focus{ - outline-offset:0; - border-color:transparent; - } - -:is(:is(.dialog .mainContainer) textarea):disabled{ - pointer-events:none; - opacity:0.4; - } - -:is(.dialog .mainContainer) .messageBar{ - --message-bar-bg-color:#ffebcd; - --message-bar-fg-color:#15141a; - --message-bar-border-color:rgb(0 0 0 / 0.08); - --message-bar-icon:url(images/messageBar_warning.svg); - --message-bar-icon-color:#cd411e; - } - -@media (prefers-color-scheme: dark){ - -:where(html:not(.is-light)) :is(.dialog .mainContainer) .messageBar{ - --message-bar-bg-color:#5a3100; - --message-bar-fg-color:#fbfbfe; - --message-bar-border-color:rgb(255 255 255 / 0.08); - --message-bar-icon-color:#e49c49; - } - } - -:where(html.is-dark) :is(.dialog .mainContainer) .messageBar{ - --message-bar-bg-color:#5a3100; - --message-bar-fg-color:#fbfbfe; - --message-bar-border-color:rgb(255 255 255 / 0.08); - --message-bar-icon-color:#e49c49; - } - -@media screen and (forced-colors: active){ - -:is(.dialog .mainContainer) .messageBar{ - --message-bar-bg-color:HighlightText; - --message-bar-fg-color:CanvasText; - --message-bar-border-color:CanvasText; - --message-bar-icon-color:CanvasText; - } - } - -:is(.dialog .mainContainer) .messageBar{ - - align-self:stretch; - } - -:is(:is(:is(.dialog .mainContainer) .messageBar) > div)::before,:is(:is(:is(.dialog .mainContainer) .messageBar) > div) > div{ - margin-block:4px; - } - -:is(:is(:is(.dialog .mainContainer) .messageBar) > div) > div{ - display:flex; - flex-direction:column; - align-items:flex-start; - gap:8px; - flex:1 0 0; - } - -:is(:is(:is(:is(.dialog .mainContainer) .messageBar) > div) > div) .title{ - font-size:13px; - font-weight:590; - } - -:is(:is(:is(:is(.dialog .mainContainer) .messageBar) > div) > div) .description{ - font-size:13px; - } - -:is(.dialog .mainContainer) .toggler{ - display:flex; - align-items:center; - gap:8px; - align-self:stretch; - } - -:is(:is(.dialog .mainContainer) .toggler) > .togglerLabel{ - -webkit-user-select:none; - -moz-user-select:none; - user-select:none; - } - -.textLayer{ - position:absolute; - text-align:initial; - inset:0; - overflow:clip; - opacity:1; - line-height:1; - -webkit-text-size-adjust:none; - -moz-text-size-adjust:none; - text-size-adjust:none; - forced-color-adjust:none; - transform-origin:0 0; - caret-color:CanvasText; - z-index:0; -} - -.textLayer.highlighting{ - touch-action:none; - } - -.textLayer :is(span,br){ - color:transparent; - position:absolute; - white-space:pre; - cursor:text; - transform-origin:0% 0%; - } - -.textLayer > :not(.markedContent),.textLayer .markedContent span:not(.markedContent){ - z-index:1; - } - -.textLayer span.markedContent{ - top:0; - height:0; - } - -.textLayer span[role="img"]{ - -webkit-user-select:none; - -moz-user-select:none; - user-select:none; - cursor:default; - } - -.textLayer .highlight{ - --highlight-bg-color:rgb(180 0 170 / 0.25); - --highlight-selected-bg-color:rgb(0 100 0 / 0.25); - --highlight-backdrop-filter:none; - --highlight-selected-backdrop-filter:none; - } - -@media screen and (forced-colors: active){ - -.textLayer .highlight{ - --highlight-bg-color:transparent; - --highlight-selected-bg-color:transparent; - --highlight-backdrop-filter:var(--hcm-highlight-filter); - --highlight-selected-backdrop-filter:var( - --hcm-highlight-selected-filter - ); - } - } - -.textLayer .highlight{ - - margin:-1px; - padding:1px; - background-color:var(--highlight-bg-color); - -webkit-backdrop-filter:var(--highlight-backdrop-filter); - backdrop-filter:var(--highlight-backdrop-filter); - border-radius:4px; - } - -.appended:is(.textLayer .highlight){ - position:initial; - } - -.begin:is(.textLayer .highlight){ - border-radius:4px 0 0 4px; - } - -.end:is(.textLayer .highlight){ - border-radius:0 4px 4px 0; - } - -.middle:is(.textLayer .highlight){ - border-radius:0; - } - -.selected:is(.textLayer .highlight){ - background-color:var(--highlight-selected-bg-color); - -webkit-backdrop-filter:var(--highlight-selected-backdrop-filter); - backdrop-filter:var(--highlight-selected-backdrop-filter); - } - -.textLayer ::-moz-selection{ - background:rgba(0 0 255 / 0.25); - background:color-mix(in srgb, AccentColor, transparent 75%); - } - -.textLayer ::selection{ - background:rgba(0 0 255 / 0.25); - background:color-mix(in srgb, AccentColor, transparent 75%); - } - -.textLayer br::-moz-selection{ - background:transparent; - } - -.textLayer br::selection{ - background:transparent; - } - -.textLayer .endOfContent{ - display:block; - position:absolute; - inset:100% 0 0; - z-index:0; - cursor:default; - -webkit-user-select:none; - -moz-user-select:none; - user-select:none; - } - -.textLayer.selecting .endOfContent{ - top:0; - } - -.annotationLayer{ - --annotation-unfocused-field-background:url("data:image/svg+xml;charset=UTF-8,"); - --input-focus-border-color:Highlight; - --input-focus-outline:1px solid Canvas; - --input-unfocused-border-color:transparent; - --input-disabled-border-color:transparent; - --input-hover-border-color:black; - --link-outline:none; -} - -@media screen and (forced-colors: active){ - -.annotationLayer{ - --input-focus-border-color:CanvasText; - --input-unfocused-border-color:ActiveText; - --input-disabled-border-color:GrayText; - --input-hover-border-color:Highlight; - --link-outline:1.5px solid LinkText; -} - - .annotationLayer .textWidgetAnnotation :is(input,textarea):required,.annotationLayer .choiceWidgetAnnotation select:required,.annotationLayer .buttonWidgetAnnotation:is(.checkBox,.radioButton) input:required{ - outline:1.5px solid selectedItem; - } - - .annotationLayer .linkAnnotation{ - outline:var(--link-outline); - } - - :is(.annotationLayer .linkAnnotation):hover{ - -webkit-backdrop-filter:var(--hcm-highlight-filter); - backdrop-filter:var(--hcm-highlight-filter); - } - - :is(.annotationLayer .linkAnnotation) > a:hover{ - opacity:0 !important; - background:none !important; - box-shadow:none; - } - - .annotationLayer .popupAnnotation .popup{ - outline:calc(1.5px * var(--scale-factor)) solid CanvasText !important; - background-color:ButtonFace !important; - color:ButtonText !important; - } - - .annotationLayer .highlightArea:hover::after{ - position:absolute; - top:0; - left:0; - width:100%; - height:100%; - -webkit-backdrop-filter:var(--hcm-highlight-filter); - backdrop-filter:var(--hcm-highlight-filter); - content:""; - pointer-events:none; - } - - .annotationLayer .popupAnnotation.focused .popup{ - outline:calc(3px * var(--scale-factor)) solid Highlight !important; - } - } - -.annotationLayer{ - - position:absolute; - top:0; - left:0; - pointer-events:none; - transform-origin:0 0; -} - -.annotationLayer[data-main-rotation="90"] .norotate{ - transform:rotate(270deg) translateX(-100%); - } - -.annotationLayer[data-main-rotation="180"] .norotate{ - transform:rotate(180deg) translate(-100%, -100%); - } - -.annotationLayer[data-main-rotation="270"] .norotate{ - transform:rotate(90deg) translateY(-100%); - } - -.annotationLayer.disabled section,.annotationLayer.disabled .popup{ - pointer-events:none; - } - -.annotationLayer .annotationContent{ - position:absolute; - width:100%; - height:100%; - pointer-events:none; - } - -.freetext:is(.annotationLayer .annotationContent){ - background:transparent; - border:none; - inset:0; - overflow:visible; - white-space:nowrap; - font:10px sans-serif; - line-height:1.35; - -webkit-user-select:none; - -moz-user-select:none; - user-select:none; - } - -.annotationLayer section{ - position:absolute; - text-align:initial; - pointer-events:auto; - box-sizing:border-box; - transform-origin:0 0; - } - -:is(.annotationLayer section):has(div.annotationContent) canvas.annotationContent{ - display:none; - } - -.textLayer.selecting ~ .annotationLayer section{ - pointer-events:none; - } - -.annotationLayer :is(.linkAnnotation,.buttonWidgetAnnotation.pushButton) > a{ - position:absolute; - font-size:1em; - top:0; - left:0; - width:100%; - height:100%; - } - -.annotationLayer :is(.linkAnnotation,.buttonWidgetAnnotation.pushButton):not(.hasBorder) > a:hover{ - opacity:0.2; - background-color:rgb(255 255 0); - box-shadow:0 2px 10px rgb(255 255 0); - } - -.annotationLayer .linkAnnotation.hasBorder:hover{ - background-color:rgb(255 255 0 / 0.2); - } - -.annotationLayer .hasBorder{ - background-size:100% 100%; - } - -.annotationLayer .textAnnotation img{ - position:absolute; - cursor:pointer; - width:100%; - height:100%; - top:0; - left:0; - } - -.annotationLayer .textWidgetAnnotation :is(input,textarea),.annotationLayer .choiceWidgetAnnotation select,.annotationLayer .buttonWidgetAnnotation:is(.checkBox,.radioButton) input{ - background-image:var(--annotation-unfocused-field-background); - border:2px solid var(--input-unfocused-border-color); - box-sizing:border-box; - font:calc(9px * var(--scale-factor)) sans-serif; - height:100%; - margin:0; - vertical-align:top; - width:100%; - } - -.annotationLayer .textWidgetAnnotation :is(input,textarea):required,.annotationLayer .choiceWidgetAnnotation select:required,.annotationLayer .buttonWidgetAnnotation:is(.checkBox,.radioButton) input:required{ - outline:1.5px solid red; - } - -.annotationLayer .choiceWidgetAnnotation select option{ - padding:0; - } - -.annotationLayer .buttonWidgetAnnotation.radioButton input{ - border-radius:50%; - } - -.annotationLayer .textWidgetAnnotation textarea{ - resize:none; - } - -.annotationLayer .textWidgetAnnotation [disabled]:is(input,textarea),.annotationLayer .choiceWidgetAnnotation select[disabled],.annotationLayer .buttonWidgetAnnotation:is(.checkBox,.radioButton) input[disabled]{ - background:none; - border:2px solid var(--input-disabled-border-color); - cursor:not-allowed; - } - -.annotationLayer .textWidgetAnnotation :is(input,textarea):hover,.annotationLayer .choiceWidgetAnnotation select:hover,.annotationLayer .buttonWidgetAnnotation:is(.checkBox,.radioButton) input:hover{ - border:2px solid var(--input-hover-border-color); - } - -.annotationLayer .textWidgetAnnotation :is(input,textarea):hover,.annotationLayer .choiceWidgetAnnotation select:hover,.annotationLayer .buttonWidgetAnnotation.checkBox input:hover{ - border-radius:2px; - } - -.annotationLayer .textWidgetAnnotation :is(input,textarea):focus,.annotationLayer .choiceWidgetAnnotation select:focus{ - background:none; - border:2px solid var(--input-focus-border-color); - border-radius:2px; - outline:var(--input-focus-outline); - } - -.annotationLayer .buttonWidgetAnnotation:is(.checkBox,.radioButton) :focus{ - background-image:none; - background-color:transparent; - } - -.annotationLayer .buttonWidgetAnnotation.checkBox :focus{ - border:2px solid var(--input-focus-border-color); - border-radius:2px; - outline:var(--input-focus-outline); - } - -.annotationLayer .buttonWidgetAnnotation.radioButton :focus{ - border:2px solid var(--input-focus-border-color); - outline:var(--input-focus-outline); - } - -.annotationLayer .buttonWidgetAnnotation.checkBox input:checked::before,.annotationLayer .buttonWidgetAnnotation.checkBox input:checked::after,.annotationLayer .buttonWidgetAnnotation.radioButton input:checked::before{ - background-color:CanvasText; - content:""; - display:block; - position:absolute; - } - -.annotationLayer .buttonWidgetAnnotation.checkBox input:checked::before,.annotationLayer .buttonWidgetAnnotation.checkBox input:checked::after{ - height:80%; - left:45%; - width:1px; - } - -.annotationLayer .buttonWidgetAnnotation.checkBox input:checked::before{ - transform:rotate(45deg); - } - -.annotationLayer .buttonWidgetAnnotation.checkBox input:checked::after{ - transform:rotate(-45deg); - } - -.annotationLayer .buttonWidgetAnnotation.radioButton input:checked::before{ - border-radius:50%; - height:50%; - left:25%; - top:25%; - width:50%; - } - -.annotationLayer .textWidgetAnnotation input.comb{ - font-family:monospace; - padding-left:2px; - padding-right:0; - } - -.annotationLayer .textWidgetAnnotation input.comb:focus{ - width:103%; - } - -.annotationLayer .buttonWidgetAnnotation:is(.checkBox,.radioButton) input{ - -webkit-appearance:none; - -moz-appearance:none; - appearance:none; - } - -.annotationLayer .fileAttachmentAnnotation .popupTriggerArea{ - height:100%; - width:100%; - } - -.annotationLayer .popupAnnotation{ - position:absolute; - font-size:calc(9px * var(--scale-factor)); - pointer-events:none; - width:-moz-max-content; - width:max-content; - max-width:45%; - height:auto; - } - -.annotationLayer .popup{ - background-color:rgb(255 255 153); - box-shadow:0 calc(2px * var(--scale-factor)) calc(5px * var(--scale-factor)) rgb(136 136 136); - border-radius:calc(2px * var(--scale-factor)); - outline:1.5px solid rgb(255 255 74); - padding:calc(6px * var(--scale-factor)); - cursor:pointer; - font:message-box; - white-space:normal; - word-wrap:break-word; - pointer-events:auto; - } - -.annotationLayer .popupAnnotation.focused .popup{ - outline-width:3px; - } - -.annotationLayer .popup *{ - font-size:calc(9px * var(--scale-factor)); - } - -.annotationLayer .popup > .header{ - display:inline-block; - } - -.annotationLayer .popup > .header h1{ - display:inline; - } - -.annotationLayer .popup > .header .popupDate{ - display:inline-block; - margin-left:calc(5px * var(--scale-factor)); - width:-moz-fit-content; - width:fit-content; - } - -.annotationLayer .popupContent{ - border-top:1px solid rgb(51 51 51); - margin-top:calc(2px * var(--scale-factor)); - padding-top:calc(2px * var(--scale-factor)); - } - -.annotationLayer .richText > *{ - white-space:pre-wrap; - font-size:calc(9px * var(--scale-factor)); - } - -.annotationLayer .popupTriggerArea{ - cursor:pointer; - } - -.annotationLayer section svg{ - position:absolute; - width:100%; - height:100%; - top:0; - left:0; - } - -.annotationLayer .annotationTextContent{ - position:absolute; - width:100%; - height:100%; - opacity:0; - color:transparent; - -webkit-user-select:none; - -moz-user-select:none; - user-select:none; - pointer-events:none; - } - -:is(.annotationLayer .annotationTextContent) span{ - width:100%; - display:inline-block; - } - -.annotationLayer svg.quadrilateralsContainer{ - contain:strict; - width:0; - height:0; - position:absolute; - top:0; - left:0; - z-index:-1; - } - -:root{ - --xfa-unfocused-field-background:url("data:image/svg+xml;charset=UTF-8,"); - --xfa-focus-outline:auto; -} - -@media screen and (forced-colors: active){ - :root{ - --xfa-focus-outline:2px solid CanvasText; - } - .xfaLayer *:required{ - outline:1.5px solid selectedItem; +@media (prefers-color-scheme: dark) { + :where(html:not(.is-light)) .messageBar { + --message-bar-close-button-hover-bg-color: rgb(251 251 254 / 0.14); + --message-bar-close-button-active-bg-color: rgb(251 251 254 / 0.21); + --message-bar-close-button-focus-bg-color: rgb(251 251 254 / 0.07); } } -.xfaLayer{ - background-color:transparent; +:where(html.is-dark) .messageBar { + --message-bar-close-button-hover-bg-color: rgb(251 251 254 / 0.14); + --message-bar-close-button-active-bg-color: rgb(251 251 254 / 0.21); + --message-bar-close-button-focus-bg-color: rgb(251 251 254 / 0.07); } -.xfaLayer .highlight{ - margin:-1px; - padding:1px; - background-color:rgb(239 203 237); - border-radius:4px; +@media screen and (forced-colors: active) { + .messageBar { + --message-bar-close-button-color: ButtonText; + --message-bar-close-button-border: 1px solid ButtonText; + --message-bar-close-button-hover-bg-color: ButtonText; + --message-bar-close-button-active-bg-color: ButtonText; + --message-bar-close-button-focus-bg-color: ButtonText; + --message-bar-close-button-color-hover: HighlightText; + } } -.xfaLayer .highlight.appended{ - position:initial; +.messageBar { + display: flex; + position: relative; + padding: 8px 8px 8px 16px; + flex-direction: column; + justify-content: center; + align-items: center; + gap: 8px; + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; + + border-radius: 4px; + + border: 1px solid var(--message-bar-border-color); + background: var(--message-bar-bg-color); + color: var(--message-bar-fg-color); } -.xfaLayer .highlight.begin{ - border-radius:4px 0 0 4px; +.messageBar > div { + display: flex; + align-items: flex-start; + gap: 8px; + align-self: stretch; } -.xfaLayer .highlight.end{ - border-radius:0 4px 4px 0; +:is(.messageBar > div)::before { + content: ''; + display: inline-block; + width: 16px; + height: 16px; + -webkit-mask-image: var(--message-bar-icon); + mask-image: var(--message-bar-icon); + -webkit-mask-size: cover; + mask-size: cover; + background-color: var(--message-bar-icon-color); + flex-shrink: 0; } -.xfaLayer .highlight.middle{ - border-radius:0; +.messageBar button { + cursor: pointer; } -.xfaLayer .highlight.selected{ - background-color:rgb(203 223 203); +:is(.messageBar button):focus-visible { + outline: var(--focus-ring-outline); + outline-offset: 2px; } -.xfaPage{ - overflow:hidden; - position:relative; +.messageBar .closeButton { + width: 32px; + height: 32px; + background: none; + border-radius: var(--message-bar-close-button-border-radius); + border: var(--message-bar-close-button-border); + + display: flex; + align-items: center; + justify-content: center; } -.xfaContentarea{ - position:absolute; +:is(.messageBar .closeButton)::before { + content: ''; + display: inline-block; + width: 16px; + height: 16px; + -webkit-mask-image: var(--closing-button-icon); + mask-image: var(--closing-button-icon); + -webkit-mask-size: cover; + mask-size: cover; + background-color: var(--message-bar-close-button-color); } -.xfaPrintOnly{ - display:none; +:is(.messageBar .closeButton):is(:hover, :active, :focus)::before { + background-color: var(--message-bar-close-button-color-hover); } -.xfaLayer{ - position:absolute; - text-align:initial; - top:0; - left:0; - transform-origin:0 0; - line-height:1.2; +:is(.messageBar .closeButton):hover { + background-color: var(--message-bar-close-button-hover-bg-color); } -.xfaLayer *{ - color:inherit; - font:inherit; - font-style:inherit; - font-weight:inherit; - font-kerning:inherit; - letter-spacing:-0.01px; - text-align:inherit; - text-decoration:inherit; - box-sizing:border-box; - background-color:transparent; - padding:0; - margin:0; - pointer-events:auto; - line-height:inherit; +:is(.messageBar .closeButton):active { + background-color: var(--message-bar-close-button-active-bg-color); } -.xfaLayer *:required{ - outline:1.5px solid red; +:is(.messageBar .closeButton):focus { + background-color: var(--message-bar-close-button-focus-bg-color); +} + +:is(.messageBar .closeButton) > span { + display: inline-block; + width: 0; + height: 0; + overflow: hidden; +} + +#editorUndoBar { + --text-primary-color: #15141a; + + --message-bar-icon: url(images/secondaryToolbarButton-documentProperties.svg); + --message-bar-icon-color: #0060df; + --message-bar-bg-color: #deeafc; + --message-bar-fg-color: var(--text-primary-color); + --message-bar-border-color: rgb(0 0 0 / 0.08); + + --undo-button-bg-color: rgb(21 20 26 / 0.07); + --undo-button-bg-color-hover: rgb(21 20 26 / 0.14); + --undo-button-bg-color-active: rgb(21 20 26 / 0.21); + + --undo-button-fg-color: var(--message-bar-fg-color); + --undo-button-fg-color-hover: var(--undo-button-fg-color); + --undo-button-fg-color-active: var(--undo-button-fg-color); + + --focus-ring-color: #0060df; + --focus-ring-outline: 2px solid var(--focus-ring-color); +} + +@media (prefers-color-scheme: dark) { + :where(html:not(.is-light)) #editorUndoBar { + --text-primary-color: #fbfbfe; + + --message-bar-icon-color: #73a7f3; + --message-bar-bg-color: #003070; + --message-bar-border-color: rgb(255 255 255 / 0.08); + + --undo-button-bg-color: rgb(255 255 255 / 0.08); + --undo-button-bg-color-hover: rgb(255 255 255 / 0.14); + --undo-button-bg-color-active: rgb(255 255 255 / 0.21); + } +} + +:where(html.is-dark) #editorUndoBar { + --text-primary-color: #fbfbfe; + + --message-bar-icon-color: #73a7f3; + --message-bar-bg-color: #003070; + --message-bar-border-color: rgb(255 255 255 / 0.08); + + --undo-button-bg-color: rgb(255 255 255 / 0.08); + --undo-button-bg-color-hover: rgb(255 255 255 / 0.14); + --undo-button-bg-color-active: rgb(255 255 255 / 0.21); +} + +@media screen and (forced-colors: active) { + #editorUndoBar { + --text-primary-color: CanvasText; + + --message-bar-icon-color: CanvasText; + --message-bar-bg-color: Canvas; + --message-bar-border-color: CanvasText; + + --undo-button-bg-color: ButtonText; + --undo-button-bg-color-hover: SelectedItem; + --undo-button-bg-color-active: SelectedItem; + + --undo-button-fg-color: ButtonFace; + --undo-button-fg-color-hover: SelectedItemText; + --undo-button-fg-color-active: SelectedItemText; + + --focus-ring-color: CanvasText; + } +} + +#editorUndoBar { + position: fixed; + top: 50px; + left: 50%; + transform: translateX(-50%); + z-index: 10; + + padding-block: 8px; + padding-inline: 16px 8px; + + font: menu; + font-size: 15px; + + cursor: default; +} + +#editorUndoBar button { + cursor: pointer; +} + +#editorUndoBar #editorUndoBarUndoButton { + border-radius: 4px; + font-weight: 590; + line-height: 19.5px; + color: var(--undo-button-fg-color); + border: none; + padding: 4px 16px; + margin-inline-start: 8px; + height: 32px; + + background-color: var(--undo-button-bg-color); +} + +:is(#editorUndoBar #editorUndoBarUndoButton):hover { + background-color: var(--undo-button-bg-color-hover); + color: var(--undo-button-fg-color-hover); +} + +:is(#editorUndoBar #editorUndoBarUndoButton):active { + background-color: var(--undo-button-bg-color-active); + color: var(--undo-button-fg-color-active); +} + +#editorUndoBar > div { + align-items: center; +} + +.dialog { + --dialog-bg-color: white; + --dialog-border-color: white; + --dialog-shadow: 0 2px 14px 0 rgb(58 57 68 / 0.2); + --text-primary-color: #15141a; + --text-secondary-color: #5b5b66; + --hover-filter: brightness(0.9); + --focus-ring-color: #0060df; + --focus-ring-outline: 2px solid var(--focus-ring-color); + --link-fg-color: #0060df; + --link-hover-fg-color: #0250bb; + --separator-color: #f0f0f4; + + --textarea-border-color: #8f8f9d; + --textarea-bg-color: white; + --textarea-fg-color: var(--text-secondary-color); + + --radio-bg-color: #f0f0f4; + --radio-checked-bg-color: #fbfbfe; + --radio-border-color: #8f8f9d; + --radio-checked-border-color: #0060df; + + --button-secondary-bg-color: #f0f0f4; + --button-secondary-fg-color: var(--text-primary-color); + --button-secondary-border-color: var(--button-secondary-bg-color); + --button-secondary-hover-bg-color: var(--button-secondary-bg-color); + --button-secondary-hover-fg-color: var(--button-secondary-fg-color); + --button-secondary-hover-border-color: var(--button-secondary-hover-bg-color); + + --button-primary-bg-color: #0060df; + --button-primary-fg-color: #fbfbfe; + --button-primary-border-color: var(--button-primary-bg-color); + --button-primary-hover-bg-color: var(--button-primary-bg-color); + --button-primary-hover-fg-color: var(--button-primary-fg-color); + --button-primary-hover-border-color: var(--button-primary-hover-bg-color); +} + +@media (prefers-color-scheme: dark) { + :where(html:not(.is-light)) .dialog { + --dialog-bg-color: #1c1b22; + --dialog-border-color: #1c1b22; + --dialog-shadow: 0 2px 14px 0 #15141a; + --text-primary-color: #fbfbfe; + --text-secondary-color: #cfcfd8; + --focus-ring-color: #0df; + --hover-filter: brightness(1.4); + --link-fg-color: #0df; + --link-hover-fg-color: #80ebff; + --separator-color: #52525e; + + --textarea-bg-color: #42414d; + + --radio-bg-color: #2b2a33; + --radio-checked-bg-color: #15141a; + --radio-checked-border-color: #0df; + + --button-secondary-bg-color: #2b2a33; + --button-primary-bg-color: #0df; + --button-primary-fg-color: #15141a; + } +} + +:where(html.is-dark) .dialog { + --dialog-bg-color: #1c1b22; + --dialog-border-color: #1c1b22; + --dialog-shadow: 0 2px 14px 0 #15141a; + --text-primary-color: #fbfbfe; + --text-secondary-color: #cfcfd8; + --focus-ring-color: #0df; + --hover-filter: brightness(1.4); + --link-fg-color: #0df; + --link-hover-fg-color: #80ebff; + --separator-color: #52525e; + + --textarea-bg-color: #42414d; + + --radio-bg-color: #2b2a33; + --radio-checked-bg-color: #15141a; + --radio-checked-border-color: #0df; + + --button-secondary-bg-color: #2b2a33; + --button-primary-bg-color: #0df; + --button-primary-fg-color: #15141a; +} + +@media screen and (forced-colors: active) { + .dialog { + --dialog-bg-color: Canvas; + --dialog-border-color: CanvasText; + --dialog-shadow: none; + --text-primary-color: CanvasText; + --text-secondary-color: CanvasText; + --hover-filter: none; + --focus-ring-color: ButtonBorder; + --link-fg-color: LinkText; + --link-hover-fg-color: LinkText; + --separator-color: CanvasText; + + --textarea-border-color: ButtonBorder; + --textarea-bg-color: Field; + --textarea-fg-color: ButtonText; + + --radio-bg-color: ButtonFace; + --radio-checked-bg-color: ButtonFace; + --radio-border-color: ButtonText; + --radio-checked-border-color: ButtonText; + + --button-secondary-bg-color: ButtonFace; + --button-secondary-fg-color: ButtonText; + --button-secondary-border-color: ButtonText; + --button-secondary-hover-bg-color: AccentColor; + --button-secondary-hover-fg-color: AccentColorText; + + --button-primary-bg-color: ButtonText; + --button-primary-fg-color: ButtonFace; + --button-primary-hover-bg-color: AccentColor; + --button-primary-hover-fg-color: AccentColorText; + } +} + +.dialog { + font: message-box; + font-size: 13px; + font-weight: 400; + line-height: 150%; + border-radius: 4px; + padding: 12px 16px; + border: 1px solid var(--dialog-border-color); + background: var(--dialog-bg-color); + color: var(--text-primary-color); + box-shadow: var(--dialog-shadow); +} + +:is(.dialog .mainContainer) *:focus-visible { + outline: var(--focus-ring-outline); + outline-offset: 2px; +} + +:is(.dialog .mainContainer) .title { + display: flex; + width: auto; + flex-direction: column; + justify-content: flex-end; + align-items: flex-start; + gap: 12px; +} + +:is(:is(.dialog .mainContainer) .title) > span { + font-size: 13px; + font-style: normal; + font-weight: 590; + line-height: 150%; +} + +:is(.dialog .mainContainer) .dialogSeparator { + width: 100%; + height: 0; + margin-block: 4px; + border-top: 1px solid var(--separator-color); + border-bottom: none; +} + +:is(.dialog .mainContainer) .dialogButtonsGroup { + display: flex; + gap: 12px; + align-self: flex-end; +} + +:is(.dialog .mainContainer) .radio { + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 4px; +} + +:is(:is(.dialog .mainContainer) .radio) > .radioButton { + display: flex; + gap: 8px; + align-self: stretch; + align-items: center; +} + +:is(:is(:is(.dialog .mainContainer) .radio) > .radioButton) input { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + box-sizing: border-box; + width: 16px; + height: 16px; + border-radius: 50%; + background-color: var(--radio-bg-color); + border: 1px solid var(--radio-border-color); +} + +:is(:is(:is(:is(.dialog .mainContainer) .radio) > .radioButton) input):hover { + filter: var(--hover-filter); +} + +:is(:is(:is(:is(.dialog .mainContainer) .radio) > .radioButton) input):checked { + background-color: var(--radio-checked-bg-color); + border: 4px solid var(--radio-checked-border-color); +} + +:is(:is(.dialog .mainContainer) .radio) > .radioLabel { + display: flex; + padding-inline-start: 24px; + align-items: flex-start; + gap: 10px; + align-self: stretch; +} + +:is(:is(:is(.dialog .mainContainer) .radio) > .radioLabel) > span { + flex: 1 0 0; + font-size: 11px; + color: var(--text-secondary-color); +} + +:is(.dialog .mainContainer) button:not(:is(.toggle-button, .closeButton)) { + border-radius: 4px; + border: 1px solid; + font: menu; + font-weight: 600; + padding: 4px 16px; + width: auto; + height: 32px; +} + +:is( + :is(.dialog .mainContainer) button:not(:is(.toggle-button, .closeButton)) + ):hover { + cursor: pointer; + filter: var(--hover-filter); +} + +.secondaryButton:is( + :is(.dialog .mainContainer) button:not(:is(.toggle-button, .closeButton)) + ) { + color: var(--button-secondary-fg-color); + background-color: var(--button-secondary-bg-color); + border-color: var(--button-secondary-border-color); +} + +.secondaryButton:is( + :is(.dialog .mainContainer) button:not(:is(.toggle-button, .closeButton)) + ):hover { + color: var(--button-secondary-hover-fg-color); + background-color: var(--button-secondary-hover-bg-color); + border-color: var(--button-secondary-hover-border-color); +} + +.primaryButton:is( + :is(.dialog .mainContainer) button:not(:is(.toggle-button, .closeButton)) + ) { + color: var(--button-primary-fg-color); + background-color: var(--button-primary-bg-color); + border-color: var(--button-primary-border-color); + opacity: 1; +} + +.primaryButton:is( + :is(.dialog .mainContainer) button:not(:is(.toggle-button, .closeButton)) + ):hover { + color: var(--button-primary-hover-fg-color); + background-color: var(--button-primary-hover-bg-color); + border-color: var(--button-primary-hover-border-color); +} + +:is(.dialog .mainContainer) a { + color: var(--link-fg-color); +} + +:is(:is(.dialog .mainContainer) a):hover { + color: var(--link-hover-fg-color); +} + +:is(.dialog .mainContainer) textarea { + font: inherit; + padding: 8px; + resize: none; + margin: 0; + box-sizing: border-box; + border-radius: 4px; + border: 1px solid var(--textarea-border-color); + background: var(--textarea-bg-color); + color: var(--textarea-fg-color); +} + +:is(:is(.dialog .mainContainer) textarea):focus { + outline-offset: 0; + border-color: transparent; +} + +:is(:is(.dialog .mainContainer) textarea):disabled { + pointer-events: none; + opacity: 0.4; +} + +:is(.dialog .mainContainer) .messageBar { + --message-bar-bg-color: #ffebcd; + --message-bar-fg-color: #15141a; + --message-bar-border-color: rgb(0 0 0 / 0.08); + --message-bar-icon: url(images/messageBar_warning.svg); + --message-bar-icon-color: #cd411e; +} + +@media (prefers-color-scheme: dark) { + :where(html:not(.is-light)) :is(.dialog .mainContainer) .messageBar { + --message-bar-bg-color: #5a3100; + --message-bar-fg-color: #fbfbfe; + --message-bar-border-color: rgb(255 255 255 / 0.08); + --message-bar-icon-color: #e49c49; + } +} + +:where(html.is-dark) :is(.dialog .mainContainer) .messageBar { + --message-bar-bg-color: #5a3100; + --message-bar-fg-color: #fbfbfe; + --message-bar-border-color: rgb(255 255 255 / 0.08); + --message-bar-icon-color: #e49c49; +} + +@media screen and (forced-colors: active) { + :is(.dialog .mainContainer) .messageBar { + --message-bar-bg-color: HighlightText; + --message-bar-fg-color: CanvasText; + --message-bar-border-color: CanvasText; + --message-bar-icon-color: CanvasText; + } +} + +:is(.dialog .mainContainer) .messageBar { + align-self: stretch; +} + +:is(:is(:is(.dialog .mainContainer) .messageBar) > div)::before, +:is(:is(:is(.dialog .mainContainer) .messageBar) > div) > div { + margin-block: 4px; +} + +:is(:is(:is(.dialog .mainContainer) .messageBar) > div) > div { + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 8px; + flex: 1 0 0; +} + +:is(:is(:is(:is(.dialog .mainContainer) .messageBar) > div) > div) .title { + font-size: 13px; + font-weight: 590; +} + +:is(:is(:is(:is(.dialog .mainContainer) .messageBar) > div) > div) + .description { + font-size: 13px; +} + +:is(.dialog .mainContainer) .toggler { + display: flex; + align-items: center; + gap: 8px; + align-self: stretch; +} + +:is(:is(.dialog .mainContainer) .toggler) > .togglerLabel { + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; +} + +.textLayer { + position: absolute; + text-align: initial; + inset: 0; + overflow: clip; + opacity: 1; + line-height: 1; + -webkit-text-size-adjust: none; + -moz-text-size-adjust: none; + text-size-adjust: none; + forced-color-adjust: none; + transform-origin: 0 0; + caret-color: CanvasText; + z-index: 0; +} + +.textLayer.highlighting { + touch-action: none; +} + +.textLayer :is(span, br) { + color: transparent; + position: absolute; + white-space: pre; + cursor: text; + transform-origin: 0% 0%; +} + +.textLayer > :not(.markedContent), +.textLayer .markedContent span:not(.markedContent) { + z-index: 1; +} + +.textLayer span.markedContent { + top: 0; + height: 0; +} + +.textLayer span[role='img'] { + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; + cursor: default; +} + +.textLayer .highlight { + --highlight-bg-color: rgb(180 0 170 / 0.25); + --highlight-selected-bg-color: rgb(0 100 0 / 0.25); + --highlight-backdrop-filter: none; + --highlight-selected-backdrop-filter: none; +} + +@media screen and (forced-colors: active) { + .textLayer .highlight { + --highlight-bg-color: transparent; + --highlight-selected-bg-color: transparent; + --highlight-backdrop-filter: var(--hcm-highlight-filter); + --highlight-selected-backdrop-filter: var(--hcm-highlight-selected-filter); + } +} + +.textLayer .highlight { + margin: -1px; + padding: 1px; + background-color: var(--highlight-bg-color); + -webkit-backdrop-filter: var(--highlight-backdrop-filter); + backdrop-filter: var(--highlight-backdrop-filter); + border-radius: 4px; +} + +.appended:is(.textLayer .highlight) { + position: initial; +} + +.begin:is(.textLayer .highlight) { + border-radius: 4px 0 0 4px; +} + +.end:is(.textLayer .highlight) { + border-radius: 0 4px 4px 0; +} + +.middle:is(.textLayer .highlight) { + border-radius: 0; +} + +.selected:is(.textLayer .highlight) { + background-color: var(--highlight-selected-bg-color); + -webkit-backdrop-filter: var(--highlight-selected-backdrop-filter); + backdrop-filter: var(--highlight-selected-backdrop-filter); +} + +.textLayer ::-moz-selection { + background: rgba(0 0 255 / 0.25); + background: color-mix(in srgb, AccentColor, transparent 75%); +} + +.textLayer ::selection { + background: rgba(0 0 255 / 0.25); + background: color-mix(in srgb, AccentColor, transparent 75%); +} + +.textLayer br::-moz-selection { + background: transparent; +} + +.textLayer br::selection { + background: transparent; +} + +.textLayer .endOfContent { + display: block; + position: absolute; + inset: 100% 0 0; + z-index: 0; + cursor: default; + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; +} + +.textLayer.selecting .endOfContent { + top: 0; +} + +.annotationLayer { + --annotation-unfocused-field-background: url("data:image/svg+xml;charset=UTF-8,"); + --input-focus-border-color: Highlight; + --input-focus-outline: 1px solid Canvas; + --input-unfocused-border-color: transparent; + --input-disabled-border-color: transparent; + --input-hover-border-color: black; + --link-outline: none; +} + +@media screen and (forced-colors: active) { + .annotationLayer { + --input-focus-border-color: CanvasText; + --input-unfocused-border-color: ActiveText; + --input-disabled-border-color: GrayText; + --input-hover-border-color: Highlight; + --link-outline: 1.5px solid LinkText; + } + + .annotationLayer .textWidgetAnnotation :is(input, textarea):required, + .annotationLayer .choiceWidgetAnnotation select:required, + .annotationLayer + .buttonWidgetAnnotation:is(.checkBox, .radioButton) + input:required { + outline: 1.5px solid selectedItem; + } + + .annotationLayer .linkAnnotation { + outline: var(--link-outline); + } + + :is(.annotationLayer .linkAnnotation):hover { + -webkit-backdrop-filter: var(--hcm-highlight-filter); + backdrop-filter: var(--hcm-highlight-filter); + } + + :is(.annotationLayer .linkAnnotation) > a:hover { + opacity: 0 !important; + background: none !important; + box-shadow: none; + } + + .annotationLayer .popupAnnotation .popup { + outline: calc(1.5px * var(--scale-factor)) solid CanvasText !important; + background-color: ButtonFace !important; + color: ButtonText !important; + } + + .annotationLayer .highlightArea:hover::after { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + -webkit-backdrop-filter: var(--hcm-highlight-filter); + backdrop-filter: var(--hcm-highlight-filter); + content: ''; + pointer-events: none; + } + + .annotationLayer .popupAnnotation.focused .popup { + outline: calc(3px * var(--scale-factor)) solid Highlight !important; + } +} + +.annotationLayer { + position: absolute; + top: 0; + left: 0; + pointer-events: none; + transform-origin: 0 0; +} + +.annotationLayer[data-main-rotation='90'] .norotate { + transform: rotate(270deg) translateX(-100%); +} + +.annotationLayer[data-main-rotation='180'] .norotate { + transform: rotate(180deg) translate(-100%, -100%); +} + +.annotationLayer[data-main-rotation='270'] .norotate { + transform: rotate(90deg) translateY(-100%); +} + +.annotationLayer.disabled section, +.annotationLayer.disabled .popup { + pointer-events: none; +} + +.annotationLayer .annotationContent { + position: absolute; + width: 100%; + height: 100%; + pointer-events: none; +} + +.freetext:is(.annotationLayer .annotationContent) { + background: transparent; + border: none; + inset: 0; + overflow: visible; + white-space: nowrap; + font: 10px sans-serif; + line-height: 1.35; + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; +} + +.annotationLayer section { + position: absolute; + text-align: initial; + pointer-events: auto; + box-sizing: border-box; + transform-origin: 0 0; +} + +:is(.annotationLayer section):has(div.annotationContent) + canvas.annotationContent { + display: none; +} + +.textLayer.selecting ~ .annotationLayer section { + pointer-events: none; +} + +.annotationLayer :is(.linkAnnotation, .buttonWidgetAnnotation.pushButton) > a { + position: absolute; + font-size: 1em; + top: 0; + left: 0; + width: 100%; + height: 100%; +} + +.annotationLayer + :is(.linkAnnotation, .buttonWidgetAnnotation.pushButton):not(.hasBorder) + > a:hover { + opacity: 0.2; + background-color: rgb(255 255 0); + box-shadow: 0 2px 10px rgb(255 255 0); +} + +.annotationLayer .linkAnnotation.hasBorder:hover { + background-color: rgb(255 255 0 / 0.2); +} + +.annotationLayer .hasBorder { + background-size: 100% 100%; +} + +.annotationLayer .textAnnotation img { + position: absolute; + cursor: pointer; + width: 100%; + height: 100%; + top: 0; + left: 0; +} + +.annotationLayer .textWidgetAnnotation :is(input, textarea), +.annotationLayer .choiceWidgetAnnotation select, +.annotationLayer .buttonWidgetAnnotation:is(.checkBox, .radioButton) input { + background-image: var(--annotation-unfocused-field-background); + border: 2px solid var(--input-unfocused-border-color); + box-sizing: border-box; + font: calc(9px * var(--scale-factor)) sans-serif; + height: 100%; + margin: 0; + vertical-align: top; + width: 100%; +} + +.annotationLayer .textWidgetAnnotation :is(input, textarea):required, +.annotationLayer .choiceWidgetAnnotation select:required, +.annotationLayer + .buttonWidgetAnnotation:is(.checkBox, .radioButton) + input:required { + outline: 1.5px solid red; +} + +.annotationLayer .choiceWidgetAnnotation select option { + padding: 0; +} + +.annotationLayer .buttonWidgetAnnotation.radioButton input { + border-radius: 50%; +} + +.annotationLayer .textWidgetAnnotation textarea { + resize: none; +} + +.annotationLayer .textWidgetAnnotation [disabled]:is(input, textarea), +.annotationLayer .choiceWidgetAnnotation select[disabled], +.annotationLayer + .buttonWidgetAnnotation:is(.checkBox, .radioButton) + input[disabled] { + background: none; + border: 2px solid var(--input-disabled-border-color); + cursor: not-allowed; +} + +.annotationLayer .textWidgetAnnotation :is(input, textarea):hover, +.annotationLayer .choiceWidgetAnnotation select:hover, +.annotationLayer + .buttonWidgetAnnotation:is(.checkBox, .radioButton) + input:hover { + border: 2px solid var(--input-hover-border-color); +} + +.annotationLayer .textWidgetAnnotation :is(input, textarea):hover, +.annotationLayer .choiceWidgetAnnotation select:hover, +.annotationLayer .buttonWidgetAnnotation.checkBox input:hover { + border-radius: 2px; +} + +.annotationLayer .textWidgetAnnotation :is(input, textarea):focus, +.annotationLayer .choiceWidgetAnnotation select:focus { + background: none; + border: 2px solid var(--input-focus-border-color); + border-radius: 2px; + outline: var(--input-focus-outline); +} + +.annotationLayer .buttonWidgetAnnotation:is(.checkBox, .radioButton) :focus { + background-image: none; + background-color: transparent; +} + +.annotationLayer .buttonWidgetAnnotation.checkBox :focus { + border: 2px solid var(--input-focus-border-color); + border-radius: 2px; + outline: var(--input-focus-outline); +} + +.annotationLayer .buttonWidgetAnnotation.radioButton :focus { + border: 2px solid var(--input-focus-border-color); + outline: var(--input-focus-outline); +} + +.annotationLayer .buttonWidgetAnnotation.checkBox input:checked::before, +.annotationLayer .buttonWidgetAnnotation.checkBox input:checked::after, +.annotationLayer .buttonWidgetAnnotation.radioButton input:checked::before { + background-color: CanvasText; + content: ''; + display: block; + position: absolute; +} + +.annotationLayer .buttonWidgetAnnotation.checkBox input:checked::before, +.annotationLayer .buttonWidgetAnnotation.checkBox input:checked::after { + height: 80%; + left: 45%; + width: 1px; +} + +.annotationLayer .buttonWidgetAnnotation.checkBox input:checked::before { + transform: rotate(45deg); +} + +.annotationLayer .buttonWidgetAnnotation.checkBox input:checked::after { + transform: rotate(-45deg); +} + +.annotationLayer .buttonWidgetAnnotation.radioButton input:checked::before { + border-radius: 50%; + height: 50%; + left: 25%; + top: 25%; + width: 50%; +} + +.annotationLayer .textWidgetAnnotation input.comb { + font-family: monospace; + padding-left: 2px; + padding-right: 0; +} + +.annotationLayer .textWidgetAnnotation input.comb:focus { + width: 103%; +} + +.annotationLayer .buttonWidgetAnnotation:is(.checkBox, .radioButton) input { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; +} + +.annotationLayer .fileAttachmentAnnotation .popupTriggerArea { + height: 100%; + width: 100%; +} + +.annotationLayer .popupAnnotation { + position: absolute; + font-size: calc(9px * var(--scale-factor)); + pointer-events: none; + width: -moz-max-content; + width: max-content; + max-width: 45%; + height: auto; +} + +.annotationLayer .popup { + background-color: rgb(255 255 153); + box-shadow: 0 calc(2px * var(--scale-factor)) calc(5px * var(--scale-factor)) + rgb(136 136 136); + border-radius: calc(2px * var(--scale-factor)); + outline: 1.5px solid rgb(255 255 74); + padding: calc(6px * var(--scale-factor)); + cursor: pointer; + font: message-box; + white-space: normal; + word-wrap: break-word; + pointer-events: auto; +} + +.annotationLayer .popupAnnotation.focused .popup { + outline-width: 3px; +} + +.annotationLayer .popup * { + font-size: calc(9px * var(--scale-factor)); +} + +.annotationLayer .popup > .header { + display: inline-block; +} + +.annotationLayer .popup > .header h1 { + display: inline; +} + +.annotationLayer .popup > .header .popupDate { + display: inline-block; + margin-left: calc(5px * var(--scale-factor)); + width: -moz-fit-content; + width: fit-content; +} + +.annotationLayer .popupContent { + border-top: 1px solid rgb(51 51 51); + margin-top: calc(2px * var(--scale-factor)); + padding-top: calc(2px * var(--scale-factor)); +} + +.annotationLayer .richText > * { + white-space: pre-wrap; + font-size: calc(9px * var(--scale-factor)); +} + +.annotationLayer .popupTriggerArea { + cursor: pointer; +} + +.annotationLayer section svg { + position: absolute; + width: 100%; + height: 100%; + top: 0; + left: 0; +} + +.annotationLayer .annotationTextContent { + position: absolute; + width: 100%; + height: 100%; + opacity: 0; + color: transparent; + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; + pointer-events: none; +} + +:is(.annotationLayer .annotationTextContent) span { + width: 100%; + display: inline-block; +} + +.annotationLayer svg.quadrilateralsContainer { + contain: strict; + width: 0; + height: 0; + position: absolute; + top: 0; + left: 0; + z-index: -1; +} + +:root { + --xfa-unfocused-field-background: url("data:image/svg+xml;charset=UTF-8,"); + --xfa-focus-outline: auto; +} + +@media screen and (forced-colors: active) { + :root { + --xfa-focus-outline: 2px solid CanvasText; + } + .xfaLayer *:required { + outline: 1.5px solid selectedItem; + } +} + +.xfaLayer { + background-color: transparent; +} + +.xfaLayer .highlight { + margin: -1px; + padding: 1px; + background-color: rgb(239 203 237); + border-radius: 4px; +} + +.xfaLayer .highlight.appended { + position: initial; +} + +.xfaLayer .highlight.begin { + border-radius: 4px 0 0 4px; +} + +.xfaLayer .highlight.end { + border-radius: 0 4px 4px 0; +} + +.xfaLayer .highlight.middle { + border-radius: 0; +} + +.xfaLayer .highlight.selected { + background-color: rgb(203 223 203); +} + +.xfaPage { + overflow: hidden; + position: relative; +} + +.xfaContentarea { + position: absolute; +} + +.xfaPrintOnly { + display: none; +} + +.xfaLayer { + position: absolute; + text-align: initial; + top: 0; + left: 0; + transform-origin: 0 0; + line-height: 1.2; +} + +.xfaLayer * { + color: inherit; + font: inherit; + font-style: inherit; + font-weight: inherit; + font-kerning: inherit; + letter-spacing: -0.01px; + text-align: inherit; + text-decoration: inherit; + box-sizing: border-box; + background-color: transparent; + padding: 0; + margin: 0; + pointer-events: auto; + line-height: inherit; +} + +.xfaLayer *:required { + outline: 1.5px solid red; } .xfaLayer div, .xfaLayer svg, -.xfaLayer svg *{ - pointer-events:none; +.xfaLayer svg * { + pointer-events: none; } -.xfaLayer a{ - color:blue; +.xfaLayer a { + color: blue; } -.xfaRich li{ - margin-left:3em; +.xfaRich li { + margin-left: 3em; } -.xfaFont{ - color:black; - font-weight:normal; - font-kerning:none; - font-size:10px; - font-style:normal; - letter-spacing:0; - text-decoration:none; - vertical-align:0; +.xfaFont { + color: black; + font-weight: normal; + font-kerning: none; + font-size: 10px; + font-style: normal; + letter-spacing: 0; + text-decoration: none; + vertical-align: 0; } -.xfaCaption{ - overflow:hidden; - flex:0 0 auto; +.xfaCaption { + overflow: hidden; + flex: 0 0 auto; } -.xfaCaptionForCheckButton{ - overflow:hidden; - flex:1 1 auto; +.xfaCaptionForCheckButton { + overflow: hidden; + flex: 1 1 auto; } -.xfaLabel{ - height:100%; - width:100%; +.xfaLabel { + height: 100%; + width: 100%; } -.xfaLeft{ - display:flex; - flex-direction:row; - align-items:center; +.xfaLeft { + display: flex; + flex-direction: row; + align-items: center; } -.xfaRight{ - display:flex; - flex-direction:row-reverse; - align-items:center; +.xfaRight { + display: flex; + flex-direction: row-reverse; + align-items: center; } -:is(.xfaLeft, .xfaRight) > :is(.xfaCaption, .xfaCaptionForCheckButton){ - max-height:100%; +:is(.xfaLeft, .xfaRight) > :is(.xfaCaption, .xfaCaptionForCheckButton) { + max-height: 100%; } -.xfaTop{ - display:flex; - flex-direction:column; - align-items:flex-start; +.xfaTop { + display: flex; + flex-direction: column; + align-items: flex-start; } -.xfaBottom{ - display:flex; - flex-direction:column-reverse; - align-items:flex-start; +.xfaBottom { + display: flex; + flex-direction: column-reverse; + align-items: flex-start; } -:is(.xfaTop, .xfaBottom) > :is(.xfaCaption, .xfaCaptionForCheckButton){ - width:100%; +:is(.xfaTop, .xfaBottom) > :is(.xfaCaption, .xfaCaptionForCheckButton) { + width: 100%; } -.xfaBorder{ - background-color:transparent; - position:absolute; - pointer-events:none; +.xfaBorder { + background-color: transparent; + position: absolute; + pointer-events: none; } -.xfaWrapped{ - width:100%; - height:100%; +.xfaWrapped { + width: 100%; + height: 100%; } -:is(.xfaTextfield, .xfaSelect):focus{ - background-image:none; - background-color:transparent; - outline:var(--xfa-focus-outline); - outline-offset:-1px; +:is(.xfaTextfield, .xfaSelect):focus { + background-image: none; + background-color: transparent; + outline: var(--xfa-focus-outline); + outline-offset: -1px; } -:is(.xfaCheckbox, .xfaRadio):focus{ - outline:var(--xfa-focus-outline); +:is(.xfaCheckbox, .xfaRadio):focus { + outline: var(--xfa-focus-outline); } .xfaTextfield, -.xfaSelect{ - height:100%; - width:100%; - flex:1 1 auto; - border:none; - resize:none; - background-image:var(--xfa-unfocused-field-background); +.xfaSelect { + height: 100%; + width: 100%; + flex: 1 1 auto; + border: none; + resize: none; + background-image: var(--xfa-unfocused-field-background); } -.xfaSelect{ - padding-inline:2px; +.xfaSelect { + padding-inline: 2px; } -:is(.xfaTop, .xfaBottom) > :is(.xfaTextfield, .xfaSelect){ - flex:0 1 auto; +:is(.xfaTop, .xfaBottom) > :is(.xfaTextfield, .xfaSelect) { + flex: 0 1 auto; } -.xfaButton{ - cursor:pointer; - width:100%; - height:100%; - border:none; - text-align:center; +.xfaButton { + cursor: pointer; + width: 100%; + height: 100%; + border: none; + text-align: center; } -.xfaLink{ - width:100%; - height:100%; - position:absolute; - top:0; - left:0; +.xfaLink { + width: 100%; + height: 100%; + position: absolute; + top: 0; + left: 0; } .xfaCheckbox, -.xfaRadio{ - width:100%; - height:100%; - flex:0 0 auto; - border:none; +.xfaRadio { + width: 100%; + height: 100%; + flex: 0 0 auto; + border: none; } -.xfaRich{ - white-space:pre-wrap; - width:100%; - height:100%; +.xfaRich { + white-space: pre-wrap; + width: 100%; + height: 100%; } -.xfaImage{ - -o-object-position:left top; - object-position:left top; - -o-object-fit:contain; - object-fit:contain; - width:100%; - height:100%; +.xfaImage { + -o-object-position: left top; + object-position: left top; + -o-object-fit: contain; + object-fit: contain; + width: 100%; + height: 100%; } .xfaLrTb, .xfaRlTb, -.xfaTb{ - display:flex; - flex-direction:column; - align-items:stretch; +.xfaTb { + display: flex; + flex-direction: column; + align-items: stretch; } -.xfaLr{ - display:flex; - flex-direction:row; - align-items:stretch; +.xfaLr { + display: flex; + flex-direction: row; + align-items: stretch; } -.xfaRl{ - display:flex; - flex-direction:row-reverse; - align-items:stretch; +.xfaRl { + display: flex; + flex-direction: row-reverse; + align-items: stretch; } -.xfaTb > div{ - justify-content:left; +.xfaTb > div { + justify-content: left; } -.xfaPosition{ - position:relative; +.xfaPosition { + position: relative; } -.xfaArea{ - position:relative; +.xfaArea { + position: relative; } -.xfaValignMiddle{ - display:flex; - align-items:center; +.xfaValignMiddle { + display: flex; + align-items: center; } -.xfaTable{ - display:flex; - flex-direction:column; - align-items:stretch; +.xfaTable { + display: flex; + flex-direction: column; + align-items: stretch; } -.xfaTable .xfaRow{ - display:flex; - flex-direction:row; - align-items:stretch; +.xfaTable .xfaRow { + display: flex; + flex-direction: row; + align-items: stretch; } -.xfaTable .xfaRlRow{ - display:flex; - flex-direction:row-reverse; - align-items:stretch; - flex:1; +.xfaTable .xfaRlRow { + display: flex; + flex-direction: row-reverse; + align-items: stretch; + flex: 1; } -.xfaTable .xfaRlRow > div{ - flex:1; +.xfaTable .xfaRlRow > div { + flex: 1; } -:is(.xfaNonInteractive, .xfaDisabled, .xfaReadOnly) :is(input, textarea){ - background:initial; +:is(.xfaNonInteractive, .xfaDisabled, .xfaReadOnly) :is(input, textarea) { + background: initial; } -@media print{ +@media print { .xfaTextfield, - .xfaSelect{ - background:transparent; + .xfaSelect { + background: transparent; } - .xfaSelect{ - -webkit-appearance:none; - -moz-appearance:none; - appearance:none; - text-indent:1px; - text-overflow:""; + .xfaSelect { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + text-indent: 1px; + text-overflow: ''; } } -.canvasWrapper svg{ - transform:none; - } - -.moving:is(.canvasWrapper svg){ - z-index:100000; - } - -[data-main-rotation="90"]:is(.highlight:is(.canvasWrapper svg),.highlightOutline:is(.canvasWrapper svg)) mask,[data-main-rotation="90"]:is(.highlight:is(.canvasWrapper svg),.highlightOutline:is(.canvasWrapper svg)) use:not(.clip,.mask){ - transform:matrix(0, 1, -1, 0, 1, 0); - } - -[data-main-rotation="180"]:is(.highlight:is(.canvasWrapper svg),.highlightOutline:is(.canvasWrapper svg)) mask,[data-main-rotation="180"]:is(.highlight:is(.canvasWrapper svg),.highlightOutline:is(.canvasWrapper svg)) use:not(.clip,.mask){ - transform:matrix(-1, 0, 0, -1, 1, 1); - } - -[data-main-rotation="270"]:is(.highlight:is(.canvasWrapper svg),.highlightOutline:is(.canvasWrapper svg)) mask,[data-main-rotation="270"]:is(.highlight:is(.canvasWrapper svg),.highlightOutline:is(.canvasWrapper svg)) use:not(.clip,.mask){ - transform:matrix(0, -1, 1, 0, 0, 1); - } - -.draw:is(.canvasWrapper svg){ - position:absolute; - mix-blend-mode:normal; - } - -.draw[data-draw-rotation="90"]:is(.canvasWrapper svg){ - transform:rotate(90deg); - } - -.draw[data-draw-rotation="180"]:is(.canvasWrapper svg){ - transform:rotate(180deg); - } - -.draw[data-draw-rotation="270"]:is(.canvasWrapper svg){ - transform:rotate(270deg); - } - -.highlight:is(.canvasWrapper svg){ - --blend-mode:multiply; - } - -@media screen and (forced-colors: active){ - -.highlight:is(.canvasWrapper svg){ - --blend-mode:difference; - } - } - -.highlight:is(.canvasWrapper svg){ - - position:absolute; - mix-blend-mode:var(--blend-mode); - } - -.highlight:is(.canvasWrapper svg):not(.free){ - fill-rule:evenodd; - } - -.highlightOutline:is(.canvasWrapper svg){ - position:absolute; - mix-blend-mode:normal; - fill-rule:evenodd; - fill:none; - } - -.highlightOutline.hovered:is(.canvasWrapper svg):not(.free):not(.selected){ - stroke:var(--hover-outline-color); - stroke-width:var(--outline-width); - } - -.highlightOutline.selected:is(.canvasWrapper svg):not(.free) .mainOutline{ - stroke:var(--outline-around-color); - stroke-width:calc( - var(--outline-width) + 2 * var(--outline-around-width) - ); - } - -.highlightOutline.selected:is(.canvasWrapper svg):not(.free) .secondaryOutline{ - stroke:var(--outline-color); - stroke-width:var(--outline-width); - } - -.highlightOutline.free.hovered:is(.canvasWrapper svg):not(.selected){ - stroke:var(--hover-outline-color); - stroke-width:calc(2 * var(--outline-width)); - } - -.highlightOutline.free.selected:is(.canvasWrapper svg) .mainOutline{ - stroke:var(--outline-around-color); - stroke-width:calc( - 2 * (var(--outline-width) + var(--outline-around-width)) - ); - } - -.highlightOutline.free.selected:is(.canvasWrapper svg) .secondaryOutline{ - stroke:var(--outline-color); - stroke-width:calc(2 * var(--outline-width)); - } - -.toggle-button{ - --button-background-color:#f0f0f4; - --button-background-color-hover:#e0e0e6; - --button-background-color-active:#cfcfd8; - --color-accent-primary:#0060df; - --color-accent-primary-hover:#0250bb; - --color-accent-primary-active:#054096; - --border-interactive-color:#8f8f9d; - --border-radius-circle:9999px; - --border-width:1px; - --size-item-small:16px; - --size-item-large:32px; - --color-canvas:white; +.canvasWrapper svg { + transform: none; } -@media (prefers-color-scheme: dark){ +.moving:is(.canvasWrapper svg) { + z-index: 100000; +} -:where(html:not(.is-light)) .toggle-button{ - --button-background-color:color-mix(in srgb, currentColor 7%, transparent); - --button-background-color-hover:color-mix( +[data-main-rotation='90']:is( + .highlight:is(.canvasWrapper svg), + .highlightOutline:is(.canvasWrapper svg) + ) + mask, +[data-main-rotation='90']:is( + .highlight:is(.canvasWrapper svg), + .highlightOutline:is(.canvasWrapper svg) + ) + use:not(.clip, .mask) { + transform: matrix(0, 1, -1, 0, 1, 0); +} + +[data-main-rotation='180']:is( + .highlight:is(.canvasWrapper svg), + .highlightOutline:is(.canvasWrapper svg) + ) + mask, +[data-main-rotation='180']:is( + .highlight:is(.canvasWrapper svg), + .highlightOutline:is(.canvasWrapper svg) + ) + use:not(.clip, .mask) { + transform: matrix(-1, 0, 0, -1, 1, 1); +} + +[data-main-rotation='270']:is( + .highlight:is(.canvasWrapper svg), + .highlightOutline:is(.canvasWrapper svg) + ) + mask, +[data-main-rotation='270']:is( + .highlight:is(.canvasWrapper svg), + .highlightOutline:is(.canvasWrapper svg) + ) + use:not(.clip, .mask) { + transform: matrix(0, -1, 1, 0, 0, 1); +} + +.draw:is(.canvasWrapper svg) { + position: absolute; + mix-blend-mode: normal; +} + +.draw[data-draw-rotation='90']:is(.canvasWrapper svg) { + transform: rotate(90deg); +} + +.draw[data-draw-rotation='180']:is(.canvasWrapper svg) { + transform: rotate(180deg); +} + +.draw[data-draw-rotation='270']:is(.canvasWrapper svg) { + transform: rotate(270deg); +} + +.highlight:is(.canvasWrapper svg) { + --blend-mode: multiply; +} + +@media screen and (forced-colors: active) { + .highlight:is(.canvasWrapper svg) { + --blend-mode: difference; + } +} + +.highlight:is(.canvasWrapper svg) { + position: absolute; + mix-blend-mode: var(--blend-mode); +} + +.highlight:is(.canvasWrapper svg):not(.free) { + fill-rule: evenodd; +} + +.highlightOutline:is(.canvasWrapper svg) { + position: absolute; + mix-blend-mode: normal; + fill-rule: evenodd; + fill: none; +} + +.highlightOutline.hovered:is(.canvasWrapper svg):not(.free):not(.selected) { + stroke: var(--hover-outline-color); + stroke-width: var(--outline-width); +} + +.highlightOutline.selected:is(.canvasWrapper svg):not(.free) .mainOutline { + stroke: var(--outline-around-color); + stroke-width: calc(var(--outline-width) + 2 * var(--outline-around-width)); +} + +.highlightOutline.selected:is(.canvasWrapper svg):not(.free) .secondaryOutline { + stroke: var(--outline-color); + stroke-width: var(--outline-width); +} + +.highlightOutline.free.hovered:is(.canvasWrapper svg):not(.selected) { + stroke: var(--hover-outline-color); + stroke-width: calc(2 * var(--outline-width)); +} + +.highlightOutline.free.selected:is(.canvasWrapper svg) .mainOutline { + stroke: var(--outline-around-color); + stroke-width: calc(2 * (var(--outline-width) + var(--outline-around-width))); +} + +.highlightOutline.free.selected:is(.canvasWrapper svg) .secondaryOutline { + stroke: var(--outline-color); + stroke-width: calc(2 * var(--outline-width)); +} + +.toggle-button { + --button-background-color: #f0f0f4; + --button-background-color-hover: #e0e0e6; + --button-background-color-active: #cfcfd8; + --color-accent-primary: #0060df; + --color-accent-primary-hover: #0250bb; + --color-accent-primary-active: #054096; + --border-interactive-color: #8f8f9d; + --border-radius-circle: 9999px; + --border-width: 1px; + --size-item-small: 16px; + --size-item-large: 32px; + --color-canvas: white; +} + +@media (prefers-color-scheme: dark) { + :where(html:not(.is-light)) .toggle-button { + --button-background-color: color-mix(in srgb, currentColor 7%, transparent); + --button-background-color-hover: color-mix( in srgb, currentColor 14%, transparent ); - --button-background-color-active:color-mix( + --button-background-color-active: color-mix( in srgb, currentColor 21%, transparent ); - --color-accent-primary:#0df; - --color-accent-primary-hover:#80ebff; - --color-accent-primary-active:#aaf2ff; - --border-interactive-color:#bfbfc9; - --color-canvas:#1c1b22; -} + --color-accent-primary: #0df; + --color-accent-primary-hover: #80ebff; + --color-accent-primary-active: #aaf2ff; + --border-interactive-color: #bfbfc9; + --color-canvas: #1c1b22; } - -:where(html.is-dark) .toggle-button{ - --button-background-color:color-mix(in srgb, currentColor 7%, transparent); - --button-background-color-hover:color-mix( - in srgb, - currentColor 14%, - transparent - ); - --button-background-color-active:color-mix( - in srgb, - currentColor 21%, - transparent - ); - --color-accent-primary:#0df; - --color-accent-primary-hover:#80ebff; - --color-accent-primary-active:#aaf2ff; - --border-interactive-color:#bfbfc9; - --color-canvas:#1c1b22; } -@media (forced-colors: active){ - -.toggle-button{ - --color-accent-primary:ButtonText; - --color-accent-primary-hover:SelectedItem; - --color-accent-primary-active:SelectedItem; - --border-interactive-color:ButtonText; - --button-background-color:ButtonFace; - --border-interactive-color-hover:SelectedItem; - --border-interactive-color-active:SelectedItem; - --border-interactive-color-disabled:GrayText; - --color-canvas:ButtonText; +:where(html.is-dark) .toggle-button { + --button-background-color: color-mix(in srgb, currentColor 7%, transparent); + --button-background-color-hover: color-mix( + in srgb, + currentColor 14%, + transparent + ); + --button-background-color-active: color-mix( + in srgb, + currentColor 21%, + transparent + ); + --color-accent-primary: #0df; + --color-accent-primary-hover: #80ebff; + --color-accent-primary-active: #aaf2ff; + --border-interactive-color: #bfbfc9; + --color-canvas: #1c1b22; } + +@media (forced-colors: active) { + .toggle-button { + --color-accent-primary: ButtonText; + --color-accent-primary-hover: SelectedItem; + --color-accent-primary-active: SelectedItem; + --border-interactive-color: ButtonText; + --button-background-color: ButtonFace; + --border-interactive-color-hover: SelectedItem; + --border-interactive-color-active: SelectedItem; + --border-interactive-color-disabled: GrayText; + --color-canvas: ButtonText; } +} -.toggle-button{ - - --toggle-background-color:var(--button-background-color); - --toggle-background-color-hover:var(--button-background-color-hover); - --toggle-background-color-active:var(--button-background-color-active); - --toggle-background-color-pressed:var(--color-accent-primary); - --toggle-background-color-pressed-hover:var(--color-accent-primary-hover); - --toggle-background-color-pressed-active:var(--color-accent-primary-active); - --toggle-border-color:var(--border-interactive-color); - --toggle-border-color-hover:var(--toggle-border-color); - --toggle-border-color-active:var(--toggle-border-color); - --toggle-border-radius:var(--border-radius-circle); - --toggle-border-width:var(--border-width); - --toggle-height:var(--size-item-small); - --toggle-width:var(--size-item-large); - --toggle-dot-background-color:var(--toggle-border-color); - --toggle-dot-background-color-hover:var(--toggle-dot-background-color); - --toggle-dot-background-color-active:var(--toggle-dot-background-color); - --toggle-dot-background-color-on-pressed:var(--color-canvas); - --toggle-dot-margin:1px; - --toggle-dot-height:calc( +.toggle-button { + --toggle-background-color: var(--button-background-color); + --toggle-background-color-hover: var(--button-background-color-hover); + --toggle-background-color-active: var(--button-background-color-active); + --toggle-background-color-pressed: var(--color-accent-primary); + --toggle-background-color-pressed-hover: var(--color-accent-primary-hover); + --toggle-background-color-pressed-active: var(--color-accent-primary-active); + --toggle-border-color: var(--border-interactive-color); + --toggle-border-color-hover: var(--toggle-border-color); + --toggle-border-color-active: var(--toggle-border-color); + --toggle-border-radius: var(--border-radius-circle); + --toggle-border-width: var(--border-width); + --toggle-height: var(--size-item-small); + --toggle-width: var(--size-item-large); + --toggle-dot-background-color: var(--toggle-border-color); + --toggle-dot-background-color-hover: var(--toggle-dot-background-color); + --toggle-dot-background-color-active: var(--toggle-dot-background-color); + --toggle-dot-background-color-on-pressed: var(--color-canvas); + --toggle-dot-margin: 1px; + --toggle-dot-height: calc( var(--toggle-height) - 2 * var(--toggle-dot-margin) - 2 * var(--toggle-border-width) ); - --toggle-dot-width:var(--toggle-dot-height); - --toggle-dot-transform-x:calc( + --toggle-dot-width: var(--toggle-dot-height); + --toggle-dot-transform-x: calc( var(--toggle-width) - 4 * var(--toggle-dot-margin) - var(--toggle-dot-width) ); - -webkit-appearance:none; + -webkit-appearance: none; - -moz-appearance:none; + -moz-appearance: none; - appearance:none; - padding:0; - margin:0; - border:var(--toggle-border-width) solid var(--toggle-border-color); - height:var(--toggle-height); - width:var(--toggle-width); - border-radius:var(--toggle-border-radius); - background:var(--toggle-background-color); - box-sizing:border-box; - flex-shrink:0; + appearance: none; + padding: 0; + margin: 0; + border: var(--toggle-border-width) solid var(--toggle-border-color); + height: var(--toggle-height); + width: var(--toggle-width); + border-radius: var(--toggle-border-radius); + background: var(--toggle-background-color); + box-sizing: border-box; + flex-shrink: 0; } -.toggle-button:focus-visible{ - outline:var(--focus-outline); - outline-offset:var(--focus-outline-offset); - } - -.toggle-button:enabled:hover{ - background:var(--toggle-background-color-hover); - border-color:var(--toggle-border-color); - } - -.toggle-button:enabled:active{ - background:var(--toggle-background-color-active); - border-color:var(--toggle-border-color); - } - -.toggle-button[aria-pressed="true"]{ - background:var(--toggle-background-color-pressed); - border-color:transparent; - } - -.toggle-button[aria-pressed="true"]:enabled:hover{ - background:var(--toggle-background-color-pressed-hover); - border-color:transparent; - } - -.toggle-button[aria-pressed="true"]:enabled:active{ - background:var(--toggle-background-color-pressed-active); - border-color:transparent; - } - -.toggle-button::before{ - display:block; - content:""; - background-color:var(--toggle-dot-background-color); - height:var(--toggle-dot-height); - width:var(--toggle-dot-width); - margin:var(--toggle-dot-margin); - border-radius:var(--toggle-border-radius); - translate:0; - } - -.toggle-button[aria-pressed="true"]::before{ - translate:var(--toggle-dot-transform-x); - background-color:var(--toggle-dot-background-color-on-pressed); - } - -.toggle-button[aria-pressed="true"]:enabled:hover::before,.toggle-button[aria-pressed="true"]:enabled:active::before{ - background-color:var(--toggle-dot-background-color-on-pressed); - } - -[dir="rtl"] .toggle-button[aria-pressed="true"]::before{ - translate:calc(-1 * var(--toggle-dot-transform-x)); - } - -@media (prefers-reduced-motion: no-preference){ - .toggle-button::before{ - transition:translate 100ms; - } - } - -@media (prefers-contrast){ - .toggle-button:enabled:hover{ - border-color:var(--toggle-border-color-hover); - } - - .toggle-button:enabled:active{ - border-color:var(--toggle-border-color-active); - } - - .toggle-button[aria-pressed="true"]:enabled{ - border-color:var(--toggle-border-color); - position:relative; - } - - .toggle-button[aria-pressed="true"]:enabled:hover,.toggle-button[aria-pressed="true"]:enabled:hover:active{ - border-color:var(--toggle-border-color-hover); - } - - .toggle-button[aria-pressed="true"]:enabled:active{ - background-color:var(--toggle-dot-background-color-active); - border-color:var(--toggle-dot-background-color-hover); - } - - .toggle-button:hover::before,.toggle-button:active::before{ - background-color:var(--toggle-dot-background-color-hover); - } - } - -@media (forced-colors){ - -.toggle-button{ - --toggle-dot-background-color:var(--color-accent-primary); - --toggle-dot-background-color-hover:var(--color-accent-primary-hover); - --toggle-dot-background-color-active:var(--color-accent-primary-active); - --toggle-dot-background-color-on-pressed:var(--button-background-color); - --toggle-background-color-disabled:var(--button-background-color-disabled); - --toggle-border-color-hover:var(--border-interactive-color-hover); - --toggle-border-color-active:var(--border-interactive-color-active); - --toggle-border-color-disabled:var(--border-interactive-color-disabled); +.toggle-button:focus-visible { + outline: var(--focus-outline); + outline-offset: var(--focus-outline-offset); } - .toggle-button[aria-pressed="true"]:enabled::after{ - border:1px solid var(--button-background-color); - content:""; - position:absolute; - height:var(--toggle-height); - width:var(--toggle-width); - display:block; - border-radius:var(--toggle-border-radius); - inset:-2px; - } +.toggle-button:enabled:hover { + background: var(--toggle-background-color-hover); + border-color: var(--toggle-border-color); +} - .toggle-button[aria-pressed="true"]:enabled:active::after{ - border-color:var(--toggle-border-color-active); - } +.toggle-button:enabled:active { + background: var(--toggle-background-color-active); + border-color: var(--toggle-border-color); +} + +.toggle-button[aria-pressed='true'] { + background: var(--toggle-background-color-pressed); + border-color: transparent; +} + +.toggle-button[aria-pressed='true']:enabled:hover { + background: var(--toggle-background-color-pressed-hover); + border-color: transparent; +} + +.toggle-button[aria-pressed='true']:enabled:active { + background: var(--toggle-background-color-pressed-active); + border-color: transparent; +} + +.toggle-button::before { + display: block; + content: ''; + background-color: var(--toggle-dot-background-color); + height: var(--toggle-dot-height); + width: var(--toggle-dot-width); + margin: var(--toggle-dot-margin); + border-radius: var(--toggle-border-radius); + translate: 0; +} + +.toggle-button[aria-pressed='true']::before { + translate: var(--toggle-dot-transform-x); + background-color: var(--toggle-dot-background-color-on-pressed); +} + +.toggle-button[aria-pressed='true']:enabled:hover::before, +.toggle-button[aria-pressed='true']:enabled:active::before { + background-color: var(--toggle-dot-background-color-on-pressed); +} + +[dir='rtl'] .toggle-button[aria-pressed='true']::before { + translate: calc(-1 * var(--toggle-dot-transform-x)); +} + +@media (prefers-reduced-motion: no-preference) { + .toggle-button::before { + transition: translate 100ms; + } +} + +@media (prefers-contrast) { + .toggle-button:enabled:hover { + border-color: var(--toggle-border-color-hover); } -:root{ - --outline-width:2px; - --outline-color:#0060df; - --outline-around-width:1px; - --outline-around-color:#f0f0f4; - --hover-outline-around-color:var(--outline-around-color); - --focus-outline:solid var(--outline-width) var(--outline-color); - --unfocus-outline:solid var(--outline-width) transparent; - --focus-outline-around:solid var(--outline-around-width) var(--outline-around-color); - --hover-outline-color:#8f8f9d; - --hover-outline:solid var(--outline-width) var(--hover-outline-color); - --hover-outline-around:solid var(--outline-around-width) var(--hover-outline-around-color); - --freetext-line-height:1.35; - --freetext-padding:2px; - --resizer-bg-color:var(--outline-color); - --resizer-size:6px; - --resizer-shift:calc( + .toggle-button:enabled:active { + border-color: var(--toggle-border-color-active); + } + + .toggle-button[aria-pressed='true']:enabled { + border-color: var(--toggle-border-color); + position: relative; + } + + .toggle-button[aria-pressed='true']:enabled:hover, + .toggle-button[aria-pressed='true']:enabled:hover:active { + border-color: var(--toggle-border-color-hover); + } + + .toggle-button[aria-pressed='true']:enabled:active { + background-color: var(--toggle-dot-background-color-active); + border-color: var(--toggle-dot-background-color-hover); + } + + .toggle-button:hover::before, + .toggle-button:active::before { + background-color: var(--toggle-dot-background-color-hover); + } +} + +@media (forced-colors) { + .toggle-button { + --toggle-dot-background-color: var(--color-accent-primary); + --toggle-dot-background-color-hover: var(--color-accent-primary-hover); + --toggle-dot-background-color-active: var(--color-accent-primary-active); + --toggle-dot-background-color-on-pressed: var(--button-background-color); + --toggle-background-color-disabled: var(--button-background-color-disabled); + --toggle-border-color-hover: var(--border-interactive-color-hover); + --toggle-border-color-active: var(--border-interactive-color-active); + --toggle-border-color-disabled: var(--border-interactive-color-disabled); + } + + .toggle-button[aria-pressed='true']:enabled::after { + border: 1px solid var(--button-background-color); + content: ''; + position: absolute; + height: var(--toggle-height); + width: var(--toggle-width); + display: block; + border-radius: var(--toggle-border-radius); + inset: -2px; + } + + .toggle-button[aria-pressed='true']:enabled:active::after { + border-color: var(--toggle-border-color-active); + } +} + +:root { + --outline-width: 2px; + --outline-color: #0060df; + --outline-around-width: 1px; + --outline-around-color: #f0f0f4; + --hover-outline-around-color: var(--outline-around-color); + --focus-outline: solid var(--outline-width) var(--outline-color); + --unfocus-outline: solid var(--outline-width) transparent; + --focus-outline-around: solid var(--outline-around-width) + var(--outline-around-color); + --hover-outline-color: #8f8f9d; + --hover-outline: solid var(--outline-width) var(--hover-outline-color); + --hover-outline-around: solid var(--outline-around-width) + var(--hover-outline-around-color); + --freetext-line-height: 1.35; + --freetext-padding: 2px; + --resizer-bg-color: var(--outline-color); + --resizer-size: 6px; + --resizer-shift: calc( 0px - (var(--outline-width) + var(--resizer-size)) / 2 - var(--outline-around-width) ); - --editorFreeText-editing-cursor:text; - --editorInk-editing-cursor:url(images/cursor-editorInk.svg) 0 16, pointer; - --editorHighlight-editing-cursor:url(images/cursor-editorTextHighlight.svg) 24 24, text; - --editorFreeHighlight-editing-cursor:url(images/cursor-editorFreeHighlight.svg) 1 18, pointer; + --editorFreeText-editing-cursor: text; + --editorInk-editing-cursor: url(images/cursor-editorInk.svg) 0 16, pointer; + --editorHighlight-editing-cursor: + url(images/cursor-editorTextHighlight.svg) 24 24, text; + --editorFreeHighlight-editing-cursor: + url(images/cursor-editorFreeHighlight.svg) 1 18, pointer; - --new-alt-text-warning-image:url(images/altText_warning.svg); + --new-alt-text-warning-image: url(images/altText_warning.svg); } -.visuallyHidden{ - position:absolute; - top:0; - left:0; - border:0; - margin:0; - padding:0; - width:0; - height:0; - overflow:hidden; - white-space:nowrap; - font-size:0; +.visuallyHidden { + position: absolute; + top: 0; + left: 0; + border: 0; + margin: 0; + padding: 0; + width: 0; + height: 0; + overflow: hidden; + white-space: nowrap; + font-size: 0; } -.textLayer.highlighting{ - cursor:var(--editorFreeHighlight-editing-cursor); - } +.textLayer.highlighting { + cursor: var(--editorFreeHighlight-editing-cursor); +} -.textLayer.highlighting:not(.free) span{ - cursor:var(--editorHighlight-editing-cursor); - } +.textLayer.highlighting:not(.free) span { + cursor: var(--editorHighlight-editing-cursor); +} -[role="img"]:is(.textLayer.highlighting:not(.free) span){ - cursor:var(--editorFreeHighlight-editing-cursor); - } +[role='img']:is(.textLayer.highlighting:not(.free) span) { + cursor: var(--editorFreeHighlight-editing-cursor); +} -.textLayer.highlighting.free span{ - cursor:var(--editorFreeHighlight-editing-cursor); - } +.textLayer.highlighting.free span { + cursor: var(--editorFreeHighlight-editing-cursor); +} -:is(#viewerContainer.pdfPresentationMode:fullscreen,.annotationEditorLayer.disabled) .noAltTextBadge{ - display:none !important; - } +:is( + #viewerContainer.pdfPresentationMode:fullscreen, + .annotationEditorLayer.disabled + ) + .noAltTextBadge { + display: none !important; +} -@media (min-resolution: 1.1dppx){ - :root{ - --editorFreeText-editing-cursor:url(images/cursor-editorFreeText.svg) 0 16, text; +@media (min-resolution: 1.1dppx) { + :root { + --editorFreeText-editing-cursor: + url(images/cursor-editorFreeText.svg) 0 16, text; } } -@media screen and (forced-colors: active){ - :root{ - --outline-color:CanvasText; - --outline-around-color:ButtonFace; - --resizer-bg-color:ButtonText; - --hover-outline-color:Highlight; - --hover-outline-around-color:SelectedItemText; +@media screen and (forced-colors: active) { + :root { + --outline-color: CanvasText; + --outline-around-color: ButtonFace; + --resizer-bg-color: ButtonText; + --hover-outline-color: Highlight; + --hover-outline-around-color: SelectedItemText; } } -[data-editor-rotation="90"]{ - transform:rotate(90deg); +[data-editor-rotation='90'] { + transform: rotate(90deg); } -[data-editor-rotation="180"]{ - transform:rotate(180deg); +[data-editor-rotation='180'] { + transform: rotate(180deg); } -[data-editor-rotation="270"]{ - transform:rotate(270deg); +[data-editor-rotation='270'] { + transform: rotate(270deg); } -.annotationEditorLayer{ - background:transparent; - position:absolute; - inset:0; - font-size:calc(100px * var(--scale-factor)); - transform-origin:0 0; - cursor:auto; +.annotationEditorLayer { + background: transparent; + position: absolute; + inset: 0; + font-size: calc(100px * var(--scale-factor)); + transform-origin: 0 0; + cursor: auto; } -.annotationEditorLayer .selectedEditor{ - z-index:100000 !important; - } - -.annotationEditorLayer.drawing *{ - pointer-events:none !important; - } - -.annotationEditorLayer.waiting{ - content:""; - cursor:wait; - position:absolute; - inset:0; - width:100%; - height:100%; +.annotationEditorLayer .selectedEditor { + z-index: 100000 !important; } -.annotationEditorLayer.disabled{ - pointer-events:none; +.annotationEditorLayer.drawing * { + pointer-events: none !important; } -.annotationEditorLayer.freetextEditing{ - cursor:var(--editorFreeText-editing-cursor); +.annotationEditorLayer.waiting { + content: ''; + cursor: wait; + position: absolute; + inset: 0; + width: 100%; + height: 100%; } -.annotationEditorLayer.inkEditing{ - cursor:var(--editorInk-editing-cursor); +.annotationEditorLayer.disabled { + pointer-events: none; } -.annotationEditorLayer .draw{ - box-sizing:border-box; +.annotationEditorLayer.freetextEditing { + cursor: var(--editorFreeText-editing-cursor); } -.annotationEditorLayer :is(.freeTextEditor, .inkEditor, .stampEditor){ - position:absolute; - background:transparent; - z-index:1; - transform-origin:0 0; - cursor:auto; - max-width:100%; - max-height:100%; - border:var(--unfocus-outline); +.annotationEditorLayer.inkEditing { + cursor: var(--editorInk-editing-cursor); } -.draggable.selectedEditor:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor)){ - cursor:move; - } - -.selectedEditor:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor)){ - border:var(--focus-outline); - outline:var(--focus-outline-around); - } - -.selectedEditor:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor))::before{ - content:""; - position:absolute; - inset:0; - border:var(--focus-outline-around); - pointer-events:none; - } - -:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor)):hover:not(.selectedEditor){ - border:var(--hover-outline); - outline:var(--hover-outline-around); - } - -:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor)):hover:not(.selectedEditor)::before{ - content:""; - position:absolute; - inset:0; - border:var(--focus-outline-around); - } - -:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor,.highlightEditor),.textLayer) .editToolbar{ - --editor-toolbar-delete-image:url(images/editor-toolbar-delete.svg); - --editor-toolbar-bg-color:#f0f0f4; - --editor-toolbar-highlight-image:url(images/toolbarButton-editorHighlight.svg); - --editor-toolbar-fg-color:#2e2e56; - --editor-toolbar-border-color:#8f8f9d; - --editor-toolbar-hover-border-color:var(--editor-toolbar-border-color); - --editor-toolbar-hover-bg-color:#e0e0e6; - --editor-toolbar-hover-fg-color:var(--editor-toolbar-fg-color); - --editor-toolbar-hover-outline:none; - --editor-toolbar-focus-outline-color:#0060df; - --editor-toolbar-shadow:0 2px 6px 0 rgb(58 57 68 / 0.2); - --editor-toolbar-vert-offset:6px; - --editor-toolbar-height:28px; - --editor-toolbar-padding:2px; - --alt-text-done-color:#2ac3a2; - --alt-text-warning-color:#0090ed; - --alt-text-hover-done-color:var(--alt-text-done-color); - --alt-text-hover-warning-color:var(--alt-text-warning-color); - } - -@media (prefers-color-scheme: dark){ - -:where(html:not(.is-light)) :is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor,.highlightEditor),.textLayer) .editToolbar{ - --editor-toolbar-bg-color:#2b2a33; - --editor-toolbar-fg-color:#fbfbfe; - --editor-toolbar-hover-bg-color:#52525e; - --editor-toolbar-focus-outline-color:#0df; - --alt-text-done-color:#54ffbd; - --alt-text-warning-color:#80ebff; - } - } - -:where(html.is-dark) :is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor,.highlightEditor),.textLayer) .editToolbar{ - --editor-toolbar-bg-color:#2b2a33; - --editor-toolbar-fg-color:#fbfbfe; - --editor-toolbar-hover-bg-color:#52525e; - --editor-toolbar-focus-outline-color:#0df; - --alt-text-done-color:#54ffbd; - --alt-text-warning-color:#80ebff; - } - -@media screen and (forced-colors: active){ - -:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor,.highlightEditor),.textLayer) .editToolbar{ - --editor-toolbar-bg-color:ButtonFace; - --editor-toolbar-fg-color:ButtonText; - --editor-toolbar-border-color:ButtonText; - --editor-toolbar-hover-border-color:AccentColor; - --editor-toolbar-hover-bg-color:ButtonFace; - --editor-toolbar-hover-fg-color:AccentColor; - --editor-toolbar-hover-outline:2px solid var(--editor-toolbar-hover-border-color); - --editor-toolbar-focus-outline-color:ButtonBorder; - --editor-toolbar-shadow:none; - --alt-text-done-color:var(--editor-toolbar-fg-color); - --alt-text-warning-color:var(--editor-toolbar-fg-color); - --alt-text-hover-done-color:var(--editor-toolbar-hover-fg-color); - --alt-text-hover-warning-color:var(--editor-toolbar-hover-fg-color); - } - } - -:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor,.highlightEditor),.textLayer) .editToolbar{ - - display:flex; - width:-moz-fit-content; - width:fit-content; - height:var(--editor-toolbar-height); - flex-direction:column; - justify-content:center; - align-items:center; - cursor:default; - pointer-events:auto; - box-sizing:content-box; - padding:var(--editor-toolbar-padding); - - position:absolute; - inset-inline-end:0; - inset-block-start:calc(100% + var(--editor-toolbar-vert-offset)); - - border-radius:6px; - background-color:var(--editor-toolbar-bg-color); - border:1px solid var(--editor-toolbar-border-color); - box-shadow:var(--editor-toolbar-shadow); - } - -.hidden:is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor,.highlightEditor),.textLayer) .editToolbar){ - display:none; - } - -:is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor,.highlightEditor),.textLayer) .editToolbar):has(:focus-visible){ - border-color:transparent; - } - -[dir="ltr"] :is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor,.highlightEditor),.textLayer) .editToolbar){ - transform-origin:100% 0; - } - -[dir="rtl"] :is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor,.highlightEditor),.textLayer) .editToolbar){ - transform-origin:0 0; - } - -:is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor,.highlightEditor),.textLayer) .editToolbar) .buttons{ - display:flex; - justify-content:center; - align-items:center; - gap:0; - height:100%; - } - -:is(:is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor,.highlightEditor),.textLayer) .editToolbar) .buttons) button{ - padding:0; - } - -:is(:is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor,.highlightEditor),.textLayer) .editToolbar) .buttons) .divider{ - width:0; - height:calc( - 2 * var(--editor-toolbar-padding) + var(--editor-toolbar-height) - ); - border-left:1px solid var(--editor-toolbar-border-color); - border-right:none; - display:inline-block; - margin-inline:2px; - } - -:is(:is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor,.highlightEditor),.textLayer) .editToolbar) .buttons) .highlightButton{ - width:var(--editor-toolbar-height); - } - -:is(:is(:is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor,.highlightEditor),.textLayer) .editToolbar) .buttons) .highlightButton)::before{ - content:""; - -webkit-mask-image:var(--editor-toolbar-highlight-image); - mask-image:var(--editor-toolbar-highlight-image); - -webkit-mask-repeat:no-repeat; - mask-repeat:no-repeat; - -webkit-mask-position:center; - mask-position:center; - display:inline-block; - background-color:var(--editor-toolbar-fg-color); - width:100%; - height:100%; - } - -:is(:is(:is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor,.highlightEditor),.textLayer) .editToolbar) .buttons) .highlightButton):hover::before{ - background-color:var(--editor-toolbar-hover-fg-color); - } - -:is(:is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor,.highlightEditor),.textLayer) .editToolbar) .buttons) .delete{ - width:var(--editor-toolbar-height); - } - -:is(:is(:is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor,.highlightEditor),.textLayer) .editToolbar) .buttons) .delete)::before{ - content:""; - -webkit-mask-image:var(--editor-toolbar-delete-image); - mask-image:var(--editor-toolbar-delete-image); - -webkit-mask-repeat:no-repeat; - mask-repeat:no-repeat; - -webkit-mask-position:center; - mask-position:center; - display:inline-block; - background-color:var(--editor-toolbar-fg-color); - width:100%; - height:100%; - } - -:is(:is(:is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor,.highlightEditor),.textLayer) .editToolbar) .buttons) .delete):hover::before{ - background-color:var(--editor-toolbar-hover-fg-color); - } - -:is(:is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor,.highlightEditor),.textLayer) .editToolbar) .buttons) > *{ - height:var(--editor-toolbar-height); - } - -:is(:is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor,.highlightEditor),.textLayer) .editToolbar) .buttons) > :not(.divider){ - border:none; - background-color:transparent; - cursor:pointer; - } - -:is(:is(:is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor,.highlightEditor),.textLayer) .editToolbar) .buttons) > :not(.divider)):hover{ - border-radius:2px; - background-color:var(--editor-toolbar-hover-bg-color); - color:var(--editor-toolbar-hover-fg-color); - outline:var(--editor-toolbar-hover-outline); - outline-offset:1px; - } - -:is(:is(:is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor,.highlightEditor),.textLayer) .editToolbar) .buttons) > :not(.divider)):hover:active{ - outline:none; - } - -:is(:is(:is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor,.highlightEditor),.textLayer) .editToolbar) .buttons) > :not(.divider)):focus-visible{ - border-radius:2px; - outline:2px solid var(--editor-toolbar-focus-outline-color); - } - -:is(:is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor,.highlightEditor),.textLayer) .editToolbar) .buttons) .altText{ - --alt-text-add-image:url(images/altText_add.svg); - --alt-text-done-image:url(images/altText_done.svg); - - display:flex; - align-items:center; - justify-content:center; - width:-moz-max-content; - width:max-content; - padding-inline:8px; - pointer-events:all; - font:menu; - font-weight:590; - font-size:12px; - color:var(--editor-toolbar-fg-color); - } - -:is(:is(:is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor,.highlightEditor),.textLayer) .editToolbar) .buttons) .altText):disabled{ - pointer-events:none; - } - -:is(:is(:is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor,.highlightEditor),.textLayer) .editToolbar) .buttons) .altText)::before{ - content:""; - -webkit-mask-image:var(--alt-text-add-image); - mask-image:var(--alt-text-add-image); - -webkit-mask-repeat:no-repeat; - mask-repeat:no-repeat; - -webkit-mask-position:center; - mask-position:center; - display:inline-block; - width:12px; - height:13px; - background-color:var(--editor-toolbar-fg-color); - margin-inline-end:4px; - } - -:is(:is(:is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor,.highlightEditor),.textLayer) .editToolbar) .buttons) .altText):hover::before{ - background-color:var(--editor-toolbar-hover-fg-color); - } - -.done:is(:is(:is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor,.highlightEditor),.textLayer) .editToolbar) .buttons) .altText)::before{ - -webkit-mask-image:var(--alt-text-done-image); - mask-image:var(--alt-text-done-image); - } - -.new:is(:is(:is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor,.highlightEditor),.textLayer) .editToolbar) .buttons) .altText)::before{ - width:16px; - height:16px; - -webkit-mask-image:var(--new-alt-text-warning-image); - mask-image:var(--new-alt-text-warning-image); - background-color:var(--alt-text-warning-color); - -webkit-mask-size:cover; - mask-size:cover; - } - -.new:is(:is(:is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor,.highlightEditor),.textLayer) .editToolbar) .buttons) .altText):hover::before{ - background-color:var(--alt-text-hover-warning-color); - } - -.new.done:is(:is(:is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor,.highlightEditor),.textLayer) .editToolbar) .buttons) .altText)::before{ - -webkit-mask-image:var(--alt-text-done-image); - mask-image:var(--alt-text-done-image); - background-color:var(--alt-text-done-color); - } - -.new.done:is(:is(:is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor,.highlightEditor),.textLayer) .editToolbar) .buttons) .altText):hover::before{ - background-color:var(--alt-text-hover-done-color); - } - -:is(:is(:is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor,.highlightEditor),.textLayer) .editToolbar) .buttons) .altText) .tooltip{ - display:none; - word-wrap:anywhere; - } - -.show:is(:is(:is(:is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor,.highlightEditor),.textLayer) .editToolbar) .buttons) .altText) .tooltip){ - --alt-text-tooltip-bg:#f0f0f4; - --alt-text-tooltip-fg:#15141a; - --alt-text-tooltip-border:#8f8f9d; - --alt-text-tooltip-shadow:0px 2px 6px 0px rgb(58 57 68 / 0.2); - } - -@media (prefers-color-scheme: dark){ - -:where(html:not(.is-light)) .show:is(:is(:is(:is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor,.highlightEditor),.textLayer) .editToolbar) .buttons) .altText) .tooltip){ - --alt-text-tooltip-bg:#1c1b22; - --alt-text-tooltip-fg:#fbfbfe; - --alt-text-tooltip-shadow:0px 2px 6px 0px #15141a; - } - } - -:where(html.is-dark) .show:is(:is(:is(:is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor,.highlightEditor),.textLayer) .editToolbar) .buttons) .altText) .tooltip){ - --alt-text-tooltip-bg:#1c1b22; - --alt-text-tooltip-fg:#fbfbfe; - --alt-text-tooltip-shadow:0px 2px 6px 0px #15141a; - } - -@media screen and (forced-colors: active){ - -.show:is(:is(:is(:is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor,.highlightEditor),.textLayer) .editToolbar) .buttons) .altText) .tooltip){ - --alt-text-tooltip-bg:Canvas; - --alt-text-tooltip-fg:CanvasText; - --alt-text-tooltip-border:CanvasText; - --alt-text-tooltip-shadow:none; - } - } - -.show:is(:is(:is(:is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor,.highlightEditor),.textLayer) .editToolbar) .buttons) .altText) .tooltip){ - - display:inline-flex; - flex-direction:column; - align-items:center; - justify-content:center; - position:absolute; - top:calc(100% + 2px); - inset-inline-start:0; - padding-block:2px 3px; - padding-inline:3px; - max-width:300px; - width:-moz-max-content; - width:max-content; - height:auto; - font-size:12px; - - border:0.5px solid var(--alt-text-tooltip-border); - background:var(--alt-text-tooltip-bg); - box-shadow:var(--alt-text-tooltip-shadow); - color:var(--alt-text-tooltip-fg); - - pointer-events:none; - } - -.annotationEditorLayer .freeTextEditor{ - padding:calc(var(--freetext-padding) * var(--scale-factor)); - width:auto; - height:auto; - touch-action:none; +.annotationEditorLayer .draw { + box-sizing: border-box; } -.annotationEditorLayer .freeTextEditor .internal{ - background:transparent; - border:none; - inset:0; - overflow:visible; - white-space:nowrap; - font:10px sans-serif; - line-height:var(--freetext-line-height); - -webkit-user-select:none; - -moz-user-select:none; - user-select:none; +.annotationEditorLayer :is(.freeTextEditor, .inkEditor, .stampEditor) { + position: absolute; + background: transparent; + z-index: 1; + transform-origin: 0 0; + cursor: auto; + max-width: 100%; + max-height: 100%; + border: var(--unfocus-outline); } -.annotationEditorLayer .freeTextEditor .overlay{ - position:absolute; - display:none; - background:transparent; - inset:0; - width:100%; - height:100%; +.draggable.selectedEditor:is( + .annotationEditorLayer :is(.freeTextEditor, .inkEditor, .stampEditor) + ) { + cursor: move; } -.annotationEditorLayer freeTextEditor .overlay.enabled{ - display:block; +.selectedEditor:is( + .annotationEditorLayer :is(.freeTextEditor, .inkEditor, .stampEditor) + ) { + border: var(--focus-outline); + outline: var(--focus-outline-around); } -.annotationEditorLayer .freeTextEditor .internal:empty::before{ - content:attr(default-content); - color:gray; +.selectedEditor:is( + .annotationEditorLayer :is(.freeTextEditor, .inkEditor, .stampEditor) + )::before { + content: ''; + position: absolute; + inset: 0; + border: var(--focus-outline-around); + pointer-events: none; } -.annotationEditorLayer .freeTextEditor .internal:focus{ - outline:none; - -webkit-user-select:auto; - -moz-user-select:auto; - user-select:auto; +:is( + .annotationEditorLayer :is(.freeTextEditor, .inkEditor, .stampEditor) + ):hover:not(.selectedEditor) { + border: var(--hover-outline); + outline: var(--hover-outline-around); } -.annotationEditorLayer .inkEditor{ - width:100%; - height:100%; +:is( + .annotationEditorLayer :is(.freeTextEditor, .inkEditor, .stampEditor) + ):hover:not(.selectedEditor)::before { + content: ''; + position: absolute; + inset: 0; + border: var(--focus-outline-around); } -.annotationEditorLayer .inkEditor.editing{ - cursor:inherit; +:is( + .annotationEditorLayer + :is(.freeTextEditor, .inkEditor, .stampEditor, .highlightEditor), + .textLayer + ) + .editToolbar { + --editor-toolbar-delete-image: url(images/editor-toolbar-delete.svg); + --editor-toolbar-bg-color: #f0f0f4; + --editor-toolbar-highlight-image: url(images/toolbarButton-editorHighlight.svg); + --editor-toolbar-fg-color: #2e2e56; + --editor-toolbar-border-color: #8f8f9d; + --editor-toolbar-hover-border-color: var(--editor-toolbar-border-color); + --editor-toolbar-hover-bg-color: #e0e0e6; + --editor-toolbar-hover-fg-color: var(--editor-toolbar-fg-color); + --editor-toolbar-hover-outline: none; + --editor-toolbar-focus-outline-color: #0060df; + --editor-toolbar-shadow: 0 2px 6px 0 rgb(58 57 68 / 0.2); + --editor-toolbar-vert-offset: 6px; + --editor-toolbar-height: 28px; + --editor-toolbar-padding: 2px; + --alt-text-done-color: #2ac3a2; + --alt-text-warning-color: #0090ed; + --alt-text-hover-done-color: var(--alt-text-done-color); + --alt-text-hover-warning-color: var(--alt-text-warning-color); } -.annotationEditorLayer .inkEditor .inkEditorCanvas{ - position:absolute; - inset:0; - width:100%; - height:100%; - touch-action:none; -} - -.annotationEditorLayer .stampEditor{ - width:auto; - height:auto; -} - -:is(.annotationEditorLayer .stampEditor) canvas{ - position:absolute; - width:100%; - height:100%; - margin:0; - top:0; - left:0; - } - -:is(.annotationEditorLayer .stampEditor) .noAltTextBadge{ - --no-alt-text-badge-border-color:#f0f0f4; - --no-alt-text-badge-bg-color:#cfcfd8; - --no-alt-text-badge-fg-color:#5b5b66; - } - -@media (prefers-color-scheme: dark){ - -:where(html:not(.is-light)) :is(.annotationEditorLayer .stampEditor) .noAltTextBadge{ - --no-alt-text-badge-border-color:#52525e; - --no-alt-text-badge-bg-color:#fbfbfe; - --no-alt-text-badge-fg-color:#15141a; - } - } - -:where(html.is-dark) :is(.annotationEditorLayer .stampEditor) .noAltTextBadge{ - --no-alt-text-badge-border-color:#52525e; - --no-alt-text-badge-bg-color:#fbfbfe; - --no-alt-text-badge-fg-color:#15141a; - } - -@media screen and (forced-colors: active){ - -:is(.annotationEditorLayer .stampEditor) .noAltTextBadge{ - --no-alt-text-badge-border-color:ButtonText; - --no-alt-text-badge-bg-color:ButtonFace; - --no-alt-text-badge-fg-color:ButtonText; - } - } - -:is(.annotationEditorLayer .stampEditor) .noAltTextBadge{ - - position:absolute; - inset-inline-end:5px; - inset-block-end:5px; - display:inline-flex; - width:32px; - height:32px; - padding:3px; - justify-content:center; - align-items:center; - pointer-events:none; - z-index:1; - - border-radius:2px; - border:1px solid var(--no-alt-text-badge-border-color); - background:var(--no-alt-text-badge-bg-color); - } - -:is(:is(.annotationEditorLayer .stampEditor) .noAltTextBadge)::before{ - content:""; - display:inline-block; - width:16px; - height:16px; - -webkit-mask-image:var(--new-alt-text-warning-image); - mask-image:var(--new-alt-text-warning-image); - -webkit-mask-size:cover; - mask-size:cover; - background-color:var(--no-alt-text-badge-fg-color); - } - -:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor)) > .resizers{ - position:absolute; - inset:0; - } - -.hidden:is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor)) > .resizers){ - display:none; - } - -:is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor)) > .resizers) > .resizer{ - width:var(--resizer-size); - height:var(--resizer-size); - background:content-box var(--resizer-bg-color); - border:var(--focus-outline-around); - border-radius:2px; - position:absolute; - } - -.topLeft:is(:is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor)) > .resizers) > .resizer){ - top:var(--resizer-shift); - left:var(--resizer-shift); - } - -.topMiddle:is(:is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor)) > .resizers) > .resizer){ - top:var(--resizer-shift); - left:calc(50% + var(--resizer-shift)); - } - -.topRight:is(:is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor)) > .resizers) > .resizer){ - top:var(--resizer-shift); - right:var(--resizer-shift); - } - -.middleRight:is(:is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor)) > .resizers) > .resizer){ - top:calc(50% + var(--resizer-shift)); - right:var(--resizer-shift); - } - -.bottomRight:is(:is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor)) > .resizers) > .resizer){ - bottom:var(--resizer-shift); - right:var(--resizer-shift); - } - -.bottomMiddle:is(:is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor)) > .resizers) > .resizer){ - bottom:var(--resizer-shift); - left:calc(50% + var(--resizer-shift)); - } - -.bottomLeft:is(:is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor)) > .resizers) > .resizer){ - bottom:var(--resizer-shift); - left:var(--resizer-shift); - } - -.middleLeft:is(:is(:is(.annotationEditorLayer :is(.freeTextEditor,.inkEditor,.stampEditor)) > .resizers) > .resizer){ - top:calc(50% + var(--resizer-shift)); - left:var(--resizer-shift); - } - -.topLeft:is(:is(.annotationEditorLayer[data-main-rotation="0"] :is([data-editor-rotation="0"],[data-editor-rotation="180"]),.annotationEditorLayer[data-main-rotation="90"] :is([data-editor-rotation="270"],[data-editor-rotation="90"]),.annotationEditorLayer[data-main-rotation="180"] :is([data-editor-rotation="180"],[data-editor-rotation="0"]),.annotationEditorLayer[data-main-rotation="270"] :is([data-editor-rotation="90"],[data-editor-rotation="270"])) > .resizers > .resizer),.bottomRight:is(:is(.annotationEditorLayer[data-main-rotation="0"] :is([data-editor-rotation="0"],[data-editor-rotation="180"]),.annotationEditorLayer[data-main-rotation="90"] :is([data-editor-rotation="270"],[data-editor-rotation="90"]),.annotationEditorLayer[data-main-rotation="180"] :is([data-editor-rotation="180"],[data-editor-rotation="0"]),.annotationEditorLayer[data-main-rotation="270"] :is([data-editor-rotation="90"],[data-editor-rotation="270"])) > .resizers > .resizer){ - cursor:nwse-resize; - } - -.topMiddle:is(:is(.annotationEditorLayer[data-main-rotation="0"] :is([data-editor-rotation="0"],[data-editor-rotation="180"]),.annotationEditorLayer[data-main-rotation="90"] :is([data-editor-rotation="270"],[data-editor-rotation="90"]),.annotationEditorLayer[data-main-rotation="180"] :is([data-editor-rotation="180"],[data-editor-rotation="0"]),.annotationEditorLayer[data-main-rotation="270"] :is([data-editor-rotation="90"],[data-editor-rotation="270"])) > .resizers > .resizer),.bottomMiddle:is(:is(.annotationEditorLayer[data-main-rotation="0"] :is([data-editor-rotation="0"],[data-editor-rotation="180"]),.annotationEditorLayer[data-main-rotation="90"] :is([data-editor-rotation="270"],[data-editor-rotation="90"]),.annotationEditorLayer[data-main-rotation="180"] :is([data-editor-rotation="180"],[data-editor-rotation="0"]),.annotationEditorLayer[data-main-rotation="270"] :is([data-editor-rotation="90"],[data-editor-rotation="270"])) > .resizers > .resizer){ - cursor:ns-resize; - } - -.topRight:is(:is(.annotationEditorLayer[data-main-rotation="0"] :is([data-editor-rotation="0"],[data-editor-rotation="180"]),.annotationEditorLayer[data-main-rotation="90"] :is([data-editor-rotation="270"],[data-editor-rotation="90"]),.annotationEditorLayer[data-main-rotation="180"] :is([data-editor-rotation="180"],[data-editor-rotation="0"]),.annotationEditorLayer[data-main-rotation="270"] :is([data-editor-rotation="90"],[data-editor-rotation="270"])) > .resizers > .resizer),.bottomLeft:is(:is(.annotationEditorLayer[data-main-rotation="0"] :is([data-editor-rotation="0"],[data-editor-rotation="180"]),.annotationEditorLayer[data-main-rotation="90"] :is([data-editor-rotation="270"],[data-editor-rotation="90"]),.annotationEditorLayer[data-main-rotation="180"] :is([data-editor-rotation="180"],[data-editor-rotation="0"]),.annotationEditorLayer[data-main-rotation="270"] :is([data-editor-rotation="90"],[data-editor-rotation="270"])) > .resizers > .resizer){ - cursor:nesw-resize; - } - -.middleRight:is(:is(.annotationEditorLayer[data-main-rotation="0"] :is([data-editor-rotation="0"],[data-editor-rotation="180"]),.annotationEditorLayer[data-main-rotation="90"] :is([data-editor-rotation="270"],[data-editor-rotation="90"]),.annotationEditorLayer[data-main-rotation="180"] :is([data-editor-rotation="180"],[data-editor-rotation="0"]),.annotationEditorLayer[data-main-rotation="270"] :is([data-editor-rotation="90"],[data-editor-rotation="270"])) > .resizers > .resizer),.middleLeft:is(:is(.annotationEditorLayer[data-main-rotation="0"] :is([data-editor-rotation="0"],[data-editor-rotation="180"]),.annotationEditorLayer[data-main-rotation="90"] :is([data-editor-rotation="270"],[data-editor-rotation="90"]),.annotationEditorLayer[data-main-rotation="180"] :is([data-editor-rotation="180"],[data-editor-rotation="0"]),.annotationEditorLayer[data-main-rotation="270"] :is([data-editor-rotation="90"],[data-editor-rotation="270"])) > .resizers > .resizer){ - cursor:ew-resize; - } - -.topLeft:is(:is(.annotationEditorLayer[data-main-rotation="0"] :is([data-editor-rotation="90"],[data-editor-rotation="270"]),.annotationEditorLayer[data-main-rotation="90"] :is([data-editor-rotation="0"],[data-editor-rotation="180"]),.annotationEditorLayer[data-main-rotation="180"] :is([data-editor-rotation="270"],[data-editor-rotation="90"]),.annotationEditorLayer[data-main-rotation="270"] :is([data-editor-rotation="180"],[data-editor-rotation="0"])) > .resizers > .resizer),.bottomRight:is(:is(.annotationEditorLayer[data-main-rotation="0"] :is([data-editor-rotation="90"],[data-editor-rotation="270"]),.annotationEditorLayer[data-main-rotation="90"] :is([data-editor-rotation="0"],[data-editor-rotation="180"]),.annotationEditorLayer[data-main-rotation="180"] :is([data-editor-rotation="270"],[data-editor-rotation="90"]),.annotationEditorLayer[data-main-rotation="270"] :is([data-editor-rotation="180"],[data-editor-rotation="0"])) > .resizers > .resizer){ - cursor:nesw-resize; - } - -.topMiddle:is(:is(.annotationEditorLayer[data-main-rotation="0"] :is([data-editor-rotation="90"],[data-editor-rotation="270"]),.annotationEditorLayer[data-main-rotation="90"] :is([data-editor-rotation="0"],[data-editor-rotation="180"]),.annotationEditorLayer[data-main-rotation="180"] :is([data-editor-rotation="270"],[data-editor-rotation="90"]),.annotationEditorLayer[data-main-rotation="270"] :is([data-editor-rotation="180"],[data-editor-rotation="0"])) > .resizers > .resizer),.bottomMiddle:is(:is(.annotationEditorLayer[data-main-rotation="0"] :is([data-editor-rotation="90"],[data-editor-rotation="270"]),.annotationEditorLayer[data-main-rotation="90"] :is([data-editor-rotation="0"],[data-editor-rotation="180"]),.annotationEditorLayer[data-main-rotation="180"] :is([data-editor-rotation="270"],[data-editor-rotation="90"]),.annotationEditorLayer[data-main-rotation="270"] :is([data-editor-rotation="180"],[data-editor-rotation="0"])) > .resizers > .resizer){ - cursor:ew-resize; - } - -.topRight:is(:is(.annotationEditorLayer[data-main-rotation="0"] :is([data-editor-rotation="90"],[data-editor-rotation="270"]),.annotationEditorLayer[data-main-rotation="90"] :is([data-editor-rotation="0"],[data-editor-rotation="180"]),.annotationEditorLayer[data-main-rotation="180"] :is([data-editor-rotation="270"],[data-editor-rotation="90"]),.annotationEditorLayer[data-main-rotation="270"] :is([data-editor-rotation="180"],[data-editor-rotation="0"])) > .resizers > .resizer),.bottomLeft:is(:is(.annotationEditorLayer[data-main-rotation="0"] :is([data-editor-rotation="90"],[data-editor-rotation="270"]),.annotationEditorLayer[data-main-rotation="90"] :is([data-editor-rotation="0"],[data-editor-rotation="180"]),.annotationEditorLayer[data-main-rotation="180"] :is([data-editor-rotation="270"],[data-editor-rotation="90"]),.annotationEditorLayer[data-main-rotation="270"] :is([data-editor-rotation="180"],[data-editor-rotation="0"])) > .resizers > .resizer){ - cursor:nwse-resize; - } - -.middleRight:is(:is(.annotationEditorLayer[data-main-rotation="0"] :is([data-editor-rotation="90"],[data-editor-rotation="270"]),.annotationEditorLayer[data-main-rotation="90"] :is([data-editor-rotation="0"],[data-editor-rotation="180"]),.annotationEditorLayer[data-main-rotation="180"] :is([data-editor-rotation="270"],[data-editor-rotation="90"]),.annotationEditorLayer[data-main-rotation="270"] :is([data-editor-rotation="180"],[data-editor-rotation="0"])) > .resizers > .resizer),.middleLeft:is(:is(.annotationEditorLayer[data-main-rotation="0"] :is([data-editor-rotation="90"],[data-editor-rotation="270"]),.annotationEditorLayer[data-main-rotation="90"] :is([data-editor-rotation="0"],[data-editor-rotation="180"]),.annotationEditorLayer[data-main-rotation="180"] :is([data-editor-rotation="270"],[data-editor-rotation="90"]),.annotationEditorLayer[data-main-rotation="270"] :is([data-editor-rotation="180"],[data-editor-rotation="0"])) > .resizers > .resizer){ - cursor:ns-resize; - } - -:is(.annotationEditorLayer :is([data-main-rotation="0"] [data-editor-rotation="90"],[data-main-rotation="90"] [data-editor-rotation="0"],[data-main-rotation="180"] [data-editor-rotation="270"],[data-main-rotation="270"] [data-editor-rotation="180"])) .editToolbar{ - rotate:270deg; - } - -[dir="ltr"] :is(:is(.annotationEditorLayer :is([data-main-rotation="0"] [data-editor-rotation="90"],[data-main-rotation="90"] [data-editor-rotation="0"],[data-main-rotation="180"] [data-editor-rotation="270"],[data-main-rotation="270"] [data-editor-rotation="180"])) .editToolbar){ - inset-inline-end:calc(0px - var(--editor-toolbar-vert-offset)); - inset-block-start:0; - } - -[dir="rtl"] :is(:is(.annotationEditorLayer :is([data-main-rotation="0"] [data-editor-rotation="90"],[data-main-rotation="90"] [data-editor-rotation="0"],[data-main-rotation="180"] [data-editor-rotation="270"],[data-main-rotation="270"] [data-editor-rotation="180"])) .editToolbar){ - inset-inline-end:calc(100% + var(--editor-toolbar-vert-offset)); - inset-block-start:0; - } - -:is(.annotationEditorLayer :is([data-main-rotation="0"] [data-editor-rotation="180"],[data-main-rotation="90"] [data-editor-rotation="90"],[data-main-rotation="180"] [data-editor-rotation="0"],[data-main-rotation="270"] [data-editor-rotation="270"])) .editToolbar{ - rotate:180deg; - inset-inline-end:100%; - inset-block-start:calc(0pc - var(--editor-toolbar-vert-offset)); - } - -:is(.annotationEditorLayer :is([data-main-rotation="0"] [data-editor-rotation="270"],[data-main-rotation="90"] [data-editor-rotation="180"],[data-main-rotation="180"] [data-editor-rotation="90"],[data-main-rotation="270"] [data-editor-rotation="0"])) .editToolbar{ - rotate:90deg; - } - -[dir="ltr"] :is(:is(.annotationEditorLayer :is([data-main-rotation="0"] [data-editor-rotation="270"],[data-main-rotation="90"] [data-editor-rotation="180"],[data-main-rotation="180"] [data-editor-rotation="90"],[data-main-rotation="270"] [data-editor-rotation="0"])) .editToolbar){ - inset-inline-end:calc(100% + var(--editor-toolbar-vert-offset)); - inset-block-start:100%; - } - -[dir="rtl"] :is(:is(.annotationEditorLayer :is([data-main-rotation="0"] [data-editor-rotation="270"],[data-main-rotation="90"] [data-editor-rotation="180"],[data-main-rotation="180"] [data-editor-rotation="90"],[data-main-rotation="270"] [data-editor-rotation="0"])) .editToolbar){ - inset-inline-start:calc(0px - var(--editor-toolbar-vert-offset)); - inset-block-start:0; - } - -.dialog.altText::backdrop{ - -webkit-mask:url(#alttext-manager-mask); - mask:url(#alttext-manager-mask); - } - -.dialog.altText.positioned{ - margin:0; - } - -.dialog.altText #altTextContainer{ - width:300px; - height:-moz-fit-content; - height:fit-content; - display:inline-flex; - flex-direction:column; - align-items:flex-start; - gap:16px; - } - -:is(.dialog.altText #altTextContainer) #overallDescription{ - display:flex; - flex-direction:column; - align-items:flex-start; - gap:4px; - align-self:stretch; - } - -:is(:is(.dialog.altText #altTextContainer) #overallDescription) span{ - align-self:stretch; - } - -:is(:is(.dialog.altText #altTextContainer) #overallDescription) .title{ - font-size:13px; - font-style:normal; - font-weight:590; - } - -:is(.dialog.altText #altTextContainer) #addDescription{ - display:flex; - flex-direction:column; - align-items:stretch; - gap:8px; - } - -:is(:is(.dialog.altText #altTextContainer) #addDescription) .descriptionArea{ - flex:1; - padding-inline:24px 10px; - } - -:is(:is(:is(.dialog.altText #altTextContainer) #addDescription) .descriptionArea) textarea{ - width:100%; - min-height:75px; - } - -:is(.dialog.altText #altTextContainer) #buttons{ - display:flex; - justify-content:flex-end; - align-items:flex-start; - gap:8px; - align-self:stretch; - } - -.dialog.newAltText{ - --new-alt-text-ai-disclaimer-icon:url(images/altText_disclaimer.svg); - --new-alt-text-spinner-icon:url(images/altText_spinner.svg); - --preview-image-bg-color:#f0f0f4; - --preview-image-border:none; -} - -@media (prefers-color-scheme: dark){ - -:where(html:not(.is-light)) .dialog.newAltText{ - --preview-image-bg-color:#2b2a33; -} - } - -:where(html.is-dark) .dialog.newAltText{ - --preview-image-bg-color:#2b2a33; -} - -@media screen and (forced-colors: active){ - -.dialog.newAltText{ - --preview-image-bg-color:ButtonFace; - --preview-image-border:1px solid ButtonText; -} - } - -.dialog.newAltText{ - - width:80%; - max-width:570px; - min-width:300px; - padding:0; -} - -.dialog.newAltText.noAi #newAltTextDisclaimer,.dialog.newAltText.noAi #newAltTextCreateAutomatically{ - display:none !important; - } - -.dialog.newAltText.aiInstalling #newAltTextCreateAutomatically{ - display:none !important; - } - -.dialog.newAltText.aiInstalling #newAltTextDownloadModel{ - display:flex !important; - } - -.dialog.newAltText.error #newAltTextNotNow{ - display:none !important; - } - -.dialog.newAltText.error #newAltTextCancel{ - display:inline-block !important; - } - -.dialog.newAltText:not(.error) #newAltTextError{ - display:none !important; - } - -.dialog.newAltText #newAltTextContainer{ - display:flex; - width:auto; - padding:16px; - flex-direction:column; - justify-content:flex-end; - align-items:flex-start; - gap:12px; - flex:0 1 auto; - line-height:normal; - } - -:is(.dialog.newAltText #newAltTextContainer) #mainContent{ - display:flex; - justify-content:flex-end; - align-items:flex-start; - gap:12px; - align-self:stretch; - flex:1 1 auto; - } - -:is(:is(.dialog.newAltText #newAltTextContainer) #mainContent) #descriptionAndSettings{ - display:flex; - flex-direction:column; - align-items:flex-start; - gap:16px; - flex:1 0 0; - align-self:stretch; - } - -:is(:is(.dialog.newAltText #newAltTextContainer) #mainContent) #descriptionInstruction{ - display:flex; - flex-direction:column; - align-items:flex-start; - gap:8px; - align-self:stretch; - flex:1 1 auto; - } - -:is(:is(:is(.dialog.newAltText #newAltTextContainer) #mainContent) #descriptionInstruction) #newAltTextDescriptionContainer{ - width:100%; - height:70px; - position:relative; - } - -:is(:is(:is(:is(.dialog.newAltText #newAltTextContainer) #mainContent) #descriptionInstruction) #newAltTextDescriptionContainer) textarea{ - width:100%; - height:100%; - padding:8px; - } - -:is(:is(:is(:is(:is(.dialog.newAltText #newAltTextContainer) #mainContent) #descriptionInstruction) #newAltTextDescriptionContainer) textarea)::-moz-placeholder{ - color:var(--text-secondary-color); - } - -:is(:is(:is(:is(:is(.dialog.newAltText #newAltTextContainer) #mainContent) #descriptionInstruction) #newAltTextDescriptionContainer) textarea)::placeholder{ - color:var(--text-secondary-color); - } - -:is(:is(:is(:is(.dialog.newAltText #newAltTextContainer) #mainContent) #descriptionInstruction) #newAltTextDescriptionContainer) .altTextSpinner{ - display:none; - position:absolute; - width:16px; - height:16px; - inset-inline-start:8px; - inset-block-start:8px; - -webkit-mask-size:cover; - mask-size:cover; - background-color:var(--text-secondary-color); - pointer-events:none; - } - -.loading:is(:is(:is(:is(.dialog.newAltText #newAltTextContainer) #mainContent) #descriptionInstruction) #newAltTextDescriptionContainer) textarea::-moz-placeholder{ - color:transparent; - } - -.loading:is(:is(:is(:is(.dialog.newAltText #newAltTextContainer) #mainContent) #descriptionInstruction) #newAltTextDescriptionContainer) textarea::placeholder{ - color:transparent; - } - -.loading:is(:is(:is(:is(.dialog.newAltText #newAltTextContainer) #mainContent) #descriptionInstruction) #newAltTextDescriptionContainer) .altTextSpinner{ - display:inline-block; - -webkit-mask-image:var(--new-alt-text-spinner-icon); - mask-image:var(--new-alt-text-spinner-icon); - } - -:is(:is(:is(.dialog.newAltText #newAltTextContainer) #mainContent) #descriptionInstruction) #newAltTextDescription{ - font-size:11px; - } - -:is(:is(:is(.dialog.newAltText #newAltTextContainer) #mainContent) #descriptionInstruction) #newAltTextDisclaimer{ - display:flex; - flex-direction:row; - align-items:flex-start; - gap:4px; - font-size:11px; - } - -:is(:is(:is(:is(.dialog.newAltText #newAltTextContainer) #mainContent) #descriptionInstruction) #newAltTextDisclaimer)::before{ - content:""; - display:inline-block; - width:17px; - height:16px; - -webkit-mask-image:var(--new-alt-text-ai-disclaimer-icon); - mask-image:var(--new-alt-text-ai-disclaimer-icon); - -webkit-mask-size:cover; - mask-size:cover; - background-color:var(--text-secondary-color); - flex:1 0 auto; - } - -:is(:is(.dialog.newAltText #newAltTextContainer) #mainContent) #newAltTextDownloadModel{ - display:flex; - align-items:center; - gap:4px; - align-self:stretch; - } - -:is(:is(:is(.dialog.newAltText #newAltTextContainer) #mainContent) #newAltTextDownloadModel)::before{ - content:""; - display:inline-block; - width:16px; - height:16px; - -webkit-mask-image:var(--new-alt-text-spinner-icon); - mask-image:var(--new-alt-text-spinner-icon); - -webkit-mask-size:cover; - mask-size:cover; - background-color:var(--text-secondary-color); - } - -:is(:is(.dialog.newAltText #newAltTextContainer) #mainContent) #newAltTextImagePreview{ - width:180px; - aspect-ratio:1; - display:flex; - justify-content:center; - align-items:center; - flex:0 0 auto; - background-color:var(--preview-image-bg-color); - border:var(--preview-image-border); - } - -:is(:is(:is(.dialog.newAltText #newAltTextContainer) #mainContent) #newAltTextImagePreview) > canvas{ - max-width:100%; - max-height:100%; - } - -.colorPicker{ - --hover-outline-color:#0250bb; - --selected-outline-color:#0060df; - --swatch-border-color:#cfcfd8; -} - -@media (prefers-color-scheme: dark){ - -:where(html:not(.is-light)) .colorPicker{ - --hover-outline-color:#80ebff; - --selected-outline-color:#aaf2ff; - --swatch-border-color:#52525e; -} - } - -:where(html.is-dark) .colorPicker{ - --hover-outline-color:#80ebff; - --selected-outline-color:#aaf2ff; - --swatch-border-color:#52525e; -} - -@media screen and (forced-colors: active){ - -.colorPicker{ - --hover-outline-color:Highlight; - --selected-outline-color:var(--hover-outline-color); - --swatch-border-color:ButtonText; -} - } - -.colorPicker .swatch{ - width:16px; - height:16px; - border:1px solid var(--swatch-border-color); - border-radius:100%; - outline-offset:2px; - box-sizing:border-box; - forced-color-adjust:none; - } - -.colorPicker button:is(:hover,.selected) > .swatch{ - border:none; - } - -.annotationEditorLayer[data-main-rotation="0"] .highlightEditor:not(.free) > .editToolbar{ - rotate:0deg; - } - -.annotationEditorLayer[data-main-rotation="90"] .highlightEditor:not(.free) > .editToolbar{ - rotate:270deg; - } - -.annotationEditorLayer[data-main-rotation="180"] .highlightEditor:not(.free) > .editToolbar{ - rotate:180deg; - } - -.annotationEditorLayer[data-main-rotation="270"] .highlightEditor:not(.free) > .editToolbar{ - rotate:90deg; - } - -.annotationEditorLayer .highlightEditor{ - position:absolute; - background:transparent; - z-index:1; - cursor:auto; - max-width:100%; - max-height:100%; - border:none; - outline:none; - pointer-events:none; - transform-origin:0 0; - } - -:is(.annotationEditorLayer .highlightEditor):not(.free){ - transform:none; - } - -:is(.annotationEditorLayer .highlightEditor) .internal{ - position:absolute; - top:0; - left:0; - width:100%; - height:100%; - pointer-events:auto; - } - -.disabled:is(.annotationEditorLayer .highlightEditor) .internal{ - pointer-events:none; - } - -.selectedEditor:is(.annotationEditorLayer .highlightEditor) .internal{ - cursor:pointer; - } - -:is(.annotationEditorLayer .highlightEditor) .editToolbar{ - --editor-toolbar-colorpicker-arrow-image:url(images/toolbarButton-menuArrow.svg); - - transform-origin:center !important; - } - -:is(:is(:is(.annotationEditorLayer .highlightEditor) .editToolbar) .buttons) .colorPicker{ - position:relative; - width:auto; - display:flex; - justify-content:center; - align-items:center; - gap:4px; - padding:4px; - } - -:is(:is(:is(:is(.annotationEditorLayer .highlightEditor) .editToolbar) .buttons) .colorPicker)::after{ - content:""; - -webkit-mask-image:var(--editor-toolbar-colorpicker-arrow-image); - mask-image:var(--editor-toolbar-colorpicker-arrow-image); - -webkit-mask-repeat:no-repeat; - mask-repeat:no-repeat; - -webkit-mask-position:center; - mask-position:center; - display:inline-block; - background-color:var(--editor-toolbar-fg-color); - width:12px; - height:12px; - } - -:is(:is(:is(:is(.annotationEditorLayer .highlightEditor) .editToolbar) .buttons) .colorPicker):hover::after{ - background-color:var(--editor-toolbar-hover-fg-color); - } - -:is(:is(:is(:is(.annotationEditorLayer .highlightEditor) .editToolbar) .buttons) .colorPicker):has(.dropdown:not(.hidden)){ - background-color:var(--editor-toolbar-hover-bg-color); - } - -:is(:is(:is(:is(.annotationEditorLayer .highlightEditor) .editToolbar) .buttons) .colorPicker):has(.dropdown:not(.hidden))::after{ - scale:-1; - } - -:is(:is(:is(:is(.annotationEditorLayer .highlightEditor) .editToolbar) .buttons) .colorPicker) .dropdown{ - position:absolute; - display:flex; - justify-content:center; - align-items:center; - flex-direction:column; - gap:11px; - padding-block:8px; - border-radius:6px; - background-color:var(--editor-toolbar-bg-color); - border:1px solid var(--editor-toolbar-border-color); - box-shadow:var(--editor-toolbar-shadow); - inset-block-start:calc(100% + 4px); - width:calc(100% + 2 * var(--editor-toolbar-padding)); - } - -:is(:is(:is(:is(:is(.annotationEditorLayer .highlightEditor) .editToolbar) .buttons) .colorPicker) .dropdown) button{ - width:100%; - height:auto; - border:none; - cursor:pointer; - display:flex; - justify-content:center; - align-items:center; - background:none; - } - -:is(:is(:is(:is(:is(:is(.annotationEditorLayer .highlightEditor) .editToolbar) .buttons) .colorPicker) .dropdown) button):is(:active,:focus-visible){ - outline:none; - } - -:is(:is(:is(:is(:is(:is(.annotationEditorLayer .highlightEditor) .editToolbar) .buttons) .colorPicker) .dropdown) button) > .swatch{ - outline-offset:2px; - } - -[aria-selected="true"]:is(:is(:is(:is(:is(:is(.annotationEditorLayer .highlightEditor) .editToolbar) .buttons) .colorPicker) .dropdown) button) > .swatch{ - outline:2px solid var(--selected-outline-color); - } - -:is(:is(:is(:is(:is(:is(.annotationEditorLayer .highlightEditor) .editToolbar) .buttons) .colorPicker) .dropdown) button):is(:hover,:active,:focus-visible) > .swatch{ - outline:2px solid var(--hover-outline-color); - } - -.editorParamsToolbar:has(#highlightParamsToolbarContainer){ - padding:unset; -} - -#highlightParamsToolbarContainer{ - gap:16px; - padding-inline:10px; - padding-block-end:12px; -} - -#highlightParamsToolbarContainer .colorPicker{ - display:flex; - flex-direction:column; - gap:8px; - } - -:is(#highlightParamsToolbarContainer .colorPicker) .dropdown{ - display:flex; - justify-content:space-between; - align-items:center; - flex-direction:row; - height:auto; - } - -:is(:is(#highlightParamsToolbarContainer .colorPicker) .dropdown) button{ - width:auto; - height:auto; - border:none; - cursor:pointer; - display:flex; - justify-content:center; - align-items:center; - background:none; - flex:0 0 auto; - padding:0; - } - -:is(:is(:is(#highlightParamsToolbarContainer .colorPicker) .dropdown) button) .swatch{ - width:24px; - height:24px; - } - -:is(:is(:is(#highlightParamsToolbarContainer .colorPicker) .dropdown) button):is(:active,:focus-visible){ - outline:none; - } - -[aria-selected="true"]:is(:is(:is(#highlightParamsToolbarContainer .colorPicker) .dropdown) button) > .swatch{ - outline:2px solid var(--selected-outline-color); - } - -:is(:is(:is(#highlightParamsToolbarContainer .colorPicker) .dropdown) button):is(:hover,:active,:focus-visible) > .swatch{ - outline:2px solid var(--hover-outline-color); - } - -#highlightParamsToolbarContainer #editorHighlightThickness{ - display:flex; - flex-direction:column; - align-items:center; - gap:4px; - align-self:stretch; - } - -:is(#highlightParamsToolbarContainer #editorHighlightThickness) .editorParamsLabel{ - height:auto; - align-self:stretch; - } - -:is(#highlightParamsToolbarContainer #editorHighlightThickness) .thicknessPicker{ - display:flex; - justify-content:space-between; - align-items:center; - align-self:stretch; - - --example-color:#bfbfc9; - } - -@media (prefers-color-scheme: dark){ - -:where(html:not(.is-light)) :is(#highlightParamsToolbarContainer #editorHighlightThickness) .thicknessPicker{ - --example-color:#80808e; - } - } - -:where(html.is-dark) :is(#highlightParamsToolbarContainer #editorHighlightThickness) .thicknessPicker{ - --example-color:#80808e; - } - -@media screen and (forced-colors: active){ - -:is(#highlightParamsToolbarContainer #editorHighlightThickness) .thicknessPicker{ - --example-color:CanvasText; - } - } - -:is(:is(:is(#highlightParamsToolbarContainer #editorHighlightThickness) .thicknessPicker) > .editorParamsSlider[disabled]){ - opacity:0.4; - } - -:is(:is(#highlightParamsToolbarContainer #editorHighlightThickness) .thicknessPicker)::before,:is(:is(#highlightParamsToolbarContainer #editorHighlightThickness) .thicknessPicker)::after{ - content:""; - width:8px; - aspect-ratio:1; - display:block; - border-radius:100%; - background-color:var(--example-color); - } - -:is(:is(#highlightParamsToolbarContainer #editorHighlightThickness) .thicknessPicker)::after{ - width:24px; - } - -:is(:is(#highlightParamsToolbarContainer #editorHighlightThickness) .thicknessPicker) .editorParamsSlider{ - width:unset; - height:14px; - } - -#highlightParamsToolbarContainer #editorHighlightVisibility{ - display:flex; - flex-direction:column; - align-items:flex-start; - gap:8px; - align-self:stretch; - } - -:is(#highlightParamsToolbarContainer #editorHighlightVisibility) .divider{ - --divider-color:#d7d7db; - } - -@media (prefers-color-scheme: dark){ - -:where(html:not(.is-light)) :is(#highlightParamsToolbarContainer #editorHighlightVisibility) .divider{ - --divider-color:#8f8f9d; - } - } - -:where(html.is-dark) :is(#highlightParamsToolbarContainer #editorHighlightVisibility) .divider{ - --divider-color:#8f8f9d; - } - -@media screen and (forced-colors: active){ - -:is(#highlightParamsToolbarContainer #editorHighlightVisibility) .divider{ - --divider-color:CanvasText; - } - } - -:is(#highlightParamsToolbarContainer #editorHighlightVisibility) .divider{ - - margin-block:4px; - width:100%; - height:1px; - background-color:var(--divider-color); - } - -:is(#highlightParamsToolbarContainer #editorHighlightVisibility) .toggler{ - display:flex; - justify-content:space-between; - align-items:center; - align-self:stretch; - } - -#altTextSettingsDialog{ - padding:16px; -} - -#altTextSettingsDialog #altTextSettingsContainer{ - display:flex; - width:573px; - flex-direction:column; - gap:16px; - } - -:is(#altTextSettingsDialog #altTextSettingsContainer) .mainContainer{ - gap:16px; - } - -:is(#altTextSettingsDialog #altTextSettingsContainer) .description{ - color:var(--text-secondary-color); - } - -:is(#altTextSettingsDialog #altTextSettingsContainer) #aiModelSettings{ - display:flex; - flex-direction:column; - gap:12px; - } - -:is(:is(#altTextSettingsDialog #altTextSettingsContainer) #aiModelSettings) button{ - width:-moz-fit-content; - width:fit-content; - } - -.download:is(:is(#altTextSettingsDialog #altTextSettingsContainer) #aiModelSettings) #deleteModelButton{ - display:none; - } - -:is(:is(#altTextSettingsDialog #altTextSettingsContainer) #aiModelSettings):not(.download) #downloadModelButton{ - display:none; - } - -:is(#altTextSettingsDialog #altTextSettingsContainer) #automaticAltText,:is(#altTextSettingsDialog #altTextSettingsContainer) #altTextEditor{ - display:flex; - flex-direction:column; - gap:8px; - } - -:is(#altTextSettingsDialog #altTextSettingsContainer) #createModelDescription,:is(#altTextSettingsDialog #altTextSettingsContainer) #aiModelSettings,:is(#altTextSettingsDialog #altTextSettingsContainer) #showAltTextDialogDescription{ - padding-inline-start:40px; - } - -:is(#altTextSettingsDialog #altTextSettingsContainer) #automaticSettings{ - display:flex; - flex-direction:column; - gap:16px; - } - -:root{ - --viewer-container-height:0; - --pdfViewer-padding-bottom:0; - --page-margin:1px auto -8px; - --page-border:9px solid transparent; - --spreadHorizontalWrapped-margin-LR:-3.5px; - --loading-icon-delay:400ms; -} - -@media screen and (forced-colors: active){ - :root{ - --pdfViewer-padding-bottom:9px; - --page-margin:8px auto -1px; - --page-border:1px solid CanvasText; - --spreadHorizontalWrapped-margin-LR:3.5px; +@media (prefers-color-scheme: dark) { + :where(html:not(.is-light)) + :is( + .annotationEditorLayer + :is(.freeTextEditor, .inkEditor, .stampEditor, .highlightEditor), + .textLayer + ) + .editToolbar { + --editor-toolbar-bg-color: #2b2a33; + --editor-toolbar-fg-color: #fbfbfe; + --editor-toolbar-hover-bg-color: #52525e; + --editor-toolbar-focus-outline-color: #0df; + --alt-text-done-color: #54ffbd; + --alt-text-warning-color: #80ebff; } } -[data-main-rotation="90"]{ - transform:rotate(90deg) translateY(-100%); +:where(html.is-dark) + :is( + .annotationEditorLayer + :is(.freeTextEditor, .inkEditor, .stampEditor, .highlightEditor), + .textLayer + ) + .editToolbar { + --editor-toolbar-bg-color: #2b2a33; + --editor-toolbar-fg-color: #fbfbfe; + --editor-toolbar-hover-bg-color: #52525e; + --editor-toolbar-focus-outline-color: #0df; + --alt-text-done-color: #54ffbd; + --alt-text-warning-color: #80ebff; } -[data-main-rotation="180"]{ - transform:rotate(180deg) translate(-100%, -100%); + +@media screen and (forced-colors: active) { + :is( + .annotationEditorLayer + :is(.freeTextEditor, .inkEditor, .stampEditor, .highlightEditor), + .textLayer + ) + .editToolbar { + --editor-toolbar-bg-color: ButtonFace; + --editor-toolbar-fg-color: ButtonText; + --editor-toolbar-border-color: ButtonText; + --editor-toolbar-hover-border-color: AccentColor; + --editor-toolbar-hover-bg-color: ButtonFace; + --editor-toolbar-hover-fg-color: AccentColor; + --editor-toolbar-hover-outline: 2px solid + var(--editor-toolbar-hover-border-color); + --editor-toolbar-focus-outline-color: ButtonBorder; + --editor-toolbar-shadow: none; + --alt-text-done-color: var(--editor-toolbar-fg-color); + --alt-text-warning-color: var(--editor-toolbar-fg-color); + --alt-text-hover-done-color: var(--editor-toolbar-hover-fg-color); + --alt-text-hover-warning-color: var(--editor-toolbar-hover-fg-color); + } } -[data-main-rotation="270"]{ - transform:rotate(270deg) translateX(-100%); + +:is( + .annotationEditorLayer + :is(.freeTextEditor, .inkEditor, .stampEditor, .highlightEditor), + .textLayer + ) + .editToolbar { + display: flex; + width: -moz-fit-content; + width: fit-content; + height: var(--editor-toolbar-height); + flex-direction: column; + justify-content: center; + align-items: center; + cursor: default; + pointer-events: auto; + box-sizing: content-box; + padding: var(--editor-toolbar-padding); + + position: absolute; + inset-inline-end: 0; + inset-block-start: calc(100% + var(--editor-toolbar-vert-offset)); + + border-radius: 6px; + background-color: var(--editor-toolbar-bg-color); + border: 1px solid var(--editor-toolbar-border-color); + box-shadow: var(--editor-toolbar-shadow); +} + +.hidden:is( + :is( + .annotationEditorLayer + :is(.freeTextEditor, .inkEditor, .stampEditor, .highlightEditor), + .textLayer + ) + .editToolbar + ) { + display: none; +} + +:is( + :is( + .annotationEditorLayer + :is(.freeTextEditor, .inkEditor, .stampEditor, .highlightEditor), + .textLayer + ) + .editToolbar + ):has(:focus-visible) { + border-color: transparent; +} + +[dir='ltr'] + :is( + :is( + .annotationEditorLayer + :is(.freeTextEditor, .inkEditor, .stampEditor, .highlightEditor), + .textLayer + ) + .editToolbar + ) { + transform-origin: 100% 0; +} + +[dir='rtl'] + :is( + :is( + .annotationEditorLayer + :is(.freeTextEditor, .inkEditor, .stampEditor, .highlightEditor), + .textLayer + ) + .editToolbar + ) { + transform-origin: 0 0; +} + +:is( + :is( + .annotationEditorLayer + :is(.freeTextEditor, .inkEditor, .stampEditor, .highlightEditor), + .textLayer + ) + .editToolbar + ) + .buttons { + display: flex; + justify-content: center; + align-items: center; + gap: 0; + height: 100%; +} + +:is( + :is( + :is( + .annotationEditorLayer + :is(.freeTextEditor, .inkEditor, .stampEditor, .highlightEditor), + .textLayer + ) + .editToolbar + ) + .buttons + ) + button { + padding: 0; +} + +:is( + :is( + :is( + .annotationEditorLayer + :is(.freeTextEditor, .inkEditor, .stampEditor, .highlightEditor), + .textLayer + ) + .editToolbar + ) + .buttons + ) + .divider { + width: 0; + height: calc( + 2 * var(--editor-toolbar-padding) + var(--editor-toolbar-height) + ); + border-left: 1px solid var(--editor-toolbar-border-color); + border-right: none; + display: inline-block; + margin-inline: 2px; +} + +:is( + :is( + :is( + .annotationEditorLayer + :is(.freeTextEditor, .inkEditor, .stampEditor, .highlightEditor), + .textLayer + ) + .editToolbar + ) + .buttons + ) + .highlightButton { + width: var(--editor-toolbar-height); +} + +:is( + :is( + :is( + :is( + .annotationEditorLayer + :is( + .freeTextEditor, + .inkEditor, + .stampEditor, + .highlightEditor + ), + .textLayer + ) + .editToolbar + ) + .buttons + ) + .highlightButton + )::before { + content: ''; + -webkit-mask-image: var(--editor-toolbar-highlight-image); + mask-image: var(--editor-toolbar-highlight-image); + -webkit-mask-repeat: no-repeat; + mask-repeat: no-repeat; + -webkit-mask-position: center; + mask-position: center; + display: inline-block; + background-color: var(--editor-toolbar-fg-color); + width: 100%; + height: 100%; +} + +:is( + :is( + :is( + :is( + .annotationEditorLayer + :is( + .freeTextEditor, + .inkEditor, + .stampEditor, + .highlightEditor + ), + .textLayer + ) + .editToolbar + ) + .buttons + ) + .highlightButton + ):hover::before { + background-color: var(--editor-toolbar-hover-fg-color); +} + +:is( + :is( + :is( + .annotationEditorLayer + :is(.freeTextEditor, .inkEditor, .stampEditor, .highlightEditor), + .textLayer + ) + .editToolbar + ) + .buttons + ) + .delete { + width: var(--editor-toolbar-height); +} + +:is( + :is( + :is( + :is( + .annotationEditorLayer + :is( + .freeTextEditor, + .inkEditor, + .stampEditor, + .highlightEditor + ), + .textLayer + ) + .editToolbar + ) + .buttons + ) + .delete + )::before { + content: ''; + -webkit-mask-image: var(--editor-toolbar-delete-image); + mask-image: var(--editor-toolbar-delete-image); + -webkit-mask-repeat: no-repeat; + mask-repeat: no-repeat; + -webkit-mask-position: center; + mask-position: center; + display: inline-block; + background-color: var(--editor-toolbar-fg-color); + width: 100%; + height: 100%; +} + +:is( + :is( + :is( + :is( + .annotationEditorLayer + :is( + .freeTextEditor, + .inkEditor, + .stampEditor, + .highlightEditor + ), + .textLayer + ) + .editToolbar + ) + .buttons + ) + .delete + ):hover::before { + background-color: var(--editor-toolbar-hover-fg-color); +} + +:is( + :is( + :is( + .annotationEditorLayer + :is(.freeTextEditor, .inkEditor, .stampEditor, .highlightEditor), + .textLayer + ) + .editToolbar + ) + .buttons + ) + > * { + height: var(--editor-toolbar-height); +} + +:is( + :is( + :is( + .annotationEditorLayer + :is(.freeTextEditor, .inkEditor, .stampEditor, .highlightEditor), + .textLayer + ) + .editToolbar + ) + .buttons + ) + > :not(.divider) { + border: none; + background-color: transparent; + cursor: pointer; +} + +:is( + :is( + :is( + :is( + .annotationEditorLayer + :is( + .freeTextEditor, + .inkEditor, + .stampEditor, + .highlightEditor + ), + .textLayer + ) + .editToolbar + ) + .buttons + ) + > :not(.divider) + ):hover { + border-radius: 2px; + background-color: var(--editor-toolbar-hover-bg-color); + color: var(--editor-toolbar-hover-fg-color); + outline: var(--editor-toolbar-hover-outline); + outline-offset: 1px; +} + +:is( + :is( + :is( + :is( + .annotationEditorLayer + :is( + .freeTextEditor, + .inkEditor, + .stampEditor, + .highlightEditor + ), + .textLayer + ) + .editToolbar + ) + .buttons + ) + > :not(.divider) + ):hover:active { + outline: none; +} + +:is( + :is( + :is( + :is( + .annotationEditorLayer + :is( + .freeTextEditor, + .inkEditor, + .stampEditor, + .highlightEditor + ), + .textLayer + ) + .editToolbar + ) + .buttons + ) + > :not(.divider) + ):focus-visible { + border-radius: 2px; + outline: 2px solid var(--editor-toolbar-focus-outline-color); +} + +:is( + :is( + :is( + .annotationEditorLayer + :is(.freeTextEditor, .inkEditor, .stampEditor, .highlightEditor), + .textLayer + ) + .editToolbar + ) + .buttons + ) + .altText { + --alt-text-add-image: url(images/altText_add.svg); + --alt-text-done-image: url(images/altText_done.svg); + + display: flex; + align-items: center; + justify-content: center; + width: -moz-max-content; + width: max-content; + padding-inline: 8px; + pointer-events: all; + font: menu; + font-weight: 590; + font-size: 12px; + color: var(--editor-toolbar-fg-color); +} + +:is( + :is( + :is( + :is( + .annotationEditorLayer + :is( + .freeTextEditor, + .inkEditor, + .stampEditor, + .highlightEditor + ), + .textLayer + ) + .editToolbar + ) + .buttons + ) + .altText + ):disabled { + pointer-events: none; +} + +:is( + :is( + :is( + :is( + .annotationEditorLayer + :is( + .freeTextEditor, + .inkEditor, + .stampEditor, + .highlightEditor + ), + .textLayer + ) + .editToolbar + ) + .buttons + ) + .altText + )::before { + content: ''; + -webkit-mask-image: var(--alt-text-add-image); + mask-image: var(--alt-text-add-image); + -webkit-mask-repeat: no-repeat; + mask-repeat: no-repeat; + -webkit-mask-position: center; + mask-position: center; + display: inline-block; + width: 12px; + height: 13px; + background-color: var(--editor-toolbar-fg-color); + margin-inline-end: 4px; +} + +:is( + :is( + :is( + :is( + .annotationEditorLayer + :is( + .freeTextEditor, + .inkEditor, + .stampEditor, + .highlightEditor + ), + .textLayer + ) + .editToolbar + ) + .buttons + ) + .altText + ):hover::before { + background-color: var(--editor-toolbar-hover-fg-color); +} + +.done:is( + :is( + :is( + :is( + .annotationEditorLayer + :is( + .freeTextEditor, + .inkEditor, + .stampEditor, + .highlightEditor + ), + .textLayer + ) + .editToolbar + ) + .buttons + ) + .altText + )::before { + -webkit-mask-image: var(--alt-text-done-image); + mask-image: var(--alt-text-done-image); +} + +.new:is( + :is( + :is( + :is( + .annotationEditorLayer + :is( + .freeTextEditor, + .inkEditor, + .stampEditor, + .highlightEditor + ), + .textLayer + ) + .editToolbar + ) + .buttons + ) + .altText + )::before { + width: 16px; + height: 16px; + -webkit-mask-image: var(--new-alt-text-warning-image); + mask-image: var(--new-alt-text-warning-image); + background-color: var(--alt-text-warning-color); + -webkit-mask-size: cover; + mask-size: cover; +} + +.new:is( + :is( + :is( + :is( + .annotationEditorLayer + :is( + .freeTextEditor, + .inkEditor, + .stampEditor, + .highlightEditor + ), + .textLayer + ) + .editToolbar + ) + .buttons + ) + .altText + ):hover::before { + background-color: var(--alt-text-hover-warning-color); +} + +.new.done:is( + :is( + :is( + :is( + .annotationEditorLayer + :is( + .freeTextEditor, + .inkEditor, + .stampEditor, + .highlightEditor + ), + .textLayer + ) + .editToolbar + ) + .buttons + ) + .altText + )::before { + -webkit-mask-image: var(--alt-text-done-image); + mask-image: var(--alt-text-done-image); + background-color: var(--alt-text-done-color); +} + +.new.done:is( + :is( + :is( + :is( + .annotationEditorLayer + :is( + .freeTextEditor, + .inkEditor, + .stampEditor, + .highlightEditor + ), + .textLayer + ) + .editToolbar + ) + .buttons + ) + .altText + ):hover::before { + background-color: var(--alt-text-hover-done-color); +} + +:is( + :is( + :is( + :is( + .annotationEditorLayer + :is( + .freeTextEditor, + .inkEditor, + .stampEditor, + .highlightEditor + ), + .textLayer + ) + .editToolbar + ) + .buttons + ) + .altText + ) + .tooltip { + display: none; + word-wrap: anywhere; +} + +.show:is( + :is( + :is( + :is( + :is( + .annotationEditorLayer + :is( + .freeTextEditor, + .inkEditor, + .stampEditor, + .highlightEditor + ), + .textLayer + ) + .editToolbar + ) + .buttons + ) + .altText + ) + .tooltip + ) { + --alt-text-tooltip-bg: #f0f0f4; + --alt-text-tooltip-fg: #15141a; + --alt-text-tooltip-border: #8f8f9d; + --alt-text-tooltip-shadow: 0px 2px 6px 0px rgb(58 57 68 / 0.2); +} + +@media (prefers-color-scheme: dark) { + :where(html:not(.is-light)) + .show:is( + :is( + :is( + :is( + :is( + .annotationEditorLayer + :is( + .freeTextEditor, + .inkEditor, + .stampEditor, + .highlightEditor + ), + .textLayer + ) + .editToolbar + ) + .buttons + ) + .altText + ) + .tooltip + ) { + --alt-text-tooltip-bg: #1c1b22; + --alt-text-tooltip-fg: #fbfbfe; + --alt-text-tooltip-shadow: 0px 2px 6px 0px #15141a; + } +} + +:where(html.is-dark) + .show:is( + :is( + :is( + :is( + :is( + .annotationEditorLayer + :is( + .freeTextEditor, + .inkEditor, + .stampEditor, + .highlightEditor + ), + .textLayer + ) + .editToolbar + ) + .buttons + ) + .altText + ) + .tooltip + ) { + --alt-text-tooltip-bg: #1c1b22; + --alt-text-tooltip-fg: #fbfbfe; + --alt-text-tooltip-shadow: 0px 2px 6px 0px #15141a; +} + +@media screen and (forced-colors: active) { + .show:is( + :is( + :is( + :is( + :is( + .annotationEditorLayer + :is( + .freeTextEditor, + .inkEditor, + .stampEditor, + .highlightEditor + ), + .textLayer + ) + .editToolbar + ) + .buttons + ) + .altText + ) + .tooltip + ) { + --alt-text-tooltip-bg: Canvas; + --alt-text-tooltip-fg: CanvasText; + --alt-text-tooltip-border: CanvasText; + --alt-text-tooltip-shadow: none; + } +} + +.show:is( + :is( + :is( + :is( + :is( + .annotationEditorLayer + :is( + .freeTextEditor, + .inkEditor, + .stampEditor, + .highlightEditor + ), + .textLayer + ) + .editToolbar + ) + .buttons + ) + .altText + ) + .tooltip + ) { + display: inline-flex; + flex-direction: column; + align-items: center; + justify-content: center; + position: absolute; + top: calc(100% + 2px); + inset-inline-start: 0; + padding-block: 2px 3px; + padding-inline: 3px; + max-width: 300px; + width: -moz-max-content; + width: max-content; + height: auto; + font-size: 12px; + + border: 0.5px solid var(--alt-text-tooltip-border); + background: var(--alt-text-tooltip-bg); + box-shadow: var(--alt-text-tooltip-shadow); + color: var(--alt-text-tooltip-fg); + + pointer-events: none; +} + +.annotationEditorLayer .freeTextEditor { + padding: calc(var(--freetext-padding) * var(--scale-factor)); + width: auto; + height: auto; + touch-action: none; +} + +.annotationEditorLayer .freeTextEditor .internal { + background: transparent; + border: none; + inset: 0; + overflow: visible; + white-space: nowrap; + font: 10px sans-serif; + line-height: var(--freetext-line-height); + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; +} + +.annotationEditorLayer .freeTextEditor .overlay { + position: absolute; + display: none; + background: transparent; + inset: 0; + width: 100%; + height: 100%; +} + +.annotationEditorLayer freeTextEditor .overlay.enabled { + display: block; +} + +.annotationEditorLayer .freeTextEditor .internal:empty::before { + content: attr(default-content); + color: gray; +} + +.annotationEditorLayer .freeTextEditor .internal:focus { + outline: none; + -webkit-user-select: auto; + -moz-user-select: auto; + user-select: auto; +} + +.annotationEditorLayer .inkEditor { + width: 100%; + height: 100%; +} + +.annotationEditorLayer .inkEditor.editing { + cursor: inherit; +} + +.annotationEditorLayer .inkEditor .inkEditorCanvas { + position: absolute; + inset: 0; + width: 100%; + height: 100%; + touch-action: none; +} + +.annotationEditorLayer .stampEditor { + width: auto; + height: auto; +} + +:is(.annotationEditorLayer .stampEditor) canvas { + position: absolute; + width: 100%; + height: 100%; + margin: 0; + top: 0; + left: 0; +} + +:is(.annotationEditorLayer .stampEditor) .noAltTextBadge { + --no-alt-text-badge-border-color: #f0f0f4; + --no-alt-text-badge-bg-color: #cfcfd8; + --no-alt-text-badge-fg-color: #5b5b66; +} + +@media (prefers-color-scheme: dark) { + :where(html:not(.is-light)) + :is(.annotationEditorLayer .stampEditor) + .noAltTextBadge { + --no-alt-text-badge-border-color: #52525e; + --no-alt-text-badge-bg-color: #fbfbfe; + --no-alt-text-badge-fg-color: #15141a; + } +} + +:where(html.is-dark) :is(.annotationEditorLayer .stampEditor) .noAltTextBadge { + --no-alt-text-badge-border-color: #52525e; + --no-alt-text-badge-bg-color: #fbfbfe; + --no-alt-text-badge-fg-color: #15141a; +} + +@media screen and (forced-colors: active) { + :is(.annotationEditorLayer .stampEditor) .noAltTextBadge { + --no-alt-text-badge-border-color: ButtonText; + --no-alt-text-badge-bg-color: ButtonFace; + --no-alt-text-badge-fg-color: ButtonText; + } +} + +:is(.annotationEditorLayer .stampEditor) .noAltTextBadge { + position: absolute; + inset-inline-end: 5px; + inset-block-end: 5px; + display: inline-flex; + width: 32px; + height: 32px; + padding: 3px; + justify-content: center; + align-items: center; + pointer-events: none; + z-index: 1; + + border-radius: 2px; + border: 1px solid var(--no-alt-text-badge-border-color); + background: var(--no-alt-text-badge-bg-color); +} + +:is(:is(.annotationEditorLayer .stampEditor) .noAltTextBadge)::before { + content: ''; + display: inline-block; + width: 16px; + height: 16px; + -webkit-mask-image: var(--new-alt-text-warning-image); + mask-image: var(--new-alt-text-warning-image); + -webkit-mask-size: cover; + mask-size: cover; + background-color: var(--no-alt-text-badge-fg-color); +} + +:is(.annotationEditorLayer :is(.freeTextEditor, .inkEditor, .stampEditor)) + > .resizers { + position: absolute; + inset: 0; +} + +.hidden:is( + :is(.annotationEditorLayer :is(.freeTextEditor, .inkEditor, .stampEditor)) + > .resizers + ) { + display: none; +} + +:is( + :is(.annotationEditorLayer :is(.freeTextEditor, .inkEditor, .stampEditor)) + > .resizers + ) + > .resizer { + width: var(--resizer-size); + height: var(--resizer-size); + background: content-box var(--resizer-bg-color); + border: var(--focus-outline-around); + border-radius: 2px; + position: absolute; +} + +.topLeft:is( + :is( + :is( + .annotationEditorLayer + :is(.freeTextEditor, .inkEditor, .stampEditor) + ) + > .resizers + ) + > .resizer + ) { + top: var(--resizer-shift); + left: var(--resizer-shift); +} + +.topMiddle:is( + :is( + :is( + .annotationEditorLayer + :is(.freeTextEditor, .inkEditor, .stampEditor) + ) + > .resizers + ) + > .resizer + ) { + top: var(--resizer-shift); + left: calc(50% + var(--resizer-shift)); +} + +.topRight:is( + :is( + :is( + .annotationEditorLayer + :is(.freeTextEditor, .inkEditor, .stampEditor) + ) + > .resizers + ) + > .resizer + ) { + top: var(--resizer-shift); + right: var(--resizer-shift); +} + +.middleRight:is( + :is( + :is( + .annotationEditorLayer + :is(.freeTextEditor, .inkEditor, .stampEditor) + ) + > .resizers + ) + > .resizer + ) { + top: calc(50% + var(--resizer-shift)); + right: var(--resizer-shift); +} + +.bottomRight:is( + :is( + :is( + .annotationEditorLayer + :is(.freeTextEditor, .inkEditor, .stampEditor) + ) + > .resizers + ) + > .resizer + ) { + bottom: var(--resizer-shift); + right: var(--resizer-shift); +} + +.bottomMiddle:is( + :is( + :is( + .annotationEditorLayer + :is(.freeTextEditor, .inkEditor, .stampEditor) + ) + > .resizers + ) + > .resizer + ) { + bottom: var(--resizer-shift); + left: calc(50% + var(--resizer-shift)); +} + +.bottomLeft:is( + :is( + :is( + .annotationEditorLayer + :is(.freeTextEditor, .inkEditor, .stampEditor) + ) + > .resizers + ) + > .resizer + ) { + bottom: var(--resizer-shift); + left: var(--resizer-shift); +} + +.middleLeft:is( + :is( + :is( + .annotationEditorLayer + :is(.freeTextEditor, .inkEditor, .stampEditor) + ) + > .resizers + ) + > .resizer + ) { + top: calc(50% + var(--resizer-shift)); + left: var(--resizer-shift); +} + +.topLeft:is( + :is( + .annotationEditorLayer[data-main-rotation='0'] + :is([data-editor-rotation='0'], [data-editor-rotation='180']), + .annotationEditorLayer[data-main-rotation='90'] + :is([data-editor-rotation='270'], [data-editor-rotation='90']), + .annotationEditorLayer[data-main-rotation='180'] + :is([data-editor-rotation='180'], [data-editor-rotation='0']), + .annotationEditorLayer[data-main-rotation='270'] + :is([data-editor-rotation='90'], [data-editor-rotation='270']) + ) + > .resizers + > .resizer + ), +.bottomRight:is( + :is( + .annotationEditorLayer[data-main-rotation='0'] + :is([data-editor-rotation='0'], [data-editor-rotation='180']), + .annotationEditorLayer[data-main-rotation='90'] + :is([data-editor-rotation='270'], [data-editor-rotation='90']), + .annotationEditorLayer[data-main-rotation='180'] + :is([data-editor-rotation='180'], [data-editor-rotation='0']), + .annotationEditorLayer[data-main-rotation='270'] + :is([data-editor-rotation='90'], [data-editor-rotation='270']) + ) + > .resizers + > .resizer + ) { + cursor: nwse-resize; +} + +.topMiddle:is( + :is( + .annotationEditorLayer[data-main-rotation='0'] + :is([data-editor-rotation='0'], [data-editor-rotation='180']), + .annotationEditorLayer[data-main-rotation='90'] + :is([data-editor-rotation='270'], [data-editor-rotation='90']), + .annotationEditorLayer[data-main-rotation='180'] + :is([data-editor-rotation='180'], [data-editor-rotation='0']), + .annotationEditorLayer[data-main-rotation='270'] + :is([data-editor-rotation='90'], [data-editor-rotation='270']) + ) + > .resizers + > .resizer + ), +.bottomMiddle:is( + :is( + .annotationEditorLayer[data-main-rotation='0'] + :is([data-editor-rotation='0'], [data-editor-rotation='180']), + .annotationEditorLayer[data-main-rotation='90'] + :is([data-editor-rotation='270'], [data-editor-rotation='90']), + .annotationEditorLayer[data-main-rotation='180'] + :is([data-editor-rotation='180'], [data-editor-rotation='0']), + .annotationEditorLayer[data-main-rotation='270'] + :is([data-editor-rotation='90'], [data-editor-rotation='270']) + ) + > .resizers + > .resizer + ) { + cursor: ns-resize; +} + +.topRight:is( + :is( + .annotationEditorLayer[data-main-rotation='0'] + :is([data-editor-rotation='0'], [data-editor-rotation='180']), + .annotationEditorLayer[data-main-rotation='90'] + :is([data-editor-rotation='270'], [data-editor-rotation='90']), + .annotationEditorLayer[data-main-rotation='180'] + :is([data-editor-rotation='180'], [data-editor-rotation='0']), + .annotationEditorLayer[data-main-rotation='270'] + :is([data-editor-rotation='90'], [data-editor-rotation='270']) + ) + > .resizers + > .resizer + ), +.bottomLeft:is( + :is( + .annotationEditorLayer[data-main-rotation='0'] + :is([data-editor-rotation='0'], [data-editor-rotation='180']), + .annotationEditorLayer[data-main-rotation='90'] + :is([data-editor-rotation='270'], [data-editor-rotation='90']), + .annotationEditorLayer[data-main-rotation='180'] + :is([data-editor-rotation='180'], [data-editor-rotation='0']), + .annotationEditorLayer[data-main-rotation='270'] + :is([data-editor-rotation='90'], [data-editor-rotation='270']) + ) + > .resizers + > .resizer + ) { + cursor: nesw-resize; +} + +.middleRight:is( + :is( + .annotationEditorLayer[data-main-rotation='0'] + :is([data-editor-rotation='0'], [data-editor-rotation='180']), + .annotationEditorLayer[data-main-rotation='90'] + :is([data-editor-rotation='270'], [data-editor-rotation='90']), + .annotationEditorLayer[data-main-rotation='180'] + :is([data-editor-rotation='180'], [data-editor-rotation='0']), + .annotationEditorLayer[data-main-rotation='270'] + :is([data-editor-rotation='90'], [data-editor-rotation='270']) + ) + > .resizers + > .resizer + ), +.middleLeft:is( + :is( + .annotationEditorLayer[data-main-rotation='0'] + :is([data-editor-rotation='0'], [data-editor-rotation='180']), + .annotationEditorLayer[data-main-rotation='90'] + :is([data-editor-rotation='270'], [data-editor-rotation='90']), + .annotationEditorLayer[data-main-rotation='180'] + :is([data-editor-rotation='180'], [data-editor-rotation='0']), + .annotationEditorLayer[data-main-rotation='270'] + :is([data-editor-rotation='90'], [data-editor-rotation='270']) + ) + > .resizers + > .resizer + ) { + cursor: ew-resize; +} + +.topLeft:is( + :is( + .annotationEditorLayer[data-main-rotation='0'] + :is([data-editor-rotation='90'], [data-editor-rotation='270']), + .annotationEditorLayer[data-main-rotation='90'] + :is([data-editor-rotation='0'], [data-editor-rotation='180']), + .annotationEditorLayer[data-main-rotation='180'] + :is([data-editor-rotation='270'], [data-editor-rotation='90']), + .annotationEditorLayer[data-main-rotation='270'] + :is([data-editor-rotation='180'], [data-editor-rotation='0']) + ) + > .resizers + > .resizer + ), +.bottomRight:is( + :is( + .annotationEditorLayer[data-main-rotation='0'] + :is([data-editor-rotation='90'], [data-editor-rotation='270']), + .annotationEditorLayer[data-main-rotation='90'] + :is([data-editor-rotation='0'], [data-editor-rotation='180']), + .annotationEditorLayer[data-main-rotation='180'] + :is([data-editor-rotation='270'], [data-editor-rotation='90']), + .annotationEditorLayer[data-main-rotation='270'] + :is([data-editor-rotation='180'], [data-editor-rotation='0']) + ) + > .resizers + > .resizer + ) { + cursor: nesw-resize; +} + +.topMiddle:is( + :is( + .annotationEditorLayer[data-main-rotation='0'] + :is([data-editor-rotation='90'], [data-editor-rotation='270']), + .annotationEditorLayer[data-main-rotation='90'] + :is([data-editor-rotation='0'], [data-editor-rotation='180']), + .annotationEditorLayer[data-main-rotation='180'] + :is([data-editor-rotation='270'], [data-editor-rotation='90']), + .annotationEditorLayer[data-main-rotation='270'] + :is([data-editor-rotation='180'], [data-editor-rotation='0']) + ) + > .resizers + > .resizer + ), +.bottomMiddle:is( + :is( + .annotationEditorLayer[data-main-rotation='0'] + :is([data-editor-rotation='90'], [data-editor-rotation='270']), + .annotationEditorLayer[data-main-rotation='90'] + :is([data-editor-rotation='0'], [data-editor-rotation='180']), + .annotationEditorLayer[data-main-rotation='180'] + :is([data-editor-rotation='270'], [data-editor-rotation='90']), + .annotationEditorLayer[data-main-rotation='270'] + :is([data-editor-rotation='180'], [data-editor-rotation='0']) + ) + > .resizers + > .resizer + ) { + cursor: ew-resize; +} + +.topRight:is( + :is( + .annotationEditorLayer[data-main-rotation='0'] + :is([data-editor-rotation='90'], [data-editor-rotation='270']), + .annotationEditorLayer[data-main-rotation='90'] + :is([data-editor-rotation='0'], [data-editor-rotation='180']), + .annotationEditorLayer[data-main-rotation='180'] + :is([data-editor-rotation='270'], [data-editor-rotation='90']), + .annotationEditorLayer[data-main-rotation='270'] + :is([data-editor-rotation='180'], [data-editor-rotation='0']) + ) + > .resizers + > .resizer + ), +.bottomLeft:is( + :is( + .annotationEditorLayer[data-main-rotation='0'] + :is([data-editor-rotation='90'], [data-editor-rotation='270']), + .annotationEditorLayer[data-main-rotation='90'] + :is([data-editor-rotation='0'], [data-editor-rotation='180']), + .annotationEditorLayer[data-main-rotation='180'] + :is([data-editor-rotation='270'], [data-editor-rotation='90']), + .annotationEditorLayer[data-main-rotation='270'] + :is([data-editor-rotation='180'], [data-editor-rotation='0']) + ) + > .resizers + > .resizer + ) { + cursor: nwse-resize; +} + +.middleRight:is( + :is( + .annotationEditorLayer[data-main-rotation='0'] + :is([data-editor-rotation='90'], [data-editor-rotation='270']), + .annotationEditorLayer[data-main-rotation='90'] + :is([data-editor-rotation='0'], [data-editor-rotation='180']), + .annotationEditorLayer[data-main-rotation='180'] + :is([data-editor-rotation='270'], [data-editor-rotation='90']), + .annotationEditorLayer[data-main-rotation='270'] + :is([data-editor-rotation='180'], [data-editor-rotation='0']) + ) + > .resizers + > .resizer + ), +.middleLeft:is( + :is( + .annotationEditorLayer[data-main-rotation='0'] + :is([data-editor-rotation='90'], [data-editor-rotation='270']), + .annotationEditorLayer[data-main-rotation='90'] + :is([data-editor-rotation='0'], [data-editor-rotation='180']), + .annotationEditorLayer[data-main-rotation='180'] + :is([data-editor-rotation='270'], [data-editor-rotation='90']), + .annotationEditorLayer[data-main-rotation='270'] + :is([data-editor-rotation='180'], [data-editor-rotation='0']) + ) + > .resizers + > .resizer + ) { + cursor: ns-resize; +} + +:is( + .annotationEditorLayer + :is( + [data-main-rotation='0'] [data-editor-rotation='90'], + [data-main-rotation='90'] [data-editor-rotation='0'], + [data-main-rotation='180'] [data-editor-rotation='270'], + [data-main-rotation='270'] [data-editor-rotation='180'] + ) + ) + .editToolbar { + rotate: 270deg; +} + +[dir='ltr'] + :is( + :is( + .annotationEditorLayer + :is( + [data-main-rotation='0'] [data-editor-rotation='90'], + [data-main-rotation='90'] [data-editor-rotation='0'], + [data-main-rotation='180'] [data-editor-rotation='270'], + [data-main-rotation='270'] [data-editor-rotation='180'] + ) + ) + .editToolbar + ) { + inset-inline-end: calc(0px - var(--editor-toolbar-vert-offset)); + inset-block-start: 0; +} + +[dir='rtl'] + :is( + :is( + .annotationEditorLayer + :is( + [data-main-rotation='0'] [data-editor-rotation='90'], + [data-main-rotation='90'] [data-editor-rotation='0'], + [data-main-rotation='180'] [data-editor-rotation='270'], + [data-main-rotation='270'] [data-editor-rotation='180'] + ) + ) + .editToolbar + ) { + inset-inline-end: calc(100% + var(--editor-toolbar-vert-offset)); + inset-block-start: 0; +} + +:is( + .annotationEditorLayer + :is( + [data-main-rotation='0'] [data-editor-rotation='180'], + [data-main-rotation='90'] [data-editor-rotation='90'], + [data-main-rotation='180'] [data-editor-rotation='0'], + [data-main-rotation='270'] [data-editor-rotation='270'] + ) + ) + .editToolbar { + rotate: 180deg; + inset-inline-end: 100%; + inset-block-start: calc(0pc - var(--editor-toolbar-vert-offset)); +} + +:is( + .annotationEditorLayer + :is( + [data-main-rotation='0'] [data-editor-rotation='270'], + [data-main-rotation='90'] [data-editor-rotation='180'], + [data-main-rotation='180'] [data-editor-rotation='90'], + [data-main-rotation='270'] [data-editor-rotation='0'] + ) + ) + .editToolbar { + rotate: 90deg; +} + +[dir='ltr'] + :is( + :is( + .annotationEditorLayer + :is( + [data-main-rotation='0'] [data-editor-rotation='270'], + [data-main-rotation='90'] [data-editor-rotation='180'], + [data-main-rotation='180'] [data-editor-rotation='90'], + [data-main-rotation='270'] [data-editor-rotation='0'] + ) + ) + .editToolbar + ) { + inset-inline-end: calc(100% + var(--editor-toolbar-vert-offset)); + inset-block-start: 100%; +} + +[dir='rtl'] + :is( + :is( + .annotationEditorLayer + :is( + [data-main-rotation='0'] [data-editor-rotation='270'], + [data-main-rotation='90'] [data-editor-rotation='180'], + [data-main-rotation='180'] [data-editor-rotation='90'], + [data-main-rotation='270'] [data-editor-rotation='0'] + ) + ) + .editToolbar + ) { + inset-inline-start: calc(0px - var(--editor-toolbar-vert-offset)); + inset-block-start: 0; +} + +.dialog.altText::backdrop { + -webkit-mask: url(#alttext-manager-mask); + mask: url(#alttext-manager-mask); +} + +.dialog.altText.positioned { + margin: 0; +} + +.dialog.altText #altTextContainer { + width: 300px; + height: -moz-fit-content; + height: fit-content; + display: inline-flex; + flex-direction: column; + align-items: flex-start; + gap: 16px; +} + +:is(.dialog.altText #altTextContainer) #overallDescription { + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 4px; + align-self: stretch; +} + +:is(:is(.dialog.altText #altTextContainer) #overallDescription) span { + align-self: stretch; +} + +:is(:is(.dialog.altText #altTextContainer) #overallDescription) .title { + font-size: 13px; + font-style: normal; + font-weight: 590; +} + +:is(.dialog.altText #altTextContainer) #addDescription { + display: flex; + flex-direction: column; + align-items: stretch; + gap: 8px; +} + +:is(:is(.dialog.altText #altTextContainer) #addDescription) .descriptionArea { + flex: 1; + padding-inline: 24px 10px; +} + +:is( + :is(:is(.dialog.altText #altTextContainer) #addDescription) .descriptionArea + ) + textarea { + width: 100%; + min-height: 75px; +} + +:is(.dialog.altText #altTextContainer) #buttons { + display: flex; + justify-content: flex-end; + align-items: flex-start; + gap: 8px; + align-self: stretch; +} + +.dialog.newAltText { + --new-alt-text-ai-disclaimer-icon: url(images/altText_disclaimer.svg); + --new-alt-text-spinner-icon: url(images/altText_spinner.svg); + --preview-image-bg-color: #f0f0f4; + --preview-image-border: none; +} + +@media (prefers-color-scheme: dark) { + :where(html:not(.is-light)) .dialog.newAltText { + --preview-image-bg-color: #2b2a33; + } +} + +:where(html.is-dark) .dialog.newAltText { + --preview-image-bg-color: #2b2a33; +} + +@media screen and (forced-colors: active) { + .dialog.newAltText { + --preview-image-bg-color: ButtonFace; + --preview-image-border: 1px solid ButtonText; + } +} + +.dialog.newAltText { + width: 80%; + max-width: 570px; + min-width: 300px; + padding: 0; +} + +.dialog.newAltText.noAi #newAltTextDisclaimer, +.dialog.newAltText.noAi #newAltTextCreateAutomatically { + display: none !important; +} + +.dialog.newAltText.aiInstalling #newAltTextCreateAutomatically { + display: none !important; +} + +.dialog.newAltText.aiInstalling #newAltTextDownloadModel { + display: flex !important; +} + +.dialog.newAltText.error #newAltTextNotNow { + display: none !important; +} + +.dialog.newAltText.error #newAltTextCancel { + display: inline-block !important; +} + +.dialog.newAltText:not(.error) #newAltTextError { + display: none !important; +} + +.dialog.newAltText #newAltTextContainer { + display: flex; + width: auto; + padding: 16px; + flex-direction: column; + justify-content: flex-end; + align-items: flex-start; + gap: 12px; + flex: 0 1 auto; + line-height: normal; +} + +:is(.dialog.newAltText #newAltTextContainer) #mainContent { + display: flex; + justify-content: flex-end; + align-items: flex-start; + gap: 12px; + align-self: stretch; + flex: 1 1 auto; +} + +:is(:is(.dialog.newAltText #newAltTextContainer) #mainContent) + #descriptionAndSettings { + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 16px; + flex: 1 0 0; + align-self: stretch; +} + +:is(:is(.dialog.newAltText #newAltTextContainer) #mainContent) + #descriptionInstruction { + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 8px; + align-self: stretch; + flex: 1 1 auto; +} + +:is( + :is(:is(.dialog.newAltText #newAltTextContainer) #mainContent) + #descriptionInstruction + ) + #newAltTextDescriptionContainer { + width: 100%; + height: 70px; + position: relative; +} + +:is( + :is( + :is(:is(.dialog.newAltText #newAltTextContainer) #mainContent) + #descriptionInstruction + ) + #newAltTextDescriptionContainer + ) + textarea { + width: 100%; + height: 100%; + padding: 8px; +} + +:is( + :is( + :is( + :is(:is(.dialog.newAltText #newAltTextContainer) #mainContent) + #descriptionInstruction + ) + #newAltTextDescriptionContainer + ) + textarea + )::-moz-placeholder { + color: var(--text-secondary-color); +} + +:is( + :is( + :is( + :is(:is(.dialog.newAltText #newAltTextContainer) #mainContent) + #descriptionInstruction + ) + #newAltTextDescriptionContainer + ) + textarea + )::placeholder { + color: var(--text-secondary-color); +} + +:is( + :is( + :is(:is(.dialog.newAltText #newAltTextContainer) #mainContent) + #descriptionInstruction + ) + #newAltTextDescriptionContainer + ) + .altTextSpinner { + display: none; + position: absolute; + width: 16px; + height: 16px; + inset-inline-start: 8px; + inset-block-start: 8px; + -webkit-mask-size: cover; + mask-size: cover; + background-color: var(--text-secondary-color); + pointer-events: none; +} + +.loading:is( + :is( + :is(:is(.dialog.newAltText #newAltTextContainer) #mainContent) + #descriptionInstruction + ) + #newAltTextDescriptionContainer + ) + textarea::-moz-placeholder { + color: transparent; +} + +.loading:is( + :is( + :is(:is(.dialog.newAltText #newAltTextContainer) #mainContent) + #descriptionInstruction + ) + #newAltTextDescriptionContainer + ) + textarea::placeholder { + color: transparent; +} + +.loading:is( + :is( + :is(:is(.dialog.newAltText #newAltTextContainer) #mainContent) + #descriptionInstruction + ) + #newAltTextDescriptionContainer + ) + .altTextSpinner { + display: inline-block; + -webkit-mask-image: var(--new-alt-text-spinner-icon); + mask-image: var(--new-alt-text-spinner-icon); +} + +:is( + :is(:is(.dialog.newAltText #newAltTextContainer) #mainContent) + #descriptionInstruction + ) + #newAltTextDescription { + font-size: 11px; +} + +:is( + :is(:is(.dialog.newAltText #newAltTextContainer) #mainContent) + #descriptionInstruction + ) + #newAltTextDisclaimer { + display: flex; + flex-direction: row; + align-items: flex-start; + gap: 4px; + font-size: 11px; +} + +:is( + :is( + :is(:is(.dialog.newAltText #newAltTextContainer) #mainContent) + #descriptionInstruction + ) + #newAltTextDisclaimer + )::before { + content: ''; + display: inline-block; + width: 17px; + height: 16px; + -webkit-mask-image: var(--new-alt-text-ai-disclaimer-icon); + mask-image: var(--new-alt-text-ai-disclaimer-icon); + -webkit-mask-size: cover; + mask-size: cover; + background-color: var(--text-secondary-color); + flex: 1 0 auto; +} + +:is(:is(.dialog.newAltText #newAltTextContainer) #mainContent) + #newAltTextDownloadModel { + display: flex; + align-items: center; + gap: 4px; + align-self: stretch; +} + +:is( + :is(:is(.dialog.newAltText #newAltTextContainer) #mainContent) + #newAltTextDownloadModel + )::before { + content: ''; + display: inline-block; + width: 16px; + height: 16px; + -webkit-mask-image: var(--new-alt-text-spinner-icon); + mask-image: var(--new-alt-text-spinner-icon); + -webkit-mask-size: cover; + mask-size: cover; + background-color: var(--text-secondary-color); +} + +:is(:is(.dialog.newAltText #newAltTextContainer) #mainContent) + #newAltTextImagePreview { + width: 180px; + aspect-ratio: 1; + display: flex; + justify-content: center; + align-items: center; + flex: 0 0 auto; + background-color: var(--preview-image-bg-color); + border: var(--preview-image-border); +} + +:is( + :is(:is(.dialog.newAltText #newAltTextContainer) #mainContent) + #newAltTextImagePreview + ) + > canvas { + max-width: 100%; + max-height: 100%; +} + +.colorPicker { + --hover-outline-color: #0250bb; + --selected-outline-color: #0060df; + --swatch-border-color: #cfcfd8; +} + +@media (prefers-color-scheme: dark) { + :where(html:not(.is-light)) .colorPicker { + --hover-outline-color: #80ebff; + --selected-outline-color: #aaf2ff; + --swatch-border-color: #52525e; + } +} + +:where(html.is-dark) .colorPicker { + --hover-outline-color: #80ebff; + --selected-outline-color: #aaf2ff; + --swatch-border-color: #52525e; +} + +@media screen and (forced-colors: active) { + .colorPicker { + --hover-outline-color: Highlight; + --selected-outline-color: var(--hover-outline-color); + --swatch-border-color: ButtonText; + } +} + +.colorPicker .swatch { + width: 16px; + height: 16px; + border: 1px solid var(--swatch-border-color); + border-radius: 100%; + outline-offset: 2px; + box-sizing: border-box; + forced-color-adjust: none; +} + +.colorPicker button:is(:hover, .selected) > .swatch { + border: none; +} + +.annotationEditorLayer[data-main-rotation='0'] + .highlightEditor:not(.free) + > .editToolbar { + rotate: 0deg; +} + +.annotationEditorLayer[data-main-rotation='90'] + .highlightEditor:not(.free) + > .editToolbar { + rotate: 270deg; +} + +.annotationEditorLayer[data-main-rotation='180'] + .highlightEditor:not(.free) + > .editToolbar { + rotate: 180deg; +} + +.annotationEditorLayer[data-main-rotation='270'] + .highlightEditor:not(.free) + > .editToolbar { + rotate: 90deg; +} + +.annotationEditorLayer .highlightEditor { + position: absolute; + background: transparent; + z-index: 1; + cursor: auto; + max-width: 100%; + max-height: 100%; + border: none; + outline: none; + pointer-events: none; + transform-origin: 0 0; +} + +:is(.annotationEditorLayer .highlightEditor):not(.free) { + transform: none; +} + +:is(.annotationEditorLayer .highlightEditor) .internal { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + pointer-events: auto; +} + +.disabled:is(.annotationEditorLayer .highlightEditor) .internal { + pointer-events: none; +} + +.selectedEditor:is(.annotationEditorLayer .highlightEditor) .internal { + cursor: pointer; +} + +:is(.annotationEditorLayer .highlightEditor) .editToolbar { + --editor-toolbar-colorpicker-arrow-image: url(images/toolbarButton-menuArrow.svg); + + transform-origin: center !important; +} + +:is(:is(:is(.annotationEditorLayer .highlightEditor) .editToolbar) .buttons) + .colorPicker { + position: relative; + width: auto; + display: flex; + justify-content: center; + align-items: center; + gap: 4px; + padding: 4px; +} + +:is( + :is(:is(:is(.annotationEditorLayer .highlightEditor) .editToolbar) .buttons) + .colorPicker + )::after { + content: ''; + -webkit-mask-image: var(--editor-toolbar-colorpicker-arrow-image); + mask-image: var(--editor-toolbar-colorpicker-arrow-image); + -webkit-mask-repeat: no-repeat; + mask-repeat: no-repeat; + -webkit-mask-position: center; + mask-position: center; + display: inline-block; + background-color: var(--editor-toolbar-fg-color); + width: 12px; + height: 12px; +} + +:is( + :is(:is(:is(.annotationEditorLayer .highlightEditor) .editToolbar) .buttons) + .colorPicker + ):hover::after { + background-color: var(--editor-toolbar-hover-fg-color); +} + +:is( + :is(:is(:is(.annotationEditorLayer .highlightEditor) .editToolbar) .buttons) + .colorPicker + ):has(.dropdown:not(.hidden)) { + background-color: var(--editor-toolbar-hover-bg-color); +} + +:is( + :is(:is(:is(.annotationEditorLayer .highlightEditor) .editToolbar) .buttons) + .colorPicker + ):has(.dropdown:not(.hidden))::after { + scale: -1; +} + +:is( + :is(:is(:is(.annotationEditorLayer .highlightEditor) .editToolbar) .buttons) + .colorPicker + ) + .dropdown { + position: absolute; + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; + gap: 11px; + padding-block: 8px; + border-radius: 6px; + background-color: var(--editor-toolbar-bg-color); + border: 1px solid var(--editor-toolbar-border-color); + box-shadow: var(--editor-toolbar-shadow); + inset-block-start: calc(100% + 4px); + width: calc(100% + 2 * var(--editor-toolbar-padding)); +} + +:is( + :is( + :is( + :is(:is(.annotationEditorLayer .highlightEditor) .editToolbar) + .buttons + ) + .colorPicker + ) + .dropdown + ) + button { + width: 100%; + height: auto; + border: none; + cursor: pointer; + display: flex; + justify-content: center; + align-items: center; + background: none; +} + +:is( + :is( + :is( + :is( + :is(:is(.annotationEditorLayer .highlightEditor) .editToolbar) + .buttons + ) + .colorPicker + ) + .dropdown + ) + button + ):is(:active, :focus-visible) { + outline: none; +} + +:is( + :is( + :is( + :is( + :is(:is(.annotationEditorLayer .highlightEditor) .editToolbar) + .buttons + ) + .colorPicker + ) + .dropdown + ) + button + ) + > .swatch { + outline-offset: 2px; +} + +[aria-selected='true']:is( + :is( + :is( + :is( + :is(:is(.annotationEditorLayer .highlightEditor) .editToolbar) + .buttons + ) + .colorPicker + ) + .dropdown + ) + button + ) + > .swatch { + outline: 2px solid var(--selected-outline-color); +} + +:is( + :is( + :is( + :is( + :is(:is(.annotationEditorLayer .highlightEditor) .editToolbar) + .buttons + ) + .colorPicker + ) + .dropdown + ) + button + ):is(:hover, :active, :focus-visible) + > .swatch { + outline: 2px solid var(--hover-outline-color); +} + +.editorParamsToolbar:has(#highlightParamsToolbarContainer) { + padding: unset; +} + +#highlightParamsToolbarContainer { + gap: 16px; + padding-inline: 10px; + padding-block-end: 12px; +} + +#highlightParamsToolbarContainer .colorPicker { + display: flex; + flex-direction: column; + gap: 8px; +} + +:is(#highlightParamsToolbarContainer .colorPicker) .dropdown { + display: flex; + justify-content: space-between; + align-items: center; + flex-direction: row; + height: auto; +} + +:is(:is(#highlightParamsToolbarContainer .colorPicker) .dropdown) button { + width: auto; + height: auto; + border: none; + cursor: pointer; + display: flex; + justify-content: center; + align-items: center; + background: none; + flex: 0 0 auto; + padding: 0; +} + +:is(:is(:is(#highlightParamsToolbarContainer .colorPicker) .dropdown) button) + .swatch { + width: 24px; + height: 24px; +} + +:is( + :is(:is(#highlightParamsToolbarContainer .colorPicker) .dropdown) button + ):is(:active, :focus-visible) { + outline: none; +} + +[aria-selected='true']:is( + :is(:is(#highlightParamsToolbarContainer .colorPicker) .dropdown) button + ) + > .swatch { + outline: 2px solid var(--selected-outline-color); +} + +:is( + :is(:is(#highlightParamsToolbarContainer .colorPicker) .dropdown) button + ):is(:hover, :active, :focus-visible) + > .swatch { + outline: 2px solid var(--hover-outline-color); +} + +#highlightParamsToolbarContainer #editorHighlightThickness { + display: flex; + flex-direction: column; + align-items: center; + gap: 4px; + align-self: stretch; +} + +:is(#highlightParamsToolbarContainer #editorHighlightThickness) + .editorParamsLabel { + height: auto; + align-self: stretch; +} + +:is(#highlightParamsToolbarContainer #editorHighlightThickness) + .thicknessPicker { + display: flex; + justify-content: space-between; + align-items: center; + align-self: stretch; + + --example-color: #bfbfc9; +} + +@media (prefers-color-scheme: dark) { + :where(html:not(.is-light)) + :is(#highlightParamsToolbarContainer #editorHighlightThickness) + .thicknessPicker { + --example-color: #80808e; + } +} + +:where(html.is-dark) + :is(#highlightParamsToolbarContainer #editorHighlightThickness) + .thicknessPicker { + --example-color: #80808e; +} + +@media screen and (forced-colors: active) { + :is(#highlightParamsToolbarContainer #editorHighlightThickness) + .thicknessPicker { + --example-color: CanvasText; + } +} + +:is( + :is( + :is(#highlightParamsToolbarContainer #editorHighlightThickness) + .thicknessPicker + ) + > .editorParamsSlider[disabled] +) { + opacity: 0.4; +} + +:is( + :is(#highlightParamsToolbarContainer #editorHighlightThickness) + .thicknessPicker + )::before, +:is( + :is(#highlightParamsToolbarContainer #editorHighlightThickness) + .thicknessPicker + )::after { + content: ''; + width: 8px; + aspect-ratio: 1; + display: block; + border-radius: 100%; + background-color: var(--example-color); +} + +:is( + :is(#highlightParamsToolbarContainer #editorHighlightThickness) + .thicknessPicker + )::after { + width: 24px; +} + +:is( + :is(#highlightParamsToolbarContainer #editorHighlightThickness) + .thicknessPicker + ) + .editorParamsSlider { + width: unset; + height: 14px; +} + +#highlightParamsToolbarContainer #editorHighlightVisibility { + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 8px; + align-self: stretch; +} + +:is(#highlightParamsToolbarContainer #editorHighlightVisibility) .divider { + --divider-color: #d7d7db; +} + +@media (prefers-color-scheme: dark) { + :where(html:not(.is-light)) + :is(#highlightParamsToolbarContainer #editorHighlightVisibility) + .divider { + --divider-color: #8f8f9d; + } +} + +:where(html.is-dark) + :is(#highlightParamsToolbarContainer #editorHighlightVisibility) + .divider { + --divider-color: #8f8f9d; +} + +@media screen and (forced-colors: active) { + :is(#highlightParamsToolbarContainer #editorHighlightVisibility) .divider { + --divider-color: CanvasText; + } +} + +:is(#highlightParamsToolbarContainer #editorHighlightVisibility) .divider { + margin-block: 4px; + width: 100%; + height: 1px; + background-color: var(--divider-color); +} + +:is(#highlightParamsToolbarContainer #editorHighlightVisibility) .toggler { + display: flex; + justify-content: space-between; + align-items: center; + align-self: stretch; +} + +#altTextSettingsDialog { + padding: 16px; +} + +#altTextSettingsDialog #altTextSettingsContainer { + display: flex; + width: 573px; + flex-direction: column; + gap: 16px; +} + +:is(#altTextSettingsDialog #altTextSettingsContainer) .mainContainer { + gap: 16px; +} + +:is(#altTextSettingsDialog #altTextSettingsContainer) .description { + color: var(--text-secondary-color); +} + +:is(#altTextSettingsDialog #altTextSettingsContainer) #aiModelSettings { + display: flex; + flex-direction: column; + gap: 12px; +} + +:is(:is(#altTextSettingsDialog #altTextSettingsContainer) #aiModelSettings) + button { + width: -moz-fit-content; + width: fit-content; +} + +.download:is( + :is(#altTextSettingsDialog #altTextSettingsContainer) #aiModelSettings + ) + #deleteModelButton { + display: none; +} + +:is(:is(#altTextSettingsDialog #altTextSettingsContainer) #aiModelSettings):not( + .download + ) + #downloadModelButton { + display: none; +} + +:is(#altTextSettingsDialog #altTextSettingsContainer) #automaticAltText, +:is(#altTextSettingsDialog #altTextSettingsContainer) #altTextEditor { + display: flex; + flex-direction: column; + gap: 8px; +} + +:is(#altTextSettingsDialog #altTextSettingsContainer) #createModelDescription, +:is(#altTextSettingsDialog #altTextSettingsContainer) #aiModelSettings, +:is(#altTextSettingsDialog #altTextSettingsContainer) + #showAltTextDialogDescription { + padding-inline-start: 40px; +} + +:is(#altTextSettingsDialog #altTextSettingsContainer) #automaticSettings { + display: flex; + flex-direction: column; + gap: 16px; +} + +:root { + --viewer-container-height: 0; + --pdfViewer-padding-bottom: 0; + --page-margin: 1px auto -8px; + --page-border: 9px solid transparent; + --spreadHorizontalWrapped-margin-LR: -3.5px; + --loading-icon-delay: 400ms; +} + +@media screen and (forced-colors: active) { + :root { + --pdfViewer-padding-bottom: 9px; + --page-margin: 8px auto -1px; + --page-border: 1px solid CanvasText; + --spreadHorizontalWrapped-margin-LR: 3.5px; + } +} + +[data-main-rotation='90'] { + transform: rotate(90deg) translateY(-100%); +} +[data-main-rotation='180'] { + transform: rotate(180deg) translate(-100%, -100%); +} +[data-main-rotation='270'] { + transform: rotate(270deg) translateX(-100%); } #hiddenCopyElement, -.hiddenCanvasElement{ - position:absolute; - top:0; - left:0; - width:0; - height:0; - display:none; +.hiddenCanvasElement { + position: absolute; + top: 0; + left: 0; + width: 0; + height: 0; + display: none; } -.pdfViewer{ - --scale-factor:1; - --page-bg-color:unset; +.pdfViewer { + --scale-factor: 1; + --page-bg-color: unset; - padding-bottom:var(--pdfViewer-padding-bottom); + padding-bottom: var(--pdfViewer-padding-bottom); - --hcm-highlight-filter:none; - --hcm-highlight-selected-filter:none; + --hcm-highlight-filter: none; + --hcm-highlight-selected-filter: none; } -@media screen and (forced-colors: active){ - -.pdfViewer{ - --hcm-highlight-filter:invert(100%); -} +@media screen and (forced-colors: active) { + .pdfViewer { + --hcm-highlight-filter: invert(100%); } - -.pdfViewer.copyAll{ - cursor:wait; - } - -.pdfViewer .canvasWrapper{ - overflow:hidden; - width:100%; - height:100%; - } - -:is(.pdfViewer .canvasWrapper) canvas{ - position:absolute; - top:0; - left:0; - margin:0; - display:block; - width:100%; - height:100%; - contain:content; - } - -:is(:is(.pdfViewer .canvasWrapper) canvas) .structTree{ - contain:strict; - } - -.pdfViewer .page{ - --scale-round-x:1px; - --scale-round-y:1px; - - direction:ltr; - width:816px; - height:1056px; - margin:var(--page-margin); - position:relative; - overflow:visible; - border:var(--page-border); - background-clip:content-box; - background-color:var(--page-bg-color, rgb(255 255 255)); } -.pdfViewer .dummyPage{ - position:relative; - width:0; - height:var(--viewer-container-height); +.pdfViewer.copyAll { + cursor: wait; } -.pdfViewer.noUserSelect{ - -webkit-user-select:none; - -moz-user-select:none; - user-select:none; +.pdfViewer .canvasWrapper { + overflow: hidden; + width: 100%; + height: 100%; } -.pdfViewer.removePageBorders .page{ - margin:0 auto 10px; - border:none; +:is(.pdfViewer .canvasWrapper) canvas { + position: absolute; + top: 0; + left: 0; + margin: 0; + display: block; + width: 100%; + height: 100%; + contain: content; +} + +:is(:is(.pdfViewer .canvasWrapper) canvas) .structTree { + contain: strict; +} + +.pdfViewer .page { + --scale-round-x: 1px; + --scale-round-y: 1px; + + direction: ltr; + width: 816px; + height: 1056px; + margin: var(--page-margin); + position: relative; + overflow: visible; + border: var(--page-border); + background-clip: content-box; + background-color: var(--page-bg-color, rgb(255 255 255)); +} + +.pdfViewer .dummyPage { + position: relative; + width: 0; + height: var(--viewer-container-height); +} + +.pdfViewer.noUserSelect { + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; +} + +.pdfViewer.removePageBorders .page { + margin: 0 auto 10px; + border: none; } .pdfViewer:is(.scrollHorizontal, .scrollWrapped), -.spread{ - margin-inline:3.5px; - text-align:center; +.spread { + margin-inline: 3.5px; + text-align: center; } .pdfViewer.scrollHorizontal, -.spread{ - white-space:nowrap; +.spread { + white-space: nowrap; } .pdfViewer.removePageBorders, -.pdfViewer:is(.scrollHorizontal, .scrollWrapped) .spread{ - margin-inline:0; +.pdfViewer:is(.scrollHorizontal, .scrollWrapped) .spread { + margin-inline: 0; } .spread :is(.page, .dummyPage), -.pdfViewer:is(.scrollHorizontal, .scrollWrapped) :is(.page, .spread){ - display:inline-block; - vertical-align:middle; +.pdfViewer:is(.scrollHorizontal, .scrollWrapped) :is(.page, .spread) { + display: inline-block; + vertical-align: middle; } .spread .page, -.pdfViewer:is(.scrollHorizontal, .scrollWrapped) .page{ - margin-inline:var(--spreadHorizontalWrapped-margin-LR); +.pdfViewer:is(.scrollHorizontal, .scrollWrapped) .page { + margin-inline: var(--spreadHorizontalWrapped-margin-LR); } .pdfViewer.removePageBorders .spread .page, -.pdfViewer.removePageBorders:is(.scrollHorizontal, .scrollWrapped) .page{ - margin-inline:5px; +.pdfViewer.removePageBorders:is(.scrollHorizontal, .scrollWrapped) .page { + margin-inline: 5px; } -.pdfViewer .page.loadingIcon::after{ - position:absolute; - top:0; - left:0; - content:""; - width:100%; - height:100%; - background:url("images/loading-icon.gif") center no-repeat; - display:none; - transition-property:display; - transition-delay:var(--loading-icon-delay); - z-index:5; - contain:strict; +.pdfViewer .page.loadingIcon::after { + position: absolute; + top: 0; + left: 0; + content: ''; + width: 100%; + height: 100%; + background: url('images/loading-icon.gif') center no-repeat; + display: none; + transition-property: display; + transition-delay: var(--loading-icon-delay); + z-index: 5; + contain: strict; } -.pdfViewer .page.loading::after{ - display:block; +.pdfViewer .page.loading::after { + display: block; } -.pdfViewer .page:not(.loading)::after{ - transition-property:none; - display:none; +.pdfViewer .page:not(.loading)::after { + transition-property: none; + display: none; } -.pdfPresentationMode .pdfViewer{ - padding-bottom:0; +.pdfPresentationMode .pdfViewer { + padding-bottom: 0; } -.pdfPresentationMode .spread{ - margin:0; +.pdfPresentationMode .spread { + margin: 0; } -.pdfPresentationMode .pdfViewer .page{ - margin:0 auto; - border:2px solid transparent; +.pdfPresentationMode .pdfViewer .page { + margin: 0 auto; + border: 2px solid transparent; } -:root{ - --dir-factor:1; - --inline-start:left; - --inline-end:right; +:root { + --dir-factor: 1; + --inline-start: left; + --inline-end: right; - --sidebar-width:200px; - --sidebar-transition-duration:200ms; - --sidebar-transition-timing-function:ease; + --sidebar-width: 200px; + --sidebar-transition-duration: 200ms; + --sidebar-transition-timing-function: ease; - --toolbar-height:32px; - --toolbar-horizontal-padding:1px; - --toolbar-vertical-padding:2px; - --icon-size:16px; + --toolbar-height: 32px; + --toolbar-horizontal-padding: 1px; + --toolbar-vertical-padding: 2px; + --icon-size: 16px; - --toolbar-icon-opacity:0.7; - --doorhanger-icon-opacity:0.9; - --doorhanger-height:8px; + --toolbar-icon-opacity: 0.7; + --doorhanger-icon-opacity: 0.9; + --doorhanger-height: 8px; - --main-color:rgb(12 12 13); - --body-bg-color:rgb(212 212 215); - --progressBar-color:rgb(10 132 255); - --progressBar-bg-color:rgb(221 221 222); - --progressBar-blend-color:rgb(116 177 239); - --scrollbar-color:auto; - --scrollbar-bg-color:auto; - --toolbar-icon-bg-color:rgb(0 0 0); - --toolbar-icon-hover-bg-color:rgb(0 0 0); + --main-color: rgb(12 12 13); + --body-bg-color: rgb(212 212 215); + --progressBar-color: rgb(10 132 255); + --progressBar-bg-color: rgb(221 221 222); + --progressBar-blend-color: rgb(116 177 239); + --scrollbar-color: auto; + --scrollbar-bg-color: auto; + --toolbar-icon-bg-color: rgb(0 0 0); + --toolbar-icon-hover-bg-color: rgb(0 0 0); - --sidebar-narrow-bg-color:rgb(212 212 215 / 0.9); - --sidebar-toolbar-bg-color:rgb(245 246 247); - --toolbar-bg-color:rgb(249 249 250); - --toolbar-border-color:rgb(184 184 184); - --toolbar-box-shadow:0 1px 0 var(--toolbar-border-color); - --toolbar-border-bottom:none; - --toolbarSidebar-box-shadow:inset calc(-1px * var(--dir-factor)) 0 0 rgb(0 0 0 / 0.25), 0 1px 0 rgb(0 0 0 / 0.15), 0 0 1px rgb(0 0 0 / 0.1); - --toolbarSidebar-border-bottom:none; - --button-hover-color:rgb(221 222 223); - --toggled-btn-color:rgb(0 0 0); - --toggled-btn-bg-color:rgb(0 0 0 / 0.3); - --toggled-hover-active-btn-color:rgb(0 0 0 / 0.4); - --toggled-hover-btn-outline:none; - --dropdown-btn-bg-color:rgb(215 215 219); - --dropdown-btn-border:none; - --separator-color:rgb(0 0 0 / 0.3); - --field-color:rgb(6 6 6); - --field-bg-color:rgb(255 255 255); - --field-border-color:rgb(187 187 188); - --treeitem-color:rgb(0 0 0 / 0.8); - --treeitem-bg-color:rgb(0 0 0 / 0.15); - --treeitem-hover-color:rgb(0 0 0 / 0.9); - --treeitem-selected-color:rgb(0 0 0 / 0.9); - --treeitem-selected-bg-color:rgb(0 0 0 / 0.25); - --thumbnail-hover-color:rgb(0 0 0 / 0.1); - --thumbnail-selected-color:rgb(0 0 0 / 0.2); - --doorhanger-bg-color:rgb(255 255 255); - --doorhanger-border-color:rgb(12 12 13 / 0.2); - --doorhanger-hover-color:rgb(12 12 13); - --doorhanger-hover-bg-color:rgb(237 237 237); - --doorhanger-separator-color:rgb(222 222 222); - --dialog-button-border:none; - --dialog-button-bg-color:rgb(12 12 13 / 0.1); - --dialog-button-hover-bg-color:rgb(12 12 13 / 0.3); + --sidebar-narrow-bg-color: rgb(212 212 215 / 0.9); + --sidebar-toolbar-bg-color: rgb(245 246 247); + --toolbar-bg-color: rgb(249 249 250); + --toolbar-border-color: rgb(184 184 184); + --toolbar-box-shadow: 0 1px 0 var(--toolbar-border-color); + --toolbar-border-bottom: none; + --toolbarSidebar-box-shadow: + inset calc(-1px * var(--dir-factor)) 0 0 rgb(0 0 0 / 0.25), + 0 1px 0 rgb(0 0 0 / 0.15), 0 0 1px rgb(0 0 0 / 0.1); + --toolbarSidebar-border-bottom: none; + --button-hover-color: rgb(221 222 223); + --toggled-btn-color: rgb(0 0 0); + --toggled-btn-bg-color: rgb(0 0 0 / 0.3); + --toggled-hover-active-btn-color: rgb(0 0 0 / 0.4); + --toggled-hover-btn-outline: none; + --dropdown-btn-bg-color: rgb(215 215 219); + --dropdown-btn-border: none; + --separator-color: rgb(0 0 0 / 0.3); + --field-color: rgb(6 6 6); + --field-bg-color: rgb(255 255 255); + --field-border-color: rgb(187 187 188); + --treeitem-color: rgb(0 0 0 / 0.8); + --treeitem-bg-color: rgb(0 0 0 / 0.15); + --treeitem-hover-color: rgb(0 0 0 / 0.9); + --treeitem-selected-color: rgb(0 0 0 / 0.9); + --treeitem-selected-bg-color: rgb(0 0 0 / 0.25); + --thumbnail-hover-color: rgb(0 0 0 / 0.1); + --thumbnail-selected-color: rgb(0 0 0 / 0.2); + --doorhanger-bg-color: rgb(255 255 255); + --doorhanger-border-color: rgb(12 12 13 / 0.2); + --doorhanger-hover-color: rgb(12 12 13); + --doorhanger-hover-bg-color: rgb(237 237 237); + --doorhanger-separator-color: rgb(222 222 222); + --dialog-button-border: none; + --dialog-button-bg-color: rgb(12 12 13 / 0.1); + --dialog-button-hover-bg-color: rgb(12 12 13 / 0.3); - --loading-icon:url(images/loading.svg); - --treeitem-expanded-icon:url(images/treeitem-expanded.svg); - --treeitem-collapsed-icon:url(images/treeitem-collapsed.svg); - --toolbarButton-editorFreeText-icon:url(images/toolbarButton-editorFreeText.svg); - --toolbarButton-editorHighlight-icon:url(images/toolbarButton-editorHighlight.svg); - --toolbarButton-editorInk-icon:url(images/toolbarButton-editorInk.svg); - --toolbarButton-editorStamp-icon:url(images/toolbarButton-editorStamp.svg); - --toolbarButton-menuArrow-icon:url(images/toolbarButton-menuArrow.svg); - --toolbarButton-sidebarToggle-icon:url(images/toolbarButton-sidebarToggle.svg); - --toolbarButton-secondaryToolbarToggle-icon:url(images/toolbarButton-secondaryToolbarToggle.svg); - --toolbarButton-pageUp-icon:url(images/toolbarButton-pageUp.svg); - --toolbarButton-pageDown-icon:url(images/toolbarButton-pageDown.svg); - --toolbarButton-zoomOut-icon:url(images/toolbarButton-zoomOut.svg); - --toolbarButton-zoomIn-icon:url(images/toolbarButton-zoomIn.svg); - --toolbarButton-presentationMode-icon:url(images/toolbarButton-presentationMode.svg); - --toolbarButton-print-icon:url(images/toolbarButton-print.svg); - --toolbarButton-openFile-icon:url(images/toolbarButton-openFile.svg); - --toolbarButton-download-icon:url(images/toolbarButton-download.svg); - --toolbarButton-bookmark-icon:url(images/toolbarButton-bookmark.svg); - --toolbarButton-viewThumbnail-icon:url(images/toolbarButton-viewThumbnail.svg); - --toolbarButton-viewOutline-icon:url(images/toolbarButton-viewOutline.svg); - --toolbarButton-viewAttachments-icon:url(images/toolbarButton-viewAttachments.svg); - --toolbarButton-viewLayers-icon:url(images/toolbarButton-viewLayers.svg); - --toolbarButton-currentOutlineItem-icon:url(images/toolbarButton-currentOutlineItem.svg); - --toolbarButton-search-icon:url(images/toolbarButton-search.svg); - --findbarButton-previous-icon:url(images/findbarButton-previous.svg); - --findbarButton-next-icon:url(images/findbarButton-next.svg); - --secondaryToolbarButton-firstPage-icon:url(images/secondaryToolbarButton-firstPage.svg); - --secondaryToolbarButton-lastPage-icon:url(images/secondaryToolbarButton-lastPage.svg); - --secondaryToolbarButton-rotateCcw-icon:url(images/secondaryToolbarButton-rotateCcw.svg); - --secondaryToolbarButton-rotateCw-icon:url(images/secondaryToolbarButton-rotateCw.svg); - --secondaryToolbarButton-selectTool-icon:url(images/secondaryToolbarButton-selectTool.svg); - --secondaryToolbarButton-handTool-icon:url(images/secondaryToolbarButton-handTool.svg); - --secondaryToolbarButton-scrollPage-icon:url(images/secondaryToolbarButton-scrollPage.svg); - --secondaryToolbarButton-scrollVertical-icon:url(images/secondaryToolbarButton-scrollVertical.svg); - --secondaryToolbarButton-scrollHorizontal-icon:url(images/secondaryToolbarButton-scrollHorizontal.svg); - --secondaryToolbarButton-scrollWrapped-icon:url(images/secondaryToolbarButton-scrollWrapped.svg); - --secondaryToolbarButton-spreadNone-icon:url(images/secondaryToolbarButton-spreadNone.svg); - --secondaryToolbarButton-spreadOdd-icon:url(images/secondaryToolbarButton-spreadOdd.svg); - --secondaryToolbarButton-spreadEven-icon:url(images/secondaryToolbarButton-spreadEven.svg); - --secondaryToolbarButton-imageAltTextSettings-icon:var( + --loading-icon: url(images/loading.svg); + --treeitem-expanded-icon: url(images/treeitem-expanded.svg); + --treeitem-collapsed-icon: url(images/treeitem-collapsed.svg); + --toolbarButton-editorFreeText-icon: url(images/toolbarButton-editorFreeText.svg); + --toolbarButton-editorHighlight-icon: url(images/toolbarButton-editorHighlight.svg); + --toolbarButton-editorInk-icon: url(images/toolbarButton-editorInk.svg); + --toolbarButton-editorStamp-icon: url(images/toolbarButton-editorStamp.svg); + --toolbarButton-menuArrow-icon: url(images/toolbarButton-menuArrow.svg); + --toolbarButton-sidebarToggle-icon: url(images/toolbarButton-sidebarToggle.svg); + --toolbarButton-secondaryToolbarToggle-icon: url(images/toolbarButton-secondaryToolbarToggle.svg); + --toolbarButton-pageUp-icon: url(images/toolbarButton-pageUp.svg); + --toolbarButton-pageDown-icon: url(images/toolbarButton-pageDown.svg); + --toolbarButton-zoomOut-icon: url(images/toolbarButton-zoomOut.svg); + --toolbarButton-zoomIn-icon: url(images/toolbarButton-zoomIn.svg); + --toolbarButton-presentationMode-icon: url(images/toolbarButton-presentationMode.svg); + --toolbarButton-print-icon: url(images/toolbarButton-print.svg); + --toolbarButton-openFile-icon: url(images/toolbarButton-openFile.svg); + --toolbarButton-download-icon: url(images/toolbarButton-download.svg); + --toolbarButton-bookmark-icon: url(images/toolbarButton-bookmark.svg); + --toolbarButton-viewThumbnail-icon: url(images/toolbarButton-viewThumbnail.svg); + --toolbarButton-viewOutline-icon: url(images/toolbarButton-viewOutline.svg); + --toolbarButton-viewAttachments-icon: url(images/toolbarButton-viewAttachments.svg); + --toolbarButton-viewLayers-icon: url(images/toolbarButton-viewLayers.svg); + --toolbarButton-currentOutlineItem-icon: url(images/toolbarButton-currentOutlineItem.svg); + --toolbarButton-search-icon: url(images/toolbarButton-search.svg); + --findbarButton-previous-icon: url(images/findbarButton-previous.svg); + --findbarButton-next-icon: url(images/findbarButton-next.svg); + --secondaryToolbarButton-firstPage-icon: url(images/secondaryToolbarButton-firstPage.svg); + --secondaryToolbarButton-lastPage-icon: url(images/secondaryToolbarButton-lastPage.svg); + --secondaryToolbarButton-rotateCcw-icon: url(images/secondaryToolbarButton-rotateCcw.svg); + --secondaryToolbarButton-rotateCw-icon: url(images/secondaryToolbarButton-rotateCw.svg); + --secondaryToolbarButton-selectTool-icon: url(images/secondaryToolbarButton-selectTool.svg); + --secondaryToolbarButton-handTool-icon: url(images/secondaryToolbarButton-handTool.svg); + --secondaryToolbarButton-scrollPage-icon: url(images/secondaryToolbarButton-scrollPage.svg); + --secondaryToolbarButton-scrollVertical-icon: url(images/secondaryToolbarButton-scrollVertical.svg); + --secondaryToolbarButton-scrollHorizontal-icon: url(images/secondaryToolbarButton-scrollHorizontal.svg); + --secondaryToolbarButton-scrollWrapped-icon: url(images/secondaryToolbarButton-scrollWrapped.svg); + --secondaryToolbarButton-spreadNone-icon: url(images/secondaryToolbarButton-spreadNone.svg); + --secondaryToolbarButton-spreadOdd-icon: url(images/secondaryToolbarButton-spreadOdd.svg); + --secondaryToolbarButton-spreadEven-icon: url(images/secondaryToolbarButton-spreadEven.svg); + --secondaryToolbarButton-imageAltTextSettings-icon: var( --toolbarButton-editorStamp-icon ); - --secondaryToolbarButton-documentProperties-icon:url(images/secondaryToolbarButton-documentProperties.svg); - --editorParams-stampAddImage-icon:url(images/toolbarButton-zoomIn.svg); + --secondaryToolbarButton-documentProperties-icon: url(images/secondaryToolbarButton-documentProperties.svg); + --editorParams-stampAddImage-icon: url(images/toolbarButton-zoomIn.svg); } -[dir="rtl"]:root{ - --dir-factor:-1; - --inline-start:right; - --inline-end:left; +[dir='rtl']:root { + --dir-factor: -1; + --inline-start: right; + --inline-end: left; } -@media (prefers-color-scheme: dark){ - :root:where(:not(.is-light)){ - --main-color:rgb(249 249 250); - --body-bg-color:rgb(42 42 46); - --progressBar-color:rgb(0 96 223); - --progressBar-bg-color:rgb(40 40 43); - --progressBar-blend-color:rgb(20 68 133); - --scrollbar-color:rgb(121 121 123); - --scrollbar-bg-color:rgb(35 35 39); - --toolbar-icon-bg-color:rgb(255 255 255); - --toolbar-icon-hover-bg-color:rgb(255 255 255); +@media (prefers-color-scheme: dark) { + :root:where(:not(.is-light)) { + --main-color: rgb(249 249 250); + --body-bg-color: rgb(42 42 46); + --progressBar-color: rgb(0 96 223); + --progressBar-bg-color: rgb(40 40 43); + --progressBar-blend-color: rgb(20 68 133); + --scrollbar-color: rgb(121 121 123); + --scrollbar-bg-color: rgb(35 35 39); + --toolbar-icon-bg-color: rgb(255 255 255); + --toolbar-icon-hover-bg-color: rgb(255 255 255); - --sidebar-narrow-bg-color:rgb(42 42 46 / 0.9); - --sidebar-toolbar-bg-color:rgb(50 50 52); - --toolbar-bg-color:rgb(56 56 61); - --toolbar-border-color:rgb(12 12 13); - --button-hover-color:rgb(102 102 103); - --toggled-btn-color:rgb(255 255 255); - --toggled-btn-bg-color:rgb(0 0 0 / 0.3); - --toggled-hover-active-btn-color:rgb(0 0 0 / 0.4); - --dropdown-btn-bg-color:rgb(74 74 79); - --separator-color:rgb(0 0 0 / 0.3); - --field-color:rgb(250 250 250); - --field-bg-color:rgb(64 64 68); - --field-border-color:rgb(115 115 115); - --treeitem-color:rgb(255 255 255 / 0.8); - --treeitem-bg-color:rgb(255 255 255 / 0.15); - --treeitem-hover-color:rgb(255 255 255 / 0.9); - --treeitem-selected-color:rgb(255 255 255 / 0.9); - --treeitem-selected-bg-color:rgb(255 255 255 / 0.25); - --thumbnail-hover-color:rgb(255 255 255 / 0.1); - --thumbnail-selected-color:rgb(255 255 255 / 0.2); - --doorhanger-bg-color:rgb(74 74 79); - --doorhanger-border-color:rgb(39 39 43); - --doorhanger-hover-color:rgb(249 249 250); - --doorhanger-hover-bg-color:rgb(93 94 98); - --doorhanger-separator-color:rgb(92 92 97); - --dialog-button-bg-color:rgb(92 92 97); - --dialog-button-hover-bg-color:rgb(115 115 115); + --sidebar-narrow-bg-color: rgb(42 42 46 / 0.9); + --sidebar-toolbar-bg-color: rgb(50 50 52); + --toolbar-bg-color: rgb(56 56 61); + --toolbar-border-color: rgb(12 12 13); + --button-hover-color: rgb(102 102 103); + --toggled-btn-color: rgb(255 255 255); + --toggled-btn-bg-color: rgb(0 0 0 / 0.3); + --toggled-hover-active-btn-color: rgb(0 0 0 / 0.4); + --dropdown-btn-bg-color: rgb(74 74 79); + --separator-color: rgb(0 0 0 / 0.3); + --field-color: rgb(250 250 250); + --field-bg-color: rgb(64 64 68); + --field-border-color: rgb(115 115 115); + --treeitem-color: rgb(255 255 255 / 0.8); + --treeitem-bg-color: rgb(255 255 255 / 0.15); + --treeitem-hover-color: rgb(255 255 255 / 0.9); + --treeitem-selected-color: rgb(255 255 255 / 0.9); + --treeitem-selected-bg-color: rgb(255 255 255 / 0.25); + --thumbnail-hover-color: rgb(255 255 255 / 0.1); + --thumbnail-selected-color: rgb(255 255 255 / 0.2); + --doorhanger-bg-color: rgb(74 74 79); + --doorhanger-border-color: rgb(39 39 43); + --doorhanger-hover-color: rgb(249 249 250); + --doorhanger-hover-bg-color: rgb(93 94 98); + --doorhanger-separator-color: rgb(92 92 97); + --dialog-button-bg-color: rgb(92 92 97); + --dialog-button-hover-bg-color: rgb(115 115 115); } } -:root:where(.is-dark){ - --main-color:rgb(249 249 250); - --body-bg-color:rgb(42 42 46); - --progressBar-color:rgb(0 96 223); - --progressBar-bg-color:rgb(40 40 43); - --progressBar-blend-color:rgb(20 68 133); - --scrollbar-color:rgb(121 121 123); - --scrollbar-bg-color:rgb(35 35 39); - --toolbar-icon-bg-color:rgb(255 255 255); - --toolbar-icon-hover-bg-color:rgb(255 255 255); +:root:where(.is-dark) { + --main-color: rgb(249 249 250); + --body-bg-color: rgb(42 42 46); + --progressBar-color: rgb(0 96 223); + --progressBar-bg-color: rgb(40 40 43); + --progressBar-blend-color: rgb(20 68 133); + --scrollbar-color: rgb(121 121 123); + --scrollbar-bg-color: rgb(35 35 39); + --toolbar-icon-bg-color: rgb(255 255 255); + --toolbar-icon-hover-bg-color: rgb(255 255 255); - --sidebar-narrow-bg-color:rgb(42 42 46 / 0.9); - --sidebar-toolbar-bg-color:rgb(50 50 52); - --toolbar-bg-color:rgb(56 56 61); - --toolbar-border-color:rgb(12 12 13); - --button-hover-color:rgb(102 102 103); - --toggled-btn-color:rgb(255 255 255); - --toggled-btn-bg-color:rgb(0 0 0 / 0.3); - --toggled-hover-active-btn-color:rgb(0 0 0 / 0.4); - --dropdown-btn-bg-color:rgb(74 74 79); - --separator-color:rgb(0 0 0 / 0.3); - --field-color:rgb(250 250 250); - --field-bg-color:rgb(64 64 68); - --field-border-color:rgb(115 115 115); - --treeitem-color:rgb(255 255 255 / 0.8); - --treeitem-bg-color:rgb(255 255 255 / 0.15); - --treeitem-hover-color:rgb(255 255 255 / 0.9); - --treeitem-selected-color:rgb(255 255 255 / 0.9); - --treeitem-selected-bg-color:rgb(255 255 255 / 0.25); - --thumbnail-hover-color:rgb(255 255 255 / 0.1); - --thumbnail-selected-color:rgb(255 255 255 / 0.2); - --doorhanger-bg-color:rgb(74 74 79); - --doorhanger-border-color:rgb(39 39 43); - --doorhanger-hover-color:rgb(249 249 250); - --doorhanger-hover-bg-color:rgb(93 94 98); - --doorhanger-separator-color:rgb(92 92 97); - --dialog-button-bg-color:rgb(92 92 97); - --dialog-button-hover-bg-color:rgb(115 115 115); - } + --sidebar-narrow-bg-color: rgb(42 42 46 / 0.9); + --sidebar-toolbar-bg-color: rgb(50 50 52); + --toolbar-bg-color: rgb(56 56 61); + --toolbar-border-color: rgb(12 12 13); + --button-hover-color: rgb(102 102 103); + --toggled-btn-color: rgb(255 255 255); + --toggled-btn-bg-color: rgb(0 0 0 / 0.3); + --toggled-hover-active-btn-color: rgb(0 0 0 / 0.4); + --dropdown-btn-bg-color: rgb(74 74 79); + --separator-color: rgb(0 0 0 / 0.3); + --field-color: rgb(250 250 250); + --field-bg-color: rgb(64 64 68); + --field-border-color: rgb(115 115 115); + --treeitem-color: rgb(255 255 255 / 0.8); + --treeitem-bg-color: rgb(255 255 255 / 0.15); + --treeitem-hover-color: rgb(255 255 255 / 0.9); + --treeitem-selected-color: rgb(255 255 255 / 0.9); + --treeitem-selected-bg-color: rgb(255 255 255 / 0.25); + --thumbnail-hover-color: rgb(255 255 255 / 0.1); + --thumbnail-selected-color: rgb(255 255 255 / 0.2); + --doorhanger-bg-color: rgb(74 74 79); + --doorhanger-border-color: rgb(39 39 43); + --doorhanger-hover-color: rgb(249 249 250); + --doorhanger-hover-bg-color: rgb(93 94 98); + --doorhanger-separator-color: rgb(92 92 97); + --dialog-button-bg-color: rgb(92 92 97); + --dialog-button-hover-bg-color: rgb(115 115 115); +} -@media screen and (forced-colors: active){ - :root{ - --button-hover-color:Highlight; - --doorhanger-hover-bg-color:Highlight; - --toolbar-icon-opacity:1; - --toolbar-icon-bg-color:ButtonText; - --toolbar-icon-hover-bg-color:ButtonFace; - --toggled-hover-active-btn-color:ButtonText; - --toggled-hover-btn-outline:2px solid ButtonBorder; - --toolbar-border-color:CanvasText; - --toolbar-border-bottom:1px solid var(--toolbar-border-color); - --toolbar-box-shadow:none; - --toggled-btn-color:HighlightText; - --toggled-btn-bg-color:LinkText; - --doorhanger-hover-color:ButtonFace; - --doorhanger-border-color-whcm:1px solid ButtonText; - --doorhanger-triangle-opacity-whcm:0; - --dialog-button-border:1px solid Highlight; - --dialog-button-hover-bg-color:Highlight; - --dialog-button-hover-color:ButtonFace; - --dropdown-btn-border:1px solid ButtonText; - --field-border-color:ButtonText; - --main-color:CanvasText; - --separator-color:GrayText; - --doorhanger-separator-color:GrayText; - --toolbarSidebar-box-shadow:none; - --toolbarSidebar-border-bottom:1px solid var(--toolbar-border-color); +@media screen and (forced-colors: active) { + :root { + --button-hover-color: Highlight; + --doorhanger-hover-bg-color: Highlight; + --toolbar-icon-opacity: 1; + --toolbar-icon-bg-color: ButtonText; + --toolbar-icon-hover-bg-color: ButtonFace; + --toggled-hover-active-btn-color: ButtonText; + --toggled-hover-btn-outline: 2px solid ButtonBorder; + --toolbar-border-color: CanvasText; + --toolbar-border-bottom: 1px solid var(--toolbar-border-color); + --toolbar-box-shadow: none; + --toggled-btn-color: HighlightText; + --toggled-btn-bg-color: LinkText; + --doorhanger-hover-color: ButtonFace; + --doorhanger-border-color-whcm: 1px solid ButtonText; + --doorhanger-triangle-opacity-whcm: 0; + --dialog-button-border: 1px solid Highlight; + --dialog-button-hover-bg-color: Highlight; + --dialog-button-hover-color: ButtonFace; + --dropdown-btn-border: 1px solid ButtonText; + --field-border-color: ButtonText; + --main-color: CanvasText; + --separator-color: GrayText; + --doorhanger-separator-color: GrayText; + --toolbarSidebar-box-shadow: none; + --toolbarSidebar-border-bottom: 1px solid var(--toolbar-border-color); } } -@media screen and (prefers-reduced-motion: reduce){ - :root{ - --sidebar-transition-duration:0; +@media screen and (prefers-reduced-motion: reduce) { + :root { + --sidebar-transition-duration: 0; } } -@keyframes progressIndeterminate{ - 0%{ - transform:translateX(calc(-142px * var(--dir-factor))); +@keyframes progressIndeterminate { + 0% { + transform: translateX(calc(-142px * var(--dir-factor))); } - 100%{ - transform:translateX(0); + 100% { + transform: translateX(0); } } -html[data-toolbar-density="compact"]{ - --toolbar-height:30px; - } +html[data-toolbar-density='compact'] { + --toolbar-height: 30px; +} -html[data-toolbar-density="touch"]{ - --toolbar-height:44px; - } +html[data-toolbar-density='touch'] { + --toolbar-height: 44px; +} html, -body{ - height:100%; - width:100%; +body { + height: 100%; + width: 100%; } -body{ - margin:0; - background-color:var(--body-bg-color); - scrollbar-color:var(--scrollbar-color) var(--scrollbar-bg-color); +body { + margin: 0; + background-color: var(--body-bg-color); + scrollbar-color: var(--scrollbar-color) var(--scrollbar-bg-color); } -body.wait::before{ - content:""; - position:fixed; - width:100%; - height:100%; - z-index:100000; - cursor:wait; - } +body.wait::before { + content: ''; + position: fixed; + width: 100%; + height: 100%; + z-index: 100000; + cursor: wait; +} .hidden, -[hidden]{ - display:none !important; +[hidden] { + display: none !important; } -#viewerContainer.pdfPresentationMode:fullscreen{ - top:0; - background-color:rgb(0 0 0); - width:100%; - height:100%; - overflow:hidden; - cursor:none; - -webkit-user-select:none; - -moz-user-select:none; - user-select:none; +#viewerContainer.pdfPresentationMode:fullscreen { + top: 0; + background-color: rgb(0 0 0); + width: 100%; + height: 100%; + overflow: hidden; + cursor: none; + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; } -.pdfPresentationMode:fullscreen section:not([data-internal-link]){ - pointer-events:none; +.pdfPresentationMode:fullscreen section:not([data-internal-link]) { + pointer-events: none; } -.pdfPresentationMode:fullscreen .textLayer span{ - cursor:none; +.pdfPresentationMode:fullscreen .textLayer span { + cursor: none; } .pdfPresentationMode.pdfPresentationModeControls > *, -.pdfPresentationMode.pdfPresentationModeControls .textLayer span{ - cursor:default; +.pdfPresentationMode.pdfPresentationModeControls .textLayer span { + cursor: default; } -#outerContainer{ - width:100%; - height:100%; - position:relative; - margin:0; +#outerContainer { + width: 100%; + height: 100%; + position: relative; + margin: 0; } -#sidebarContainer{ - position:absolute; - inset-block:var(--toolbar-height) 0; - inset-inline-start:calc(-1 * var(--sidebar-width)); - width:var(--sidebar-width); - visibility:hidden; - z-index:1; - font:message-box; - border-top:1px solid transparent; - border-inline-end:var(--doorhanger-border-color-whcm); - transition-property:inset-inline-start; - transition-duration:var(--sidebar-transition-duration); - transition-timing-function:var(--sidebar-transition-timing-function); +#sidebarContainer { + position: absolute; + inset-block: var(--toolbar-height) 0; + inset-inline-start: calc(-1 * var(--sidebar-width)); + width: var(--sidebar-width); + visibility: hidden; + z-index: 1; + font: message-box; + border-top: 1px solid transparent; + border-inline-end: var(--doorhanger-border-color-whcm); + transition-property: inset-inline-start; + transition-duration: var(--sidebar-transition-duration); + transition-timing-function: var(--sidebar-transition-timing-function); } -#outerContainer:is(.sidebarMoving, .sidebarOpen) #sidebarContainer{ - visibility:visible; +#outerContainer:is(.sidebarMoving, .sidebarOpen) #sidebarContainer { + visibility: visible; } -#outerContainer.sidebarOpen #sidebarContainer{ - inset-inline-start:0; +#outerContainer.sidebarOpen #sidebarContainer { + inset-inline-start: 0; } -#mainContainer{ - position:absolute; - inset:0; - min-width:350px; - margin:0; - display:flex; - flex-direction:column; +#mainContainer { + position: absolute; + inset: 0; + min-width: 350px; + margin: 0; + display: flex; + flex-direction: column; } -#sidebarContent{ - inset-block:var(--toolbar-height) 0; - inset-inline-start:0; - overflow:auto; - position:absolute; - width:100%; - box-shadow:inset calc(-1px * var(--dir-factor)) 0 0 rgb(0 0 0 / 0.25); +#sidebarContent { + inset-block: var(--toolbar-height) 0; + inset-inline-start: 0; + overflow: auto; + position: absolute; + width: 100%; + box-shadow: inset calc(-1px * var(--dir-factor)) 0 0 rgb(0 0 0 / 0.25); } -#viewerContainer{ - overflow:auto; - position:absolute; - inset:var(--toolbar-height) 0 0; - outline:none; - z-index:0; +#viewerContainer { + overflow: auto; + position: absolute; + inset: var(--toolbar-height) 0 0; + outline: none; + z-index: 0; } -#viewerContainer:not(.pdfPresentationMode){ - transition-duration:var(--sidebar-transition-duration); - transition-timing-function:var(--sidebar-transition-timing-function); +#viewerContainer:not(.pdfPresentationMode) { + transition-duration: var(--sidebar-transition-duration); + transition-timing-function: var(--sidebar-transition-timing-function); } -#outerContainer.sidebarOpen #viewerContainer:not(.pdfPresentationMode){ - inset-inline-start:var(--sidebar-width); - transition-property:inset-inline-start; +#outerContainer.sidebarOpen #viewerContainer:not(.pdfPresentationMode) { + inset-inline-start: var(--sidebar-width); + transition-property: inset-inline-start; } -#sidebarContainer :is(input, button, select){ - font:message-box; +#sidebarContainer :is(input, button, select) { + font: message-box; } -.toolbar{ - z-index:2; +.toolbar { + z-index: 2; } -#toolbarSidebar{ - width:100%; - height:var(--toolbar-height); - background-color:var(--sidebar-toolbar-bg-color); - box-shadow:var(--toolbarSidebar-box-shadow); - border-bottom:var(--toolbarSidebar-border-bottom); - padding:var(--toolbar-vertical-padding) var(--toolbar-horizontal-padding); - justify-content:space-between; +#toolbarSidebar { + width: 100%; + height: var(--toolbar-height); + background-color: var(--sidebar-toolbar-bg-color); + box-shadow: var(--toolbarSidebar-box-shadow); + border-bottom: var(--toolbarSidebar-border-bottom); + padding: var(--toolbar-vertical-padding) var(--toolbar-horizontal-padding); + justify-content: space-between; } -#toolbarSidebar #toolbarSidebarLeft{ - width:auto; - height:100%; - } - -:is(#toolbarSidebar #toolbarSidebarLeft) #viewThumbnail::before{ - -webkit-mask-image:var(--toolbarButton-viewThumbnail-icon); - mask-image:var(--toolbarButton-viewThumbnail-icon); - } - -:is(#toolbarSidebar #toolbarSidebarLeft) #viewOutline::before{ - -webkit-mask-image:var(--toolbarButton-viewOutline-icon); - mask-image:var(--toolbarButton-viewOutline-icon); - transform:scaleX(var(--dir-factor)); - } - -:is(#toolbarSidebar #toolbarSidebarLeft) #viewAttachments::before{ - -webkit-mask-image:var(--toolbarButton-viewAttachments-icon); - mask-image:var(--toolbarButton-viewAttachments-icon); - } - -:is(#toolbarSidebar #toolbarSidebarLeft) #viewLayers::before{ - -webkit-mask-image:var(--toolbarButton-viewLayers-icon); - mask-image:var(--toolbarButton-viewLayers-icon); - } - -#toolbarSidebar #toolbarSidebarRight{ - width:auto; - height:100%; - padding-inline-end:2px; - } - -#sidebarResizer{ - position:absolute; - inset-block:0; - inset-inline-end:-6px; - width:6px; - z-index:200; - cursor:ew-resize; +#toolbarSidebar #toolbarSidebarLeft { + width: auto; + height: 100%; } -#outerContainer.sidebarOpen #loadingBar{ - inset-inline-start:var(--sidebar-width); +:is(#toolbarSidebar #toolbarSidebarLeft) #viewThumbnail::before { + -webkit-mask-image: var(--toolbarButton-viewThumbnail-icon); + mask-image: var(--toolbarButton-viewThumbnail-icon); +} + +:is(#toolbarSidebar #toolbarSidebarLeft) #viewOutline::before { + -webkit-mask-image: var(--toolbarButton-viewOutline-icon); + mask-image: var(--toolbarButton-viewOutline-icon); + transform: scaleX(var(--dir-factor)); +} + +:is(#toolbarSidebar #toolbarSidebarLeft) #viewAttachments::before { + -webkit-mask-image: var(--toolbarButton-viewAttachments-icon); + mask-image: var(--toolbarButton-viewAttachments-icon); +} + +:is(#toolbarSidebar #toolbarSidebarLeft) #viewLayers::before { + -webkit-mask-image: var(--toolbarButton-viewLayers-icon); + mask-image: var(--toolbarButton-viewLayers-icon); +} + +#toolbarSidebar #toolbarSidebarRight { + width: auto; + height: 100%; + padding-inline-end: 2px; +} + +#sidebarResizer { + position: absolute; + inset-block: 0; + inset-inline-end: -6px; + width: 6px; + z-index: 200; + cursor: ew-resize; +} + +#outerContainer.sidebarOpen #loadingBar { + inset-inline-start: var(--sidebar-width); } #outerContainer.sidebarResizing - :is(#sidebarContainer, #viewerContainer, #loadingBar){ - transition-duration:0s; + :is(#sidebarContainer, #viewerContainer, #loadingBar) { + transition-duration: 0s; } .doorHanger, -.doorHangerRight{ - border-radius:2px; - box-shadow:0 1px 5px var(--doorhanger-border-color), 0 0 0 1px var(--doorhanger-border-color); - border:var(--doorhanger-border-color-whcm); - background-color:var(--doorhanger-bg-color); - inset-block-start:calc(100% + var(--doorhanger-height) - 2px); +.doorHangerRight { + border-radius: 2px; + box-shadow: + 0 1px 5px var(--doorhanger-border-color), + 0 0 0 1px var(--doorhanger-border-color); + border: var(--doorhanger-border-color-whcm); + background-color: var(--doorhanger-bg-color); + inset-block-start: calc(100% + var(--doorhanger-height) - 2px); } -:is(.doorHanger,.doorHangerRight)::after,:is(.doorHanger,.doorHangerRight)::before{ - bottom:100%; - border-style:solid; - border-color:transparent; - content:""; - height:0; - width:0; - position:absolute; - pointer-events:none; - opacity:var(--doorhanger-triangle-opacity-whcm); - } - -:is(.doorHanger,.doorHangerRight)::before{ - border-width:calc(var(--doorhanger-height) + 2px); - border-bottom-color:var(--doorhanger-border-color); - } - -:is(.doorHanger,.doorHangerRight)::after{ - border-width:var(--doorhanger-height); - } - -.doorHangerRight{ - inset-inline-end:calc(50% - var(--doorhanger-height) - 1px); +:is(.doorHanger, .doorHangerRight)::after, +:is(.doorHanger, .doorHangerRight)::before { + bottom: 100%; + border-style: solid; + border-color: transparent; + content: ''; + height: 0; + width: 0; + position: absolute; + pointer-events: none; + opacity: var(--doorhanger-triangle-opacity-whcm); } -.doorHangerRight::before{ - inset-inline-end:-1px; - } - -.doorHangerRight::after{ - border-bottom-color:var(--doorhanger-bg-color); - inset-inline-end:1px; - } - -.doorHanger{ - inset-inline-start:calc(50% - var(--doorhanger-height) - 1px); +:is(.doorHanger, .doorHangerRight)::before { + border-width: calc(var(--doorhanger-height) + 2px); + border-bottom-color: var(--doorhanger-border-color); } -.doorHanger::before{ - inset-inline-start:-1px; - } - -.doorHanger::after{ - border-bottom-color:var(--toolbar-bg-color); - inset-inline-start:1px; - } - -.dialogButton{ - border:none; - background:none; - width:28px; - height:28px; - outline:none; +:is(.doorHanger, .doorHangerRight)::after { + border-width: var(--doorhanger-height); } -.dialogButton:is(:hover, :focus-visible){ - background-color:var(--dialog-button-hover-bg-color); +.doorHangerRight { + inset-inline-end: calc(50% - var(--doorhanger-height) - 1px); } -.dialogButton:is(:hover, :focus-visible) > span{ - color:var(--dialog-button-hover-color); +.doorHangerRight::before { + inset-inline-end: -1px; } -.splitToolbarButtonSeparator{ - float:var(--inline-start); - width:0; - height:62%; - border-left:1px solid var(--separator-color); - border-right:none; +.doorHangerRight::after { + border-bottom-color: var(--doorhanger-bg-color); + inset-inline-end: 1px; } -.dialogButton{ - min-width:16px; - margin:2px 1px; - padding:2px 6px 0; - border:none; - border-radius:2px; - color:var(--main-color); - font-size:12px; - line-height:14px; - -webkit-user-select:none; - -moz-user-select:none; - user-select:none; - cursor:default; - box-sizing:border-box; +.doorHanger { + inset-inline-start: calc(50% - var(--doorhanger-height) - 1px); } -.treeItemToggler::before{ - position:absolute; - display:inline-block; - width:16px; - height:16px; - - content:""; - background-color:var(--toolbar-icon-bg-color); - -webkit-mask-size:cover; - mask-size:cover; +.doorHanger::before { + inset-inline-start: -1px; } -#sidebarToggleButton::before{ - -webkit-mask-image:var(--toolbarButton-sidebarToggle-icon); - mask-image:var(--toolbarButton-sidebarToggle-icon); - transform:scaleX(var(--dir-factor)); +.doorHanger::after { + border-bottom-color: var(--toolbar-bg-color); + inset-inline-start: 1px; } -#secondaryToolbarToggleButton::before{ - -webkit-mask-image:var(--toolbarButton-secondaryToolbarToggle-icon); - mask-image:var(--toolbarButton-secondaryToolbarToggle-icon); - transform:scaleX(var(--dir-factor)); +.dialogButton { + border: none; + background: none; + width: 28px; + height: 28px; + outline: none; } -#previous::before{ - -webkit-mask-image:var(--toolbarButton-pageUp-icon); - mask-image:var(--toolbarButton-pageUp-icon); +.dialogButton:is(:hover, :focus-visible) { + background-color: var(--dialog-button-hover-bg-color); } -#next::before{ - -webkit-mask-image:var(--toolbarButton-pageDown-icon); - mask-image:var(--toolbarButton-pageDown-icon); +.dialogButton:is(:hover, :focus-visible) > span { + color: var(--dialog-button-hover-color); } -#zoomOutButton::before{ - -webkit-mask-image:var(--toolbarButton-zoomOut-icon); - mask-image:var(--toolbarButton-zoomOut-icon); +.splitToolbarButtonSeparator { + float: var(--inline-start); + width: 0; + height: 62%; + border-left: 1px solid var(--separator-color); + border-right: none; } -#zoomInButton::before{ - -webkit-mask-image:var(--toolbarButton-zoomIn-icon); - mask-image:var(--toolbarButton-zoomIn-icon); +.dialogButton { + min-width: 16px; + margin: 2px 1px; + padding: 2px 6px 0; + border: none; + border-radius: 2px; + color: var(--main-color); + font-size: 12px; + line-height: 14px; + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; + cursor: default; + box-sizing: border-box; } -#presentationMode::before{ - -webkit-mask-image:var(--toolbarButton-presentationMode-icon); - mask-image:var(--toolbarButton-presentationMode-icon); +.treeItemToggler::before { + position: absolute; + display: inline-block; + width: 16px; + height: 16px; + + content: ''; + background-color: var(--toolbar-icon-bg-color); + -webkit-mask-size: cover; + mask-size: cover; } -#editorFreeTextButton::before{ - -webkit-mask-image:var(--toolbarButton-editorFreeText-icon); - mask-image:var(--toolbarButton-editorFreeText-icon); +#sidebarToggleButton::before { + -webkit-mask-image: var(--toolbarButton-sidebarToggle-icon); + mask-image: var(--toolbarButton-sidebarToggle-icon); + transform: scaleX(var(--dir-factor)); } -#editorHighlightButton::before{ - -webkit-mask-image:var(--toolbarButton-editorHighlight-icon); - mask-image:var(--toolbarButton-editorHighlight-icon); +#secondaryToolbarToggleButton::before { + -webkit-mask-image: var(--toolbarButton-secondaryToolbarToggle-icon); + mask-image: var(--toolbarButton-secondaryToolbarToggle-icon); + transform: scaleX(var(--dir-factor)); } -#editorInkButton::before{ - -webkit-mask-image:var(--toolbarButton-editorInk-icon); - mask-image:var(--toolbarButton-editorInk-icon); +#previous::before { + -webkit-mask-image: var(--toolbarButton-pageUp-icon); + mask-image: var(--toolbarButton-pageUp-icon); } -#editorStampButton::before{ - -webkit-mask-image:var(--toolbarButton-editorStamp-icon); - mask-image:var(--toolbarButton-editorStamp-icon); +#next::before { + -webkit-mask-image: var(--toolbarButton-pageDown-icon); + mask-image: var(--toolbarButton-pageDown-icon); } -#printButton::before{ - -webkit-mask-image:var(--toolbarButton-print-icon); - mask-image:var(--toolbarButton-print-icon); +#zoomOutButton::before { + -webkit-mask-image: var(--toolbarButton-zoomOut-icon); + mask-image: var(--toolbarButton-zoomOut-icon); } -#secondaryOpenFile::before{ - -webkit-mask-image:var(--toolbarButton-openFile-icon); - mask-image:var(--toolbarButton-openFile-icon); +#zoomInButton::before { + -webkit-mask-image: var(--toolbarButton-zoomIn-icon); + mask-image: var(--toolbarButton-zoomIn-icon); } -#downloadButton::before{ - -webkit-mask-image:var(--toolbarButton-download-icon); - mask-image:var(--toolbarButton-download-icon); +#presentationMode::before { + -webkit-mask-image: var(--toolbarButton-presentationMode-icon); + mask-image: var(--toolbarButton-presentationMode-icon); } -#viewBookmark::before{ - -webkit-mask-image:var(--toolbarButton-bookmark-icon); - mask-image:var(--toolbarButton-bookmark-icon); +#editorFreeTextButton::before { + -webkit-mask-image: var(--toolbarButton-editorFreeText-icon); + mask-image: var(--toolbarButton-editorFreeText-icon); } -#currentOutlineItem::before{ - -webkit-mask-image:var(--toolbarButton-currentOutlineItem-icon); - mask-image:var(--toolbarButton-currentOutlineItem-icon); - transform:scaleX(var(--dir-factor)); +#editorHighlightButton::before { + -webkit-mask-image: var(--toolbarButton-editorHighlight-icon); + mask-image: var(--toolbarButton-editorHighlight-icon); } -#viewFindButton::before{ - -webkit-mask-image:var(--toolbarButton-search-icon); - mask-image:var(--toolbarButton-search-icon); +#editorInkButton::before { + -webkit-mask-image: var(--toolbarButton-editorInk-icon); + mask-image: var(--toolbarButton-editorInk-icon); } -.pdfSidebarNotification::after{ - position:absolute; - display:inline-block; - top:2px; - inset-inline-end:2px; - content:""; - background-color:rgb(112 219 85); - height:9px; - width:9px; - border-radius:50%; +#editorStampButton::before { + -webkit-mask-image: var(--toolbarButton-editorStamp-icon); + mask-image: var(--toolbarButton-editorStamp-icon); } -.verticalToolbarSeparator{ - display:block; - margin-inline:2px; - width:0; - height:80%; - border-left:1px solid var(--separator-color); - border-right:none; - box-sizing:border-box; +#printButton::before { + -webkit-mask-image: var(--toolbarButton-print-icon); + mask-image: var(--toolbarButton-print-icon); } -.horizontalToolbarSeparator{ - display:block; - margin:6px 0; - border-top:1px solid var(--doorhanger-separator-color); - border-bottom:none; - height:0; - width:100%; +#secondaryOpenFile::before { + -webkit-mask-image: var(--toolbarButton-openFile-icon); + mask-image: var(--toolbarButton-openFile-icon); } -.toggleButton{ - display:inline; +#downloadButton::before { + -webkit-mask-image: var(--toolbarButton-download-icon); + mask-image: var(--toolbarButton-download-icon); } -.toggleButton:has( > input:checked){ - color:var(--toggled-btn-color); - background-color:var(--toggled-btn-bg-color); - } - -.toggleButton:is(:hover,:has( > input:focus-visible)){ - color:var(--toggled-btn-color); - background-color:var(--button-hover-color); - } - -.toggleButton > input{ - position:absolute; - top:50%; - left:50%; - opacity:0; - width:0; - height:0; - } - -.toolbarField{ - padding:4px 7px; - margin:3px 0; - border-radius:2px; - background-color:var(--field-bg-color); - background-clip:padding-box; - border:1px solid var(--field-border-color); - box-shadow:none; - color:var(--field-color); - font-size:12px; - line-height:16px; - outline:none; +#viewBookmark::before { + -webkit-mask-image: var(--toolbarButton-bookmark-icon); + mask-image: var(--toolbarButton-bookmark-icon); } -.toolbarField:focus{ - border-color:#0a84ff; - } - -#pageNumber{ - -moz-appearance:textfield; - text-align:end; - width:40px; - background-size:0 0; - transition-property:none; +#currentOutlineItem::before { + -webkit-mask-image: var(--toolbarButton-currentOutlineItem-icon); + mask-image: var(--toolbarButton-currentOutlineItem-icon); + transform: scaleX(var(--dir-factor)); } -#pageNumber::-webkit-inner-spin-button{ - -webkit-appearance:none; - } - -.loadingInput:has( > .loading:is(#pageNumber))::after{ - display:inline; - visibility:visible; - - transition-property:visibility; - transition-delay:var(--loading-icon-delay); - } - -.loadingInput{ - position:relative; +#viewFindButton::before { + -webkit-mask-image: var(--toolbarButton-search-icon); + mask-image: var(--toolbarButton-search-icon); } -.loadingInput::after{ - position:absolute; - visibility:hidden; - display:none; - width:var(--icon-size); - height:var(--icon-size); +.pdfSidebarNotification::after { + position: absolute; + display: inline-block; + top: 2px; + inset-inline-end: 2px; + content: ''; + background-color: rgb(112 219 85); + height: 9px; + width: 9px; + border-radius: 50%; +} - content:""; - background-color:var(--toolbar-icon-bg-color); - -webkit-mask-size:cover; - mask-size:cover; - -webkit-mask-image:var(--loading-icon); - mask-image:var(--loading-icon); - } +.verticalToolbarSeparator { + display: block; + margin-inline: 2px; + width: 0; + height: 80%; + border-left: 1px solid var(--separator-color); + border-right: none; + box-sizing: border-box; +} -.loadingInput.start::after{ - inset-inline-start:4px; - } +.horizontalToolbarSeparator { + display: block; + margin: 6px 0; + border-top: 1px solid var(--doorhanger-separator-color); + border-bottom: none; + height: 0; + width: 100%; +} -.loadingInput.end::after{ - inset-inline-end:4px; - } +.toggleButton { + display: inline; +} + +.toggleButton:has(> input:checked) { + color: var(--toggled-btn-color); + background-color: var(--toggled-btn-bg-color); +} + +.toggleButton:is(:hover, :has(> input:focus-visible)) { + color: var(--toggled-btn-color); + background-color: var(--button-hover-color); +} + +.toggleButton > input { + position: absolute; + top: 50%; + left: 50%; + opacity: 0; + width: 0; + height: 0; +} + +.toolbarField { + padding: 4px 7px; + margin: 3px 0; + border-radius: 2px; + background-color: var(--field-bg-color); + background-clip: padding-box; + border: 1px solid var(--field-border-color); + box-shadow: none; + color: var(--field-color); + font-size: 12px; + line-height: 16px; + outline: none; +} + +.toolbarField:focus { + border-color: #0a84ff; +} + +#pageNumber { + -moz-appearance: textfield; + text-align: end; + width: 40px; + background-size: 0 0; + transition-property: none; +} + +#pageNumber::-webkit-inner-spin-button { + -webkit-appearance: none; +} + +.loadingInput:has(> .loading:is(#pageNumber))::after { + display: inline; + visibility: visible; + + transition-property: visibility; + transition-delay: var(--loading-icon-delay); +} + +.loadingInput { + position: relative; +} + +.loadingInput::after { + position: absolute; + visibility: hidden; + display: none; + width: var(--icon-size); + height: var(--icon-size); + + content: ''; + background-color: var(--toolbar-icon-bg-color); + -webkit-mask-size: cover; + mask-size: cover; + -webkit-mask-image: var(--loading-icon); + mask-image: var(--loading-icon); +} + +.loadingInput.start::after { + inset-inline-start: 4px; +} + +.loadingInput.end::after { + inset-inline-end: 4px; +} #thumbnailView, #outlineView, #attachmentsView, -#layersView{ - position:absolute; - width:calc(100% - 8px); - inset-block:0; - padding:4px 4px 0; - overflow:auto; - -webkit-user-select:none; - -moz-user-select:none; - user-select:none; +#layersView { + position: absolute; + width: calc(100% - 8px); + inset-block: 0; + padding: 4px 4px 0; + overflow: auto; + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; } -#thumbnailView{ - width:calc(100% - 60px); - padding:10px 30px 0; +#thumbnailView { + width: calc(100% - 60px); + padding: 10px 30px 0; } -#thumbnailView > a:is(:active, :focus){ - outline:0; +#thumbnailView > a:is(:active, :focus) { + outline: 0; } -.thumbnail{ - --thumbnail-width:0; - --thumbnail-height:0; +.thumbnail { + --thumbnail-width: 0; + --thumbnail-height: 0; - float:var(--inline-start); - width:var(--thumbnail-width); - height:var(--thumbnail-height); - margin:0 10px 5px; - padding:1px; - border:7px solid transparent; - border-radius:2px; + float: var(--inline-start); + width: var(--thumbnail-width); + height: var(--thumbnail-height); + margin: 0 10px 5px; + padding: 1px; + border: 7px solid transparent; + border-radius: 2px; } -#thumbnailView > a:last-of-type > .thumbnail{ - margin-bottom:10px; +#thumbnailView > a:last-of-type > .thumbnail { + margin-bottom: 10px; } a:focus > .thumbnail, -.thumbnail:hover{ - border-color:var(--thumbnail-hover-color); +.thumbnail:hover { + border-color: var(--thumbnail-hover-color); } -.thumbnail.selected{ - border-color:var(--thumbnail-selected-color) !important; +.thumbnail.selected { + border-color: var(--thumbnail-selected-color) !important; } -.thumbnailImage{ - width:var(--thumbnail-width); - height:var(--thumbnail-height); - opacity:0.9; +.thumbnailImage { + width: var(--thumbnail-width); + height: var(--thumbnail-height); + opacity: 0.9; } a:focus > .thumbnail > .thumbnailImage, -.thumbnail:hover > .thumbnailImage{ - opacity:0.95; +.thumbnail:hover > .thumbnailImage { + opacity: 0.95; } -.thumbnail.selected > .thumbnailImage{ - opacity:1 !important; +.thumbnail.selected > .thumbnailImage { + opacity: 1 !important; } -.thumbnail:not([data-loaded]) > .thumbnailImage{ - width:calc(var(--thumbnail-width) - 2px); - height:calc(var(--thumbnail-height) - 2px); - border:1px dashed rgb(132 132 132); +.thumbnail:not([data-loaded]) > .thumbnailImage { + width: calc(var(--thumbnail-width) - 2px); + height: calc(var(--thumbnail-height) - 2px); + border: 1px dashed rgb(132 132 132); } .treeWithDeepNesting > .treeItem, -.treeItem > .treeItems{ - margin-inline-start:20px; +.treeItem > .treeItems { + margin-inline-start: 20px; } -.treeItem > a{ - text-decoration:none; - display:inline-block; - min-width:calc(100% - 4px); - height:auto; - margin-bottom:1px; - padding:2px 0 5px; - padding-inline-start:4px; - border-radius:2px; - color:var(--treeitem-color); - font-size:13px; - line-height:15px; - -webkit-user-select:none; - -moz-user-select:none; - user-select:none; - white-space:normal; - cursor:pointer; +.treeItem > a { + text-decoration: none; + display: inline-block; + min-width: calc(100% - 4px); + height: auto; + margin-bottom: 1px; + padding: 2px 0 5px; + padding-inline-start: 4px; + border-radius: 2px; + color: var(--treeitem-color); + font-size: 13px; + line-height: 15px; + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; + white-space: normal; + cursor: pointer; } -#layersView .treeItem > a *{ - cursor:pointer; +#layersView .treeItem > a * { + cursor: pointer; } -#layersView .treeItem > a > label{ - padding-inline-start:4px; +#layersView .treeItem > a > label { + padding-inline-start: 4px; } -#layersView .treeItem > a > label > input{ - float:var(--inline-start); - margin-top:1px; +#layersView .treeItem > a > label > input { + float: var(--inline-start); + margin-top: 1px; } -.treeItemToggler{ - position:relative; - float:var(--inline-start); - height:0; - width:0; - color:rgb(255 255 255 / 0.5); +.treeItemToggler { + position: relative; + float: var(--inline-start); + height: 0; + width: 0; + color: rgb(255 255 255 / 0.5); } -.treeItemToggler::before{ - inset-inline-end:4px; - -webkit-mask-image:var(--treeitem-expanded-icon); - mask-image:var(--treeitem-expanded-icon); +.treeItemToggler::before { + inset-inline-end: 4px; + -webkit-mask-image: var(--treeitem-expanded-icon); + mask-image: var(--treeitem-expanded-icon); } -.treeItemToggler.treeItemsHidden::before{ - -webkit-mask-image:var(--treeitem-collapsed-icon); - mask-image:var(--treeitem-collapsed-icon); - transform:scaleX(var(--dir-factor)); +.treeItemToggler.treeItemsHidden::before { + -webkit-mask-image: var(--treeitem-collapsed-icon); + mask-image: var(--treeitem-collapsed-icon); + transform: scaleX(var(--dir-factor)); } -.treeItemToggler.treeItemsHidden ~ .treeItems{ - display:none; +.treeItemToggler.treeItemsHidden ~ .treeItems { + display: none; } -.treeItem.selected > a{ - background-color:var(--treeitem-selected-bg-color); - color:var(--treeitem-selected-color); +.treeItem.selected > a { + background-color: var(--treeitem-selected-bg-color); + color: var(--treeitem-selected-color); } .treeItemToggler:hover, .treeItemToggler:hover + a, .treeItemToggler:hover ~ .treeItems, -.treeItem > a:hover{ - background-color:var(--treeitem-bg-color); - background-clip:padding-box; - border-radius:2px; - color:var(--treeitem-hover-color); +.treeItem > a:hover { + background-color: var(--treeitem-bg-color); + background-clip: padding-box; + border-radius: 2px; + color: var(--treeitem-hover-color); } -#outlineOptionsContainer{ - display:none; +#outlineOptionsContainer { + display: none; } -#sidebarContainer:has(#outlineView:not(.hidden)) #outlineOptionsContainer{ - display:inline flex; - } - -.dialogButton{ - width:auto; - margin:3px 4px 2px !important; - padding:2px 11px; - color:var(--main-color); - background-color:var(--dialog-button-bg-color); - border:var(--dialog-button-border) !important; +#sidebarContainer:has(#outlineView:not(.hidden)) #outlineOptionsContainer { + display: inline flex; } -dialog{ - margin:auto; - padding:15px; - border-spacing:4px; - color:var(--main-color); - font:message-box; - font-size:12px; - line-height:14px; - background-color:var(--doorhanger-bg-color); - border:1px solid rgb(0 0 0 / 0.5); - border-radius:4px; - box-shadow:0 1px 4px rgb(0 0 0 / 0.3); +.dialogButton { + width: auto; + margin: 3px 4px 2px !important; + padding: 2px 11px; + color: var(--main-color); + background-color: var(--dialog-button-bg-color); + border: var(--dialog-button-border) !important; } -dialog::backdrop{ - background-color:rgb(0 0 0 / 0.2); +dialog { + margin: auto; + padding: 15px; + border-spacing: 4px; + color: var(--main-color); + font: message-box; + font-size: 12px; + line-height: 14px; + background-color: var(--doorhanger-bg-color); + border: 1px solid rgb(0 0 0 / 0.5); + border-radius: 4px; + box-shadow: 0 1px 4px rgb(0 0 0 / 0.3); } -dialog > .row{ - display:table-row; +dialog::backdrop { + background-color: rgb(0 0 0 / 0.2); } -dialog > .row > *{ - display:table-cell; +dialog > .row { + display: table-row; } -dialog .toolbarField{ - margin:5px 0; +dialog > .row > * { + display: table-cell; } -dialog .separator{ - display:block; - margin:4px 0; - height:0; - width:100%; - border-top:1px solid var(--separator-color); - border-bottom:none; +dialog .toolbarField { + margin: 5px 0; } -dialog .buttonRow{ - text-align:center; - vertical-align:middle; +dialog .separator { + display: block; + margin: 4px 0; + height: 0; + width: 100%; + border-top: 1px solid var(--separator-color); + border-bottom: none; } -dialog :link{ - color:rgb(255 255 255); +dialog .buttonRow { + text-align: center; + vertical-align: middle; } -#passwordDialog{ - text-align:center; +dialog :link { + color: rgb(255 255 255); } -#passwordDialog .toolbarField{ - width:200px; +#passwordDialog { + text-align: center; } -#documentPropertiesDialog{ - text-align:left; +#passwordDialog .toolbarField { + width: 200px; } -#documentPropertiesDialog .row > *{ - min-width:100px; - text-align:start; +#documentPropertiesDialog { + text-align: left; } -#documentPropertiesDialog .row > span{ - width:125px; - word-wrap:break-word; +#documentPropertiesDialog .row > * { + min-width: 100px; + text-align: start; } -#documentPropertiesDialog .row > p{ - max-width:225px; - word-wrap:break-word; +#documentPropertiesDialog .row > span { + width: 125px; + word-wrap: break-word; } -#documentPropertiesDialog .buttonRow{ - margin-top:10px; +#documentPropertiesDialog .row > p { + max-width: 225px; + word-wrap: break-word; } -.grab-to-pan-grab{ - cursor:grab !important; +#documentPropertiesDialog .buttonRow { + margin-top: 10px; +} + +.grab-to-pan-grab { + cursor: grab !important; } .grab-to-pan-grab - *:not(input):not(textarea):not(button):not(select):not(:link){ - cursor:inherit !important; + *:not(input):not(textarea):not(button):not(select):not(:link) { + cursor: inherit !important; } .grab-to-pan-grab:active, -.grab-to-pan-grabbing{ - cursor:grabbing !important; +.grab-to-pan-grabbing { + cursor: grabbing !important; } -.grab-to-pan-grabbing{ - position:fixed; - background:rgb(0 0 0 / 0); - display:block; - inset:0; - overflow:hidden; - z-index:50000; +.grab-to-pan-grabbing { + position: fixed; + background: rgb(0 0 0 / 0); + display: block; + inset: 0; + overflow: hidden; + z-index: 50000; } -.toolbarButton{ - height:100%; - aspect-ratio:1; - display:flex; - align-items:center; - justify-content:center; - background:none; - border:none; - color:var(--main-color); - outline:none; - border-radius:2px; - box-sizing:border-box; - font:message-box; - flex:none; - position:relative; - padding:0; +.toolbarButton { + height: 100%; + aspect-ratio: 1; + display: flex; + align-items: center; + justify-content: center; + background: none; + border: none; + color: var(--main-color); + outline: none; + border-radius: 2px; + box-sizing: border-box; + font: message-box; + flex: none; + position: relative; + padding: 0; } -.toolbarButton > span{ - display:inline-block; - width:0; - height:0; - overflow:hidden; - } - -.toolbarButton::before{ - opacity:var(--toolbar-icon-opacity); - display:inline-block; - width:var(--icon-size); - height:var(--icon-size); - content:""; - background-color:var(--toolbar-icon-bg-color); - -webkit-mask-size:cover; - mask-size:cover; - -webkit-mask-position:center; - mask-position:center; - } - -.toolbarButton.toggled{ - background-color:var(--toggled-btn-bg-color); - color:var(--toggled-btn-color); - } - -.toolbarButton.toggled::before{ - background-color:var(--toggled-btn-color); - } - -.toolbarButton.toggled:hover{ - outline:var(--toggled-hover-btn-outline) !important; - } - -.toolbarButton.toggled:hover:active{ - background-color:var(--toggled-hover-active-btn-color); - } - -.toolbarButton:is(:hover,:focus-visible){ - background-color:var(--button-hover-color); - } - -.toolbarButton:is(:hover,:focus-visible)::before{ - background-color:var(--toolbar-icon-hover-bg-color); - } - -.toolbarButton:is([disabled="disabled"],[disabled]){ - opacity:0.5; - pointer-events:none; - } - -.toolbarButton.labeled{ - width:100%; - min-height:var(--menuitem-height); - justify-content:flex-start; - gap:8px; - padding-inline-start:12px; - aspect-ratio:unset; - text-align:start; - white-space:normal; - cursor:default; - } - -.toolbarButton.labeled:is(a){ - text-decoration:none; - } - -.toolbarButton.labeled[href="#"]:is(a){ - opacity:0.5; - pointer-events:none; - } - -.toolbarButton.labeled::before{ - opacity:var(--doorhanger-icon-opacity); - } - -.toolbarButton.labeled:is(:hover,:focus-visible){ - background-color:var(--doorhanger-hover-bg-color); - color:var(--doorhanger-hover-color); - } - -.toolbarButton.labeled > span{ - display:inline-block; - width:-moz-max-content; - width:max-content; - height:auto; - } - -.toolbarButtonWithContainer{ - height:100%; - aspect-ratio:1; - display:inline-block; - position:relative; - flex:none; +.toolbarButton > span { + display: inline-block; + width: 0; + height: 0; + overflow: hidden; } -.toolbarButtonWithContainer > .toolbarButton{ - width:100%; - height:100%; - } - -.toolbarButtonWithContainer .menu{ - padding-block:5px; - } - -.toolbarButtonWithContainer .menuContainer{ - width:100%; - height:auto; - max-height:calc( - var(--viewer-container-height) - var(--toolbar-height) - - var(--doorhanger-height) - ); - display:flex; - flex-direction:column; - box-sizing:border-box; - overflow-y:auto; - } - -.toolbarButtonWithContainer .editorParamsToolbar{ - height:auto; - width:220px; - position:absolute; - z-index:30000; - cursor:default; - } - -:is(.toolbarButtonWithContainer .editorParamsToolbar) #editorStampAddImage::before{ - -webkit-mask-image:var(--editorParams-stampAddImage-icon); - mask-image:var(--editorParams-stampAddImage-icon); - } - -:is(.toolbarButtonWithContainer .editorParamsToolbar) .editorParamsLabel{ - flex:none; - font:menu; - font-size:13px; - font-style:normal; - font-weight:400; - line-height:150%; - color:var(--main-color); - width:-moz-fit-content; - width:fit-content; - inset-inline-start:0; - } - -:is(.toolbarButtonWithContainer .editorParamsToolbar) .editorParamsToolbarContainer{ - width:100%; - height:auto; - display:flex; - flex-direction:column; - box-sizing:border-box; - padding-inline:10px; - padding-block:10px; - } - -:is(:is(.toolbarButtonWithContainer .editorParamsToolbar) .editorParamsToolbarContainer) > .editorParamsSetter{ - min-height:26px; - display:flex; - align-items:center; - justify-content:space-between; - } - -:is(:is(.toolbarButtonWithContainer .editorParamsToolbar) .editorParamsToolbarContainer) .editorParamsColor{ - width:32px; - height:32px; - flex:none; - padding:0; - } - -:is(:is(.toolbarButtonWithContainer .editorParamsToolbar) .editorParamsToolbarContainer) .editorParamsSlider{ - background-color:transparent; - width:90px; - flex:0 1 0; - font:message-box; - } - -:is(:is(:is(.toolbarButtonWithContainer .editorParamsToolbar) .editorParamsToolbarContainer) .editorParamsSlider)::-moz-range-progress{ - background-color:black; - } - -:is(:is(:is(.toolbarButtonWithContainer .editorParamsToolbar) .editorParamsToolbarContainer) .editorParamsSlider)::-webkit-slider-runnable-track,:is(:is(:is(.toolbarButtonWithContainer .editorParamsToolbar) .editorParamsToolbarContainer) .editorParamsSlider)::-moz-range-track{ - background-color:black; - } - -:is(:is(:is(.toolbarButtonWithContainer .editorParamsToolbar) .editorParamsToolbarContainer) .editorParamsSlider)::-webkit-slider-thumb,:is(:is(:is(.toolbarButtonWithContainer .editorParamsToolbar) .editorParamsToolbarContainer) .editorParamsSlider)::-moz-range-thumb{ - background-color:white; - } - -#secondaryToolbar{ - height:auto; - width:220px; - position:absolute; - z-index:30000; - cursor:default; - min-height:26px; - max-height:calc(var(--viewer-container-height) - 40px); +.toolbarButton::before { + opacity: var(--toolbar-icon-opacity); + display: inline-block; + width: var(--icon-size); + height: var(--icon-size); + content: ''; + background-color: var(--toolbar-icon-bg-color); + -webkit-mask-size: cover; + mask-size: cover; + -webkit-mask-position: center; + mask-position: center; } -:is(#secondaryToolbar #secondaryToolbarButtonContainer) #secondaryOpenFile::before{ - -webkit-mask-image:var(--toolbarButton-openFile-icon); - mask-image:var(--toolbarButton-openFile-icon); - } - -:is(#secondaryToolbar #secondaryToolbarButtonContainer) #secondaryPrint::before{ - -webkit-mask-image:var(--toolbarButton-print-icon); - mask-image:var(--toolbarButton-print-icon); - } - -:is(#secondaryToolbar #secondaryToolbarButtonContainer) #secondaryDownload::before{ - -webkit-mask-image:var(--toolbarButton-download-icon); - mask-image:var(--toolbarButton-download-icon); - } - -:is(#secondaryToolbar #secondaryToolbarButtonContainer) #presentationMode::before{ - -webkit-mask-image:var(--toolbarButton-presentationMode-icon); - mask-image:var(--toolbarButton-presentationMode-icon); - } - -:is(#secondaryToolbar #secondaryToolbarButtonContainer) #viewBookmark::before{ - -webkit-mask-image:var(--toolbarButton-bookmark-icon); - mask-image:var(--toolbarButton-bookmark-icon); - } - -:is(#secondaryToolbar #secondaryToolbarButtonContainer) #firstPage::before{ - -webkit-mask-image:var(--secondaryToolbarButton-firstPage-icon); - mask-image:var(--secondaryToolbarButton-firstPage-icon); - } - -:is(#secondaryToolbar #secondaryToolbarButtonContainer) #lastPage::before{ - -webkit-mask-image:var(--secondaryToolbarButton-lastPage-icon); - mask-image:var(--secondaryToolbarButton-lastPage-icon); - } - -:is(#secondaryToolbar #secondaryToolbarButtonContainer) #pageRotateCcw::before{ - -webkit-mask-image:var(--secondaryToolbarButton-rotateCcw-icon); - mask-image:var(--secondaryToolbarButton-rotateCcw-icon); - } - -:is(#secondaryToolbar #secondaryToolbarButtonContainer) #pageRotateCw::before{ - -webkit-mask-image:var(--secondaryToolbarButton-rotateCw-icon); - mask-image:var(--secondaryToolbarButton-rotateCw-icon); - } - -:is(#secondaryToolbar #secondaryToolbarButtonContainer) #cursorSelectTool::before{ - -webkit-mask-image:var(--secondaryToolbarButton-selectTool-icon); - mask-image:var(--secondaryToolbarButton-selectTool-icon); - } - -:is(#secondaryToolbar #secondaryToolbarButtonContainer) #cursorHandTool::before{ - -webkit-mask-image:var(--secondaryToolbarButton-handTool-icon); - mask-image:var(--secondaryToolbarButton-handTool-icon); - } - -:is(#secondaryToolbar #secondaryToolbarButtonContainer) #scrollPage::before{ - -webkit-mask-image:var(--secondaryToolbarButton-scrollPage-icon); - mask-image:var(--secondaryToolbarButton-scrollPage-icon); - } - -:is(#secondaryToolbar #secondaryToolbarButtonContainer) #scrollVertical::before{ - -webkit-mask-image:var(--secondaryToolbarButton-scrollVertical-icon); - mask-image:var(--secondaryToolbarButton-scrollVertical-icon); - } - -:is(#secondaryToolbar #secondaryToolbarButtonContainer) #scrollHorizontal::before{ - -webkit-mask-image:var(--secondaryToolbarButton-scrollHorizontal-icon); - mask-image:var(--secondaryToolbarButton-scrollHorizontal-icon); - } - -:is(#secondaryToolbar #secondaryToolbarButtonContainer) #scrollWrapped::before{ - -webkit-mask-image:var(--secondaryToolbarButton-scrollWrapped-icon); - mask-image:var(--secondaryToolbarButton-scrollWrapped-icon); - } - -:is(#secondaryToolbar #secondaryToolbarButtonContainer) #spreadNone::before{ - -webkit-mask-image:var(--secondaryToolbarButton-spreadNone-icon); - mask-image:var(--secondaryToolbarButton-spreadNone-icon); - } - -:is(#secondaryToolbar #secondaryToolbarButtonContainer) #spreadOdd::before{ - -webkit-mask-image:var(--secondaryToolbarButton-spreadOdd-icon); - mask-image:var(--secondaryToolbarButton-spreadOdd-icon); - } - -:is(#secondaryToolbar #secondaryToolbarButtonContainer) #spreadEven::before{ - -webkit-mask-image:var(--secondaryToolbarButton-spreadEven-icon); - mask-image:var(--secondaryToolbarButton-spreadEven-icon); - } - -:is(#secondaryToolbar #secondaryToolbarButtonContainer) #imageAltTextSettings::before{ - -webkit-mask-image:var(--secondaryToolbarButton-imageAltTextSettings-icon); - mask-image:var(--secondaryToolbarButton-imageAltTextSettings-icon); - } - -:is(#secondaryToolbar #secondaryToolbarButtonContainer) #documentProperties::before{ - -webkit-mask-image:var(--secondaryToolbarButton-documentProperties-icon); - mask-image:var(--secondaryToolbarButton-documentProperties-icon); - } - -#findbar{ - --input-horizontal-padding:4px; - --findbar-padding:2px; - - width:-moz-max-content; - - width:max-content; - max-width:90vw; - min-height:var(--toolbar-height); - height:auto; - position:absolute; - z-index:30000; - cursor:default; - padding:0; - min-width:300px; - background-color:var(--toolbar-bg-color); - box-sizing:border-box; - flex-wrap:wrap; - justify-content:flex-start; +.toolbarButton.toggled { + background-color: var(--toggled-btn-bg-color); + color: var(--toggled-btn-color); } -#findbar > *{ - height:var(--toolbar-height); - padding:var(--findbar-padding); - } - -#findbar #findInputContainer{ - margin-inline-start:2px; - } - -:is(#findbar #findInputContainer) #findPreviousButton::before{ - -webkit-mask-image:var(--findbarButton-previous-icon); - mask-image:var(--findbarButton-previous-icon); - } - -:is(#findbar #findInputContainer) #findNextButton::before{ - -webkit-mask-image:var(--findbarButton-next-icon); - mask-image:var(--findbarButton-next-icon); - } - -:is(#findbar #findInputContainer) #findInput{ - width:200px; - padding:5px var(--input-horizontal-padding); - } - -:is(:is(#findbar #findInputContainer) #findInput)::-moz-placeholder{ - font-style:normal; - } - -:is(:is(#findbar #findInputContainer) #findInput)::placeholder{ - font-style:normal; - } - -.loadingInput:has( > [data-status="pending"]:is(:is(#findbar #findInputContainer) #findInput))::after{ - display:inline; - visibility:visible; - inset-inline-end:calc(var(--input-horizontal-padding) + 1px); - } - -[data-status="notFound"]:is(:is(#findbar #findInputContainer) #findInput){ - background-color:rgb(255 102 102); - } - -#findbar #findbarMessageContainer{ - display:none; - gap:4px; - } - -:is(#findbar #findbarMessageContainer):has( > :is(#findResultsCount,#findMsg):not(:empty)){ - display:inline flex; - } - -:is(#findbar #findbarMessageContainer) #findResultsCount{ - background-color:rgb(217 217 217); - color:rgb(82 82 82); - padding-block:4px; - } - -:is(:is(#findbar #findbarMessageContainer) #findResultsCount):empty{ - display:none; - } - -[data-status="notFound"]:is(:is(#findbar #findbarMessageContainer) #findMsg){ - font-weight:bold; - } - -:is(:is(#findbar #findbarMessageContainer) #findMsg):empty{ - display:none; - } - -#findbar.wrapContainers{ - flex-direction:column; - align-items:flex-start; - height:-moz-max-content; - height:max-content; - } - -#findbar.wrapContainers .toolbarLabel{ - margin:0 4px; - } - -#findbar.wrapContainers #findbarMessageContainer{ - flex-wrap:wrap; - flex-flow:column nowrap; - align-items:flex-start; - height:-moz-max-content; - height:max-content; - } - -:is(#findbar.wrapContainers #findbarMessageContainer) #findResultsCount{ - height:calc(var(--toolbar-height) - 2 * var(--findbar-padding)); - } - -:is(#findbar.wrapContainers #findbarMessageContainer) #findMsg{ - min-height:var(--toolbar-height); - } - -@page{ - margin:0; +.toolbarButton.toggled::before { + background-color: var(--toggled-btn-color); } -#printContainer{ - display:none; +.toolbarButton.toggled:hover { + outline: var(--toggled-hover-btn-outline) !important; } -@media print{ - body{ - background:rgb(0 0 0 / 0) none; +.toolbarButton.toggled:hover:active { + background-color: var(--toggled-hover-active-btn-color); +} + +.toolbarButton:is(:hover, :focus-visible) { + background-color: var(--button-hover-color); +} + +.toolbarButton:is(:hover, :focus-visible)::before { + background-color: var(--toolbar-icon-hover-bg-color); +} + +.toolbarButton:is([disabled='disabled'], [disabled]) { + opacity: 0.5; + pointer-events: none; +} + +.toolbarButton.labeled { + width: 100%; + min-height: var(--menuitem-height); + justify-content: flex-start; + gap: 8px; + padding-inline-start: 12px; + aspect-ratio: unset; + text-align: start; + white-space: normal; + cursor: default; +} + +.toolbarButton.labeled:is(a) { + text-decoration: none; +} + +.toolbarButton.labeled[href='#']:is(a) { + opacity: 0.5; + pointer-events: none; +} + +.toolbarButton.labeled::before { + opacity: var(--doorhanger-icon-opacity); +} + +.toolbarButton.labeled:is(:hover, :focus-visible) { + background-color: var(--doorhanger-hover-bg-color); + color: var(--doorhanger-hover-color); +} + +.toolbarButton.labeled > span { + display: inline-block; + width: -moz-max-content; + width: max-content; + height: auto; +} + +.toolbarButtonWithContainer { + height: 100%; + aspect-ratio: 1; + display: inline-block; + position: relative; + flex: none; +} + +.toolbarButtonWithContainer > .toolbarButton { + width: 100%; + height: 100%; +} + +.toolbarButtonWithContainer .menu { + padding-block: 5px; +} + +.toolbarButtonWithContainer .menuContainer { + width: 100%; + height: auto; + max-height: calc( + var(--viewer-container-height) - var(--toolbar-height) - + var(--doorhanger-height) + ); + display: flex; + flex-direction: column; + box-sizing: border-box; + overflow-y: auto; +} + +.toolbarButtonWithContainer .editorParamsToolbar { + height: auto; + width: 220px; + position: absolute; + z-index: 30000; + cursor: default; +} + +:is(.toolbarButtonWithContainer .editorParamsToolbar) + #editorStampAddImage::before { + -webkit-mask-image: var(--editorParams-stampAddImage-icon); + mask-image: var(--editorParams-stampAddImage-icon); +} + +:is(.toolbarButtonWithContainer .editorParamsToolbar) .editorParamsLabel { + flex: none; + font: menu; + font-size: 13px; + font-style: normal; + font-weight: 400; + line-height: 150%; + color: var(--main-color); + width: -moz-fit-content; + width: fit-content; + inset-inline-start: 0; +} + +:is(.toolbarButtonWithContainer .editorParamsToolbar) + .editorParamsToolbarContainer { + width: 100%; + height: auto; + display: flex; + flex-direction: column; + box-sizing: border-box; + padding-inline: 10px; + padding-block: 10px; +} + +:is( + :is(.toolbarButtonWithContainer .editorParamsToolbar) + .editorParamsToolbarContainer + ) + > .editorParamsSetter { + min-height: 26px; + display: flex; + align-items: center; + justify-content: space-between; +} + +:is( + :is(.toolbarButtonWithContainer .editorParamsToolbar) + .editorParamsToolbarContainer + ) + .editorParamsColor { + width: 32px; + height: 32px; + flex: none; + padding: 0; +} + +:is( + :is(.toolbarButtonWithContainer .editorParamsToolbar) + .editorParamsToolbarContainer + ) + .editorParamsSlider { + background-color: transparent; + width: 90px; + flex: 0 1 0; + font: message-box; +} + +:is( + :is( + :is(.toolbarButtonWithContainer .editorParamsToolbar) + .editorParamsToolbarContainer + ) + .editorParamsSlider + )::-moz-range-progress { + background-color: black; +} + +:is( + :is( + :is(.toolbarButtonWithContainer .editorParamsToolbar) + .editorParamsToolbarContainer + ) + .editorParamsSlider + )::-webkit-slider-runnable-track, +:is( + :is( + :is(.toolbarButtonWithContainer .editorParamsToolbar) + .editorParamsToolbarContainer + ) + .editorParamsSlider + )::-moz-range-track { + background-color: black; +} + +:is( + :is( + :is(.toolbarButtonWithContainer .editorParamsToolbar) + .editorParamsToolbarContainer + ) + .editorParamsSlider + )::-webkit-slider-thumb, +:is( + :is( + :is(.toolbarButtonWithContainer .editorParamsToolbar) + .editorParamsToolbarContainer + ) + .editorParamsSlider + )::-moz-range-thumb { + background-color: white; +} + +#secondaryToolbar { + height: auto; + width: 220px; + position: absolute; + z-index: 30000; + cursor: default; + min-height: 26px; + max-height: calc(var(--viewer-container-height) - 40px); +} + +:is(#secondaryToolbar #secondaryToolbarButtonContainer) + #secondaryOpenFile::before { + -webkit-mask-image: var(--toolbarButton-openFile-icon); + mask-image: var(--toolbarButton-openFile-icon); +} + +:is(#secondaryToolbar #secondaryToolbarButtonContainer) + #secondaryPrint::before { + -webkit-mask-image: var(--toolbarButton-print-icon); + mask-image: var(--toolbarButton-print-icon); +} + +:is(#secondaryToolbar #secondaryToolbarButtonContainer) + #secondaryDownload::before { + -webkit-mask-image: var(--toolbarButton-download-icon); + mask-image: var(--toolbarButton-download-icon); +} + +:is(#secondaryToolbar #secondaryToolbarButtonContainer) + #presentationMode::before { + -webkit-mask-image: var(--toolbarButton-presentationMode-icon); + mask-image: var(--toolbarButton-presentationMode-icon); +} + +:is(#secondaryToolbar #secondaryToolbarButtonContainer) #viewBookmark::before { + -webkit-mask-image: var(--toolbarButton-bookmark-icon); + mask-image: var(--toolbarButton-bookmark-icon); +} + +:is(#secondaryToolbar #secondaryToolbarButtonContainer) #firstPage::before { + -webkit-mask-image: var(--secondaryToolbarButton-firstPage-icon); + mask-image: var(--secondaryToolbarButton-firstPage-icon); +} + +:is(#secondaryToolbar #secondaryToolbarButtonContainer) #lastPage::before { + -webkit-mask-image: var(--secondaryToolbarButton-lastPage-icon); + mask-image: var(--secondaryToolbarButton-lastPage-icon); +} + +:is(#secondaryToolbar #secondaryToolbarButtonContainer) #pageRotateCcw::before { + -webkit-mask-image: var(--secondaryToolbarButton-rotateCcw-icon); + mask-image: var(--secondaryToolbarButton-rotateCcw-icon); +} + +:is(#secondaryToolbar #secondaryToolbarButtonContainer) #pageRotateCw::before { + -webkit-mask-image: var(--secondaryToolbarButton-rotateCw-icon); + mask-image: var(--secondaryToolbarButton-rotateCw-icon); +} + +:is(#secondaryToolbar #secondaryToolbarButtonContainer) + #cursorSelectTool::before { + -webkit-mask-image: var(--secondaryToolbarButton-selectTool-icon); + mask-image: var(--secondaryToolbarButton-selectTool-icon); +} + +:is(#secondaryToolbar #secondaryToolbarButtonContainer) + #cursorHandTool::before { + -webkit-mask-image: var(--secondaryToolbarButton-handTool-icon); + mask-image: var(--secondaryToolbarButton-handTool-icon); +} + +:is(#secondaryToolbar #secondaryToolbarButtonContainer) #scrollPage::before { + -webkit-mask-image: var(--secondaryToolbarButton-scrollPage-icon); + mask-image: var(--secondaryToolbarButton-scrollPage-icon); +} + +:is(#secondaryToolbar #secondaryToolbarButtonContainer) + #scrollVertical::before { + -webkit-mask-image: var(--secondaryToolbarButton-scrollVertical-icon); + mask-image: var(--secondaryToolbarButton-scrollVertical-icon); +} + +:is(#secondaryToolbar #secondaryToolbarButtonContainer) + #scrollHorizontal::before { + -webkit-mask-image: var(--secondaryToolbarButton-scrollHorizontal-icon); + mask-image: var(--secondaryToolbarButton-scrollHorizontal-icon); +} + +:is(#secondaryToolbar #secondaryToolbarButtonContainer) #scrollWrapped::before { + -webkit-mask-image: var(--secondaryToolbarButton-scrollWrapped-icon); + mask-image: var(--secondaryToolbarButton-scrollWrapped-icon); +} + +:is(#secondaryToolbar #secondaryToolbarButtonContainer) #spreadNone::before { + -webkit-mask-image: var(--secondaryToolbarButton-spreadNone-icon); + mask-image: var(--secondaryToolbarButton-spreadNone-icon); +} + +:is(#secondaryToolbar #secondaryToolbarButtonContainer) #spreadOdd::before { + -webkit-mask-image: var(--secondaryToolbarButton-spreadOdd-icon); + mask-image: var(--secondaryToolbarButton-spreadOdd-icon); +} + +:is(#secondaryToolbar #secondaryToolbarButtonContainer) #spreadEven::before { + -webkit-mask-image: var(--secondaryToolbarButton-spreadEven-icon); + mask-image: var(--secondaryToolbarButton-spreadEven-icon); +} + +:is(#secondaryToolbar #secondaryToolbarButtonContainer) + #imageAltTextSettings::before { + -webkit-mask-image: var(--secondaryToolbarButton-imageAltTextSettings-icon); + mask-image: var(--secondaryToolbarButton-imageAltTextSettings-icon); +} + +:is(#secondaryToolbar #secondaryToolbarButtonContainer) + #documentProperties::before { + -webkit-mask-image: var(--secondaryToolbarButton-documentProperties-icon); + mask-image: var(--secondaryToolbarButton-documentProperties-icon); +} + +#findbar { + --input-horizontal-padding: 4px; + --findbar-padding: 2px; + + width: -moz-max-content; + + width: max-content; + max-width: 90vw; + min-height: var(--toolbar-height); + height: auto; + position: absolute; + z-index: 30000; + cursor: default; + padding: 0; + min-width: 300px; + background-color: var(--toolbar-bg-color); + box-sizing: border-box; + flex-wrap: wrap; + justify-content: flex-start; +} + +#findbar > * { + height: var(--toolbar-height); + padding: var(--findbar-padding); +} + +#findbar #findInputContainer { + margin-inline-start: 2px; +} + +:is(#findbar #findInputContainer) #findPreviousButton::before { + -webkit-mask-image: var(--findbarButton-previous-icon); + mask-image: var(--findbarButton-previous-icon); +} + +:is(#findbar #findInputContainer) #findNextButton::before { + -webkit-mask-image: var(--findbarButton-next-icon); + mask-image: var(--findbarButton-next-icon); +} + +:is(#findbar #findInputContainer) #findInput { + width: 200px; + padding: 5px var(--input-horizontal-padding); +} + +:is(:is(#findbar #findInputContainer) #findInput)::-moz-placeholder { + font-style: normal; +} + +:is(:is(#findbar #findInputContainer) #findInput)::placeholder { + font-style: normal; +} + +.loadingInput:has( + > [data-status='pending']:is(:is(#findbar #findInputContainer) #findInput) + )::after { + display: inline; + visibility: visible; + inset-inline-end: calc(var(--input-horizontal-padding) + 1px); +} + +[data-status='notFound']:is(:is(#findbar #findInputContainer) #findInput) { + background-color: rgb(255 102 102); +} + +#findbar #findbarMessageContainer { + display: none; + gap: 4px; +} + +:is(#findbar #findbarMessageContainer):has( + > :is(#findResultsCount, #findMsg):not(:empty) + ) { + display: inline flex; +} + +:is(#findbar #findbarMessageContainer) #findResultsCount { + background-color: rgb(217 217 217); + color: rgb(82 82 82); + padding-block: 4px; +} + +:is(:is(#findbar #findbarMessageContainer) #findResultsCount):empty { + display: none; +} + +[data-status='notFound']:is(:is(#findbar #findbarMessageContainer) #findMsg) { + font-weight: bold; +} + +:is(:is(#findbar #findbarMessageContainer) #findMsg):empty { + display: none; +} + +#findbar.wrapContainers { + flex-direction: column; + align-items: flex-start; + height: -moz-max-content; + height: max-content; +} + +#findbar.wrapContainers .toolbarLabel { + margin: 0 4px; +} + +#findbar.wrapContainers #findbarMessageContainer { + flex-wrap: wrap; + flex-flow: column nowrap; + align-items: flex-start; + height: -moz-max-content; + height: max-content; +} + +:is(#findbar.wrapContainers #findbarMessageContainer) #findResultsCount { + height: calc(var(--toolbar-height) - 2 * var(--findbar-padding)); +} + +:is(#findbar.wrapContainers #findbarMessageContainer) #findMsg { + min-height: var(--toolbar-height); +} + +@page { + margin: 0; +} + +#printContainer { + display: none; +} + +@media print { + body { + background: rgb(0 0 0 / 0) none; } - body[data-pdfjsprinting] #outerContainer{ - display:none; + body[data-pdfjsprinting] #outerContainer { + display: none; } - body[data-pdfjsprinting] #printContainer{ - display:block; + body[data-pdfjsprinting] #printContainer { + display: block; } - #printContainer{ - height:100%; + #printContainer { + height: 100%; } - #printContainer > .printedPage{ - page-break-after:always; - page-break-inside:avoid; - height:100%; - width:100%; + #printContainer > .printedPage { + page-break-after: always; + page-break-inside: avoid; + height: 100%; + width: 100%; - display:flex; - flex-direction:column; - justify-content:center; - align-items:center; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; } - #printContainer > .xfaPrintedPage .xfaPage{ - position:absolute; + #printContainer > .xfaPrintedPage .xfaPage { + position: absolute; } - #printContainer > .xfaPrintedPage{ - page-break-after:always; - page-break-inside:avoid; - width:100%; - height:100%; - position:relative; + #printContainer > .xfaPrintedPage { + page-break-after: always; + page-break-inside: avoid; + width: 100%; + height: 100%; + position: relative; } - #printContainer > .printedPage :is(canvas, img){ - max-width:100%; - max-height:100%; + #printContainer > .printedPage :is(canvas, img) { + max-width: 100%; + max-height: 100%; - direction:ltr; - display:block; + direction: ltr; + display: block; } } -.visibleMediumView{ - display:none !important; +.visibleMediumView { + display: none !important; } -.toolbarLabel{ - width:-moz-max-content; - width:max-content; - min-width:16px; - height:100%; - padding-inline:4px; - margin:2px; - border-radius:2px; - color:var(--main-color); - font-size:12px; - line-height:14px; - text-align:left; - -webkit-user-select:none; - -moz-user-select:none; - user-select:none; - cursor:default; - box-sizing:border-box; +.toolbarLabel { + width: -moz-max-content; + width: max-content; + min-width: 16px; + height: 100%; + padding-inline: 4px; + margin: 2px; + border-radius: 2px; + color: var(--main-color); + font-size: 12px; + line-height: 14px; + text-align: left; + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; + cursor: default; + box-sizing: border-box; - display:inline flex; - flex-direction:column; - align-items:center; - justify-content:center; + display: inline flex; + flex-direction: column; + align-items: center; + justify-content: center; } -.toolbarLabel > label{ - width:100%; - } - -.toolbarHorizontalGroup{ - height:100%; - display:inline flex; - flex-direction:row; - align-items:center; - justify-content:space-between; - gap:1px; - box-sizing:border-box; +.toolbarLabel > label { + width: 100%; } -.dropdownToolbarButton{ - display:inline flex; - flex-direction:row; - align-items:center; - justify-content:center; - position:relative; - - width:-moz-fit-content; - - width:fit-content; - min-width:140px; - padding:0; - background-color:var(--dropdown-btn-bg-color); - border:var(--dropdown-btn-border); - border-radius:2px; - color:var(--main-color); - font-size:12px; - line-height:14px; - -webkit-user-select:none; - -moz-user-select:none; - user-select:none; - cursor:default; - box-sizing:border-box; - outline:none; +.toolbarHorizontalGroup { + height: 100%; + display: inline flex; + flex-direction: row; + align-items: center; + justify-content: space-between; + gap: 1px; + box-sizing: border-box; } -.dropdownToolbarButton:hover{ - background-color:var(--button-hover-color); - } +.dropdownToolbarButton { + display: inline flex; + flex-direction: row; + align-items: center; + justify-content: center; + position: relative; -.dropdownToolbarButton > select{ - -webkit-appearance:none; - -moz-appearance:none; - appearance:none; - width:inherit; - min-width:inherit; - height:28px; - font:message-box; - font-size:12px; - color:var(--main-color); - margin:0; - padding-block:1px 2px; - padding-inline:6px 38px; - border:none; - outline:none; - background-color:var(--dropdown-btn-bg-color); - } + width: -moz-fit-content; -:is(.dropdownToolbarButton > select) > option{ - background:var(--doorhanger-bg-color); - color:var(--main-color); - } - -:is(.dropdownToolbarButton > select):is(:hover,:focus-visible){ - background-color:var(--button-hover-color); - color:var(--toggled-btn-color); - } - -.dropdownToolbarButton::after{ - position:absolute; - display:inline; - width:var(--icon-size); - height:var(--icon-size); - - content:""; - background-color:var(--toolbar-icon-bg-color); - -webkit-mask-size:cover; - mask-size:cover; - - inset-inline-end:4px; - pointer-events:none; - -webkit-mask-image:var(--toolbarButton-menuArrow-icon); - mask-image:var(--toolbarButton-menuArrow-icon); - } - -.dropdownToolbarButton:is(:hover,:focus-visible,:active)::after{ - background-color:var(--toolbar-icon-hover-bg-color); - } - -#toolbarContainer{ - --menuitem-height:calc(var(--toolbar-height) - 6px); - - width:100%; - height:var(--toolbar-height); - padding:var(--toolbar-vertical-padding) var(--toolbar-horizontal-padding); - position:relative; - box-sizing:border-box; - font:message-box; - background-color:var(--toolbar-bg-color); - box-shadow:var(--toolbar-box-shadow); - border-bottom:var(--toolbar-border-bottom); + width: fit-content; + min-width: 140px; + padding: 0; + background-color: var(--dropdown-btn-bg-color); + border: var(--dropdown-btn-border); + border-radius: 2px; + color: var(--main-color); + font-size: 12px; + line-height: 14px; + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; + cursor: default; + box-sizing: border-box; + outline: none; } -#toolbarContainer #toolbarViewer{ - width:100%; - height:100%; - justify-content:space-between; +.dropdownToolbarButton:hover { + background-color: var(--button-hover-color); +} + +.dropdownToolbarButton > select { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + width: inherit; + min-width: inherit; + height: 28px; + font: message-box; + font-size: 12px; + color: var(--main-color); + margin: 0; + padding-block: 1px 2px; + padding-inline: 6px 38px; + border: none; + outline: none; + background-color: var(--dropdown-btn-bg-color); +} + +:is(.dropdownToolbarButton > select) > option { + background: var(--doorhanger-bg-color); + color: var(--main-color); +} + +:is(.dropdownToolbarButton > select):is(:hover, :focus-visible) { + background-color: var(--button-hover-color); + color: var(--toggled-btn-color); +} + +.dropdownToolbarButton::after { + position: absolute; + display: inline; + width: var(--icon-size); + height: var(--icon-size); + + content: ''; + background-color: var(--toolbar-icon-bg-color); + -webkit-mask-size: cover; + mask-size: cover; + + inset-inline-end: 4px; + pointer-events: none; + -webkit-mask-image: var(--toolbarButton-menuArrow-icon); + mask-image: var(--toolbarButton-menuArrow-icon); +} + +.dropdownToolbarButton:is(:hover, :focus-visible, :active)::after { + background-color: var(--toolbar-icon-hover-bg-color); +} + +#toolbarContainer { + --menuitem-height: calc(var(--toolbar-height) - 6px); + + width: 100%; + height: var(--toolbar-height); + padding: var(--toolbar-vertical-padding) var(--toolbar-horizontal-padding); + position: relative; + box-sizing: border-box; + font: message-box; + background-color: var(--toolbar-bg-color); + box-shadow: var(--toolbar-box-shadow); + border-bottom: var(--toolbar-border-bottom); +} + +#toolbarContainer #toolbarViewer { + width: 100%; + height: 100%; + justify-content: space-between; +} + +:is(#toolbarContainer #toolbarViewer) > * { + flex: none; +} + +:is(#toolbarContainer #toolbarViewer) input { + font: message-box; +} + +:is(#toolbarContainer #toolbarViewer) .toolbarButtonSpacer { + width: 30px; + display: block; + height: 1px; +} + +:is(#toolbarContainer #toolbarViewer) + #toolbarViewerLeft + #numPages.toolbarLabel { + padding-inline-start: 3px; + flex: none; +} + +#toolbarContainer #loadingBar { + --progressBar-percent: 0%; + --progressBar-end-offset: 0; + + position: absolute; + top: var(--toolbar-height); + inset-inline: 0 var(--progressBar-end-offset); + height: 4px; + background-color: var(--progressBar-bg-color); + border-bottom: 1px solid var(--toolbar-border-color); + transition-property: inset-inline-start; + transition-duration: var(--sidebar-transition-duration); + transition-timing-function: var(--sidebar-transition-timing-function); +} + +:is(#toolbarContainer #loadingBar) .progress { + position: absolute; + top: 0; + inset-inline-start: 0; + width: 100%; + transform: scaleX(var(--progressBar-percent)); + transform-origin: calc(50% - 50% * var(--dir-factor)) 0; + height: 100%; + background-color: var(--progressBar-color); + overflow: hidden; + transition: transform 200ms; +} + +.indeterminate:is(#toolbarContainer #loadingBar) .progress { + transform: none; + background-color: var(--progressBar-bg-color); + transition: none; +} + +:is(.indeterminate:is(#toolbarContainer #loadingBar) .progress) .glimmer { + position: absolute; + top: 0; + inset-inline-start: 0; + height: 100%; + width: calc(100% + 150px); + background: repeating-linear-gradient( + 135deg, + var(--progressBar-blend-color) 0, + var(--progressBar-bg-color) 5px, + var(--progressBar-bg-color) 45px, + var(--progressBar-color) 55px, + var(--progressBar-color) 95px, + var(--progressBar-blend-color) 100px + ); + animation: progressIndeterminate 1s linear infinite; +} + +#secondaryToolbar #firstPage::before { + -webkit-mask-image: var(--secondaryToolbarButton-firstPage-icon); + mask-image: var(--secondaryToolbarButton-firstPage-icon); +} + +#secondaryToolbar #lastPage::before { + -webkit-mask-image: var(--secondaryToolbarButton-lastPage-icon); + mask-image: var(--secondaryToolbarButton-lastPage-icon); +} + +#secondaryToolbar #pageRotateCcw::before { + -webkit-mask-image: var(--secondaryToolbarButton-rotateCcw-icon); + mask-image: var(--secondaryToolbarButton-rotateCcw-icon); +} + +#secondaryToolbar #pageRotateCw::before { + -webkit-mask-image: var(--secondaryToolbarButton-rotateCw-icon); + mask-image: var(--secondaryToolbarButton-rotateCw-icon); +} + +#secondaryToolbar #cursorSelectTool::before { + -webkit-mask-image: var(--secondaryToolbarButton-selectTool-icon); + mask-image: var(--secondaryToolbarButton-selectTool-icon); +} + +#secondaryToolbar #cursorHandTool::before { + -webkit-mask-image: var(--secondaryToolbarButton-handTool-icon); + mask-image: var(--secondaryToolbarButton-handTool-icon); +} + +#secondaryToolbar #scrollPage::before { + -webkit-mask-image: var(--secondaryToolbarButton-scrollPage-icon); + mask-image: var(--secondaryToolbarButton-scrollPage-icon); +} + +#secondaryToolbar #scrollVertical::before { + -webkit-mask-image: var(--secondaryToolbarButton-scrollVertical-icon); + mask-image: var(--secondaryToolbarButton-scrollVertical-icon); +} + +#secondaryToolbar #scrollHorizontal::before { + -webkit-mask-image: var(--secondaryToolbarButton-scrollHorizontal-icon); + mask-image: var(--secondaryToolbarButton-scrollHorizontal-icon); +} + +#secondaryToolbar #scrollWrapped::before { + -webkit-mask-image: var(--secondaryToolbarButton-scrollWrapped-icon); + mask-image: var(--secondaryToolbarButton-scrollWrapped-icon); +} + +#secondaryToolbar #spreadNone::before { + -webkit-mask-image: var(--secondaryToolbarButton-spreadNone-icon); + mask-image: var(--secondaryToolbarButton-spreadNone-icon); +} + +#secondaryToolbar #spreadOdd::before { + -webkit-mask-image: var(--secondaryToolbarButton-spreadOdd-icon); + mask-image: var(--secondaryToolbarButton-spreadOdd-icon); +} + +#secondaryToolbar #spreadEven::before { + -webkit-mask-image: var(--secondaryToolbarButton-spreadEven-icon); + mask-image: var(--secondaryToolbarButton-spreadEven-icon); +} + +#secondaryToolbar #documentProperties::before { + -webkit-mask-image: var(--secondaryToolbarButton-documentProperties-icon); + mask-image: var(--secondaryToolbarButton-documentProperties-icon); +} + +@media all and (max-width: 840px) { + #sidebarContainer { + background-color: var(--sidebar-narrow-bg-color); } - -:is(#toolbarContainer #toolbarViewer) > *{ - flex:none; - } - -:is(#toolbarContainer #toolbarViewer) input{ - font:message-box; - } - -:is(#toolbarContainer #toolbarViewer) .toolbarButtonSpacer{ - width:30px; - display:block; - height:1px; - } - -:is(#toolbarContainer #toolbarViewer) #toolbarViewerLeft #numPages.toolbarLabel{ - padding-inline-start:3px; - flex:none; - } - -#toolbarContainer #loadingBar{ - --progressBar-percent:0%; - --progressBar-end-offset:0; - - position:absolute; - top:var(--toolbar-height); - inset-inline:0 var(--progressBar-end-offset); - height:4px; - background-color:var(--progressBar-bg-color); - border-bottom:1px solid var(--toolbar-border-color); - transition-property:inset-inline-start; - transition-duration:var(--sidebar-transition-duration); - transition-timing-function:var(--sidebar-transition-timing-function); - } - -:is(#toolbarContainer #loadingBar) .progress{ - position:absolute; - top:0; - inset-inline-start:0; - width:100%; - transform:scaleX(var(--progressBar-percent)); - transform-origin:calc(50% - 50% * var(--dir-factor)) 0; - height:100%; - background-color:var(--progressBar-color); - overflow:hidden; - transition:transform 200ms; - } - -.indeterminate:is(#toolbarContainer #loadingBar) .progress{ - transform:none; - background-color:var(--progressBar-bg-color); - transition:none; - } - -:is(.indeterminate:is(#toolbarContainer #loadingBar) .progress) .glimmer{ - position:absolute; - top:0; - inset-inline-start:0; - height:100%; - width:calc(100% + 150px); - background:repeating-linear-gradient( - 135deg, - var(--progressBar-blend-color) 0, - var(--progressBar-bg-color) 5px, - var(--progressBar-bg-color) 45px, - var(--progressBar-color) 55px, - var(--progressBar-color) 95px, - var(--progressBar-blend-color) 100px - ); - animation:progressIndeterminate 1s linear infinite; - } - -#secondaryToolbar #firstPage::before{ - -webkit-mask-image:var(--secondaryToolbarButton-firstPage-icon); - mask-image:var(--secondaryToolbarButton-firstPage-icon); - } - -#secondaryToolbar #lastPage::before{ - -webkit-mask-image:var(--secondaryToolbarButton-lastPage-icon); - mask-image:var(--secondaryToolbarButton-lastPage-icon); - } - -#secondaryToolbar #pageRotateCcw::before{ - -webkit-mask-image:var(--secondaryToolbarButton-rotateCcw-icon); - mask-image:var(--secondaryToolbarButton-rotateCcw-icon); - } - -#secondaryToolbar #pageRotateCw::before{ - -webkit-mask-image:var(--secondaryToolbarButton-rotateCw-icon); - mask-image:var(--secondaryToolbarButton-rotateCw-icon); - } - -#secondaryToolbar #cursorSelectTool::before{ - -webkit-mask-image:var(--secondaryToolbarButton-selectTool-icon); - mask-image:var(--secondaryToolbarButton-selectTool-icon); - } - -#secondaryToolbar #cursorHandTool::before{ - -webkit-mask-image:var(--secondaryToolbarButton-handTool-icon); - mask-image:var(--secondaryToolbarButton-handTool-icon); - } - -#secondaryToolbar #scrollPage::before{ - -webkit-mask-image:var(--secondaryToolbarButton-scrollPage-icon); - mask-image:var(--secondaryToolbarButton-scrollPage-icon); - } - -#secondaryToolbar #scrollVertical::before{ - -webkit-mask-image:var(--secondaryToolbarButton-scrollVertical-icon); - mask-image:var(--secondaryToolbarButton-scrollVertical-icon); - } - -#secondaryToolbar #scrollHorizontal::before{ - -webkit-mask-image:var(--secondaryToolbarButton-scrollHorizontal-icon); - mask-image:var(--secondaryToolbarButton-scrollHorizontal-icon); - } - -#secondaryToolbar #scrollWrapped::before{ - -webkit-mask-image:var(--secondaryToolbarButton-scrollWrapped-icon); - mask-image:var(--secondaryToolbarButton-scrollWrapped-icon); - } - -#secondaryToolbar #spreadNone::before{ - -webkit-mask-image:var(--secondaryToolbarButton-spreadNone-icon); - mask-image:var(--secondaryToolbarButton-spreadNone-icon); - } - -#secondaryToolbar #spreadOdd::before{ - -webkit-mask-image:var(--secondaryToolbarButton-spreadOdd-icon); - mask-image:var(--secondaryToolbarButton-spreadOdd-icon); - } - -#secondaryToolbar #spreadEven::before{ - -webkit-mask-image:var(--secondaryToolbarButton-spreadEven-icon); - mask-image:var(--secondaryToolbarButton-spreadEven-icon); - } - -#secondaryToolbar #documentProperties::before{ - -webkit-mask-image:var(--secondaryToolbarButton-documentProperties-icon); - mask-image:var(--secondaryToolbarButton-documentProperties-icon); - } - -@media all and (max-width: 840px){ - #sidebarContainer{ - background-color:var(--sidebar-narrow-bg-color); - } - #outerContainer.sidebarOpen #viewerContainer{ - inset-inline-start:0 !important; + #outerContainer.sidebarOpen #viewerContainer { + inset-inline-start: 0 !important; } } -@media all and (max-width: 750px){ - #outerContainer .hiddenMediumView{ - display:none !important; +@media all and (max-width: 750px) { + #outerContainer .hiddenMediumView { + display: none !important; } - #outerContainer .visibleMediumView:not(.hidden, [hidden]){ - display:inline-block !important; + #outerContainer .visibleMediumView:not(.hidden, [hidden]) { + display: inline-block !important; } } -@media all and (max-width: 690px){ +@media all and (max-width: 690px) { .hiddenSmallView, - .hiddenSmallView *{ - display:none !important; + .hiddenSmallView * { + display: none !important; } - #toolbarContainer #toolbarViewer .toolbarButtonSpacer{ - width:0; + #toolbarContainer #toolbarViewer .toolbarButtonSpacer { + width: 0; } } -@media all and (max-width: 560px){ - #scaleSelectContainer{ - display:none; +@media all and (max-width: 560px) { + #scaleSelectContainer { + display: none; } } From 0172e769b03047e6bbaae739c9f207bb1cbc6898 Mon Sep 17 00:00:00 2001 From: Nicola Benaglia Date: Sat, 12 Apr 2025 19:14:52 +0200 Subject: [PATCH 31/31] Refactor and use prettier --- src/App.tsx | 1 + src/components/Apps/Apps-styles.tsx | 338 +++++++++--------- .../Apps/AppsDevModeTabComponent.tsx | 47 +-- src/components/Apps/AppsHome.tsx | 57 ++- src/components/Apps/AppsLibrary.tsx | 2 + 5 files changed, 226 insertions(+), 219 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 4cb416a..ba2329b 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -606,6 +606,7 @@ function App() { }); }); } catch (error) { + console.log(error); } finally { setIsLoading(false); } diff --git a/src/components/Apps/Apps-styles.tsx b/src/components/Apps/Apps-styles.tsx index 631186b..15cdb9d 100644 --- a/src/components/Apps/Apps-styles.tsx +++ b/src/components/Apps/Apps-styles.tsx @@ -1,78 +1,78 @@ -import { Typography, Box, ButtonBase } from "@mui/material"; -import { styled } from "@mui/system"; +import { Typography, Box, ButtonBase } from '@mui/material'; +import { styled } from '@mui/system'; export const AppsParent = styled(Box)(({ theme }) => ({ - display: "flex", - width: "100%", - flexDirection: "column", - height: "100%", - alignItems: "center", - overflow: "auto", + display: 'flex', + width: '100%', + flexDirection: 'column', + height: '100%', + alignItems: 'center', + overflow: 'auto', // For WebKit-based browsers (Chrome, Safari, etc.) - "::-webkit-scrollbar": { - width: "0px", // Set the width to 0 to hide the scrollbar - height: "0px", // Set the height to 0 for horizontal scrollbar + '::-webkit-scrollbar': { + width: '0px', // Set the width to 0 to hide the scrollbar + height: '0px', // Set the height to 0 for horizontal scrollbar }, // For Firefox - scrollbarWidth: "none", // Hides the scrollbar in Firefox + scrollbarWidth: 'none', // Hides the scrollbar in Firefox // Optional for better cross-browser consistency - "-msOverflowStyle": "none", // Hides scrollbar in IE and Edge + '-msOverflowStyle': 'none', // Hides scrollbar in IE and Edge backgroundColor: theme.palette.background.default, color: theme.palette.text.primary, })); export const AppsContainer = styled(Box)(({ theme }) => ({ - display: "flex", - width: "90%", - justifyContent: "space-evenly", - gap: "24px", - flexWrap: "wrap", - alignItems: "flex-start", - alignSelf: "center", + display: 'flex', + width: '90%', + justifyContent: 'space-evenly', + gap: '24px', + flexWrap: 'wrap', + alignItems: 'flex-start', + alignSelf: 'center', backgroundColor: theme.palette.background.default, color: theme.palette.text.primary, })); export const AppsLibraryContainer = styled(Box)(({ theme }) => ({ - display: "flex", - width: "100%", - flexDirection: "column", - justifyContent: "flex-start", - alignItems: "center", + display: 'flex', + width: '100%', + flexDirection: 'column', + justifyContent: 'flex-start', + alignItems: 'center', backgroundColor: theme.palette.background.paper, })); export const AppsWidthLimiter = styled(Box)(({ theme }) => ({ - display: "flex", - width: "90%", - flexDirection: "column", - justifyContent: "flex-start", - alignItems: "flex-start", + display: 'flex', + width: '90%', + flexDirection: 'column', + justifyContent: 'flex-start', + alignItems: 'flex-start', backgroundColor: theme.palette.background.default, color: theme.palette.text.primary, })); export const AppsSearchContainer = styled(Box)(({ theme }) => ({ - display: "flex", - width: "90%", - justifyContent: "space-between", - alignItems: "center", - borderRadius: "8px", - padding: "0px 10px", - height: "36px", + display: 'flex', + width: '90%', + justifyContent: 'space-between', + alignItems: 'center', + borderRadius: '8px', + padding: '0px 10px', + height: '36px', backgroundColor: theme.palette.background.default, color: theme.palette.text.primary, })); export const AppsSearchLeft = styled(Box)(({ theme }) => ({ - display: "flex", - width: "90%", - justifyContent: "flex-start", - alignItems: "center", - gap: "10px", + display: 'flex', + width: '90%', + justifyContent: 'flex-start', + alignItems: 'center', + gap: '10px', flexGrow: 1, flexShrink: 0, backgroundColor: theme.palette.background.default, @@ -80,51 +80,50 @@ export const AppsSearchLeft = styled(Box)(({ theme }) => ({ })); export const AppsSearchRight = styled(Box)(({ theme }) => ({ - display: "flex", - width: "90%", - justifyContent: "flex-end", - alignItems: "center", + display: 'flex', + width: '90%', + justifyContent: 'flex-end', + alignItems: 'center', flexShrink: 1, backgroundColor: theme.palette.background.default, color: theme.palette.text.primary, })); export const AppCircleContainer = styled(Box)(({ theme }) => ({ - display: "flex", - flexDirection: "column", - gap: "5px", - alignItems: "center", - width: "100%", + display: 'flex', + flexDirection: 'column', + gap: '5px', + alignItems: 'center', + width: '100%', backgroundColor: theme.palette.background.default, color: theme.palette.text.primary, })); export const Add = styled(Typography)(({ theme }) => ({ - fontSize: "36px", + fontSize: '36px', fontWeight: 500, - lineHeight: "43.57px", - textAlign: "left", + lineHeight: '43.57px', + textAlign: 'left', backgroundColor: theme.palette.background.default, color: theme.palette.text.primary, })); export const AppCircleLabel = styled(Typography)(({ theme }) => ({ - fontSize: "14px", - fontWeight: 500, - lineHeight: 1.2, - // whiteSpace: 'nowrap', - overflow: "hidden", - textOverflow: "ellipsis", - width: "120%", - "-webkit-line-clamp": "2", - "-webkit-box-orient": "vertical", - display: "-webkit-box", + '-webkit-box-orient': 'vertical', + '-webkit-line-clamp': '2', backgroundColor: theme.palette.background.default, color: theme.palette.text.primary, + display: '-webkit-box', + fontSize: '14px', + fontWeight: 500, + lineHeight: 1.2, + overflow: 'hidden', + textOverflow: 'ellipsis', + width: '120%', })); export const AppLibrarySubTitle = styled(Typography)(({ theme }) => ({ - fontSize: "16px", + fontSize: '16px', fontWeight: 500, lineHeight: 1.2, backgroundColor: theme.palette.background.default, @@ -132,58 +131,63 @@ export const AppLibrarySubTitle = styled(Typography)(({ theme }) => ({ })); export const AppCircle = styled(Box)(({ theme }) => ({ - display: "flex", - width: "75px", - flexDirection: "column", - height: "75px", - alignItems: "center", - justifyContent: "center", - borderRadius: "50%", + alignItems: 'center', backgroundColor: theme.palette.background.default, + borderColor: + theme.palette.mode === 'dark' + ? 'rgb(209, 209, 209)' + : 'rgba(41, 41, 43, 1)', + borderWidth: '1px', + borderRadius: '50%', + borderStyle: 'solid', color: theme.palette.text.primary, - border: "1px solid #FFFFFF", + display: 'flex', + flexDirection: 'column', + height: '75px', + justifyContent: 'center', + width: '75px', })); export const AppInfoSnippetContainer = styled(Box)(({ theme }) => ({ - display: "flex", - justifyContent: "space-between", - alignItems: "center", - width: "100%", + alignItems: 'center', backgroundColor: theme.palette.background.default, color: theme.palette.text.primary, + display: 'flex', + justifyContent: 'space-between', + width: '100%', })); export const AppInfoSnippetLeft = styled(Box)(({ theme }) => ({ - display: "flex", - justifyContent: "flex-start", - alignItems: "center", - gap: "12px", + alignItems: 'center', backgroundColor: theme.palette.background.default, color: theme.palette.text.primary, + display: 'flex', + gap: '12px', + justifyContent: 'flex-start', })); export const AppInfoSnippetRight = styled(Box)(({ theme }) => ({ - display: "flex", - justifyContent: "flex-end", - alignItems: "center", + display: 'flex', + justifyContent: 'flex-end', + alignItems: 'center', backgroundColor: theme.palette.background.default, color: theme.palette.text.primary, })); export const AppDownloadButton = styled(ButtonBase)(({ theme }) => ({ - backgroundColor: "#247C0E", + backgroundColor: '#247C0E', color: theme.palette.text.primary, - width: "101px", - height: "29px", - display: "flex", - justifyContent: "center", - alignItems: "center", - borderRadius: "25px", - alignSelf: "center", + width: '101px', + height: '29px', + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + borderRadius: '25px', + alignSelf: 'center', })); export const AppDownloadButtonText = styled(Typography)(({ theme }) => ({ - fontSize: "14px", + fontSize: '14px', fontWeight: 500, lineHeight: 1.2, backgroundColor: theme.palette.background.default, @@ -191,50 +195,50 @@ export const AppDownloadButtonText = styled(Typography)(({ theme }) => ({ })); export const AppPublishTagsContainer = styled(Box)(({ theme }) => ({ - gap: "10px", - flexWrap: "wrap", - justifyContent: "flex-start", - width: "100%", - display: "flex", + gap: '10px', + flexWrap: 'wrap', + justifyContent: 'flex-start', + width: '100%', + display: 'flex', backgroundColor: theme.palette.background.default, color: theme.palette.text.primary, })); export const AppInfoSnippetMiddle = styled(Box)(({ theme }) => ({ - display: "flex", - flexDirection: "column", - justifyContent: "center", - alignItems: "flex-start", + display: 'flex', + flexDirection: 'column', + justifyContent: 'center', + alignItems: 'flex-start', backgroundColor: theme.palette.background.default, color: theme.palette.text.primary, })); export const AppInfoAppName = styled(Typography)(({ theme }) => ({ - fontSize: "16px", + fontSize: '16px', fontWeight: 500, lineHeight: 1.2, - textAlign: "start", + textAlign: 'start', backgroundColor: theme.palette.background.default, color: theme.palette.text.primary, })); export const AppInfoUserName = styled(Typography)(({ theme }) => ({ - fontSize: "13px", + fontSize: '13px', fontWeight: 400, lineHeight: 1.2, - textAlign: "start", + textAlign: 'start', backgroundColor: theme.palette.background.default, color: theme.palette.text.primary, })); export const AppsNavBarParent = styled(Box)(({ theme }) => ({ - display: "flex", - justifyContent: "space-between", - alignItems: "center", - width: "100%", - height: "60px", - padding: "0px 10px", - position: "fixed", + display: 'flex', + justifyContent: 'space-between', + alignItems: 'center', + width: '100%', + height: '60px', + padding: '0px 10px', + position: 'fixed', bottom: 0, zIndex: 1, backgroundColor: theme.palette.background.default, @@ -242,120 +246,120 @@ export const AppsNavBarParent = styled(Box)(({ theme }) => ({ })); export const AppsNavBarLeft = styled(Box)(({ theme }) => ({ - display: "flex", - justifyContent: "flex-start", - alignItems: "center", + display: 'flex', + justifyContent: 'flex-start', + alignItems: 'center', flexGrow: 1, backgroundColor: theme.palette.background.default, color: theme.palette.text.primary, })); export const AppsNavBarRight = styled(Box)(({ theme }) => ({ - display: "flex", - justifyContent: "flex-end", - alignItems: "center", + display: 'flex', + justifyContent: 'flex-end', + alignItems: 'center', backgroundColor: theme.palette.background.default, color: theme.palette.text.primary, })); export const TabParent = styled(Box)(({ theme }) => ({ - height: "36px", - width: "36px", - position: "relative", - borderRadius: "50%", - display: "flex", - alignItems: "center", - justifyContent: "center", + height: '36px', + width: '36px', + position: 'relative', + borderRadius: '50%', + display: 'flex', + alignItems: 'center', + justifyContent: 'center', backgroundColor: theme.palette.background.default, color: theme.palette.text.primary, })); export const PublishQAppCTAParent = styled(Box)(({ theme }) => ({ - display: "flex", - justifyContent: "space-between", - alignItems: "center", - width: "100%", + display: 'flex', + justifyContent: 'space-between', + alignItems: 'center', + width: '100%', backgroundColor: theme.palette.background.default, color: theme.palette.text.primary, })); export const PublishQAppCTALeft = styled(Box)(({ theme }) => ({ - display: "flex", - justifyContent: "flex-start", - alignItems: "center", + display: 'flex', + justifyContent: 'flex-start', + alignItems: 'center', backgroundColor: theme.palette.background.default, color: theme.palette.text.primary, })); export const PublishQAppCTARight = styled(Box)(({ theme }) => ({ - display: "flex", - justifyContent: "flex-end", - alignItems: "center", + display: 'flex', + justifyContent: 'flex-end', + alignItems: 'center', backgroundColor: theme.palette.background.default, color: theme.palette.text.primary, })); export const PublishQAppCTAButton = styled(ButtonBase)(({ theme }) => ({ - width: "101px", - height: "29px", - display: "flex", - justifyContent: "center", - alignItems: "center", - borderRadius: "25px", - border: "1px solid #FFFFFF", + width: '101px', + height: '29px', + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + borderRadius: '25px', + border: '1px solid #FFFFFF', backgroundColor: theme.palette.background.default, color: theme.palette.text.primary, })); export const PublishQAppDotsBG = styled(Box)(({ theme }) => ({ - display: "flex", - justifyContent: "center", - alignItems: "center", - width: "60px", - height: "60px", + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + width: '60px', + height: '60px', backgroundColor: theme.palette.background.default, color: theme.palette.text.primary, })); export const PublishQAppInfo = styled(Typography)(({ theme }) => ({ - fontSize: "10px", + fontSize: '10px', fontWeight: 400, lineHeight: 1.2, - fontStyle: "italic", + fontStyle: 'italic', backgroundColor: theme.palette.background.default, color: theme.palette.text.primary, })); export const PublishQAppChoseFile = styled(ButtonBase)(({ theme }) => ({ - width: "101px", - height: "30px", - display: "flex", - justifyContent: "center", - alignItems: "center", - borderRadius: "5px", + width: '101px', + height: '30px', + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + borderRadius: '5px', fontWeight: 600, - fontSize: "10px", + fontSize: '10px', backgroundColor: theme.palette.background.default, color: theme.palette.text.primary, })); export const AppsCategoryInfo = styled(Box)(({ theme }) => ({ - display: "flex", - alignItems: "center", - width: "100%", + display: 'flex', + alignItems: 'center', + width: '100%', backgroundColor: theme.palette.background.default, color: theme.palette.text.primary, })); export const AppsCategoryInfoSub = styled(Box)(({ theme }) => ({ - display: "flex", - flexDirection: "column", + display: 'flex', + flexDirection: 'column', backgroundColor: theme.palette.background.default, color: theme.palette.text.primary, })); export const AppsCategoryInfoLabel = styled(Typography)(({ theme }) => ({ - fontSize: "12px", + fontSize: '12px', fontWeight: 700, lineHeight: 1.2, backgroundColor: theme.palette.background.default, @@ -363,7 +367,7 @@ export const AppsCategoryInfoLabel = styled(Typography)(({ theme }) => ({ })); export const AppsCategoryInfoValue = styled(Typography)(({ theme }) => ({ - fontSize: "12px", + fontSize: '12px', fontWeight: 500, lineHeight: 1.2, backgroundColor: theme.palette.background.default, @@ -371,11 +375,11 @@ export const AppsCategoryInfoValue = styled(Typography)(({ theme }) => ({ })); export const AppsInfoDescription = styled(Typography)(({ theme }) => ({ - fontSize: "13px", + fontSize: '13px', fontWeight: 300, lineHeight: 1.2, - width: "90%", - textAlign: "start", + width: '90%', + textAlign: 'start', backgroundColor: theme.palette.background.default, color: theme.palette.text.primary, })); diff --git a/src/components/Apps/AppsDevModeTabComponent.tsx b/src/components/Apps/AppsDevModeTabComponent.tsx index 46372a5..e25bf11 100644 --- a/src/components/Apps/AppsDevModeTabComponent.tsx +++ b/src/components/Apps/AppsDevModeTabComponent.tsx @@ -1,22 +1,21 @@ -import React from "react"; -import { TabParent } from "./Apps-styles"; -import NavCloseTab from "../../assets/svgs/NavCloseTab.svg"; -import { getBaseApiReact } from "../../App"; -import { Avatar, ButtonBase } from "@mui/material"; -import LogoSelected from "../../assets/svgs/LogoSelected.svg"; -import { executeEvent } from "../../utils/events"; +import { TabParent } from './Apps-styles'; +import NavCloseTab from '../../assets/svgs/NavCloseTab.svg'; +import { getBaseApiReact } from '../../App'; +import { Avatar, ButtonBase } from '@mui/material'; +import LogoSelected from '../../assets/svgs/LogoSelected.svg'; +import { executeEvent } from '../../utils/events'; export const AppsDevModeTabComponent = ({ isSelected, app }) => { return ( { if (isSelected) { - executeEvent("removeTabDevMode", { + executeEvent('removeTabDevMode', { data: app, }); return; } - executeEvent("setSelectedTabDevMode", { + executeEvent('setSelectedTabDevMode', { data: app, isDevMode: true, }); @@ -24,15 +23,15 @@ export const AppsDevModeTabComponent = ({ isSelected, app }) => { > {isSelected && ( { )} center-icon diff --git a/src/components/Apps/AppsHome.tsx b/src/components/Apps/AppsHome.tsx index 81fc9b8..4b5b030 100644 --- a/src/components/Apps/AppsHome.tsx +++ b/src/components/Apps/AppsHome.tsx @@ -1,4 +1,4 @@ -import React, { useMemo, useState } from "react"; +import React, { useMemo, useState } from 'react'; import { AppCircle, AppCircleContainer, @@ -6,52 +6,51 @@ import { AppLibrarySubTitle, AppsContainer, AppsParent, -} from "./Apps-styles"; -import { Avatar, ButtonBase } from "@mui/material"; -import { Add } from "@mui/icons-material"; -import { getBaseApiReact, isMobile } from "../../App"; -import LogoSelected from "../../assets/svgs/LogoSelected.svg"; -import { executeEvent } from "../../utils/events"; -import { SortablePinnedApps } from "./SortablePinnedApps"; -import { Spacer } from "../../common/Spacer"; +} from './Apps-styles'; +import { Avatar, ButtonBase } from '@mui/material'; +import { Add } from '@mui/icons-material'; +import { getBaseApiReact, isMobile } from '../../App'; +import LogoSelected from '../../assets/svgs/LogoSelected.svg'; +import { executeEvent } from '../../utils/events'; +import { SortablePinnedApps } from './SortablePinnedApps'; +import { Spacer } from '../../common/Spacer'; -export const AppsHome = ({ setMode, myApp, myWebsite, availableQapps }) => { +export const AppsHome = ({ setMode, myApp, myWebsite, availableQapps }) => { return ( <> - - - Apps Dashboard - - - - + Apps Dashboard + + { - setMode("library"); + setMode('library'); }} > - + + Library - - - + + - + ); }; diff --git a/src/components/Apps/AppsLibrary.tsx b/src/components/Apps/AppsLibrary.tsx index 3eee1c2..c79e1af 100644 --- a/src/components/Apps/AppsLibrary.tsx +++ b/src/components/Apps/AppsLibrary.tsx @@ -58,6 +58,8 @@ const officialAppList = [ 'q-search', ]; +// TODO: apply dark/light style + const ScrollerStyled = styled('div')({ // Hide scrollbar for WebKit browsers (Chrome, Safari) '::-webkit-scrollbar': {