batch of updates 7

This commit is contained in:
PhilReact 2024-12-30 08:13:33 +02:00
parent a4c1fd564b
commit f6cac4b91f
19 changed files with 810 additions and 415 deletions

45
package-lock.json generated
View File

@ -40,6 +40,7 @@
"file-saver": "^2.0.5",
"html-to-text": "^9.0.5",
"jssha": "3.3.1",
"lit": "^3.2.1",
"lodash": "^4.17.21",
"mime": "^4.0.4",
"moment": "^2.30.1",
@ -1360,6 +1361,19 @@
"resolved": "https://registry.npmjs.org/@juggle/resize-observer/-/resize-observer-3.4.0.tgz",
"integrity": "sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA=="
},
"node_modules/@lit-labs/ssr-dom-shim": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@lit-labs/ssr-dom-shim/-/ssr-dom-shim-1.2.1.tgz",
"integrity": "sha512-wx4aBmgeGvFmOKucFKY+8VFJSYZxs9poN3SDNQFF6lT6NrQUnHiPB2PWz2sc4ieEcAaYYzN+1uWahEeTq2aRIQ=="
},
"node_modules/@lit/reactive-element": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-2.0.4.tgz",
"integrity": "sha512-GFn91inaUa2oHLak8awSIigYz0cU0Payr1rcFsrkf5OJ5eSPxElyZfKh0f2p9FsTiZWXQdWGJeXZICEfXXYSXQ==",
"dependencies": {
"@lit-labs/ssr-dom-shim": "^1.2.0"
}
},
"node_modules/@mui/base": {
"version": "5.0.0-beta.40",
"resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.40.tgz",
@ -2876,8 +2890,7 @@
"node_modules/@types/trusted-types": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
"integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==",
"dev": true
"integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw=="
},
"node_modules/@types/use-sync-external-store": {
"version": "0.0.6",
@ -6029,6 +6042,34 @@
"uc.micro": "^2.0.0"
}
},
"node_modules/lit": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/lit/-/lit-3.2.1.tgz",
"integrity": "sha512-1BBa1E/z0O9ye5fZprPtdqnc0BFzxIxTTOO/tQFmyC/hj1O3jL4TfmLBw0WEwjAokdLwpclkvGgDJwTIh0/22w==",
"dependencies": {
"@lit/reactive-element": "^2.0.4",
"lit-element": "^4.1.0",
"lit-html": "^3.2.0"
}
},
"node_modules/lit-element": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/lit-element/-/lit-element-4.1.1.tgz",
"integrity": "sha512-HO9Tkkh34QkTeUmEdNYhMT8hzLid7YlMlATSi1q4q17HE5d9mrrEHJ/o8O2D0cMi182zK1F3v7x0PWFjrhXFew==",
"dependencies": {
"@lit-labs/ssr-dom-shim": "^1.2.0",
"@lit/reactive-element": "^2.0.4",
"lit-html": "^3.2.0"
}
},
"node_modules/lit-html": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/lit-html/-/lit-html-3.2.1.tgz",
"integrity": "sha512-qI/3lziaPMSKsrwlxH/xMgikhQ0EGOX2ICU73Bi/YHFvz2j/yMCIrw4+puF2IpQ4+upd3EWbvnHM9+PnJn48YA==",
"dependencies": {
"@types/trusted-types": "^2.0.2"
}
},
"node_modules/local-pkg": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.5.0.tgz",

View File

@ -65,6 +65,7 @@
"react-virtuoso": "^4.10.4",
"recoil": "^0.7.7",
"short-unique-id": "^5.2.0",
"lit": "^3.2.1",
"slate": "^0.103.0",
"slate-react": "^0.109.0",
"tiptap-extension-resize-image": "^1.1.8",

View File

@ -77,7 +77,7 @@ display: flex;
border: 1px solid var(--50-white, rgba(255, 255, 255, 0.5));
justify-content: space-between;
align-items: center;
width: 140px;
width: auto;
height: 25px;
padding: 5px 15px 5px 15px;
gap: 5px;

View File

@ -51,6 +51,7 @@ import {
createAccount,
generateRandomSentence,
saveFileToDisk,
saveSeedPhraseToDisk
} from "./utils/generateWallet/generateWallet";
import { kdf } from "./deps/kdf";
import { generateSaveWalletData } from "./utils/generateWallet/storeWallet";
@ -116,6 +117,8 @@ import { useHandleTutorials } from "./components/Tutorials/useHandleTutorials";
import { CoreSyncStatus } from "./components/CoreSyncStatus";
import BoundedNumericTextField from "./common/BoundedNumericTextField";
import { Wallets } from "./Wallets";
import './utils/seedPhrase/RandomSentenceGenerator';
import { test } from "vitest";
type extStates =
| "not-authenticated"
@ -323,7 +326,8 @@ function App() {
const { isShow, onCancel, onOk, show, message } = useModal();
const { isShow: isShowUnsavedChanges, onCancel: onCancelUnsavedChanges, onOk: onOkUnsavedChanges, show: showUnsavedChanges, message: messageUnsavedChanges } = useModal();
const {downloadResource} = useFetchResources()
const [showSeed, setShowSeed] = useState(false)
const [creationStep, setCreationStep] = useState(1)
const {
isShow: isShowInfo,
onCancel: onCancelInfo,
@ -366,6 +370,11 @@ function App() {
const [fullScreen, setFullScreen] = useRecoilState(fullScreenAtom);
const resetAtomIsUsingImportExportSettingsAtom = useResetRecoilState(isUsingImportExportSettingsAtom)
const { toggleFullScreen } = useAppFullScreen(setFullScreen);
const generatorRef = useRef(null)
const exportSeedphrase = ()=> {
const seedPhrase = generatorRef.current.parsedString
saveSeedPhraseToDisk(seedPhrase)
}
const {showTutorial, openTutorialModal, shownTutorialsInitiated, setOpenTutorialModal} = useHandleTutorials()
const passwordRef = useRef<HTMLInputElement>(null);
@ -1028,7 +1037,8 @@ function App() {
res();
}, 250);
});
const res = await createAccount();
const res = await createAccount(generatorRef.current.parsedString);
const wallet = await res.generateSaveWalletData(
walletToBeDownloadedPassword,
crypto.kdfThreads,
@ -1102,6 +1112,8 @@ function App() {
setExtstate("authenticated");
setIsOpenSendQort(false);
setIsOpenSendQortSuccess(false);
setShowSeed(false)
setCreationStep(1)
};
const resetAllStates = () => {
@ -1131,6 +1143,8 @@ function App() {
setTxList([]);
setMemberGroups([]);
resetAllRecoil()
setShowSeed(false)
setCreationStep(1)
};
function roundUpToDecimals(number, decimals = 8) {
@ -1353,9 +1367,10 @@ function App() {
return (
<AuthenticatedContainer
sx={{
width: isMobile ? "100vw" : "350px",
width: isMobile ? "100vw" : "auto",
display: "flex",
backgroundColor: "var(--bg-2)",
justifyContent: 'flex-end'
}}
>
{isMobile && (
@ -1378,11 +1393,13 @@ function App() {
</Box>
)}
<AuthenticatedContainerInnerLeft
sx={{
overflowY: isMobile && "auto",
}}
>
{desktopViewMode !== "apps" && desktopViewMode !== "dev" && desktopViewMode !== "chat" && (
<AuthenticatedContainerInnerLeft
sx={{
overflowY: isMobile && "auto",
padding: '0px 20px'
}}
>
<Spacer height="48px" />
{authenticatedMode === "ltc" ? (
@ -1534,6 +1551,7 @@ function App() {
Get QORT at Q-Trade
</TextP>
</AuthenticatedContainerInnerLeft>
)}
<AuthenticatedContainerInnerRight sx={{
height: "100%",
justifyContent: "space-between",
@ -1620,7 +1638,7 @@ function App() {
showTutorial('qapps', true)
} else {
showTutorial('create-account', true)
showTutorial('getting-started', true)
}
}} >
@ -1652,10 +1670,10 @@ function App() {
<AppContainer
sx={{
height: isMobile ? "100%" : "100vh",
backgroundImage: desktopViewMode === 'apps' && 'url("appsBg.svg")',
backgroundSize: desktopViewMode === 'apps' && 'cover',
backgroundPosition: desktopViewMode === 'apps' && 'center',
backgroundRepeat: desktopViewMode === 'apps' && 'no-repeat',
// backgroundImage: desktopViewMode === 'apps' && 'url("appsBg.svg")',
// backgroundSize: desktopViewMode === 'apps' && 'cover',
// backgroundPosition: desktopViewMode === 'apps' && 'center',
// backgroundRepeat: desktopViewMode === 'apps' && 'no-repeat',
}}
>
<GlobalContext.Provider value={{
@ -1714,7 +1732,7 @@ function App() {
desktopViewMode={desktopViewMode}
setDesktopViewMode={setDesktopViewMode}
/>
{(!isMobile && desktopViewMode !== 'apps') && renderProfile()}
{!isMobile && renderProfile()}
</Box>
<Box
@ -2519,7 +2537,7 @@ function App() {
)}
</>
)}
{extState === "create-wallet" && (
{extState === "create-wallet" && (
<>
{!walletToBeDownloaded && (
<>
@ -2538,7 +2556,15 @@ function App() {
cursor: "pointer",
}}
onClick={() => {
if(creationStep === 2){
setCreationStep(1)
return
}
setExtstate("not-authenticated");
setShowSeed(false)
setCreationStep(1)
setWalletToBeDownloadedPasswordConfirm('')
setWalletToBeDownloadedPassword('')
}}
src={Return}
/>
@ -2564,6 +2590,117 @@ function App() {
Set up your Qortal account
</TextP>
<Spacer height="14px" />
<Box sx={{
display: 'flex',
maxWidth: '100%',
justifyContent: 'center',
padding: '10px'
}}>
<Box sx={{
display: creationStep === 1 ? 'flex' : 'none',
flexDirection: 'column',
alignItems: 'center',
width: '350px',
maxWidth: '95%'
}}>
<Typography sx={{
fontSize: '14px'
}}>
A <span onClick={()=> {
setShowSeed(true)
}} style={{
fontSize: '14px',
color: 'steelblue',
cursor: 'pointer'
}}>SEEDPHRASE</span> has been randomly generated in the background.
</Typography>
<Typography sx={{
fontSize: '14px',
marginTop: '5px'
}}>
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.
</Typography>
<Typography sx={{
fontSize: '16px',
marginTop: '15px',
textAlign: 'center'
}}>
Create your Qortal account by clicking <span style={{
fontWeight: 'bold'
}}>NEXT</span> below.
</Typography>
<Spacer height="17px" />
<CustomButton onClick={()=> {
setCreationStep(2)
}}>
Next
</CustomButton>
</Box>
<div style={{
display: 'none'
}}>
<random-sentence-generator
ref={generatorRef}
template="adverb verb noun adjective noun adverb verb noun adjective noun adjective verbed adjective noun"
></random-sentence-generator>
</div>
<Dialog
open={showSeed}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogContent>
<Box sx={{
flexDirection: 'column',
maxWidth: '400px',
alignItems: 'center',
gap: '10px',
display: showSeed ? 'flex' : 'none'
}}>
<Typography sx={{
fontSize: '14px'
}}>Your seedphrase</Typography>
<Box sx={{
textAlign: 'center',
width: '100%',
backgroundColor: '#1f2023',
borderRadius: '5px',
padding: '10px',
}}>
{generatorRef.current?.parsedString}
</Box>
<CustomButton sx={{
padding: '7px',
fontSize: '12px'
}} onClick={exportSeedphrase}>
Export Seedphrase
</CustomButton>
</Box>
</DialogContent>
<DialogActions>
<Button variant="contained" onClick={()=> setShowSeed(false)}>
close
</Button>
</DialogActions>
</Dialog>
</Box>
<Box sx={{
display: creationStep === 2 ? 'flex' : 'none',
flexDirection: 'column',
alignItems: 'center',
}}>
<Spacer height="14px" />
<CustomLabel htmlFor="standard-adornment-password">
Wallet Password
</CustomLabel>
@ -2592,6 +2729,7 @@ function App() {
<CustomButton onClick={createAccountFunc}>
Create Account
</CustomButton>
</Box>
<ErrorText>{walletToBeDownloadedError}</ErrorText>
</>
)}
@ -2611,9 +2749,12 @@ function App() {
</TextP>
<Spacer height="100px" />
<CustomButton
onClick={() => {
saveFileToDiskFunc();
onClick={async () => {
await saveFileToDiskFunc();
returnToMain();
await showInfo({
message: `Keep your wallet file secure.`,
});
}}
>
Backup Account

View File

@ -1,4 +1,4 @@
import React, { useCallback, useEffect, useRef, useState } from "react";
import React, { useCallback, useContext, useEffect, useRef, useState } from "react";
import { Spacer } from "../common/Spacer";
import { CustomButton, TextItalic, TextP, TextSpan } from "../App-styles";
import {
@ -14,6 +14,7 @@ import {
Switch,
Tooltip,
Typography,
ButtonBase
} from "@mui/material";
import Logo1 from "../assets/svgs/Logo1.svg";
import Logo1Dark from "../assets/svgs/Logo1Dark.svg";
@ -21,6 +22,8 @@ import Info from "../assets/svgs/Info.svg";
import { CustomizedSnackbars } from "../components/Snackbar/Snackbar";
import { set } from "lodash";
import { cleanUrl, isUsingLocal } from "../background";
import HelpIcon from '@mui/icons-material/Help';
import { GlobalContext } from "../App";
const manifestData = chrome?.runtime?.getManifest();
@ -55,6 +58,8 @@ export const NotAuthenticated = ({
const importedApiKeyRef = useRef(null)
const currentNodeRef = useRef(null)
const hasLocalNodeRef = useRef(null)
const { showTutorial } = useContext(GlobalContext);
const isLocal = cleanUrl(currentNode?.url) === "127.0.0.1:12391";
const handleFileChangeApiKey = (event) => {
const file = event.target.files[0]; // Get the selected file
@ -627,6 +632,17 @@ export const NotAuthenticated = ({
</DialogActions>
</Dialog>
)}
<ButtonBase onClick={()=> {
showTutorial('create-account', true)
}} sx={{
position: 'fixed',
bottom: '25px',
right: '25px'
}}>
<HelpIcon sx={{
color: 'var(--unread)'
}} />
</ButtonBase>
</>
);
};

View File

@ -34,46 +34,8 @@ import { RESOURCE_TYPE_NUMBER_GROUP_CHAT_REACTIONS } from "./constants/resourceT
import TradeBotRespondRequest from './transactions/TradeBotRespondRequest';
let inMemoryKey: CryptoKey | null = null;
let inMemoryIV: Uint8Array | null = null;
async function initializeKeyAndIV() {
if (!inMemoryKey) {
inMemoryKey = await generateKey(); // Generates the key in memory
}
}
async function generateKey(): Promise<CryptoKey> {
return await crypto.subtle.generateKey(
{
name: "AES-GCM",
length: 256
},
true,
["encrypt", "decrypt"]
);
}
async function encryptData(data: string, key: CryptoKey): Promise<{ iv: Uint8Array; encryptedData: ArrayBuffer }> {
const encoder = new TextEncoder();
const encodedData = encoder.encode(data);
// Generate a random IV each time you encrypt
const iv = crypto.getRandomValues(new Uint8Array(12));
const encryptedData = await crypto.subtle.encrypt(
{
name: "AES-GCM",
iv: iv
},
key,
encodedData
);
return { iv, encryptedData };
}
export function cleanUrl(url) {
return url?.replace(/^(https?:\/\/)?(www\.)?/, '');
}
@ -1064,15 +1026,7 @@ function base64ToJson(base64) {
export async function getKeyPair() {
const res = await chrome.storage.local.get(["keyPair"]);
if (res?.keyPair) {
const combinedData = atob(res?.keyPair)
.split("")
.map((c) => c.charCodeAt(0));
const iv = new Uint8Array(combinedData.slice(0, 12)); // First 12 bytes are the IV
const encryptedData = new Uint8Array(combinedData.slice(12)).buffer;
const decryptedBase64Data = await decryptData(encryptedData, inMemoryKey, iv);
return decryptedBase64Data
return res.keyPair;
} else {
throw new Error("Wallet not authenticated");
}
@ -1573,14 +1527,8 @@ async function decryptWallet({ password, wallet, walletVersion }) {
rvnPrivateKey: wallet2._addresses[0].rvnWallet.derivedMasterPrivateKey
};
const dataString = JSON.stringify(toSave);
await initializeKeyAndIV();
const { iv, encryptedData } = await encryptData(dataString, inMemoryKey);
// Combine IV and encrypted data into a single Uint8Array
const combinedData = new Uint8Array([...iv, ...new Uint8Array(encryptedData)]);
const encryptedBase64Data = btoa(String.fromCharCode(...combinedData));
await new Promise((resolve, reject) => {
chrome.storage.local.set({ keyPair: encryptedBase64Data }, () => {
chrome.storage.local.set({ keyPair: dataString }, () => {
if (chrome.runtime.lastError) {
reject(new Error(chrome.runtime.lastError.message));
} else {

View File

@ -20,10 +20,12 @@ import { HomeIcon } from "../../assets/Icons/HomeIcon";
import { MessagingIcon } from "../../assets/Icons/MessagingIcon";
import { Save } from "../Save/Save";
import { HubsIcon } from "../../assets/Icons/HubsIcon";
import { IconWrapper } from "../Desktop/DesktopFooter";
import { AppsIcon } from "../../assets/Icons/AppsIcon";
const uid = new ShortUniqueId({ length: 8 });
export const AppsDesktop = ({ mode, setMode, show , myName, goToHome, setDesktopSideView, hasUnreadDirects, isDirects, isGroups, hasUnreadGroups, toggleSideViewGroups, toggleSideViewDirects}) => {
export const AppsDesktop = ({ mode, setMode, show , myName, goToHome, setDesktopSideView, hasUnreadDirects, isDirects, isGroups, hasUnreadGroups, toggleSideViewGroups, toggleSideViewDirects, setDesktopViewMode, isApps, desktopViewMode}) => {
const [availableQapps, setAvailableQapps] = useState([]);
const [selectedAppInfo, setSelectedAppInfo] = useState(null);
const [selectedCategory, setSelectedCategory] = useState(null)
@ -334,46 +336,44 @@ export const AppsDesktop = ({ mode, setMode, show , myName, goToHome, setDesktop
<HomeIcon
height={34}
color="rgba(250, 250, 250, 0.5)"
color={desktopViewMode === 'home' ? 'white': "rgba(250, 250, 250, 0.5)"}
/>
</ButtonBase>
<ButtonBase
onClick={() => {
setDesktopSideView("directs");
toggleSideViewDirects()
setDesktopViewMode('apps')
}}
>
<IconWrapper
color={isApps ? 'white' :"rgba(250, 250, 250, 0.5)"}
label="Apps"
disableWidth
>
<AppsIcon height={30} color={isApps ? 'white' :"rgba(250, 250, 250, 0.5)"} />
</IconWrapper>
</ButtonBase>
<ButtonBase
onClick={() => {
setDesktopViewMode('chat')
}}
>
<IconWrapper
color={(hasUnreadDirects || hasUnreadGroups) ? "var(--unread)" : desktopViewMode === 'chat' ? 'white' :"rgba(250, 250, 250, 0.5)"}
label="Chat"
disableWidth
>
<MessagingIcon
height={30}
color={
hasUnreadDirects
(hasUnreadDirects || hasUnreadGroups)
? "var(--unread)"
: isDirects
: desktopViewMode === 'chat'
? "white"
: "rgba(250, 250, 250, 0.5)"
}
/>
</ButtonBase>
<ButtonBase
onClick={() => {
setDesktopSideView("groups");
toggleSideViewGroups()
}}
>
<HubsIcon
height={30}
color={
hasUnreadGroups
? "var(--unread)"
: isGroups
? "white"
: "rgba(250, 250, 250, 0.5)"
}
/>
</IconWrapper>
</ButtonBase>
<Save isDesktop myName={myName} />
{mode !== 'home' && (

View File

@ -18,7 +18,7 @@ import AppIcon from "../../assets/svgs/AppIcon.svg";
import { HomeIcon } from "../../assets/Icons/HomeIcon";
import { Save } from "../Save/Save";
export const IconWrapper = ({ children, label, color, selected }) => {
export const IconWrapper = ({ children, label, color, selected, disableWidth, customWidth }) => {
return (
<Box
sx={{
@ -27,8 +27,8 @@ export const IconWrapper = ({ children, label, color, selected }) => {
alignItems: "center",
gap: "5px",
flexDirection: "column",
height: "89px",
width: "89px",
height: customWidth ? customWidth : disableWidth ? 'auto' : "89px",
width: customWidth? customWidth : disableWidth ? 'auto' : "89px",
borderRadius: "50%",
backgroundColor: selected ? "rgba(28, 29, 32, 1)" : "transparent",
}}

View File

@ -128,74 +128,7 @@ export const DesktopHeader = ({
alignItems: "center",
}}
>
<ButtonBase
onClick={() => {
goToHome();
}}
>
<IconWrapper
color="rgba(250, 250, 250, 0.5)"
label="Home"
selected={isHome}
>
<HomeIcon
height={25}
color={isHome ? "white" : "rgba(250, 250, 250, 0.5)"}
/>
</IconWrapper>
</ButtonBase>
<ButtonBase
onClick={() => {
setDesktopSideView("groups");
}}
>
<IconWrapper
color="rgba(250, 250, 250, 0.5)"
label="Groups"
selected={isGroups}
>
<HubsIcon
height={25}
color={
hasUnreadGroups
? "var(--unread)"
: isGroups
? "white"
: "rgba(250, 250, 250, 0.5)"
}
/>
</IconWrapper>
</ButtonBase>
<ButtonBase
onClick={() => {
setDesktopSideView("directs");
}}
>
<IconWrapper
color="rgba(250, 250, 250, 0.5)"
label="Messaging"
selected={isDirects}
>
<MessagingIcon
height={25}
color={
hasUnreadDirects
? "var(--unread)"
: isDirects
? "white"
: "rgba(250, 250, 250, 0.5)"
}
/>
</IconWrapper>
</ButtonBase>
<Box
sx={{
width: "1px",
height: "50px",
background: "white",
borderRadius: "50px",
}}
/>
<ButtonBase
onClick={() => {
goToAnnouncements()
@ -231,6 +164,7 @@ export const DesktopHeader = ({
label="Chat"
selected={isChat}
selectColor="#09b6e8"
customHeight="55px"
>
<ChatIcon
height={25}
@ -257,6 +191,7 @@ export const DesktopHeader = ({
label="Threads"
selected={isForum}
selectColor="#09b6e8"
customHeight="55px"
>
<ThreadsIcon
height={25}
@ -279,6 +214,7 @@ export const DesktopHeader = ({
color="rgba(250, 250, 250, 0.5)"
label="Members"
selected={false}
customHeight="55px"
>
<MembersIcon
height={25}

View File

@ -0,0 +1,104 @@
import { Box, ButtonBase } from '@mui/material';
import React from 'react'
import { HomeIcon } from "../assets/Icons/HomeIcon";
import { MessagingIcon } from "../assets/Icons/MessagingIcon";
import { Save } from "./Save/Save";
import { HubsIcon } from "../assets/Icons/HubsIcon";
import { CoreSyncStatus } from "./CoreSyncStatus";
import { IconWrapper } from './Desktop/DesktopFooter';
import AppIcon from "./../assets/svgs/AppIcon.svg";
import { useRecoilState } from 'recoil';
import { AppsIcon } from '../assets/Icons/AppsIcon';
export const DesktopSideBar = ({goToHome, setDesktopSideView, toggleSideViewDirects, hasUnreadDirects, isDirects, toggleSideViewGroups,hasUnreadGroups, isGroups, isApps, setDesktopViewMode, desktopViewMode, myName }) => {
return (
<Box sx={{
width: '60px',
flexDirection: 'column',
height: '100vh',
alignItems: 'center',
display: 'flex',
gap: '25px'
}}>
<ButtonBase
sx={{
width: '60px',
height: '60px',
paddingTop: '23px'
}}
onClick={() => {
goToHome();
}}
>
<HomeIcon
height={34}
color={desktopViewMode === 'home' ? 'white': "rgba(250, 250, 250, 0.5)"}
/>
</ButtonBase>
<ButtonBase
onClick={() => {
setDesktopViewMode('apps')
// setIsOpenSideViewDirects(false)
// setIsOpenSideViewGroups(false)
}}
>
<IconWrapper
color={isApps ? 'white' : "rgba(250, 250, 250, 0.5)"}
label="Apps"
selected={isApps}
disableWidth
>
<AppsIcon color={isApps ? 'white' : "rgba(250, 250, 250, 0.5)"} height={30} />
</IconWrapper>
</ButtonBase>
<ButtonBase
onClick={() => {
setDesktopViewMode('chat')
}}
>
<IconWrapper
color={(hasUnreadDirects || hasUnreadGroups) ? "var(--unread)" : desktopViewMode === 'chat' ? 'white' :"rgba(250, 250, 250, 0.5)"}
label="Chat"
disableWidth
>
<MessagingIcon
height={30}
color={
(hasUnreadDirects || hasUnreadGroups)
? "var(--unread)"
: desktopViewMode === 'chat'
? "white"
: "rgba(250, 250, 250, 0.5)"
}
/>
</IconWrapper>
</ButtonBase>
{/* <ButtonBase
onClick={() => {
setDesktopSideView("groups");
toggleSideViewGroups()
}}
>
<HubsIcon
height={30}
color={
hasUnreadGroups
? "var(--danger)"
: isGroups
? "white"
: "rgba(250, 250, 250, 0.5)"
}
/>
</ButtonBase> */}
<Save isDesktop disableWidth myName={myName} />
{/* <CoreSyncStatus imageSize="30px" position="left" /> */}
</Box>
)
}

View File

@ -86,7 +86,7 @@ import { getRootHeight } from "../../utils/mobile/mobileUtils";
import { ReturnIcon } from "../../assets/Icons/ReturnIcon";
import { ExitIcon } from "../../assets/Icons/ExitIcon";
import { HomeDesktop } from "./HomeDesktop";
import { DesktopFooter } from "../Desktop/DesktopFooter";
import { DesktopFooter, IconWrapper } from "../Desktop/DesktopFooter";
import { DesktopHeader } from "../Desktop/DesktopHeader";
import { Apps } from "../Apps/Apps";
import { AppsNavBar } from "../Apps/AppsNavBar";
@ -98,6 +98,9 @@ import { useSetRecoilState } from "recoil";
import { selectedGroupIdAtom } from "../../atoms/global";
import { sortArrayByTimestampAndGroupName } from "../../utils/time";
import { AdminSpace } from "../Chat/AdminSpace";
import { HubsIcon } from "../../assets/Icons/HubsIcon";
import { MessagingIcon } from "../../assets/Icons/MessagingIcon";
import { DesktopSideBar } from "../DesktopSideBar";
// let touchStartY = 0;
// let disablePullToRefresh = false;
@ -1359,6 +1362,10 @@ export const Group = ({
setupGroupWebsocketInterval.current = null;
settimeoutForRefetchSecretKey.current = null;
initiatedGetMembers.current = false;
if(!isMobile){
setDesktopViewMode('home')
}
};
const openDevModeFunc = () => {
@ -1445,7 +1452,9 @@ export const Group = ({
setTriedToFetchSecretKey(false);
setFirstSecretKeyInCreation(false);
setGroupSection("chat");
if(!isMobile){
setDesktopViewMode('chat')
}
chrome?.runtime?.sendMessage({
action: "addTimestampEnterChat",
payload: {
@ -1458,7 +1467,6 @@ export const Group = ({
setSelectedGroup(findGroup);
setMobileViewMode("group");
setDesktopSideView('groups')
setDesktopViewMode('home')
getTimestampEnterChat();
isLoadingOpenSectionFromNotification.current = false;
}, 200);
@ -1497,6 +1505,9 @@ export const Group = ({
setTriedToFetchSecretKey(false);
setFirstSecretKeyInCreation(false);
setGroupSection("announcement");
if(!isMobile){
setDesktopViewMode('chat')
}
chrome?.runtime?.sendMessage({
action: "addGroupNotificationTimestamp",
payload: {
@ -1508,7 +1519,6 @@ export const Group = ({
setSelectedGroup(findGroup);
setMobileViewMode("group");
setDesktopSideView('groups')
setDesktopViewMode('home')
getGroupAnnouncements();
}, 200);
}
@ -1560,12 +1570,13 @@ export const Group = ({
setFirstSecretKeyInCreation(false);
setGroupSection("forum");
setDefaultThread(data);
if(!isMobile){
setDesktopViewMode('chat')
}
setTimeout(() => {
setSelectedGroup(findGroup);
setMobileViewMode("group");
setDesktopSideView('groups')
setDesktopViewMode('home')
getGroupAnnouncements();
}, 200);
}
@ -1591,32 +1602,13 @@ export const Group = ({
}
setDesktopViewMode('home')
setGroupSection("default");
clearAllQueues();
await new Promise((res) => {
setTimeout(() => {
res(null);
}, 200);
});
setGroupSection("home");
setSelectedGroup(null);
setNewChat(false);
setSelectedDirect(null);
setSecretKey(null);
setGroupOwner(null)
lastFetchedSecretKey.current = null;
initiatedGetMembers.current = false;
setSecretKeyPublishDate(null);
setAdmins([]);
setSecretKeyDetails(null);
setAdminsWithNames([]);
setMembers([]);
setMemberCountFromSecretKeyData(null);
setIsForceShowCreationKeyPopup(false)
setTriedToFetchSecretKey(false);
setFirstSecretKeyInCreation(false);
setIsOpenSideViewDirects(false)
setIsOpenSideViewGroups(false)
};
const goToAnnouncements = async () => {
@ -1690,6 +1682,66 @@ export const Group = ({
borderRadius: !isMobile && '0px 15px 15px 0px'
}}
>
{!isMobile && (
<Box sx={{
width: '100%',
alignItems: 'center',
justifyContent: 'center',
display: 'flex',
gap: '10px'
}}>
<ButtonBase
onClick={() => {
setDesktopSideView("groups");
}}
>
<IconWrapper
color={(groupChatHasUnread ||
groupsAnnHasUnread)
? "var(--unread)"
: desktopSideView === 'groups' ? 'white' :"rgba(250, 250, 250, 0.5)"}
label="Groups"
selected={desktopSideView === 'groups'}
customWidth="75px"
>
<HubsIcon
height={24}
color={
(groupChatHasUnread ||
groupsAnnHasUnread)
? "var(--unread)"
: desktopSideView === 'groups'
? "white"
: "rgba(250, 250, 250, 0.5)"
}
/>
</IconWrapper>
</ButtonBase>
<ButtonBase
onClick={() => {
setDesktopSideView("directs");
}}
>
<IconWrapper
customWidth="75px"
color={directChatHasUnread ? "var(--unread)" : desktopSideView === 'directs' ? 'white' :"rgba(250, 250, 250, 0.5)"}
label="Messaging"
selected={desktopSideView === 'directs'}
>
<MessagingIcon
height={24}
color={
directChatHasUnread
? "var(--unread)"
: desktopSideView === 'directs'
? "white"
: "rgba(250, 250, 250, 0.5)"
}
/>
</IconWrapper>
</ButtonBase>
</Box>
)}
{isMobile && (
<Box
sx={{
@ -1899,172 +1951,66 @@ export const Group = ({
borderRadius: !isMobile && '0px 15px 15px 0px'
}}
>
{/* <div
style={{
display: "flex",
width: "100%",
justifyContent: "center",
gap: "20px",
padding: "10px",
flexDirection: "column",
}}
>
{isMobile && (
<Box
sx={{
width: "100%",
display: "flex",
justifyContent: "flex-end",
}}
>
<CloseIcon
onClick={() => {
setIsOpenDrawer(false);
}}
sx={{
cursor: "pointer",
color: "white",
}}
/>
</Box>
)}
<CustomButton
{!isMobile && (
<Box sx={{
width: '100%',
alignItems: 'center',
justifyContent: 'center',
display: 'flex',
gap: '10px'
}}>
<ButtonBase
onClick={() => {
setChatMode((prev) =>
prev === "directs" ? "groups" : "directs"
);
}}
sx={{
backgroundColor: chatMode === 'directs' && ( groupChatHasUnread || groupsAnnHasUnread) ? 'red' : 'revert'
setDesktopSideView("groups");
}}
>
{chatMode === "groups" && (
<>
<MarkUnreadChatAltIcon
sx={{
color: directChatHasUnread ? "red" : "white",
}}
/>
</>
)}
{chatMode === "directs" ? "Switch to groups" : "Direct msgs"}
</CustomButton>
</div> */}
{/* <div
style={{
display: "flex",
width: "100%",
flexDirection: "column",
alignItems: "flex-start",
flexGrow: 1,
overflowY: "auto",
visibility: chatMode === "groups" && "hidden",
position: chatMode === "groups" && "fixed",
left: chatMode === "groups" && "-1000px",
}}
>
{directs.map((direct: any) => (
<List sx={{
width: '100%'
}} className="group-list" dense={true}>
<ListItem
// secondaryAction={
// <IconButton edge="end" aria-label="delete">
// <SettingsIcon />
// </IconButton>
// }
onClick={() => {
setSelectedDirect(null);
setNewChat(false);
// setSelectedGroup(null);
setIsOpenDrawer(false);
chrome?.runtime?.sendMessage({
action: "addTimestampEnterChat",
payload: {
timestamp: Date.now(),
groupId: direct.address,
},
});
setTimeout(() => {
setSelectedDirect(direct);
getTimestampEnterChat();
}, 200);
}}
sx={{
display: "flex",
width: "100%",
flexDirection: "column",
cursor: "pointer",
border: "1px #232428 solid",
padding: "2px",
borderRadius: "2px",
background:
direct?.address === selectedDirect?.address && "white",
}}
>
<Box
sx={{
display: "flex",
width: "100%",
}}
>
<ListItemAvatar>
<Avatar
sx={{
background: "#232428",
color: "white",
}}
alt={direct?.name || direct?.address}
// src={`${getBaseApiReact()}/arbitrary/THUMBNAIL/${groupOwner?.name}/qortal_group_avatar_${group.groupId}?async=true`}
>
{(direct?.name || direct?.address)?.charAt(0)}
</Avatar>
</ListItemAvatar>
<ListItemText
primary={direct?.name || direct?.address}
primaryTypographyProps={{
style: {
color:
direct?.address === selectedDirect?.address &&
"black",
textWrap: "wrap",
overflow: "hidden",
},
}} // Change the color of the primary text
secondaryTypographyProps={{
style: {
color:
direct?.address === selectedDirect?.address &&
"black",
},
}}
sx={{
width: "150px",
fontFamily: "Inter",
fontSize: "16px",
}}
/>
{direct?.sender !== myAddress &&
direct?.timestamp &&
((!timestampEnterData[direct?.address] &&
Date.now() - direct?.timestamp <
timeDifferenceForNotificationChats) ||
timestampEnterData[direct?.address] <
direct?.timestamp) && (
<MarkChatUnreadIcon
sx={{
color: "red",
}}
/>
)}
</Box>
</ListItem>
</List>
))}
</div> */}
<IconWrapper
color={(groupChatHasUnread ||
groupsAnnHasUnread)
? "var(--unread)"
: desktopSideView === 'groups' ? 'white' :"rgba(250, 250, 250, 0.5)"}
label="Groups"
selected={desktopSideView === 'groups'}
customWidth="75px"
>
<HubsIcon
height={24}
color={
(groupChatHasUnread ||
groupsAnnHasUnread)
? "var(--unread)"
: desktopSideView === 'groups'
? "white"
: "rgba(250, 250, 250, 0.5)"
}
/>
</IconWrapper>
</ButtonBase>
<ButtonBase
onClick={() => {
setDesktopSideView("directs");
}}
>
<IconWrapper
customWidth="75px"
color={directChatHasUnread ? "var(--unread)" : desktopSideView === 'directs' ? 'white' :"rgba(250, 250, 250, 0.5)"}
label="Messaging"
selected={desktopSideView === 'directs' }
>
<MessagingIcon
height={24}
color={
directChatHasUnread
? "var(--unread)"
: desktopSideView === 'directs'
? "white"
: "rgba(250, 250, 250, 0.5)"
}
/>
</IconWrapper>
</ButtonBase>
</Box>
)}
<div
style={{
display: "flex",
@ -2095,7 +2041,6 @@ export const Group = ({
onClick={() => {
setMobileViewMode("group");
setDesktopSideView('groups')
setDesktopViewMode('home')
initiatedGetMembers.current = false;
clearAllQueues();
setSelectedDirect(null);
@ -2347,8 +2292,14 @@ export const Group = ({
alignItems: "flex-start",
}}
>
{!isMobile && ((desktopSideView === 'groups' && desktopViewMode !== 'apps') || isOpenSideViewGroups) && renderGroups()}
{!isMobile && ((desktopSideView === 'directs' && desktopViewMode !== 'apps') || isOpenSideViewDirects) && renderDirects()}
{!isMobile && ((desktopViewMode !== 'apps' && desktopViewMode !== 'dev') || isOpenSideViewGroups) && (
<DesktopSideBar desktopViewMode={desktopViewMode} toggleSideViewGroups={toggleSideViewGroups} toggleSideViewDirects={toggleSideViewDirects} goToHome={goToHome} mode={appsMode} setMode={setAppsMode} setDesktopSideView={setDesktopSideView} hasUnreadDirects={directChatHasUnread} isApps={desktopViewMode === 'apps'} myName={userInfo?.name} isGroups={isOpenSideViewGroups}
isDirects={isOpenSideViewDirects} hasUnreadGroups={groupChatHasUnread ||
groupsAnnHasUnread} setDesktopViewMode={setDesktopViewMode} />
)}
{!isMobile && desktopViewMode === 'chat' && desktopSideView !== 'directs' && renderGroups()}
{!isMobile && desktopViewMode === 'chat' && desktopSideView === 'directs' && renderDirects()}
<Box
sx={{
@ -2454,9 +2405,37 @@ export const Group = ({
</Box>
</>
)}
{selectedGroup && mobileViewMode !== 'groups' && (
<>
{!isMobile && selectedGroup && (
{desktopViewMode === 'chat' && !selectedGroup && (
<Box
sx={{
width: "100%",
display: "flex",
justifyContent: "center",
alignItems: 'center',
height: '100%',
}}
>
<Typography
sx={{
fontSize: "14px",
fontWeight: 400,
color: 'rgba(255, 255, 255, 0.2)'
}}
>
No group selected
</Typography>
</Box>
)}
{mobileViewMode !== 'groups' && (
<div style={{
width: '100%',
display: selectedGroup? 'block' : 'none',
opacity: !(desktopViewMode === 'chat' && selectedGroup) ? 0 : 1,
position: !(desktopViewMode === 'chat' && selectedGroup) ? 'absolute' : 'relative',
left: !(desktopViewMode === 'chat' && selectedGroup) ? '-100000px' : '0px',
}}>
{!isMobile && (
<DesktopHeader
isPrivate={isPrivate}
@ -2753,7 +2732,7 @@ export const Group = ({
isOwner={groupOwner?.owner === myAddress}
/>
)}
</>
</div>
)}
{selectedDirect && !newChat && (
@ -2797,43 +2776,7 @@ export const Group = ({
</Box>
</>
)}
{!isMobile && groupSection === "home" && (
<DesktopFooter
isPrivate={isPrivate}
selectedGroup={selectedGroup}
groupSection={groupSection}
isUnread={isUnread}
goToAnnouncements={goToAnnouncements}
isUnreadChat={isUnreadChat}
goToChat={goToChat}
goToThreads={goToThreads}
setOpenManageMembers={setOpenManageMembers}
groupChatHasUnread={groupChatHasUnread}
groupsAnnHasUnread={groupsAnnHasUnread}
directChatHasUnread={directChatHasUnread}
chatMode={chatMode}
openDrawerGroups={openDrawerGroups}
goToHome={goToHome}
setIsOpenDrawerProfile={setIsOpenDrawerProfile}
mobileViewMode={mobileViewMode}
setMobileViewMode={setMobileViewMode}
setMobileViewModeKeepOpen={setMobileViewModeKeepOpen}
hasUnreadGroups={groupChatHasUnread ||
groupsAnnHasUnread}
hasUnreadDirects={directChatHasUnread}
myName={userInfo?.name || null}
isHome={groupSection === "home" && desktopViewMode === 'home'}
isGroups={desktopSideView === 'groups' && desktopViewMode !== 'apps'}
isDirects={desktopSideView === 'directs' && desktopViewMode !== 'apps'}
setDesktopViewMode={setDesktopViewMode}
isApps={desktopViewMode === 'apps'}
setDesktopSideView={setDesktopSideView}
desktopViewMode={desktopViewMode}
hide={desktopViewMode === 'apps'}
setIsOpenSideViewDirects={setIsOpenSideViewDirects}
setIsOpenSideViewGroups={setIsOpenSideViewGroups}
/>
)}
{isMobile && mobileViewMode === "home" && (
<Home
refreshHomeDataFunc={refreshHomeDataFunc}
@ -2848,20 +2791,20 @@ export const Group = ({
setOpenManageMembers={setOpenManageMembers}
setOpenAddGroup={setOpenAddGroup}
setMobileViewMode={setMobileViewMode}
setDesktopViewMode={setDesktopViewMode}
/>
)}
{isMobile && (
<Apps mode={appsMode} setMode={setAppsMode} show={mobileViewMode === "apps"} myName={userInfo?.name} />
)}
{!isMobile && (
{!isMobile && (
<AppsDesktop toggleSideViewGroups={toggleSideViewGroups} toggleSideViewDirects={toggleSideViewDirects} goToHome={goToHome} mode={appsMode} setMode={setAppsMode} setDesktopSideView={setDesktopSideView} hasUnreadDirects={directChatHasUnread} show={desktopViewMode === "apps"} myName={userInfo?.name} isGroups={isOpenSideViewGroups}
isDirects={isOpenSideViewDirects} hasUnreadGroups={groupChatHasUnread ||
groupsAnnHasUnread} />
groupsAnnHasUnread} setDesktopViewMode={setDesktopViewMode} isApps={desktopViewMode === 'apps'} desktopViewMode={desktopViewMode} />
)}
{!isMobile && !selectedGroup &&
groupSection === "home" && desktopViewMode !== "apps" && (
{!isMobile && desktopViewMode === 'home' && (
<HomeDesktop
refreshHomeDataFunc={refreshHomeDataFunc}
myAddress={myAddress}
@ -2875,6 +2818,8 @@ export const Group = ({
setOpenManageMembers={setOpenManageMembers}
setOpenAddGroup={setOpenAddGroup}
setMobileViewMode={setMobileViewMode}
setDesktopViewMode={setDesktopViewMode}
/>
)}
@ -2886,7 +2831,7 @@ export const Group = ({
width: "31px",
// minWidth: "135px",
padding: "5px",
display: (isMobile || desktopViewMode === 'apps') ? "none" : "flex",
display: (isMobile || desktopViewMode === 'apps' || desktopViewMode === 'dev' || desktopViewMode === 'chat') ? "none" : "flex",
}}
>

View File

@ -20,7 +20,7 @@ import { myGroupsWhereIAmAdminAtom } from "../../atoms/global";
import { useSetRecoilState } from "recoil";
export const requestQueueGroupJoinRequests = new RequestQueueWithPromise(2)
export const GroupJoinRequests = ({ myAddress, groups, setOpenManageMembers, getTimestampEnterChat, setSelectedGroup, setGroupSection, setMobileViewMode }) => {
export const GroupJoinRequests = ({ myAddress, groups, setOpenManageMembers, getTimestampEnterChat, setSelectedGroup, setGroupSection, setMobileViewMode, setDesktopViewMode }) => {
const [groupsWithJoinRequests, setGroupsWithJoinRequests] = React.useState([])
const [loading, setLoading] = React.useState(true)
const {txList, setTxList} = React.useContext(MyContext)
@ -185,6 +185,9 @@ export const GroupJoinRequests = ({ myAddress, groups, setOpenManageMembers, get
getTimestampEnterChat()
setGroupSection("announcement")
setOpenManageMembers(true)
if(!isMobile){
setDesktopViewMode('chat')
}
setTimeout(() => {
executeEvent("openGroupJoinRequest", {});

View File

@ -21,6 +21,7 @@ export const Home = ({
setOpenManageMembers,
setOpenAddGroup,
setMobileViewMode,
setDesktopViewMode
}) => {
return (
<Box
@ -97,6 +98,7 @@ export const Home = ({
myAddress={myAddress}
groups={groups}
setMobileViewMode={setMobileViewMode}
setDesktopViewMode={setDesktopViewMode}
/>
<GroupInvites
setOpenAddGroup={setOpenAddGroup}

View File

@ -21,6 +21,7 @@ export const HomeDesktop = ({
setOpenManageMembers,
setOpenAddGroup,
setMobileViewMode,
setDesktopViewMode
}) => {
return (
<Box
@ -67,7 +68,7 @@ export const HomeDesktop = ({
display: "flex",
gap: "15px",
flexWrap: "wrap",
justifyContent: "flex-start",
justifyContent: "center",
}}
>
<Box sx={{
@ -106,6 +107,7 @@ export const HomeDesktop = ({
myAddress={myAddress}
groups={groups}
setMobileViewMode={setMobileViewMode}
setDesktopViewMode={setDesktopViewMode}
/>
</Box>
<Box sx={{

View File

@ -57,6 +57,8 @@ useEffect(()=> {
}, [])
const showTutorial = useCallback(async (type, isForce) => {
try {
console.log('type, isForce', type, isForce)
const isOnline = await checkIfGatewayIsOnline()
if(!isOnline) return
switch (type) {

View File

@ -79,8 +79,8 @@ const hasExtension = (filename) => {
return filename.includes(".") && filename.split(".").pop().length > 0;
};
export const createAccount = async()=> {
const generatedSeedPhrase = generateRandomSentence()
export const createAccount = async(generatedSeedPhrase)=> {
if(!generatedSeedPhrase) throw new Error('No generated seed-phrase')
const threads = doInitWorkers(crypto.kdfThreads)
const seed = await kdf(generatedSeedPhrase, void 0, threads)
@ -117,4 +117,13 @@ fileName = hasExtension(fileName) ? fileName : fileName + "." + fileExtension;
await saveAs(blob, fileName);
}
export const saveSeedPhraseToDisk = async (data) => {
const blob = new Blob([data], { type: 'text/plain;charset=utf-8' })
const fileName = "qortal_seedphrase.txt"
await saveAs(blob, fileName);
}

View File

@ -0,0 +1,173 @@
// Author: irontiga <irontiga@gmail.com>
import { html, LitElement, css } from 'lit'
import * as WORDLISTS from './wordList'
class RandomSentenceGenerator extends LitElement {
static get properties() {
return {
template: { type: String, attribute: 'template' },
parsedString: { type: String },
fetchedWordlistCount: { type: Number, value: 0 },
capitalize: { type: Boolean },
partsOfSpeechMap: { type: Object },
templateEntropy: { type: Number, reflect: true, attribute: 'template-entropy' },
maxWordLength: { type: Number, attribute: 'max-word-length' }
}
}
constructor() {
super()
this.template = 'adjective noun verb adverb.'
this.maxWordLength = 0
this.parsedString = ''
this.fetchedWordlistCount = 0
this.capitalize = true
this.partsOfSpeechMap = {
'noun': 'nouns',
'adverb': 'adverbs',
'adv': 'adverbs',
'verb': 'verbs',
'interjection': 'interjections',
'adjective': 'adjectives',
'adj': 'adjectives',
'verbed': 'verbed'
}
this.partsOfSpeech = Object.keys(this.partsOfSpeechMap)
this._wordlists = WORDLISTS
}
static styles = css`
div {
text-align: center;
width: 100%;
background-color: #1f2023;
border-radius: 5px;
padding: 10px;
}
`;
render() {
return html`
<div>${this.parsedString}</div>
`
}
firstUpdated() {
// ...
}
updated(changedProperties) {
let regen = false
if (changedProperties.has('template')) {
regen = true
}
if (changedProperties.has('maxWordLength')) {
console.dir(this.maxWordLength)
if (this.maxWordLength) {
const wl = { ...this._wordlists }
for (const partOfSpeech in this._wordlists) {
if (Array.isArray(this._wordlists[partOfSpeech])) {
wl[partOfSpeech] = this._wordlists[partOfSpeech].filter(word => word.length <= this.maxWordLength)
}
}
this._wordlists = wl
}
regen = true
}
if (regen) this.generate()
}
_RNG(entropy) {
if (entropy > 1074) {
throw new Error('Javascript can not handle that much entropy!')
}
let randNum = 0
const crypto = window.crypto || window.msCrypto
if (crypto) {
const entropy256 = Math.ceil(entropy / 8)
let buffer = new Uint8Array(entropy256)
crypto.getRandomValues(buffer)
randNum = buffer.reduce((num, value) => {
return num * value
}, 1) / Math.pow(256, entropy256)
} else {
console.warn('Secure RNG not found. Using Math.random')
randNum = Math.random()
}
return randNum
}
setRNG(fn) {
this._RNG = fn
}
_captitalize(str) {
return str.charAt(0).toUpperCase() + str.slice(1)
}
getWord(partOfSpeech) {
const words = this._wordlists[this.partsOfSpeechMap[partOfSpeech]]
const requiredEntropy = Math.log(words.length) / Math.log(2)
const index = this._RNG(requiredEntropy) * words.length
return {
word: words[Math.round(index)],
entropy: words.length
}
}
generate() {
this.parsedString = this.parse(this.template)
}
parse(template) {
const split = template.split(/[\s]/g)
let entropy = 1
const final = split.map(word => {
const lower = word.toLowerCase()
this.partsOfSpeech.some(partOfSpeech => {
const partOfSpeechIndex = lower.indexOf(partOfSpeech) // Check it exists
const nextChar = word.charAt(partOfSpeech.length)
if (partOfSpeechIndex === 0 && !(nextChar && (nextChar.match(/[a-zA-Z]/g) != null))) {
const replacement = this.getWord(partOfSpeech)
word = replacement.word + word.slice(partOfSpeech.length) // Append the rest of the "word" (punctuation)
entropy = entropy * replacement.entropy
return true
}
})
return word
})
this.templateEntropy = Math.floor(Math.log(entropy) / Math.log(8))
return final.join(' ')
}
}
window.customElements.define('random-sentence-generator', RandomSentenceGenerator)
export default RandomSentenceGenerator

View File

@ -0,0 +1,40 @@
export const EXCEPTIONS = {
'are': 'were',
'eat': 'ate',
'go': 'went',
'have': 'had',
'inherit': 'inherited',
'is': 'was',
'run': 'ran',
'sit': 'sat',
'visit': 'visited'
}
export const getPastTense = (verb, exceptions = EXCEPTIONS) => {
if (exceptions[verb]) {
return exceptions[verb]
}
if ((/e$/i).test(verb)) {
return verb + 'd'
}
if ((/[aeiou]c$/i).test(verb)) {
return verb + 'ked'
}
// for american english only
if ((/el$/i).test(verb)) {
return verb + 'ed'
}
if ((/[aeio][aeiou][dlmnprst]$/).test(verb)) {
return verb + 'ed'
}
if ((/[aeiou][bdglmnprst]$/i).test(verb)) {
return verb.replace(/(.+[aeiou])([bdglmnprst])/, '$1$2$2ed')
}
return verb + 'ed'
}

File diff suppressed because one or more lines are too long