mirror of
https://github.com/Qortal/qortal-mobile.git
synced 2025-07-04 04:41:20 +00:00
Compare commits
No commits in common. "feature/initial-conversion" and "v0.5.4-pre" have entirely different histories.
feature/in
...
v0.5.4-pre
@ -14,7 +14,6 @@ dependencies {
|
|||||||
implementation project(':capacitor-filesystem')
|
implementation project(':capacitor-filesystem')
|
||||||
implementation project(':capacitor-local-notifications')
|
implementation project(':capacitor-local-notifications')
|
||||||
implementation project(':capacitor-preferences')
|
implementation project(':capacitor-preferences')
|
||||||
implementation project(':capacitor-screen-orientation')
|
|
||||||
implementation project(':capacitor-splash-screen')
|
implementation project(':capacitor-splash-screen')
|
||||||
implementation project(':capawesome-capacitor-file-picker')
|
implementation project(':capawesome-capacitor-file-picker')
|
||||||
implementation project(':evva-capacitor-secure-storage-plugin')
|
implementation project(':evva-capacitor-secure-storage-plugin')
|
||||||
|
@ -13,7 +13,6 @@
|
|||||||
android:usesCleartextTraffic="true">
|
android:usesCleartextTraffic="true">
|
||||||
<activity
|
<activity
|
||||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|smallestScreenSize|screenLayout|uiMode"
|
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|smallestScreenSize|screenLayout|uiMode"
|
||||||
android:screenOrientation="unspecified"
|
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
android:label="@string/title_activity_main"
|
android:label="@string/title_activity_main"
|
||||||
android:theme="@style/AppTheme.NoActionBarLaunch"
|
android:theme="@style/AppTheme.NoActionBarLaunch"
|
||||||
|
@ -4,8 +4,6 @@ import com.getcapacitor.BridgeActivity;
|
|||||||
import com.github.Qortal.qortalMobile.NativeBcrypt;
|
import com.github.Qortal.qortalMobile.NativeBcrypt;
|
||||||
import com.github.Qortal.qortalMobile.NativePOW;
|
import com.github.Qortal.qortalMobile.NativePOW;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.webkit.WebSettings;
|
|
||||||
import android.webkit.WebView;
|
|
||||||
|
|
||||||
public class MainActivity extends BridgeActivity {
|
public class MainActivity extends BridgeActivity {
|
||||||
@Override
|
@Override
|
||||||
@ -14,9 +12,6 @@ public class MainActivity extends BridgeActivity {
|
|||||||
registerPlugin(NativePOW.class);
|
registerPlugin(NativePOW.class);
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
// ✅ Enable mixed content mode for WebView
|
|
||||||
WebView webView = this.bridge.getWebView();
|
|
||||||
WebSettings webSettings = webView.getSettings();
|
|
||||||
webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,9 +17,6 @@ project(':capacitor-local-notifications').projectDir = new File('../node_modules
|
|||||||
include ':capacitor-preferences'
|
include ':capacitor-preferences'
|
||||||
project(':capacitor-preferences').projectDir = new File('../node_modules/@capacitor/preferences/android')
|
project(':capacitor-preferences').projectDir = new File('../node_modules/@capacitor/preferences/android')
|
||||||
|
|
||||||
include ':capacitor-screen-orientation'
|
|
||||||
project(':capacitor-screen-orientation').projectDir = new File('../node_modules/@capacitor/screen-orientation/android')
|
|
||||||
|
|
||||||
include ':capacitor-splash-screen'
|
include ':capacitor-splash-screen'
|
||||||
project(':capacitor-splash-screen').projectDir = new File('../node_modules/@capacitor/splash-screen/android')
|
project(':capacitor-splash-screen').projectDir = new File('../node_modules/@capacitor/splash-screen/android')
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ const config: CapacitorConfig = {
|
|||||||
"splashImmersive": true
|
"splashImmersive": true
|
||||||
},
|
},
|
||||||
CapacitorHttp: {
|
CapacitorHttp: {
|
||||||
enabled: false,
|
enabled: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
14
package-lock.json
generated
14
package-lock.json
generated
@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "qortal-go",
|
"name": "qortal-go",
|
||||||
"version": "0.5.3",
|
"version": "0.5.2",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "qortal-go",
|
"name": "qortal-go",
|
||||||
"version": "0.5.3",
|
"version": "0.5.2",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@capacitor/android": "^6.1.2",
|
"@capacitor/android": "^6.1.2",
|
||||||
"@capacitor/app": "^6.0.1",
|
"@capacitor/app": "^6.0.1",
|
||||||
@ -16,7 +16,6 @@
|
|||||||
"@capacitor/filesystem": "^6.0.1",
|
"@capacitor/filesystem": "^6.0.1",
|
||||||
"@capacitor/local-notifications": "^6.1.0",
|
"@capacitor/local-notifications": "^6.1.0",
|
||||||
"@capacitor/preferences": "^6.0.3",
|
"@capacitor/preferences": "^6.0.3",
|
||||||
"@capacitor/screen-orientation": "^6.0.3",
|
|
||||||
"@capacitor/splash-screen": "^6.0.2",
|
"@capacitor/splash-screen": "^6.0.2",
|
||||||
"@capawesome/capacitor-file-picker": "^6.1.0",
|
"@capawesome/capacitor-file-picker": "^6.1.0",
|
||||||
"@chatscope/chat-ui-kit-react": "^2.0.3",
|
"@chatscope/chat-ui-kit-react": "^2.0.3",
|
||||||
@ -1701,15 +1700,6 @@
|
|||||||
"@capacitor/core": "^6.0.0"
|
"@capacitor/core": "^6.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@capacitor/screen-orientation": {
|
|
||||||
"version": "6.0.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/@capacitor/screen-orientation/-/screen-orientation-6.0.3.tgz",
|
|
||||||
"integrity": "sha512-5R+tf+twRNnkZFGSWsQkEBz1MFyP1kzZDyqOA9rtXJlTQYNcFJWouSXEuNa+Ba6i6nEi4X83BuXVzEFJ7zDrgQ==",
|
|
||||||
"license": "MIT",
|
|
||||||
"peerDependencies": {
|
|
||||||
"@capacitor/core": "^6.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@capacitor/splash-screen": {
|
"node_modules/@capacitor/splash-screen": {
|
||||||
"version": "6.0.2",
|
"version": "6.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/@capacitor/splash-screen/-/splash-screen-6.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/@capacitor/splash-screen/-/splash-screen-6.0.2.tgz",
|
||||||
|
@ -20,7 +20,6 @@
|
|||||||
"@capacitor/filesystem": "^6.0.1",
|
"@capacitor/filesystem": "^6.0.1",
|
||||||
"@capacitor/local-notifications": "^6.1.0",
|
"@capacitor/local-notifications": "^6.1.0",
|
||||||
"@capacitor/preferences": "^6.0.3",
|
"@capacitor/preferences": "^6.0.3",
|
||||||
"@capacitor/screen-orientation": "^6.0.3",
|
|
||||||
"@capacitor/splash-screen": "^6.0.2",
|
"@capacitor/splash-screen": "^6.0.2",
|
||||||
"@capawesome/capacitor-file-picker": "^6.1.0",
|
"@capawesome/capacitor-file-picker": "^6.1.0",
|
||||||
"@chatscope/chat-ui-kit-react": "^2.0.3",
|
"@chatscope/chat-ui-kit-react": "^2.0.3",
|
||||||
|
183
src/App.tsx
183
src/App.tsx
@ -21,18 +21,14 @@ import {
|
|||||||
DialogContentText,
|
DialogContentText,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
Divider,
|
Divider,
|
||||||
FormControlLabel,
|
|
||||||
Input,
|
Input,
|
||||||
InputLabel,
|
InputLabel,
|
||||||
Popover,
|
Popover,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
Typography,
|
Typography,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import { ScreenOrientation } from '@capacitor/screen-orientation';
|
|
||||||
|
|
||||||
import { decryptStoredWallet } from "./utils/decryptWallet";
|
import { decryptStoredWallet } from "./utils/decryptWallet";
|
||||||
import AccountBalanceWalletIcon from '@mui/icons-material/AccountBalanceWallet';
|
import AccountBalanceWalletIcon from '@mui/icons-material/AccountBalanceWallet';
|
||||||
import PriorityHighIcon from '@mui/icons-material/PriorityHigh';
|
|
||||||
|
|
||||||
import { JsonView, allExpanded, darkStyles } from 'react-json-view-lite';
|
import { JsonView, allExpanded, darkStyles } from 'react-json-view-lite';
|
||||||
import 'react-json-view-lite/dist/index.css';
|
import 'react-json-view-lite/dist/index.css';
|
||||||
@ -131,7 +127,6 @@ import {
|
|||||||
isUsingImportExportSettingsAtom,
|
isUsingImportExportSettingsAtom,
|
||||||
lastEnteredGroupIdAtom,
|
lastEnteredGroupIdAtom,
|
||||||
mailsAtom,
|
mailsAtom,
|
||||||
myGroupsWhereIAmAdminAtom,
|
|
||||||
oldPinnedAppsAtom,
|
oldPinnedAppsAtom,
|
||||||
qMailLastEnteredTimestampAtom,
|
qMailLastEnteredTimestampAtom,
|
||||||
settingsLocalLastUpdatedAtom,
|
settingsLocalLastUpdatedAtom,
|
||||||
@ -158,7 +153,6 @@ import { BuyQortInformation } from "./components/BuyQortInformation";
|
|||||||
import { InstallPWA } from "./components/InstallPWA";
|
import { InstallPWA } from "./components/InstallPWA";
|
||||||
import { QortPayment } from "./components/QortPayment";
|
import { QortPayment } from "./components/QortPayment";
|
||||||
import { PdfViewer } from "./common/PdfViewer";
|
import { PdfViewer } from "./common/PdfViewer";
|
||||||
import { DownloadWallet } from "./components/Auth/DownloadWallet";
|
|
||||||
|
|
||||||
|
|
||||||
type extStates =
|
type extStates =
|
||||||
@ -445,7 +439,7 @@ function App() {
|
|||||||
const { isShow, onCancel, onOk, show, message } = useModal();
|
const { isShow, onCancel, onOk, show, message } = useModal();
|
||||||
const {isUserBlocked,
|
const {isUserBlocked,
|
||||||
addToBlockList,
|
addToBlockList,
|
||||||
removeBlockFromList, getAllBlockedUsers} = useBlockedAddresses(extState === 'authenticated')
|
removeBlockFromList, getAllBlockedUsers} = useBlockedAddresses()
|
||||||
const {
|
const {
|
||||||
isShow: isShowUnsavedChanges,
|
isShow: isShowUnsavedChanges,
|
||||||
onCancel: onCancelUnsavedChanges,
|
onCancel: onCancelUnsavedChanges,
|
||||||
@ -492,8 +486,6 @@ function App() {
|
|||||||
url: "http://127.0.0.1:12391",
|
url: "http://127.0.0.1:12391",
|
||||||
});
|
});
|
||||||
const [useLocalNode, setUseLocalNode] = useState(false);
|
const [useLocalNode, setUseLocalNode] = useState(false);
|
||||||
const [confirmRequestRead, setConfirmRequestRead] = useState(false);
|
|
||||||
|
|
||||||
const [isSettingsOpen, setIsSettingsOpen] = useState(false);
|
const [isSettingsOpen, setIsSettingsOpen] = useState(false);
|
||||||
const [showSeed, setShowSeed] = useState(false)
|
const [showSeed, setShowSeed] = useState(false)
|
||||||
const [creationStep, setCreationStep] = useState(1)
|
const [creationStep, setCreationStep] = useState(1)
|
||||||
@ -520,15 +512,6 @@ function App() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
try {
|
|
||||||
ScreenOrientation.lock({ orientation: 'portrait' });
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error)
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
useEffect(()=> {
|
useEffect(()=> {
|
||||||
if(!shownTutorialsInitiated) return
|
if(!shownTutorialsInitiated) return
|
||||||
if(extState === 'not-authenticated'){
|
if(extState === 'not-authenticated'){
|
||||||
@ -575,9 +558,6 @@ function App() {
|
|||||||
const resetAtomQMailLastEnteredTimestampAtom = useResetRecoilState(qMailLastEnteredTimestampAtom)
|
const resetAtomQMailLastEnteredTimestampAtom = useResetRecoilState(qMailLastEnteredTimestampAtom)
|
||||||
const resetAtomMailsAtom = useResetRecoilState(mailsAtom)
|
const resetAtomMailsAtom = useResetRecoilState(mailsAtom)
|
||||||
const resetLastEnteredGroupIdAtom = useResetRecoilState(lastEnteredGroupIdAtom)
|
const resetLastEnteredGroupIdAtom = useResetRecoilState(lastEnteredGroupIdAtom)
|
||||||
const resetMyGroupsWhereIAmAdminAtom = useResetRecoilState(
|
|
||||||
myGroupsWhereIAmAdminAtom
|
|
||||||
);
|
|
||||||
const resetAllRecoil = () => {
|
const resetAllRecoil = () => {
|
||||||
resetAtomSortablePinnedAppsAtom();
|
resetAtomSortablePinnedAppsAtom();
|
||||||
resetAtomCanSaveSettingToQdnAtom();
|
resetAtomCanSaveSettingToQdnAtom();
|
||||||
@ -589,7 +569,6 @@ function App() {
|
|||||||
resetAtomMailsAtom()
|
resetAtomMailsAtom()
|
||||||
resetGroupPropertiesAtom()
|
resetGroupPropertiesAtom()
|
||||||
resetLastEnteredGroupIdAtom()
|
resetLastEnteredGroupIdAtom()
|
||||||
resetMyGroupsWhereIAmAdminAtom()
|
|
||||||
};
|
};
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isMobile) return;
|
if (!isMobile) return;
|
||||||
@ -871,24 +850,6 @@ function App() {
|
|||||||
});
|
});
|
||||||
balanceSetInterval()
|
balanceSetInterval()
|
||||||
};
|
};
|
||||||
|
|
||||||
const refetchUserInfo = () => {
|
|
||||||
window
|
|
||||||
.sendMessage('userInfo')
|
|
||||||
.then((response) => {
|
|
||||||
if (response && !response.error) {
|
|
||||||
setUserInfo(response);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
console.error('Failed to get user info:', error);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const getBalanceAndUserInfoFunc = () => {
|
|
||||||
getBalanceFunc();
|
|
||||||
refetchUserInfo();
|
|
||||||
};
|
|
||||||
const getLtcBalanceFunc = () => {
|
const getLtcBalanceFunc = () => {
|
||||||
setLtcBalanceLoading(true);
|
setLtcBalanceLoading(true);
|
||||||
window
|
window
|
||||||
@ -917,8 +878,6 @@ function App() {
|
|||||||
if(message?.payload?.checkbox1){
|
if(message?.payload?.checkbox1){
|
||||||
qortalRequestCheckbox1Ref.current = message?.payload?.checkbox1?.value || false
|
qortalRequestCheckbox1Ref.current = message?.payload?.checkbox1?.value || false
|
||||||
}
|
}
|
||||||
setConfirmRequestRead(false)
|
|
||||||
|
|
||||||
await showQortalRequestExtension(message?.payload);
|
await showQortalRequestExtension(message?.payload);
|
||||||
if (qortalRequestCheckbox1Ref.current) {
|
if (qortalRequestCheckbox1Ref.current) {
|
||||||
event.source.postMessage(
|
event.source.postMessage(
|
||||||
@ -1673,7 +1632,7 @@ function App() {
|
|||||||
{balance?.toFixed(2)} QORT
|
{balance?.toFixed(2)} QORT
|
||||||
</TextP>
|
</TextP>
|
||||||
<RefreshIcon
|
<RefreshIcon
|
||||||
onClick={getBalanceAndUserInfoFunc}
|
onClick={getBalanceFunc}
|
||||||
sx={{
|
sx={{
|
||||||
fontSize: "16px",
|
fontSize: "16px",
|
||||||
color: "white",
|
color: "white",
|
||||||
@ -2619,14 +2578,87 @@ function App() {
|
|||||||
)}
|
)}
|
||||||
{extState === "download-wallet" && (
|
{extState === "download-wallet" && (
|
||||||
<>
|
<>
|
||||||
<DownloadWallet
|
<Spacer height="22px" />
|
||||||
returnToMain={returnToMain}
|
<Box
|
||||||
setIsLoading={setIsLoading}
|
sx={{
|
||||||
showInfo={showInfo}
|
display: "flex",
|
||||||
rawWallet={rawWallet}
|
width: "100%",
|
||||||
setWalletToBeDownloaded={setWalletToBeDownloaded}
|
justifyContent: "flex-start",
|
||||||
walletToBeDownloaded={walletToBeDownloaded}
|
paddingLeft: "22px",
|
||||||
/>
|
boxSizing: "border-box",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
style={{
|
||||||
|
cursor: "pointer",
|
||||||
|
}}
|
||||||
|
onClick={returnToMain}
|
||||||
|
src={Return}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
<Spacer height="10px" />
|
||||||
|
<div
|
||||||
|
className="image-container"
|
||||||
|
style={{
|
||||||
|
width: "136px",
|
||||||
|
height: "154px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<img src={Logo1Dark} className="base-image" />
|
||||||
|
</div>
|
||||||
|
<Spacer height="35px" />
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
alignItems: "flex-start",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<TextP
|
||||||
|
sx={{
|
||||||
|
textAlign: "start",
|
||||||
|
lineHeight: "24px",
|
||||||
|
fontSize: "20px",
|
||||||
|
fontWeight: 600,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Download Account
|
||||||
|
</TextP>
|
||||||
|
</Box>
|
||||||
|
<Spacer height="35px" />
|
||||||
|
{!walletToBeDownloaded && (
|
||||||
|
<>
|
||||||
|
<CustomLabel htmlFor="standard-adornment-password">
|
||||||
|
Confirm Wallet Password
|
||||||
|
</CustomLabel>
|
||||||
|
<Spacer height="5px" />
|
||||||
|
<PasswordField
|
||||||
|
id="standard-adornment-password"
|
||||||
|
value={walletToBeDownloadedPassword}
|
||||||
|
onChange={(e) =>
|
||||||
|
setWalletToBeDownloadedPassword(e.target.value)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Spacer height="20px" />
|
||||||
|
<CustomButton onClick={confirmPasswordToDownload}>
|
||||||
|
Confirm password
|
||||||
|
</CustomButton>
|
||||||
|
<ErrorText>{walletToBeDownloadedError}</ErrorText>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{walletToBeDownloaded && (
|
||||||
|
<>
|
||||||
|
<CustomButton onClick={async ()=> {
|
||||||
|
await saveFileToDiskFunc()
|
||||||
|
await showInfo({
|
||||||
|
message: isNative ? `Your account file was saved to internal storage, in the document folder. Keep that file secure.` : `Your account file was downloaded by your browser. Keep that file secure.` ,
|
||||||
|
})
|
||||||
|
}}>
|
||||||
|
Download account
|
||||||
|
</CustomButton>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{extState === "create-wallet" && (
|
{extState === "create-wallet" && (
|
||||||
@ -3078,7 +3110,7 @@ function App() {
|
|||||||
>
|
>
|
||||||
<CountdownCircleTimer
|
<CountdownCircleTimer
|
||||||
isPlaying
|
isPlaying
|
||||||
duration={60}
|
duration={30}
|
||||||
colors={["#004777", "#F7B801", "#A30000", "#A30000"]}
|
colors={["#004777", "#F7B801", "#A30000", "#A30000"]}
|
||||||
colorsTime={[7, 5, 2, 0]}
|
colorsTime={[7, 5, 2, 0]}
|
||||||
onComplete={() => {
|
onComplete={() => {
|
||||||
@ -3159,14 +3191,12 @@ function App() {
|
|||||||
>
|
>
|
||||||
{messageQortalRequestExtension?.text3}
|
{messageQortalRequestExtension?.text3}
|
||||||
</TextP>
|
</TextP>
|
||||||
|
<Spacer height="15px" />
|
||||||
</Box>
|
</Box>
|
||||||
<Spacer height="15px" />
|
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{messageQortalRequestExtension?.text4 && (
|
{messageQortalRequestExtension?.text4 && (
|
||||||
<>
|
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
@ -3184,8 +3214,6 @@ function App() {
|
|||||||
{messageQortalRequestExtension?.text4}
|
{messageQortalRequestExtension?.text4}
|
||||||
</TextP>
|
</TextP>
|
||||||
</Box>
|
</Box>
|
||||||
<Spacer height="15px" />
|
|
||||||
</>
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{messageQortalRequestExtension?.html && (
|
{messageQortalRequestExtension?.html && (
|
||||||
@ -3313,35 +3341,6 @@ function App() {
|
|||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
{messageQortalRequestExtension?.confirmCheckbox && (
|
|
||||||
<FormControlLabel
|
|
||||||
control={
|
|
||||||
<Checkbox
|
|
||||||
onChange={(e) => setConfirmRequestRead(e.target.checked)}
|
|
||||||
checked={confirmRequestRead}
|
|
||||||
edge="start"
|
|
||||||
tabIndex={-1}
|
|
||||||
disableRipple
|
|
||||||
sx={{
|
|
||||||
"&.Mui-checked": {
|
|
||||||
color: "white",
|
|
||||||
},
|
|
||||||
"& .MuiSvgIcon-root": {
|
|
||||||
color: "white",
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
label={
|
|
||||||
<Box sx={{ display: 'flex', alignItems: 'center' }}>
|
|
||||||
<Typography sx={{ fontSize: "14px" }}>
|
|
||||||
I have read this request
|
|
||||||
</Typography>
|
|
||||||
<PriorityHighIcon color="warning" />
|
|
||||||
</Box>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Spacer height="29px" />
|
<Spacer height="29px" />
|
||||||
<Box
|
<Box
|
||||||
@ -3351,21 +3350,13 @@ function App() {
|
|||||||
gap: "14px",
|
gap: "14px",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<CustomButtonAccept
|
<CustomButtonAccept
|
||||||
color="black"
|
color="black"
|
||||||
bgColor="var(--green)"
|
bgColor="var(--green)"
|
||||||
sx={{
|
sx={{
|
||||||
minWidth: "102px",
|
minWidth: "102px",
|
||||||
opacity: messageQortalRequestExtension?.confirmCheckbox && !confirmRequestRead ? 0.1 : 0.7,
|
|
||||||
cursor: messageQortalRequestExtension?.confirmCheckbox && !confirmRequestRead ? 'default' : 'pointer',
|
|
||||||
"&:hover": {
|
|
||||||
opacity: messageQortalRequestExtension?.confirmCheckbox && !confirmRequestRead ? 0.1 : 1,
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
onClick={() => {
|
|
||||||
if(messageQortalRequestExtension?.confirmCheckbox && !confirmRequestRead) return
|
|
||||||
onOkQortalRequestExtension("accepted")
|
|
||||||
}}
|
}}
|
||||||
|
onClick={() => onOkQortalRequestExtension("accepted")}
|
||||||
>
|
>
|
||||||
accept
|
accept
|
||||||
</CustomButtonAccept>
|
</CustomButtonAccept>
|
||||||
|
@ -41,14 +41,6 @@ export const sortablePinnedAppsAtom = atom({
|
|||||||
{
|
{
|
||||||
name: 'Q-Wallets',
|
name: 'Q-Wallets',
|
||||||
service: 'APP'
|
service: 'APP'
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Q-Search',
|
|
||||||
service: 'APP'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Q-Nodecontrol',
|
|
||||||
service: 'APP'
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
@ -188,8 +180,3 @@ export const lastPaymentSeenTimestampAtom = atom<null | number>({
|
|||||||
key: 'lastPaymentSeenTimestampAtom',
|
key: 'lastPaymentSeenTimestampAtom',
|
||||||
default: null,
|
default: null,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const isOpenBlockedModalAtom = atom({
|
|
||||||
key: 'isOpenBlockedModalAtom',
|
|
||||||
default: false,
|
|
||||||
});
|
|
@ -358,12 +358,11 @@ export async function sendCoinCase(request, event) {
|
|||||||
|
|
||||||
export async function inviteToGroupCase(request, event) {
|
export async function inviteToGroupCase(request, event) {
|
||||||
try {
|
try {
|
||||||
const { groupId, qortalAddress, inviteTime, txGroupId = 0 } = request.payload;
|
const { groupId, qortalAddress, inviteTime } = request.payload;
|
||||||
const response = await inviteToGroup({
|
const response = await inviteToGroup({
|
||||||
groupId,
|
groupId,
|
||||||
qortalAddress,
|
qortalAddress,
|
||||||
inviteTime,
|
inviteTime,
|
||||||
txGroupId
|
|
||||||
});
|
});
|
||||||
|
|
||||||
event.source.postMessage(
|
event.source.postMessage(
|
||||||
@ -484,8 +483,8 @@ export async function createGroupCase(request, event) {
|
|||||||
|
|
||||||
export async function cancelInvitationToGroupCase(request, event) {
|
export async function cancelInvitationToGroupCase(request, event) {
|
||||||
try {
|
try {
|
||||||
const { groupId, qortalAddress,txGroupId = 0 } = request.payload;
|
const { groupId, qortalAddress } = request.payload;
|
||||||
const response = await cancelInvitationToGroup({ groupId, qortalAddress, txGroupId });
|
const response = await cancelInvitationToGroup({ groupId, qortalAddress });
|
||||||
|
|
||||||
event.source.postMessage(
|
event.source.postMessage(
|
||||||
{
|
{
|
||||||
@ -565,12 +564,11 @@ export async function joinGroupCase(request, event) {
|
|||||||
|
|
||||||
export async function kickFromGroupCase(request, event) {
|
export async function kickFromGroupCase(request, event) {
|
||||||
try {
|
try {
|
||||||
const { groupId, qortalAddress, rBanReason, txGroupId = 0 } = request.payload;
|
const { groupId, qortalAddress, rBanReason } = request.payload;
|
||||||
const response = await kickFromGroup({
|
const response = await kickFromGroup({
|
||||||
groupId,
|
groupId,
|
||||||
qortalAddress,
|
qortalAddress,
|
||||||
rBanReason,
|
rBanReason,
|
||||||
txGroupId
|
|
||||||
});
|
});
|
||||||
|
|
||||||
event.source.postMessage(
|
event.source.postMessage(
|
||||||
@ -597,13 +595,12 @@ export async function kickFromGroupCase(request, event) {
|
|||||||
|
|
||||||
export async function banFromGroupCase(request, event) {
|
export async function banFromGroupCase(request, event) {
|
||||||
try {
|
try {
|
||||||
const { groupId, qortalAddress, rBanReason, rBanTime, txGroupId = 0 } = request.payload;
|
const { groupId, qortalAddress, rBanReason, rBanTime } = request.payload;
|
||||||
const response = await banFromGroup({
|
const response = await banFromGroup({
|
||||||
groupId,
|
groupId,
|
||||||
qortalAddress,
|
qortalAddress,
|
||||||
rBanReason,
|
rBanReason,
|
||||||
rBanTime,
|
rBanTime,
|
||||||
txGroupId
|
|
||||||
});
|
});
|
||||||
|
|
||||||
event.source.postMessage(
|
event.source.postMessage(
|
||||||
@ -737,8 +734,8 @@ export async function getUserSettingsCase(request, event) {
|
|||||||
|
|
||||||
export async function cancelBanCase(request, event) {
|
export async function cancelBanCase(request, event) {
|
||||||
try {
|
try {
|
||||||
const { groupId, qortalAddress, txGroupId = 0 } = request.payload;
|
const { groupId, qortalAddress } = request.payload;
|
||||||
const response = await cancelBan({ groupId, qortalAddress, txGroupId });
|
const response = await cancelBan({ groupId, qortalAddress });
|
||||||
|
|
||||||
event.source.postMessage(
|
event.source.postMessage(
|
||||||
{
|
{
|
||||||
@ -791,8 +788,8 @@ export async function registerNameCase(request, event) {
|
|||||||
|
|
||||||
export async function makeAdminCase(request, event) {
|
export async function makeAdminCase(request, event) {
|
||||||
try {
|
try {
|
||||||
const { groupId, qortalAddress,txGroupId = 0 } = request.payload;
|
const { groupId, qortalAddress } = request.payload;
|
||||||
const response = await makeAdmin({ groupId, qortalAddress, txGroupId });
|
const response = await makeAdmin({ groupId, qortalAddress });
|
||||||
|
|
||||||
event.source.postMessage(
|
event.source.postMessage(
|
||||||
{
|
{
|
||||||
@ -818,8 +815,8 @@ export async function makeAdminCase(request, event) {
|
|||||||
|
|
||||||
export async function removeAdminCase(request, event) {
|
export async function removeAdminCase(request, event) {
|
||||||
try {
|
try {
|
||||||
const { groupId, qortalAddress, txGroupId = 0 } = request.payload;
|
const { groupId, qortalAddress } = request.payload;
|
||||||
const response = await removeAdmin({ groupId, qortalAddress, txGroupId });
|
const response = await removeAdmin({ groupId, qortalAddress });
|
||||||
|
|
||||||
event.source.postMessage(
|
event.source.postMessage(
|
||||||
{
|
{
|
||||||
@ -1332,7 +1329,6 @@ export async function publishOnQDNCase(request, event) {
|
|||||||
try {
|
try {
|
||||||
const {
|
const {
|
||||||
data,
|
data,
|
||||||
name = "",
|
|
||||||
identifier,
|
identifier,
|
||||||
service,
|
service,
|
||||||
title,
|
title,
|
||||||
@ -1350,7 +1346,6 @@ export async function publishOnQDNCase(request, event) {
|
|||||||
identifier,
|
identifier,
|
||||||
service,
|
service,
|
||||||
title,
|
title,
|
||||||
name,
|
|
||||||
description,
|
description,
|
||||||
category,
|
category,
|
||||||
tag1,
|
tag1,
|
||||||
|
@ -795,35 +795,33 @@ export async function getNameInfo() {
|
|||||||
const wallet = await getSaveWallet();
|
const wallet = await getSaveWallet();
|
||||||
const address = wallet.address0;
|
const address = wallet.address0;
|
||||||
const validApi = await getBaseApi();
|
const validApi = await getBaseApi();
|
||||||
const response = await fetch(validApi + '/names/primary/' + address);
|
const response = await fetch(validApi + "/names/address/" + address);
|
||||||
const nameData = await response.json();
|
const nameData = await response.json();
|
||||||
if (nameData?.name) {
|
if (nameData?.length > 0) {
|
||||||
return nameData.name;
|
return nameData[0].name;
|
||||||
} else {
|
} else {
|
||||||
return '';
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getNameInfoForOthers(address) {
|
export async function getNameInfoForOthers(address) {
|
||||||
if (!address) return '';
|
|
||||||
const validApi = await getBaseApi();
|
const validApi = await getBaseApi();
|
||||||
const response = await fetch(validApi + '/names/primary/' + address);
|
const response = await fetch(validApi + "/names/address/" + address);
|
||||||
const nameData = await response.json();
|
const nameData = await response.json();
|
||||||
if (nameData?.name) {
|
if (nameData?.length > 0) {
|
||||||
return nameData?.name;
|
return nameData[0].name;
|
||||||
} else {
|
} else {
|
||||||
return '';
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export async function getAddressInfo(address) {
|
export async function getAddressInfo(address) {
|
||||||
const validApi = await getBaseApi();
|
const validApi = await getBaseApi();
|
||||||
const response = await fetch(validApi + "/addresses/" + address);
|
const response = await fetch(validApi + "/addresses/" + address);
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
|
|
||||||
if (!response?.ok && data?.error !== 124)
|
if (!response?.ok && data?.error !== 124)
|
||||||
throw new Error("Cannot retrieve address info");
|
throw new Error("Cannot fetch address info");
|
||||||
if (data?.error === 124) {
|
if (data?.error === 124) {
|
||||||
return {
|
return {
|
||||||
address,
|
address,
|
||||||
@ -930,59 +928,6 @@ export async function getBalanceInfo() {
|
|||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getAssetBalanceInfo(assetId: number) {
|
|
||||||
const wallet = await getSaveWallet();
|
|
||||||
const address = wallet.address0;
|
|
||||||
const validApi = await getBaseApi();
|
|
||||||
const response = await fetch(validApi + `/assets/balances?address=${address}&assetid=${assetId}&ordering=ASSET_BALANCE_ACCOUNT&limit=1`);
|
|
||||||
|
|
||||||
if (!response?.ok) throw new Error("Cannot fetch asset balance");
|
|
||||||
const data = await response.json();
|
|
||||||
return +data?.[0]?.balance
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getAssetInfo(assetId: number) {
|
|
||||||
const validApi = await getBaseApi();
|
|
||||||
const response = await fetch(validApi + `/assets/info?assetId=${assetId}`);
|
|
||||||
|
|
||||||
if (!response?.ok) throw new Error("Cannot fetch asset info");
|
|
||||||
const data = await response.json();
|
|
||||||
return data
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function transferAsset({
|
|
||||||
amount,
|
|
||||||
recipient,
|
|
||||||
assetId,
|
|
||||||
}) {
|
|
||||||
const lastReference = await getLastRef();
|
|
||||||
const resKeyPair = await getKeyPair();
|
|
||||||
const parsedData = resKeyPair;
|
|
||||||
const uint8PrivateKey = Base58.decode(parsedData.privateKey);
|
|
||||||
const uint8PublicKey = Base58.decode(parsedData.publicKey);
|
|
||||||
const keyPair = {
|
|
||||||
privateKey: uint8PrivateKey,
|
|
||||||
publicKey: uint8PublicKey,
|
|
||||||
};
|
|
||||||
const feeres = await getFee("TRANSFER_ASSET");
|
|
||||||
|
|
||||||
const tx = await createTransaction(12, keyPair, {
|
|
||||||
fee: feeres.fee,
|
|
||||||
recipient: recipient,
|
|
||||||
amount: amount,
|
|
||||||
assetId: assetId,
|
|
||||||
lastReference: lastReference,
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
const signedBytes = Base58.encode(tx.signedBytes);
|
|
||||||
|
|
||||||
const res = await processTransactionVersion2(signedBytes);
|
|
||||||
if (!res?.signature)
|
|
||||||
throw new Error(res?.message || "Transaction was not able to be processed");
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
export async function getLTCBalance() {
|
export async function getLTCBalance() {
|
||||||
const wallet = await getSaveWallet();
|
const wallet = await getSaveWallet();
|
||||||
let _url = `${buyTradeNodeBaseUrl}/crosschain/ltc/walletbalance`;
|
let _url = `${buyTradeNodeBaseUrl}/crosschain/ltc/walletbalance`;
|
||||||
@ -2034,7 +1979,7 @@ export async function joinGroup({ groupId }) {
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function cancelInvitationToGroup({ groupId, qortalAddress, txGroupId = 0 }) {
|
export async function cancelInvitationToGroup({ groupId, qortalAddress }) {
|
||||||
const lastReference = await getLastRef();
|
const lastReference = await getLastRef();
|
||||||
const resKeyPair = await getKeyPair();
|
const resKeyPair = await getKeyPair();
|
||||||
const parsedData = resKeyPair;
|
const parsedData = resKeyPair;
|
||||||
@ -2051,7 +1996,6 @@ export async function cancelInvitationToGroup({ groupId, qortalAddress, txGroupI
|
|||||||
recipient: qortalAddress,
|
recipient: qortalAddress,
|
||||||
rGroupId: groupId,
|
rGroupId: groupId,
|
||||||
lastReference: lastReference,
|
lastReference: lastReference,
|
||||||
groupID: txGroupId
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const signedBytes = Base58.encode(tx.signedBytes);
|
const signedBytes = Base58.encode(tx.signedBytes);
|
||||||
@ -2062,7 +2006,7 @@ export async function cancelInvitationToGroup({ groupId, qortalAddress, txGroupI
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function cancelBan({ groupId, qortalAddress, txGroupId = 0 }) {
|
export async function cancelBan({ groupId, qortalAddress }) {
|
||||||
const lastReference = await getLastRef();
|
const lastReference = await getLastRef();
|
||||||
const resKeyPair = await getKeyPair();
|
const resKeyPair = await getKeyPair();
|
||||||
const parsedData = resKeyPair;
|
const parsedData = resKeyPair;
|
||||||
@ -2079,7 +2023,6 @@ export async function cancelBan({ groupId, qortalAddress, txGroupId = 0 }) {
|
|||||||
recipient: qortalAddress,
|
recipient: qortalAddress,
|
||||||
rGroupId: groupId,
|
rGroupId: groupId,
|
||||||
lastReference: lastReference,
|
lastReference: lastReference,
|
||||||
groupID: txGroupId
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const signedBytes = Base58.encode(tx.signedBytes);
|
const signedBytes = Base58.encode(tx.signedBytes);
|
||||||
@ -2144,7 +2087,7 @@ export async function updateName({ newName, oldName, description }) {
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function makeAdmin({ groupId, qortalAddress, txGroupId = 0 }) {
|
export async function makeAdmin({ groupId, qortalAddress }) {
|
||||||
const lastReference = await getLastRef();
|
const lastReference = await getLastRef();
|
||||||
const resKeyPair = await getKeyPair();
|
const resKeyPair = await getKeyPair();
|
||||||
const parsedData = resKeyPair;
|
const parsedData = resKeyPair;
|
||||||
@ -2161,7 +2104,6 @@ export async function makeAdmin({ groupId, qortalAddress, txGroupId = 0 }) {
|
|||||||
recipient: qortalAddress,
|
recipient: qortalAddress,
|
||||||
rGroupId: groupId,
|
rGroupId: groupId,
|
||||||
lastReference: lastReference,
|
lastReference: lastReference,
|
||||||
groupID: txGroupId
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const signedBytes = Base58.encode(tx.signedBytes);
|
const signedBytes = Base58.encode(tx.signedBytes);
|
||||||
@ -2172,7 +2114,7 @@ export async function makeAdmin({ groupId, qortalAddress, txGroupId = 0 }) {
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function removeAdmin({ groupId, qortalAddress, txGroupId = 0 }) {
|
export async function removeAdmin({ groupId, qortalAddress }) {
|
||||||
const lastReference = await getLastRef();
|
const lastReference = await getLastRef();
|
||||||
const resKeyPair = await getKeyPair();
|
const resKeyPair = await getKeyPair();
|
||||||
const parsedData = resKeyPair;
|
const parsedData = resKeyPair;
|
||||||
@ -2189,7 +2131,6 @@ export async function removeAdmin({ groupId, qortalAddress, txGroupId = 0 }) {
|
|||||||
recipient: qortalAddress,
|
recipient: qortalAddress,
|
||||||
rGroupId: groupId,
|
rGroupId: groupId,
|
||||||
lastReference: lastReference,
|
lastReference: lastReference,
|
||||||
groupID: txGroupId
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const signedBytes = Base58.encode(tx.signedBytes);
|
const signedBytes = Base58.encode(tx.signedBytes);
|
||||||
@ -2205,7 +2146,6 @@ export async function banFromGroup({
|
|||||||
qortalAddress,
|
qortalAddress,
|
||||||
rBanReason = "",
|
rBanReason = "",
|
||||||
rBanTime,
|
rBanTime,
|
||||||
txGroupId = 0
|
|
||||||
}) {
|
}) {
|
||||||
const lastReference = await getLastRef();
|
const lastReference = await getLastRef();
|
||||||
const resKeyPair = await getKeyPair();
|
const resKeyPair = await getKeyPair();
|
||||||
@ -2225,7 +2165,6 @@ export async function banFromGroup({
|
|||||||
rBanReason: rBanReason,
|
rBanReason: rBanReason,
|
||||||
rBanTime,
|
rBanTime,
|
||||||
lastReference: lastReference,
|
lastReference: lastReference,
|
||||||
groupID: txGroupId
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const signedBytes = Base58.encode(tx.signedBytes);
|
const signedBytes = Base58.encode(tx.signedBytes);
|
||||||
@ -2240,7 +2179,6 @@ export async function kickFromGroup({
|
|||||||
groupId,
|
groupId,
|
||||||
qortalAddress,
|
qortalAddress,
|
||||||
rBanReason = "",
|
rBanReason = "",
|
||||||
txGroupId = 0
|
|
||||||
}) {
|
}) {
|
||||||
const lastReference = await getLastRef();
|
const lastReference = await getLastRef();
|
||||||
const resKeyPair = await getKeyPair();
|
const resKeyPair = await getKeyPair();
|
||||||
@ -2259,7 +2197,6 @@ export async function kickFromGroup({
|
|||||||
rGroupId: groupId,
|
rGroupId: groupId,
|
||||||
rBanReason: rBanReason,
|
rBanReason: rBanReason,
|
||||||
lastReference: lastReference,
|
lastReference: lastReference,
|
||||||
groupID: txGroupId
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const signedBytes = Base58.encode(tx.signedBytes);
|
const signedBytes = Base58.encode(tx.signedBytes);
|
||||||
@ -2310,153 +2247,7 @@ export async function createGroup({
|
|||||||
if (!res?.signature) throw new Error(res?.message || "Transaction was not able to be processed");
|
if (!res?.signature) throw new Error(res?.message || "Transaction was not able to be processed");
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
export async function inviteToGroup({ groupId, qortalAddress, inviteTime }) {
|
||||||
export async function sellName({
|
|
||||||
name,
|
|
||||||
sellPrice
|
|
||||||
}) {
|
|
||||||
const wallet = await getSaveWallet();
|
|
||||||
const address = wallet.address0;
|
|
||||||
if (!address) throw new Error("Cannot find user");
|
|
||||||
const lastReference = await getLastRef();
|
|
||||||
const feeres = await getFee("SELL_NAME");
|
|
||||||
const resKeyPair = await getKeyPair();
|
|
||||||
const parsedData = resKeyPair;
|
|
||||||
const uint8PrivateKey = Base58.decode(parsedData.privateKey);
|
|
||||||
const uint8PublicKey = Base58.decode(parsedData.publicKey);
|
|
||||||
const keyPair = {
|
|
||||||
privateKey: uint8PrivateKey,
|
|
||||||
publicKey: uint8PublicKey,
|
|
||||||
};
|
|
||||||
|
|
||||||
const tx = await createTransaction(5, keyPair, {
|
|
||||||
fee: feeres.fee,
|
|
||||||
name,
|
|
||||||
sellPrice: sellPrice,
|
|
||||||
lastReference: lastReference,
|
|
||||||
});
|
|
||||||
|
|
||||||
const signedBytes = Base58.encode(tx.signedBytes);
|
|
||||||
|
|
||||||
const res = await processTransactionVersion2(signedBytes);
|
|
||||||
if (!res?.signature)
|
|
||||||
throw new Error(res?.message || "Transaction was not able to be processed");
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function cancelSellName({
|
|
||||||
name
|
|
||||||
}) {
|
|
||||||
const wallet = await getSaveWallet();
|
|
||||||
const address = wallet.address0;
|
|
||||||
if (!address) throw new Error("Cannot find user");
|
|
||||||
const lastReference = await getLastRef();
|
|
||||||
const feeres = await getFee("SELL_NAME");
|
|
||||||
const resKeyPair = await getKeyPair();
|
|
||||||
const parsedData = resKeyPair;
|
|
||||||
const uint8PrivateKey = Base58.decode(parsedData.privateKey);
|
|
||||||
const uint8PublicKey = Base58.decode(parsedData.publicKey);
|
|
||||||
const keyPair = {
|
|
||||||
privateKey: uint8PrivateKey,
|
|
||||||
publicKey: uint8PublicKey,
|
|
||||||
};
|
|
||||||
|
|
||||||
const tx = await createTransaction(6, keyPair, {
|
|
||||||
fee: feeres.fee,
|
|
||||||
name,
|
|
||||||
lastReference: lastReference,
|
|
||||||
});
|
|
||||||
|
|
||||||
const signedBytes = Base58.encode(tx.signedBytes);
|
|
||||||
|
|
||||||
const res = await processTransactionVersion2(signedBytes);
|
|
||||||
if (!res?.signature)
|
|
||||||
throw new Error(res?.message || "Transaction was not able to be processed");
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function buyName({
|
|
||||||
name,
|
|
||||||
sellerAddress,
|
|
||||||
sellPrice
|
|
||||||
}) {
|
|
||||||
const wallet = await getSaveWallet();
|
|
||||||
const address = wallet.address0;
|
|
||||||
if (!address) throw new Error("Cannot find user");
|
|
||||||
const lastReference = await getLastRef();
|
|
||||||
const feeres = await getFee("BUY_NAME");
|
|
||||||
const resKeyPair = await getKeyPair();
|
|
||||||
const parsedData = resKeyPair;
|
|
||||||
const uint8PrivateKey = Base58.decode(parsedData.privateKey);
|
|
||||||
const uint8PublicKey = Base58.decode(parsedData.publicKey);
|
|
||||||
const keyPair = {
|
|
||||||
privateKey: uint8PrivateKey,
|
|
||||||
publicKey: uint8PublicKey,
|
|
||||||
};
|
|
||||||
|
|
||||||
const tx = await createTransaction(7, keyPair, {
|
|
||||||
fee: feeres.fee,
|
|
||||||
name,
|
|
||||||
sellPrice,
|
|
||||||
recipient: sellerAddress,
|
|
||||||
lastReference: lastReference,
|
|
||||||
});
|
|
||||||
|
|
||||||
const signedBytes = Base58.encode(tx.signedBytes);
|
|
||||||
|
|
||||||
const res = await processTransactionVersion2(signedBytes);
|
|
||||||
if (!res?.signature)
|
|
||||||
throw new Error(res?.message || "Transaction was not able to be processed");
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function updateGroup({
|
|
||||||
groupId,
|
|
||||||
newOwner,
|
|
||||||
newIsOpen,
|
|
||||||
newDescription,
|
|
||||||
newApprovalThreshold,
|
|
||||||
newMinimumBlockDelay,
|
|
||||||
newMaximumBlockDelay,
|
|
||||||
txGroupId = 0
|
|
||||||
}) {
|
|
||||||
const wallet = await getSaveWallet();
|
|
||||||
const address = wallet.address0;
|
|
||||||
if (!address) throw new Error("Cannot find user");
|
|
||||||
const lastReference = await getLastRef();
|
|
||||||
const feeres = await getFee("UPDATE_GROUP");
|
|
||||||
const resKeyPair = await getKeyPair();
|
|
||||||
const parsedData = resKeyPair;
|
|
||||||
const uint8PrivateKey = Base58.decode(parsedData.privateKey);
|
|
||||||
const uint8PublicKey = Base58.decode(parsedData.publicKey);
|
|
||||||
const keyPair = {
|
|
||||||
privateKey: uint8PrivateKey,
|
|
||||||
publicKey: uint8PublicKey,
|
|
||||||
};
|
|
||||||
|
|
||||||
const tx = await createTransaction(23, keyPair, {
|
|
||||||
fee: feeres.fee,
|
|
||||||
_groupId: groupId,
|
|
||||||
newOwner,
|
|
||||||
newIsOpen,
|
|
||||||
newDescription,
|
|
||||||
newApprovalThreshold,
|
|
||||||
newMinimumBlockDelay,
|
|
||||||
newMaximumBlockDelay,
|
|
||||||
lastReference: lastReference,
|
|
||||||
groupID: txGroupId
|
|
||||||
});
|
|
||||||
|
|
||||||
const signedBytes = Base58.encode(tx.signedBytes);
|
|
||||||
|
|
||||||
const res = await processTransactionVersion2(signedBytes);
|
|
||||||
if (!res?.signature)
|
|
||||||
throw new Error(res?.message || "Transaction was not able to be processed");
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export async function inviteToGroup({ groupId, qortalAddress, inviteTime, txGroupId = 0 }) {
|
|
||||||
const address = await getNameOrAddress(qortalAddress);
|
const address = await getNameOrAddress(qortalAddress);
|
||||||
if (!address) throw new Error("Cannot find user");
|
if (!address) throw new Error("Cannot find user");
|
||||||
const lastReference = await getLastRef();
|
const lastReference = await getLastRef();
|
||||||
@ -2476,14 +2267,13 @@ export async function inviteToGroup({ groupId, qortalAddress, inviteTime, txGrou
|
|||||||
rGroupId: groupId,
|
rGroupId: groupId,
|
||||||
rInviteTime: inviteTime,
|
rInviteTime: inviteTime,
|
||||||
lastReference: lastReference,
|
lastReference: lastReference,
|
||||||
groupID: txGroupId
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const signedBytes = Base58.encode(tx.signedBytes);
|
const signedBytes = Base58.encode(tx.signedBytes);
|
||||||
|
|
||||||
const res = await processTransactionVersion2(signedBytes);
|
const res = await processTransactionVersion2(signedBytes);
|
||||||
if (!res?.signature)
|
if (!res?.signature)
|
||||||
throw new Error(res?.message || "Transaction was not able to be processed");
|
throw new Error("Transaction was not able to be processed");
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3198,7 +2988,6 @@ function setupMessageListener() {
|
|||||||
break;
|
break;
|
||||||
case "updateThreadActivity":
|
case "updateThreadActivity":
|
||||||
updateThreadActivityCase(request, event);
|
updateThreadActivityCase(request, event);
|
||||||
break;
|
|
||||||
case "decryptGroupEncryption":
|
case "decryptGroupEncryption":
|
||||||
decryptGroupEncryptionCase(request, event);
|
decryptGroupEncryptionCase(request, event);
|
||||||
break;
|
break;
|
||||||
|
@ -47,29 +47,18 @@ async function getSaveWallet() {
|
|||||||
throw new Error("No wallet saved");
|
throw new Error("No wallet saved");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export async function getNameInfo() {
|
export async function getNameInfo() {
|
||||||
const wallet = await getSaveWallet();
|
const wallet = await getSaveWallet();
|
||||||
const address = wallet.address0;
|
const address = wallet.address0;
|
||||||
const validApi = await getBaseApi();
|
const validApi = await getBaseApi()
|
||||||
const response = await fetch(validApi + '/names/primary/' + address);
|
const response = await fetch(validApi + "/names/address/" + address);
|
||||||
const nameData = await response.json();
|
const nameData = await response.json();
|
||||||
if (nameData?.name) {
|
if (nameData?.length > 0) {
|
||||||
return nameData?.name;
|
return nameData[0].name;
|
||||||
} else {
|
} else {
|
||||||
return '';
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export async function getAllUserNames() {
|
|
||||||
const wallet = await getSaveWallet();
|
|
||||||
const address = wallet.address0;
|
|
||||||
const validApi = await getBaseApi();
|
|
||||||
const response = await fetch(validApi + '/names/address/' + address);
|
|
||||||
const nameData = await response.json();
|
|
||||||
return nameData.map((item) => item.name);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getKeyPair() {
|
async function getKeyPair() {
|
||||||
const res = await getData<any>("keyPair").catch(() => null);
|
const res = await getData<any>("keyPair").catch(() => null);
|
||||||
if (res) {
|
if (res) {
|
||||||
@ -162,7 +151,7 @@ async function getKeyPair() {
|
|||||||
if(encryptedData){
|
if(encryptedData){
|
||||||
const registeredName = await getNameInfo()
|
const registeredName = await getNameInfo()
|
||||||
const data = await publishData({
|
const data = await publishData({
|
||||||
registeredName, data: encryptedData, service: 'DOCUMENT_PRIVATE', identifier: `admins-symmetric-qchat-group-${groupId}`, uploadType: 'base64', withFee: true
|
registeredName, file: encryptedData, service: 'DOCUMENT_PRIVATE', identifier: `admins-symmetric-qchat-group-${groupId}`, uploadType: 'file', isBase64: true, withFee: true
|
||||||
})
|
})
|
||||||
return {
|
return {
|
||||||
data,
|
data,
|
||||||
@ -213,7 +202,7 @@ export const encryptAndPublishSymmetricKeyGroupChat = async ({groupId, previousD
|
|||||||
if(encryptedData){
|
if(encryptedData){
|
||||||
const registeredName = await getNameInfo()
|
const registeredName = await getNameInfo()
|
||||||
const data = await publishData({
|
const data = await publishData({
|
||||||
registeredName, data: encryptedData, service: 'DOCUMENT_PRIVATE', identifier: `symmetric-qchat-group-${groupId}`, uploadType: 'base64', withFee: true
|
registeredName, file: encryptedData, service: 'DOCUMENT_PRIVATE', identifier: `symmetric-qchat-group-${groupId}`, uploadType: 'file', isBase64: true, withFee: true
|
||||||
})
|
})
|
||||||
return {
|
return {
|
||||||
data,
|
data,
|
||||||
@ -234,7 +223,7 @@ export const publishGroupEncryptedResource = async ({encryptedData, identifier})
|
|||||||
const registeredName = await getNameInfo()
|
const registeredName = await getNameInfo()
|
||||||
if(!registeredName) throw new Error('You need a name to publish')
|
if(!registeredName) throw new Error('You need a name to publish')
|
||||||
const data = await publishData({
|
const data = await publishData({
|
||||||
registeredName, data: encryptedData, service: 'DOCUMENT', identifier, uploadType: 'base64', withFee: true
|
registeredName, file: encryptedData, service: 'DOCUMENT', identifier, uploadType: 'file', isBase64: true, withFee: true
|
||||||
})
|
})
|
||||||
return data
|
return data
|
||||||
|
|
||||||
@ -253,16 +242,15 @@ export const publishOnQDN = async ({data, identifier, service, title,
|
|||||||
tag3,
|
tag3,
|
||||||
tag4,
|
tag4,
|
||||||
tag5,
|
tag5,
|
||||||
name,
|
|
||||||
uploadType = 'file'
|
uploadType = 'file'
|
||||||
}) => {
|
}) => {
|
||||||
|
|
||||||
if(data && service){
|
if(data && service){
|
||||||
const registeredName = name || await getNameInfo()
|
const registeredName = await getNameInfo()
|
||||||
if(!registeredName) throw new Error('You need a name to publish')
|
if(!registeredName) throw new Error('You need a name to publish')
|
||||||
|
|
||||||
const res = await publishData({
|
const res = await publishData({
|
||||||
registeredName, data, service, identifier, uploadType, withFee: true, title,
|
registeredName, file: data, service, identifier, uploadType, isBase64: true, withFee: true, title,
|
||||||
description,
|
description,
|
||||||
category,
|
category,
|
||||||
tag1,
|
tag1,
|
||||||
|
@ -130,17 +130,12 @@ export const BoundedNumericTextField = ({
|
|||||||
...props?.InputProps,
|
...props?.InputProps,
|
||||||
endAdornment: addIconButtons ? (
|
endAdornment: addIconButtons ? (
|
||||||
<InputAdornment position="end">
|
<InputAdornment position="end">
|
||||||
<IconButton size="small" onClick={() =>
|
<IconButton size="small" onClick={() => changeValueWithIncDecButton(1)}>
|
||||||
changeValueWithIncDecButton(1)
|
|
||||||
|
|
||||||
} onTouchStart={(e)=> e.stopPropagation()}>
|
|
||||||
<AddIcon sx={{
|
<AddIcon sx={{
|
||||||
color: 'white'
|
color: 'white'
|
||||||
}} />{" "}
|
}} />{" "}
|
||||||
</IconButton>
|
</IconButton>
|
||||||
<IconButton onTouchStart={(e)=> e.stopPropagation()} size="small" onClick={() =>
|
<IconButton size="small" onClick={() => changeValueWithIncDecButton(-1)}>
|
||||||
changeValueWithIncDecButton(-1)
|
|
||||||
}>
|
|
||||||
<RemoveIcon sx={{
|
<RemoveIcon sx={{
|
||||||
color: 'white'
|
color: 'white'
|
||||||
}} />{" "}
|
}} />{" "}
|
||||||
|
@ -48,7 +48,7 @@ export const useModal = () => {
|
|||||||
const onCancel = () => {
|
const onCancel = () => {
|
||||||
const { reject } = promiseConfig.current;
|
const { reject } = promiseConfig.current;
|
||||||
hide();
|
hide();
|
||||||
reject('Declined');
|
reject();
|
||||||
setMessage({
|
setMessage({
|
||||||
publishFee: "",
|
publishFee: "",
|
||||||
message: ""
|
message: ""
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { useCallback, useContext, useEffect, useMemo, useState } from "react";
|
import React, { useContext, useEffect, useMemo, useState } from "react";
|
||||||
import {
|
import {
|
||||||
AppCircle,
|
AppCircle,
|
||||||
AppCircleContainer,
|
AppCircleContainer,
|
||||||
@ -49,7 +49,6 @@ import { LoadingSnackbar } from "../Snackbar/LoadingSnackbar";
|
|||||||
import { CustomizedSnackbars } from "../Snackbar/Snackbar";
|
import { CustomizedSnackbars } from "../Snackbar/Snackbar";
|
||||||
import { getFee } from "../../background";
|
import { getFee } from "../../background";
|
||||||
import { fileToBase64 } from "../../utils/fileReading";
|
import { fileToBase64 } from "../../utils/fileReading";
|
||||||
import { useSortedMyNames } from "../../hooks/useSortedMyNames";
|
|
||||||
|
|
||||||
const CustomSelect = styled(Select)({
|
const CustomSelect = styled(Select)({
|
||||||
border: "0.5px solid var(--50-white, #FFFFFF80)",
|
border: "0.5px solid var(--50-white, #FFFFFF80)",
|
||||||
@ -83,8 +82,7 @@ const CustomMenuItem = styled(MenuItem)({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export const AppPublish = ({ categories, myAddress, myName }) => {
|
export const AppPublish = ({ names, categories }) => {
|
||||||
const [names, setNames] = useState([]);
|
|
||||||
const [name, setName] = useState("");
|
const [name, setName] = useState("");
|
||||||
const [title, setTitle] = useState("");
|
const [title, setTitle] = useState("");
|
||||||
const [description, setDescription] = useState("");
|
const [description, setDescription] = useState("");
|
||||||
@ -101,8 +99,6 @@ export const AppPublish = ({ categories, myAddress, myName }) => {
|
|||||||
const [openSnack, setOpenSnack] = useState(false);
|
const [openSnack, setOpenSnack] = useState(false);
|
||||||
const [infoSnack, setInfoSnack] = useState(null);
|
const [infoSnack, setInfoSnack] = useState(null);
|
||||||
const [isLoading, setIsLoading] = useState("");
|
const [isLoading, setIsLoading] = useState("");
|
||||||
const mySortedNames = useSortedMyNames(names, myName);
|
|
||||||
|
|
||||||
const maxFileSize = appType === "APP" ? 50 * 1024 * 1024 : 400 * 1024 * 1024; // 50MB or 400MB
|
const maxFileSize = appType === "APP" ? 50 * 1024 * 1024 : 400 * 1024 * 1024; // 50MB or 400MB
|
||||||
const { getRootProps, getInputProps } = useDropzone({
|
const { getRootProps, getInputProps } = useDropzone({
|
||||||
accept: {
|
accept: {
|
||||||
@ -166,25 +162,6 @@ export const AppPublish = ({ categories, myAddress, myName }) => {
|
|||||||
getQapp(name, appType);
|
getQapp(name, appType);
|
||||||
}, [name, appType]);
|
}, [name, appType]);
|
||||||
|
|
||||||
const getNames = useCallback(async () => {
|
|
||||||
if (!myAddress) return;
|
|
||||||
try {
|
|
||||||
setIsLoading('Loading names');
|
|
||||||
const res = await fetch(
|
|
||||||
`${getBaseApiReact()}/names/address/${myAddress}?limit=0`
|
|
||||||
);
|
|
||||||
const data = await res.json();
|
|
||||||
setNames(data?.map((item) => item.name));
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
} finally {
|
|
||||||
setIsLoading('');
|
|
||||||
}
|
|
||||||
}, [myAddress]);
|
|
||||||
useEffect(() => {
|
|
||||||
getNames();
|
|
||||||
}, [getNames]);
|
|
||||||
|
|
||||||
const publishApp = async () => {
|
const publishApp = async () => {
|
||||||
try {
|
try {
|
||||||
const data = {
|
const data = {
|
||||||
@ -222,10 +199,10 @@ export const AppPublish = ({ categories, myAddress, myName }) => {
|
|||||||
publishFee: fee.fee + " QORT",
|
publishFee: fee.fee + " QORT",
|
||||||
});
|
});
|
||||||
setIsLoading("Publishing... Please wait.");
|
setIsLoading("Publishing... Please wait.");
|
||||||
|
const fileBase64 = await fileToBase64(file);
|
||||||
await new Promise((res, rej) => {
|
await new Promise((res, rej) => {
|
||||||
window.sendMessage("publishOnQDN", {
|
window.sendMessage("publishOnQDN", {
|
||||||
data: file,
|
data: fileBase64,
|
||||||
service: appType,
|
service: appType,
|
||||||
title,
|
title,
|
||||||
description,
|
description,
|
||||||
@ -236,7 +213,6 @@ export const AppPublish = ({ categories, myAddress, myName }) => {
|
|||||||
tag4,
|
tag4,
|
||||||
tag5,
|
tag5,
|
||||||
uploadType: "zip",
|
uploadType: "zip",
|
||||||
name
|
|
||||||
})
|
})
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
if (!response?.error) {
|
if (!response?.error) {
|
||||||
@ -311,7 +287,7 @@ export const AppPublish = ({ categories, myAddress, myName }) => {
|
|||||||
</em>{" "}
|
</em>{" "}
|
||||||
{/* This is the placeholder item */}
|
{/* This is the placeholder item */}
|
||||||
</CustomMenuItem>
|
</CustomMenuItem>
|
||||||
{mySortedNames.map((name) => {
|
{names.map((name) => {
|
||||||
return <CustomMenuItem value={name}>{name}</CustomMenuItem>;
|
return <CustomMenuItem value={name}>{name}</CustomMenuItem>;
|
||||||
})}
|
})}
|
||||||
</CustomSelect>
|
</CustomSelect>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { useCallback, useContext, useEffect, useMemo, useState } from "react";
|
import React, { useContext, useEffect, useMemo, useState } from "react";
|
||||||
|
|
||||||
import { Avatar, Box, } from "@mui/material";
|
import { Avatar, Box, } from "@mui/material";
|
||||||
import { Add } from "@mui/icons-material";
|
import { Add } from "@mui/icons-material";
|
||||||
@ -100,57 +100,6 @@ export const AppViewer = React.forwardRef(({ app , hide, isDevMode, skipAuth}, i
|
|||||||
};
|
};
|
||||||
}, [app, path]);
|
}, [app, path]);
|
||||||
|
|
||||||
const receiveChunksFunc = useCallback(
|
|
||||||
(e) => {
|
|
||||||
const iframe = iframeRef?.current;
|
|
||||||
if (!iframe || !iframe?.src) return;
|
|
||||||
if (app?.tabId !== e.detail?.tabId) return;
|
|
||||||
const publishLocation = e.detail?.publishLocation;
|
|
||||||
const chunksSubmitted = e.detail?.chunksSubmitted;
|
|
||||||
const totalChunks = e.detail?.totalChunks;
|
|
||||||
const retry = e.detail?.retry;
|
|
||||||
const filename = e.detail?.filename;
|
|
||||||
try {
|
|
||||||
if (publishLocation === undefined || publishLocation === null) return;
|
|
||||||
const dataToBeSent = {};
|
|
||||||
if (chunksSubmitted !== undefined && chunksSubmitted !== null) {
|
|
||||||
dataToBeSent.chunks = chunksSubmitted;
|
|
||||||
}
|
|
||||||
if (totalChunks !== undefined && totalChunks !== null) {
|
|
||||||
dataToBeSent.totalChunks = totalChunks;
|
|
||||||
}
|
|
||||||
if (retry !== undefined && retry !== null) {
|
|
||||||
dataToBeSent.retry = retry;
|
|
||||||
}
|
|
||||||
if (filename !== undefined && filename !== null) {
|
|
||||||
dataToBeSent.filename = filename;
|
|
||||||
}
|
|
||||||
const targetOrigin = new URL(iframe.src).origin;
|
|
||||||
iframe.contentWindow?.postMessage(
|
|
||||||
{
|
|
||||||
action: 'PUBLISH_STATUS',
|
|
||||||
publishLocation,
|
|
||||||
...dataToBeSent,
|
|
||||||
requestedHandler: 'UI',
|
|
||||||
processed: e.detail?.processed || false,
|
|
||||||
},
|
|
||||||
targetOrigin
|
|
||||||
);
|
|
||||||
} catch (err) {
|
|
||||||
console.error('Failed to send theme change to iframe:', err);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[iframeRef, app?.tabId]
|
|
||||||
);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
subscribeToEvent('receiveChunks', receiveChunksFunc);
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
unsubscribeFromEvent('receiveChunks', receiveChunksFunc);
|
|
||||||
};
|
|
||||||
}, [receiveChunksFunc]);
|
|
||||||
|
|
||||||
// Function to navigate back in iframe
|
// Function to navigate back in iframe
|
||||||
const navigateBackInIframe = async () => {
|
const navigateBackInIframe = async () => {
|
||||||
if (iframeRef.current && iframeRef.current.contentWindow && history?.currentIndex > 0) {
|
if (iframeRef.current && iframeRef.current.contentWindow && history?.currentIndex > 0) {
|
||||||
@ -245,7 +194,7 @@ export const AppViewer = React.forwardRef(({ app , hide, isDevMode, skipAuth}, i
|
|||||||
height: !isMobile ? '100vh' : `calc(${rootHeight} - 60px - 45px )`,
|
height: !isMobile ? '100vh' : `calc(${rootHeight} - 60px - 45px )`,
|
||||||
border: 'none',
|
border: 'none',
|
||||||
width: '100%'
|
width: '100%'
|
||||||
}} id="browser-iframe" src={defaultUrl} sandbox="allow-scripts allow-same-origin allow-forms allow-downloads allow-modals allow-orientation-lock"
|
}} id="browser-iframe" src={defaultUrl} sandbox="allow-scripts allow-same-origin allow-forms allow-downloads allow-modals"
|
||||||
allow="fullscreen; clipboard-read; clipboard-write">
|
allow="fullscreen; clipboard-read; clipboard-write">
|
||||||
|
|
||||||
</iframe>
|
</iframe>
|
||||||
|
@ -17,7 +17,7 @@ import { AppsLibrary } from "./AppsLibrary";
|
|||||||
|
|
||||||
const uid = new ShortUniqueId({ length: 8 });
|
const uid = new ShortUniqueId({ length: 8 });
|
||||||
|
|
||||||
export const Apps = ({ mode, setMode, show , myName, myAddress}) => {
|
export const Apps = ({ mode, setMode, show , myName}) => {
|
||||||
const [availableQapps, setAvailableQapps] = useState([]);
|
const [availableQapps, setAvailableQapps] = useState([]);
|
||||||
const [selectedAppInfo, setSelectedAppInfo] = useState(null);
|
const [selectedAppInfo, setSelectedAppInfo] = useState(null);
|
||||||
const [selectedCategory, setSelectedCategory] = useState(null)
|
const [selectedCategory, setSelectedCategory] = useState(null)
|
||||||
@ -298,7 +298,7 @@ export const Apps = ({ mode, setMode, show , myName, myAddress}) => {
|
|||||||
>
|
>
|
||||||
{mode !== "viewer" && !selectedTab && <Spacer height="30px" />}
|
{mode !== "viewer" && !selectedTab && <Spacer height="30px" />}
|
||||||
{mode === "home" && (
|
{mode === "home" && (
|
||||||
<AppsHome myName={myName} availableQapps={availableQapps} setMode={setMode} myApp={myApp} myWebsite={myWebsite} myAddress={myAddress} />
|
<AppsHome myName={myName} availableQapps={availableQapps} setMode={setMode} myApp={myApp} myWebsite={myWebsite} />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<AppsLibrary
|
<AppsLibrary
|
||||||
@ -314,7 +314,7 @@ export const Apps = ({ mode, setMode, show , myName, myAddress}) => {
|
|||||||
{mode === "appInfo" && !selectedTab && <AppInfo app={selectedAppInfo} myName={myName} />}
|
{mode === "appInfo" && !selectedTab && <AppInfo app={selectedAppInfo} myName={myName} />}
|
||||||
{mode === "appInfo-from-category" && !selectedTab && <AppInfo app={selectedAppInfo} myName={myName} />}
|
{mode === "appInfo-from-category" && !selectedTab && <AppInfo app={selectedAppInfo} myName={myName} />}
|
||||||
<AppsCategory availableQapps={availableQapps} isShow={mode === 'category' && !selectedTab} category={selectedCategory} myName={myName} />
|
<AppsCategory availableQapps={availableQapps} isShow={mode === 'category' && !selectedTab} category={selectedCategory} myName={myName} />
|
||||||
{mode === "publish" && !selectedTab && <AppPublish categories={categories} myAddress={myAddress} myName={myName} />}
|
{mode === "publish" && !selectedTab && <AppPublish names={myName ? [myName] : []} categories={categories} />}
|
||||||
|
|
||||||
{tabs.map((tab) => {
|
{tabs.map((tab) => {
|
||||||
if (!iframeRefs.current[tab.tabId]) {
|
if (!iframeRefs.current[tab.tabId]) {
|
||||||
@ -335,7 +335,7 @@ export const Apps = ({ mode, setMode, show , myName, myAddress}) => {
|
|||||||
{isNewTabWindow && mode === "viewer" && (
|
{isNewTabWindow && mode === "viewer" && (
|
||||||
<>
|
<>
|
||||||
<Spacer height="30px" />
|
<Spacer height="30px" />
|
||||||
<AppsHome myName={myName} availableQapps={availableQapps} setMode={setMode} myApp={myApp} myWebsite={myWebsite} myAddress={myAddress} />
|
<AppsHome myName={myName} availableQapps={availableQapps} setMode={setMode} myApp={myApp} myWebsite={myWebsite} />
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{mode !== "viewer" && !selectedTab && <Spacer height="180px" />}
|
{mode !== "viewer" && !selectedTab && <Spacer height="180px" />}
|
||||||
|
@ -41,9 +41,7 @@ const officialAppList = [
|
|||||||
"q-trade",
|
"q-trade",
|
||||||
"q-support",
|
"q-support",
|
||||||
"q-manager",
|
"q-manager",
|
||||||
"q-wallets",
|
"q-wallets"
|
||||||
"q-search",
|
|
||||||
"q-nodecontrol"
|
|
||||||
];
|
];
|
||||||
|
|
||||||
const ScrollerStyled = styled('div')({
|
const ScrollerStyled = styled('div')({
|
||||||
|
@ -47,9 +47,7 @@ const officialAppList = [
|
|||||||
"q-fund",
|
"q-fund",
|
||||||
"q-shop",
|
"q-shop",
|
||||||
"q-manager",
|
"q-manager",
|
||||||
"q-wallets",
|
"q-wallets"
|
||||||
"q-search",
|
|
||||||
"q-nodecontrol"
|
|
||||||
];
|
];
|
||||||
|
|
||||||
const ScrollerStyled = styled("div")({
|
const ScrollerStyled = styled("div")({
|
||||||
|
@ -20,7 +20,7 @@ import HelpIcon from '@mui/icons-material/Help';
|
|||||||
import { useHandleTutorials } from "../Tutorials/useHandleTutorials";
|
import { useHandleTutorials } from "../Tutorials/useHandleTutorials";
|
||||||
import { AppsPrivate } from "./AppsPrivate";
|
import { AppsPrivate } from "./AppsPrivate";
|
||||||
|
|
||||||
export const AppsHome = ({ setMode, myApp, myWebsite, availableQapps, myName, myAddress }) => {
|
export const AppsHome = ({ setMode, myApp, myWebsite, availableQapps, myName }) => {
|
||||||
const [qortalUrl, setQortalUrl] = useState('')
|
const [qortalUrl, setQortalUrl] = useState('')
|
||||||
const { showTutorial } = useContext(GlobalContext);
|
const { showTutorial } = useContext(GlobalContext);
|
||||||
|
|
||||||
@ -146,7 +146,7 @@ export const AppsHome = ({ setMode, myApp, myWebsite, availableQapps, myName, m
|
|||||||
<AppCircleLabel>Library</AppCircleLabel>
|
<AppCircleLabel>Library</AppCircleLabel>
|
||||||
</AppCircleContainer>
|
</AppCircleContainer>
|
||||||
</ButtonBase>
|
</ButtonBase>
|
||||||
<AppsPrivate myName={myName} myAddress={myAddress} />
|
<AppsPrivate myName={myName} />
|
||||||
|
|
||||||
<SortablePinnedApps availableQapps={availableQapps} myWebsite={myWebsite} myApp={myApp} />
|
<SortablePinnedApps availableQapps={availableQapps} myWebsite={myWebsite} myApp={myApp} />
|
||||||
|
|
||||||
|
@ -45,9 +45,7 @@ const officialAppList = [
|
|||||||
"q-support",
|
"q-support",
|
||||||
"q-manager",
|
"q-manager",
|
||||||
"q-mintership",
|
"q-mintership",
|
||||||
"q-wallets",
|
"q-wallets"
|
||||||
"q-search",
|
|
||||||
"q-nodecontrol"
|
|
||||||
];
|
];
|
||||||
|
|
||||||
const ScrollerStyled = styled('div')({
|
const ScrollerStyled = styled('div')({
|
||||||
|
@ -56,9 +56,7 @@ const officialAppList = [
|
|||||||
"q-shop",
|
"q-shop",
|
||||||
"q-manager",
|
"q-manager",
|
||||||
"q-mintership",
|
"q-mintership",
|
||||||
"q-wallets",
|
"q-wallets"
|
||||||
"q-search",
|
|
||||||
"q-nodecontrol"
|
|
||||||
];
|
];
|
||||||
|
|
||||||
const ScrollerStyled = styled("div")({
|
const ScrollerStyled = styled("div")({
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { useCallback, useContext, useEffect, useMemo, useState } from "react";
|
import React, { useContext, useMemo, useState } from "react";
|
||||||
import {
|
import {
|
||||||
Avatar,
|
Avatar,
|
||||||
Box,
|
Box,
|
||||||
@ -30,18 +30,15 @@ import {
|
|||||||
PublishQAppInfo,
|
PublishQAppInfo,
|
||||||
} from "./Apps-styles";
|
} from "./Apps-styles";
|
||||||
import ImageUploader from "../../common/ImageUploader";
|
import ImageUploader from "../../common/ImageUploader";
|
||||||
import { getBaseApiReact, isMobile, MyContext } from "../../App";
|
import { isMobile, MyContext } from "../../App";
|
||||||
import { fileToBase64 } from "../../utils/fileReading";
|
import { fileToBase64 } from "../../utils/fileReading";
|
||||||
import { objectToBase64 } from "../../qdn/encryption/group-encryption";
|
import { objectToBase64 } from "../../qdn/encryption/group-encryption";
|
||||||
import { getFee } from "../../background";
|
import { getFee } from "../../background";
|
||||||
import { useSortedMyNames } from "../../hooks/useSortedMyNames";
|
|
||||||
|
|
||||||
const maxFileSize = 50 * 1024 * 1024; // 50MB
|
const maxFileSize = 50 * 1024 * 1024; // 50MB
|
||||||
|
|
||||||
export const AppsPrivate = ({myName, myAddress}) => {
|
export const AppsPrivate = ({myName}) => {
|
||||||
const { openApp } = useHandlePrivateApps();
|
const { openApp } = useHandlePrivateApps();
|
||||||
const [names, setNames] = useState([]);
|
|
||||||
const [name, setName] = useState(0);
|
|
||||||
const [file, setFile] = useState(null);
|
const [file, setFile] = useState(null);
|
||||||
const [logo, setLogo] = useState(null);
|
const [logo, setLogo] = useState(null);
|
||||||
const [qortalUrl, setQortalUrl] = useState("");
|
const [qortalUrl, setQortalUrl] = useState("");
|
||||||
@ -51,7 +48,6 @@ export const AppsPrivate = ({myName, myAddress}) => {
|
|||||||
const [myGroupsWhereIAmAdminFromGlobal] = useRecoilState(
|
const [myGroupsWhereIAmAdminFromGlobal] = useRecoilState(
|
||||||
myGroupsWhereIAmAdminAtom
|
myGroupsWhereIAmAdminAtom
|
||||||
);
|
);
|
||||||
const mySortedNames = useSortedMyNames(names, myName);
|
|
||||||
|
|
||||||
const myGroupsWhereIAmAdmin = useMemo(()=> {
|
const myGroupsWhereIAmAdmin = useMemo(()=> {
|
||||||
return myGroupsWhereIAmAdminFromGlobal?.filter((group)=> groupsProperties[group?.groupId]?.isOpen === false)
|
return myGroupsWhereIAmAdminFromGlobal?.filter((group)=> groupsProperties[group?.groupId]?.isOpen === false)
|
||||||
@ -169,8 +165,6 @@ export const AppsPrivate = ({myName, myAddress}) => {
|
|||||||
data: decryptedData,
|
data: decryptedData,
|
||||||
identifier: newPrivateAppValues?.identifier,
|
identifier: newPrivateAppValues?.identifier,
|
||||||
service: newPrivateAppValues?.service,
|
service: newPrivateAppValues?.service,
|
||||||
uploadType: 'base64',
|
|
||||||
name,
|
|
||||||
})
|
})
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
if (!response?.error) {
|
if (!response?.error) {
|
||||||
@ -187,7 +181,7 @@ export const AppsPrivate = ({myName, myAddress}) => {
|
|||||||
{
|
{
|
||||||
identifier: newPrivateAppValues?.identifier,
|
identifier: newPrivateAppValues?.identifier,
|
||||||
service: newPrivateAppValues?.service,
|
service: newPrivateAppValues?.service,
|
||||||
name,
|
name: myName,
|
||||||
groupId: selectedGroup,
|
groupId: selectedGroup,
|
||||||
},
|
},
|
||||||
true
|
true
|
||||||
@ -202,24 +196,6 @@ export const AppsPrivate = ({myName, myAddress}) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const getNames = useCallback(async () => {
|
|
||||||
if (!myAddress) return;
|
|
||||||
try {
|
|
||||||
const res = await fetch(
|
|
||||||
`${getBaseApiReact()}/names/address/${myAddress}?limit=0`
|
|
||||||
);
|
|
||||||
const data = await res.json();
|
|
||||||
setNames(data?.map((item) => item.name));
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
}
|
|
||||||
}, [myAddress]);
|
|
||||||
useEffect(() => {
|
|
||||||
if (isOpenPrivateModal) {
|
|
||||||
getNames();
|
|
||||||
}
|
|
||||||
}, [getNames, isOpenPrivateModal]);
|
|
||||||
|
|
||||||
const handleChange = (event: React.SyntheticEvent, newValue: number) => {
|
const handleChange = (event: React.SyntheticEvent, newValue: number) => {
|
||||||
setValueTabPrivateApp(newValue);
|
setValueTabPrivateApp(newValue);
|
||||||
};
|
};
|
||||||
@ -456,34 +432,6 @@ export const AppsPrivate = ({myName, myAddress}) => {
|
|||||||
{file ? "Change" : "Choose"} File
|
{file ? "Change" : "Choose"} File
|
||||||
</PublishQAppChoseFile>
|
</PublishQAppChoseFile>
|
||||||
<Spacer height="20px" />
|
<Spacer height="20px" />
|
||||||
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'column',
|
|
||||||
gap: '5px',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Label>Select a Qortal name</Label>
|
|
||||||
|
|
||||||
<Select
|
|
||||||
labelId="demo-simple-select-label"
|
|
||||||
id="demo-simple-select"
|
|
||||||
value={name}
|
|
||||||
label="Groups where you are an admin"
|
|
||||||
onChange={(e) => setName(e.target.value)}
|
|
||||||
>
|
|
||||||
<MenuItem value={0}>No name selected</MenuItem>
|
|
||||||
{mySortedNames.map((name) => {
|
|
||||||
return (
|
|
||||||
<MenuItem key={name} value={name}>
|
|
||||||
{name}
|
|
||||||
</MenuItem>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</Select>
|
|
||||||
</Box>
|
|
||||||
<Spacer height="20px" />
|
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
|
@ -10,99 +10,9 @@ import { MyContext } from '../../App';
|
|||||||
import FileSaver from 'file-saver';
|
import FileSaver from 'file-saver';
|
||||||
|
|
||||||
import { Capacitor } from '@capacitor/core';
|
import { Capacitor } from '@capacitor/core';
|
||||||
import { createEndpoint } from '../../background';
|
|
||||||
import { uint8ArrayToBase64 } from '../../backgroundFunctions/encryption';
|
|
||||||
|
|
||||||
export const isNative = Capacitor.isNativePlatform();
|
export const isNative = Capacitor.isNativePlatform();
|
||||||
|
|
||||||
export const saveFileInChunksFromUrl = async (
|
|
||||||
location,
|
|
||||||
) => {
|
|
||||||
let fileName = location.filename
|
|
||||||
let locationUrl = `/arbitrary/${location.service}/${location.name}`;
|
|
||||||
if (location.identifier) {
|
|
||||||
locationUrl = locationUrl + `/${location.identifier}`;
|
|
||||||
}
|
|
||||||
const endpoint = await createEndpoint(
|
|
||||||
locationUrl +
|
|
||||||
`?attachment=true&attachmentFilename=${location?.filename}`
|
|
||||||
);
|
|
||||||
const response = await fetch(endpoint);
|
|
||||||
|
|
||||||
if (!response.ok || !response.body) {
|
|
||||||
throw new Error('Failed to fetch file or no readable stream');
|
|
||||||
}
|
|
||||||
|
|
||||||
const contentType = response.headers.get('Content-Type') || 'application/octet-stream';
|
|
||||||
const base64Prefix = `data:${contentType};base64,`;
|
|
||||||
|
|
||||||
const getExtensionFromFileName = (name: string): string => {
|
|
||||||
const lastDotIndex = name.lastIndexOf('.');
|
|
||||||
return lastDotIndex !== -1 ? name.substring(lastDotIndex) : '';
|
|
||||||
};
|
|
||||||
|
|
||||||
const existingExtension = getExtensionFromFileName(fileName);
|
|
||||||
|
|
||||||
if (existingExtension) {
|
|
||||||
fileName = fileName.substring(0, fileName.lastIndexOf('.'));
|
|
||||||
}
|
|
||||||
|
|
||||||
const mimeTypeToExtension = (mimeType: string): string => {
|
|
||||||
return mimeToExtensionMap[mimeType] || existingExtension || '';
|
|
||||||
};
|
|
||||||
|
|
||||||
const extension = mimeTypeToExtension(contentType);
|
|
||||||
const fullFileName = `${fileName}_${Date.now()}${extension}`;
|
|
||||||
const reader = response.body.getReader();
|
|
||||||
let isFirstChunk = true;
|
|
||||||
let done = false;
|
|
||||||
|
|
||||||
let buffer = new Uint8Array(0);
|
|
||||||
const preferredChunkSize = 1024 * 1024; // 1MB
|
|
||||||
|
|
||||||
while (!done) {
|
|
||||||
const result = await reader.read();
|
|
||||||
done = result.done;
|
|
||||||
|
|
||||||
if (result.value) {
|
|
||||||
// Combine new value with existing buffer
|
|
||||||
const newBuffer = new Uint8Array(buffer.length + result.value.length);
|
|
||||||
newBuffer.set(buffer);
|
|
||||||
newBuffer.set(result.value, buffer.length);
|
|
||||||
buffer = newBuffer;
|
|
||||||
|
|
||||||
// While we have enough data, process 1MB chunks
|
|
||||||
while (buffer.length >= preferredChunkSize) {
|
|
||||||
const chunk = buffer.slice(0, preferredChunkSize);
|
|
||||||
buffer = buffer.slice(preferredChunkSize);
|
|
||||||
|
|
||||||
const base64Chunk = uint8ArrayToBase64(chunk);
|
|
||||||
await Filesystem.writeFile({
|
|
||||||
path: fullFileName,
|
|
||||||
data: isFirstChunk ? base64Prefix + base64Chunk : base64Chunk,
|
|
||||||
directory: Directory.Documents,
|
|
||||||
recursive: true,
|
|
||||||
append: !isFirstChunk,
|
|
||||||
});
|
|
||||||
|
|
||||||
isFirstChunk = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write remaining buffer (if any)
|
|
||||||
if (buffer.length > 0) {
|
|
||||||
const base64Chunk = uint8ArrayToBase64(buffer);
|
|
||||||
await Filesystem.writeFile({
|
|
||||||
path: fullFileName,
|
|
||||||
data: isFirstChunk ? base64Prefix + base64Chunk : base64Chunk,
|
|
||||||
directory: Directory.Documents,
|
|
||||||
recursive: true,
|
|
||||||
append: !isFirstChunk,
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const saveFileInChunks = async (
|
export const saveFileInChunks = async (
|
||||||
blob: Blob,
|
blob: Blob,
|
||||||
@ -345,15 +255,7 @@ export function openIndexedDB() {
|
|||||||
'GET_NODE_INFO',
|
'GET_NODE_INFO',
|
||||||
'GET_NODE_STATUS',
|
'GET_NODE_STATUS',
|
||||||
'GET_ARRR_SYNC_STATUS',
|
'GET_ARRR_SYNC_STATUS',
|
||||||
'SHOW_PDF_READER',
|
'SHOW_PDF_READER'
|
||||||
'UPDATE_GROUP',
|
|
||||||
'SELL_NAME',
|
|
||||||
'CANCEL_SELL_NAME',
|
|
||||||
'BUY_NAME', 'MULTI_ASSET_PAYMENT_WITH_PRIVATE_DATA',
|
|
||||||
'TRANSFER_ASSET',
|
|
||||||
'SIGN_FOREIGN_FEES',
|
|
||||||
'GET_PRIMARY_NAME',
|
|
||||||
'SCREEN-ORIENTATION'
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@ -367,13 +269,7 @@ const UIQortalRequests = [
|
|||||||
'GET_SERVER_CONNECTION_HISTORY', 'SET_CURRENT_FOREIGN_SERVER',
|
'GET_SERVER_CONNECTION_HISTORY', 'SET_CURRENT_FOREIGN_SERVER',
|
||||||
'ADD_FOREIGN_SERVER', 'REMOVE_FOREIGN_SERVER', 'GET_DAY_SUMMARY', 'CREATE_TRADE_BUY_ORDER',
|
'ADD_FOREIGN_SERVER', 'REMOVE_FOREIGN_SERVER', 'GET_DAY_SUMMARY', 'CREATE_TRADE_BUY_ORDER',
|
||||||
'CREATE_TRADE_SELL_ORDER', 'CANCEL_TRADE_SELL_ORDER', 'IS_USING_PUBLIC_NODE', 'SIGN_TRANSACTION', 'ADMIN_ACTION', 'OPEN_NEW_TAB', 'CREATE_AND_COPY_EMBED_LINK', 'DECRYPT_QORTAL_GROUP_DATA', 'DECRYPT_DATA_WITH_SHARING_KEY', 'DELETE_HOSTED_DATA', 'GET_HOSTED_DATA', 'SHOW_ACTIONS', 'REGISTER_NAME', 'UPDATE_NAME', 'LEAVE_GROUP', 'INVITE_TO_GROUP', 'KICK_FROM_GROUP', 'BAN_FROM_GROUP', 'CANCEL_GROUP_BAN', 'ADD_GROUP_ADMIN', 'REMOVE_GROUP_ADMIN','DECRYPT_AESGCM', 'CANCEL_GROUP_INVITE', 'CREATE_GROUP', 'GET_USER_WALLET_TRANSACTIONS', 'GET_NODE_INFO',
|
'CREATE_TRADE_SELL_ORDER', 'CANCEL_TRADE_SELL_ORDER', 'IS_USING_PUBLIC_NODE', 'SIGN_TRANSACTION', 'ADMIN_ACTION', 'OPEN_NEW_TAB', 'CREATE_AND_COPY_EMBED_LINK', 'DECRYPT_QORTAL_GROUP_DATA', 'DECRYPT_DATA_WITH_SHARING_KEY', 'DELETE_HOSTED_DATA', 'GET_HOSTED_DATA', 'SHOW_ACTIONS', 'REGISTER_NAME', 'UPDATE_NAME', 'LEAVE_GROUP', 'INVITE_TO_GROUP', 'KICK_FROM_GROUP', 'BAN_FROM_GROUP', 'CANCEL_GROUP_BAN', 'ADD_GROUP_ADMIN', 'REMOVE_GROUP_ADMIN','DECRYPT_AESGCM', 'CANCEL_GROUP_INVITE', 'CREATE_GROUP', 'GET_USER_WALLET_TRANSACTIONS', 'GET_NODE_INFO',
|
||||||
'GET_NODE_STATUS', 'GET_ARRR_SYNC_STATUS', 'SHOW_PDF_READER', 'UPDATE_GROUP', 'SELL_NAME',
|
'GET_NODE_STATUS', 'GET_ARRR_SYNC_STATUS', 'SHOW_PDF_READER'
|
||||||
'CANCEL_SELL_NAME',
|
|
||||||
'BUY_NAME', 'MULTI_ASSET_PAYMENT_WITH_PRIVATE_DATA',
|
|
||||||
'TRANSFER_ASSET',
|
|
||||||
'SIGN_FOREIGN_FEES',
|
|
||||||
'GET_PRIMARY_NAME',
|
|
||||||
'SCREEN_ORIENTATION'
|
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
@ -646,18 +542,8 @@ isDOMContentLoaded: false
|
|||||||
if (event?.data?.requestedHandler !== 'UI') return;
|
if (event?.data?.requestedHandler !== 'UI') return;
|
||||||
|
|
||||||
const sendMessageToRuntime = (message, eventPort) => {
|
const sendMessageToRuntime = (message, eventPort) => {
|
||||||
let timeout: number = 300000;
|
window.sendMessage(message.action, message.payload, 300000, message.isExtension, {
|
||||||
if (
|
name: appName, service: appService
|
||||||
message?.action === 'PUBLISH_MULTIPLE_QDN_RESOURCES' &&
|
|
||||||
message?.payload?.resources?.length > 0
|
|
||||||
) {
|
|
||||||
timeout = message?.payload?.resources?.length * 1200000;
|
|
||||||
} else if (message?.action === 'PUBLISH_QDN_RESOURCE') {
|
|
||||||
timeout = 1200000;
|
|
||||||
}
|
|
||||||
|
|
||||||
window.sendMessage(message.action, message.payload, timeout, message.isExtension, {
|
|
||||||
name: appName, service: appService, tabId,
|
|
||||||
}, skipAuth)
|
}, skipAuth)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
if (response.error) {
|
if (response.error) {
|
||||||
@ -665,7 +551,7 @@ isDOMContentLoaded: false
|
|||||||
result: null,
|
result: null,
|
||||||
error: {
|
error: {
|
||||||
error: response.error,
|
error: response.error,
|
||||||
message: typeof response?.error === 'string' ? response?.error : typeof response?.message === 'string' ? response?.message : 'An error has occurred'
|
message: typeof response?.error === 'string' ? response.error : 'An error has occurred'
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@ -690,26 +576,38 @@ isDOMContentLoaded: false
|
|||||||
} else if(event?.data?.action === 'SAVE_FILE'
|
} else if(event?.data?.action === 'SAVE_FILE'
|
||||||
){
|
){
|
||||||
try {
|
try {
|
||||||
await saveFile(event.data, null, true, {
|
const res = await saveFile( event.data, null, true, {
|
||||||
openSnackGlobal,
|
openSnackGlobal,
|
||||||
setOpenSnackGlobal,
|
setOpenSnackGlobal,
|
||||||
infoSnackCustom,
|
infoSnackCustom,
|
||||||
setInfoSnackCustom,
|
setInfoSnackCustom
|
||||||
});
|
|
||||||
event.ports[0].postMessage({
|
|
||||||
result: true,
|
|
||||||
error: null,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
event.ports[0].postMessage({
|
|
||||||
result: null,
|
|
||||||
error: error?.message || 'Failed to save file',
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
} else if (
|
} else if (
|
||||||
|
event?.data?.action === 'PUBLISH_MULTIPLE_QDN_RESOURCES' ||
|
||||||
|
event?.data?.action === 'PUBLISH_QDN_RESOURCE' ||
|
||||||
event?.data?.action === 'ENCRYPT_DATA' || event?.data?.action === 'ENCRYPT_DATA_WITH_SHARING_KEY' || event?.data?.action === 'ENCRYPT_QORTAL_GROUP_DATA'
|
event?.data?.action === 'ENCRYPT_DATA' || event?.data?.action === 'ENCRYPT_DATA_WITH_SHARING_KEY' || event?.data?.action === 'ENCRYPT_QORTAL_GROUP_DATA'
|
||||||
|
|
||||||
) {
|
) {
|
||||||
|
if (
|
||||||
|
event?.data?.action === 'PUBLISH_MULTIPLE_QDN_RESOURCES' ||
|
||||||
|
event?.data?.action === 'PUBLISH_QDN_RESOURCE'
|
||||||
|
|
||||||
|
){
|
||||||
|
try {
|
||||||
|
checkMobileSizeConstraints(event.data)
|
||||||
|
} catch (error) {
|
||||||
|
event.ports[0].postMessage({
|
||||||
|
result: null,
|
||||||
|
error: error?.message,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let data;
|
let data;
|
||||||
try {
|
try {
|
||||||
data = await storeFilesInIndexedDB(event.data);
|
data = await storeFilesInIndexedDB(event.data);
|
||||||
@ -732,29 +630,6 @@ isDOMContentLoaded: false
|
|||||||
error: 'Failed to prepare data for publishing',
|
error: 'Failed to prepare data for publishing',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else if (
|
|
||||||
event?.data?.action === 'PUBLISH_MULTIPLE_QDN_RESOURCES' ||
|
|
||||||
event?.data?.action === 'PUBLISH_QDN_RESOURCE'
|
|
||||||
) {
|
|
||||||
|
|
||||||
const data = event.data;
|
|
||||||
|
|
||||||
if (data) {
|
|
||||||
sendMessageToRuntime(
|
|
||||||
{
|
|
||||||
action: event.data.action,
|
|
||||||
type: 'qortalRequest',
|
|
||||||
payload: data,
|
|
||||||
isExtension: true,
|
|
||||||
},
|
|
||||||
event.ports[0]
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
event.ports[0].postMessage({
|
|
||||||
result: null,
|
|
||||||
error: 'Failed to prepare data for publishing',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else if(event?.data?.action === 'LINK_TO_QDN_RESOURCE' ||
|
} else if(event?.data?.action === 'LINK_TO_QDN_RESOURCE' ||
|
||||||
event?.data?.action === 'QDN_RESOURCE_DISPLAYED'){
|
event?.data?.action === 'QDN_RESOURCE_DISPLAYED'){
|
||||||
const pathUrl = event?.data?.path != null ? (event?.data?.path.startsWith('/') ? '' : '/') + event?.data?.path : null
|
const pathUrl = event?.data?.path != null ? (event?.data?.path.startsWith('/') ? '' : '/') + event?.data?.path : null
|
||||||
@ -812,7 +687,7 @@ isDOMContentLoaded: false
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
}, [appName, appService, tabId]); // Empty dependency array to run once when the component mounts
|
}, [appName, appService]); // Empty dependency array to run once when the component mounts
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,248 +0,0 @@
|
|||||||
import {
|
|
||||||
Box,
|
|
||||||
Checkbox,
|
|
||||||
FormControlLabel,
|
|
||||||
Typography,
|
|
||||||
useTheme,
|
|
||||||
} from '@mui/material';
|
|
||||||
import { Spacer } from '../../common/Spacer';
|
|
||||||
import { PasswordField } from '../PasswordField/PasswordField';
|
|
||||||
import { ErrorText } from '../ErrorText/ErrorText';
|
|
||||||
import Logo1Dark from '../../assets/svgs/Logo1Dark.svg';
|
|
||||||
import { saveFileToDisk } from '../../utils/generateWallet/generateWallet';
|
|
||||||
import { useState } from 'react';
|
|
||||||
import { decryptStoredWallet } from '../../utils/decryptWallet';
|
|
||||||
import PhraseWallet from '../../utils/generateWallet/phrase-wallet';
|
|
||||||
import { crypto, walletVersion } from '../../constants/decryptWallet';
|
|
||||||
import Return from "../../assets/svgs/Return.svg";
|
|
||||||
import { CustomButton, CustomLabel, TextP } from '../../App-styles';
|
|
||||||
|
|
||||||
export const DownloadWallet = ({
|
|
||||||
returnToMain,
|
|
||||||
setIsLoading,
|
|
||||||
showInfo,
|
|
||||||
rawWallet,
|
|
||||||
setWalletToBeDownloaded,
|
|
||||||
walletToBeDownloaded,
|
|
||||||
}) => {
|
|
||||||
const [walletToBeDownloadedPassword, setWalletToBeDownloadedPassword] =
|
|
||||||
useState<string>('');
|
|
||||||
const [newPassword, setNewPassword] = useState<string>('');
|
|
||||||
const [keepCurrentPassword, setKeepCurrentPassword] = useState<boolean>(true);
|
|
||||||
const theme = useTheme();
|
|
||||||
const [walletToBeDownloadedError, setWalletToBeDownloadedError] =
|
|
||||||
useState<string>('');
|
|
||||||
|
|
||||||
|
|
||||||
const saveFileToDiskFunc = async () => {
|
|
||||||
try {
|
|
||||||
await saveFileToDisk(
|
|
||||||
walletToBeDownloaded.wallet,
|
|
||||||
walletToBeDownloaded.qortAddress
|
|
||||||
);
|
|
||||||
} catch (error: any) {
|
|
||||||
setWalletToBeDownloadedError(error?.message);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const saveWalletFunc = async (password: string, newPassword) => {
|
|
||||||
let wallet = structuredClone(rawWallet);
|
|
||||||
|
|
||||||
const res = await decryptStoredWallet(password, wallet);
|
|
||||||
const wallet2 = new PhraseWallet(res, wallet?.version || walletVersion);
|
|
||||||
const passwordToUse = newPassword || password;
|
|
||||||
wallet = await wallet2.generateSaveWalletData(
|
|
||||||
passwordToUse,
|
|
||||||
crypto.kdfThreads,
|
|
||||||
() => {}
|
|
||||||
);
|
|
||||||
|
|
||||||
setWalletToBeDownloaded({
|
|
||||||
wallet,
|
|
||||||
qortAddress: rawWallet.address0,
|
|
||||||
});
|
|
||||||
return {
|
|
||||||
wallet,
|
|
||||||
qortAddress: rawWallet.address0,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const confirmPasswordToDownload = async () => {
|
|
||||||
try {
|
|
||||||
setWalletToBeDownloadedError('');
|
|
||||||
if (!keepCurrentPassword && !newPassword) {
|
|
||||||
setWalletToBeDownloadedError(
|
|
||||||
'Please enter a new password'
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!walletToBeDownloadedPassword) {
|
|
||||||
setWalletToBeDownloadedError(
|
|
||||||
'Please enter your password'
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setIsLoading(true);
|
|
||||||
await new Promise<void>((res) => {
|
|
||||||
setTimeout(() => {
|
|
||||||
res();
|
|
||||||
}, 250);
|
|
||||||
});
|
|
||||||
const newPasswordForWallet = !keepCurrentPassword ? newPassword : null;
|
|
||||||
const res = await saveWalletFunc(
|
|
||||||
walletToBeDownloadedPassword,
|
|
||||||
newPasswordForWallet
|
|
||||||
);
|
|
||||||
} catch (error: any) {
|
|
||||||
setWalletToBeDownloadedError(error?.message);
|
|
||||||
} finally {
|
|
||||||
setIsLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Spacer height="22px" />
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
boxSizing: 'border-box',
|
|
||||||
display: 'flex',
|
|
||||||
justifyContent: 'flex-start',
|
|
||||||
maxWidth: '700px',
|
|
||||||
paddingLeft: '22px',
|
|
||||||
width: '100%',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
style={{
|
|
||||||
cursor: "pointer",
|
|
||||||
height: '24px'
|
|
||||||
}}
|
|
||||||
onClick={returnToMain}
|
|
||||||
src={Return}
|
|
||||||
/>
|
|
||||||
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
<Spacer height="10px" />
|
|
||||||
|
|
||||||
<div
|
|
||||||
className="image-container"
|
|
||||||
style={{
|
|
||||||
width: '136px',
|
|
||||||
height: '154px',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<img src={Logo1Dark} className="base-image" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Spacer height="35px" />
|
|
||||||
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'column',
|
|
||||||
alignItems: 'flex-start',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<TextP
|
|
||||||
sx={{
|
|
||||||
textAlign: 'start',
|
|
||||||
lineHeight: '24px',
|
|
||||||
fontSize: '20px',
|
|
||||||
fontWeight: 600,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Download account
|
|
||||||
</TextP>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
<Spacer height="35px" />
|
|
||||||
|
|
||||||
{!walletToBeDownloaded && (
|
|
||||||
<>
|
|
||||||
<CustomLabel htmlFor="standard-adornment-password">
|
|
||||||
Confirm password
|
|
||||||
</CustomLabel>
|
|
||||||
|
|
||||||
<Spacer height="5px" />
|
|
||||||
|
|
||||||
<PasswordField
|
|
||||||
id="standard-adornment-password"
|
|
||||||
value={walletToBeDownloadedPassword}
|
|
||||||
onChange={(e) => setWalletToBeDownloadedPassword(e.target.value)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Spacer height="20px" />
|
|
||||||
|
|
||||||
<FormControlLabel
|
|
||||||
sx={{
|
|
||||||
margin: 0,
|
|
||||||
}}
|
|
||||||
control={
|
|
||||||
<Checkbox
|
|
||||||
onChange={(e) => setKeepCurrentPassword(e.target.checked)}
|
|
||||||
checked={keepCurrentPassword}
|
|
||||||
edge="start"
|
|
||||||
tabIndex={-1}
|
|
||||||
disableRipple
|
|
||||||
sx={{
|
|
||||||
'&.Mui-checked': {
|
|
||||||
color: theme.palette.text.secondary,
|
|
||||||
},
|
|
||||||
'& .MuiSvgIcon-root': {
|
|
||||||
color: theme.palette.text.secondary,
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
label={
|
|
||||||
<Box sx={{ display: 'flex', alignItems: 'center' }}>
|
|
||||||
<Typography sx={{ fontSize: '14px' }}>
|
|
||||||
Keep current password
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<Spacer height="20px" />
|
|
||||||
{!keepCurrentPassword && (
|
|
||||||
<>
|
|
||||||
<CustomLabel htmlFor="standard-adornment-password">
|
|
||||||
New password
|
|
||||||
</CustomLabel>
|
|
||||||
|
|
||||||
<Spacer height="5px" />
|
|
||||||
<PasswordField
|
|
||||||
id="standard-adornment-password"
|
|
||||||
value={newPassword}
|
|
||||||
onChange={(e) => setNewPassword(e.target.value)}
|
|
||||||
/>
|
|
||||||
<Spacer height="20px" />
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<CustomButton onClick={confirmPasswordToDownload}>
|
|
||||||
Confirm wallet password
|
|
||||||
</CustomButton>
|
|
||||||
|
|
||||||
<ErrorText>{walletToBeDownloadedError}</ErrorText>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{walletToBeDownloaded && (
|
|
||||||
<>
|
|
||||||
<CustomButton
|
|
||||||
onClick={async () => {
|
|
||||||
await saveFileToDiskFunc();
|
|
||||||
await showInfo({
|
|
||||||
message: 'Keep your account file secure',
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Download account
|
|
||||||
</CustomButton>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
@ -79,7 +79,7 @@ export const AdminSpaceInner = ({
|
|||||||
const res = await fetch(
|
const res = await fetch(
|
||||||
`${getBaseApiReact()}/arbitrary/DOCUMENT_PRIVATE/${
|
`${getBaseApiReact()}/arbitrary/DOCUMENT_PRIVATE/${
|
||||||
getLatestPublish.name
|
getLatestPublish.name
|
||||||
}/${getLatestPublish.identifier}?encoding=base64&rebuild=true`
|
}/${getLatestPublish.identifier}?encoding=base64`
|
||||||
);
|
);
|
||||||
data = await res.text();
|
data = await res.text();
|
||||||
|
|
||||||
|
@ -15,12 +15,12 @@ import { CustomizedSnackbars } from '../Snackbar/Snackbar'
|
|||||||
import { PUBLIC_NOTIFICATION_CODE_FIRST_SECRET_KEY } from '../../constants/codes'
|
import { PUBLIC_NOTIFICATION_CODE_FIRST_SECRET_KEY } from '../../constants/codes'
|
||||||
import { useMessageQueue } from '../../MessageQueueContext'
|
import { useMessageQueue } from '../../MessageQueueContext'
|
||||||
import { executeEvent, subscribeToEvent, unsubscribeFromEvent } from '../../utils/events'
|
import { executeEvent, subscribeToEvent, unsubscribeFromEvent } from '../../utils/events'
|
||||||
import { Box, ButtonBase, Divider, IconButton, Tooltip, Typography } from '@mui/material'
|
import { Box, ButtonBase, Divider, Typography } from '@mui/material'
|
||||||
import ShortUniqueId from "short-unique-id";
|
import ShortUniqueId from "short-unique-id";
|
||||||
import { ReplyPreview } from './MessageItem'
|
import { ReplyPreview } from './MessageItem'
|
||||||
import { ExitIcon } from '../../assets/Icons/ExitIcon'
|
import { ExitIcon } from '../../assets/Icons/ExitIcon'
|
||||||
import { RESOURCE_TYPE_NUMBER_GROUP_CHAT_REACTIONS } from '../../constants/resourceTypes'
|
import { RESOURCE_TYPE_NUMBER_GROUP_CHAT_REACTIONS } from '../../constants/resourceTypes'
|
||||||
import { getFee, isExtMsg } from '../../background'
|
import { isExtMsg } from '../../background'
|
||||||
import MentionList from './MentionList'
|
import MentionList from './MentionList'
|
||||||
import { ChatOptions } from './ChatOptions'
|
import { ChatOptions } from './ChatOptions'
|
||||||
import { isFocusedParentGroupAtom } from '../../atoms/global'
|
import { isFocusedParentGroupAtom } from '../../atoms/global'
|
||||||
@ -28,10 +28,6 @@ import { useRecoilState } from 'recoil'
|
|||||||
import AppViewerContainer from '../Apps/AppViewerContainer'
|
import AppViewerContainer from '../Apps/AppViewerContainer'
|
||||||
import CloseIcon from "@mui/icons-material/Close";
|
import CloseIcon from "@mui/icons-material/Close";
|
||||||
import { throttle } from 'lodash'
|
import { throttle } from 'lodash'
|
||||||
import ImageIcon from '@mui/icons-material/Image';
|
|
||||||
import { messageHasImage } from '../../utils/chat'
|
|
||||||
|
|
||||||
const uidImages = new ShortUniqueId({ length: 12 });
|
|
||||||
|
|
||||||
const uid = new ShortUniqueId({ length: 5 });
|
const uid = new ShortUniqueId({ length: 5 });
|
||||||
|
|
||||||
@ -59,9 +55,8 @@ export const ChatGroup = ({selectedGroup, secretKey, setSecretKey, getSecretKey,
|
|||||||
const editorRef = useRef(null);
|
const editorRef = useRef(null);
|
||||||
const { queueChats, addToQueue, processWithNewMessages } = useMessageQueue();
|
const { queueChats, addToQueue, processWithNewMessages } = useMessageQueue();
|
||||||
const handleUpdateRef = useRef(null);
|
const handleUpdateRef = useRef(null);
|
||||||
const {isUserBlocked, show} = useContext(MyContext)
|
const {isUserBlocked} = useContext(MyContext)
|
||||||
const [chatImagesToSave, setChatImagesToSave] = useState([]);
|
|
||||||
const [isDeleteImage, setIsDeleteImage] = useState(false);
|
|
||||||
|
|
||||||
const lastReadTimestamp = useRef(null)
|
const lastReadTimestamp = useRef(null)
|
||||||
|
|
||||||
@ -629,8 +624,6 @@ if(isFocusedParent === false){
|
|||||||
setReplyMessage(null)
|
setReplyMessage(null)
|
||||||
setOnEditMessage(null)
|
setOnEditMessage(null)
|
||||||
clearEditorContent()
|
clearEditorContent()
|
||||||
setIsDeleteImage(false);
|
|
||||||
setChatImagesToSave([]);
|
|
||||||
}
|
}
|
||||||
}, [isFocusedParent])
|
}, [isFocusedParent])
|
||||||
const clearEditorContent = () => {
|
const clearEditorContent = () => {
|
||||||
@ -651,193 +644,88 @@ const clearEditorContent = () => {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
const sendMessage = async () => {
|
const sendMessage = async ()=> {
|
||||||
try {
|
try {
|
||||||
if (messageSize > 4000) return; // TODO magic number
|
if(messageSize > 4000) return
|
||||||
if (isPrivate === null)
|
if(isPrivate === null) throw new Error('Unable to determine if group is private')
|
||||||
throw new Error(
|
if(isSending) return
|
||||||
"Onable to determine if group is private"
|
if(+balance < 4) throw new Error('You need at least 4 QORT to send a message')
|
||||||
);
|
pauseAllQueues()
|
||||||
if (isSending) return;
|
|
||||||
if (+balance < 4)
|
|
||||||
// TODO magic number
|
|
||||||
throw new Error(
|
|
||||||
"You need at least 4 QORT to send a message"
|
|
||||||
);
|
|
||||||
pauseAllQueues();
|
|
||||||
if (editorRef.current) {
|
if (editorRef.current) {
|
||||||
let htmlContent = editorRef.current.getHTML();
|
const htmlContent = editorRef.current.getHTML();
|
||||||
const deleteImage =
|
|
||||||
onEditMessage && isDeleteImage && messageHasImage(onEditMessage);
|
|
||||||
|
|
||||||
const hasImage =
|
if(!htmlContent?.trim() || htmlContent?.trim() === '<p></p>') return
|
||||||
chatImagesToSave?.length > 0 || onEditMessage?.images?.length > 0;
|
|
||||||
if (
|
|
||||||
(!htmlContent?.trim() || htmlContent?.trim() === '<p></p>') &&
|
|
||||||
!hasImage &&
|
|
||||||
!deleteImage
|
|
||||||
)
|
|
||||||
return;
|
|
||||||
if (htmlContent?.trim() === '<p></p>') {
|
|
||||||
htmlContent = null;
|
|
||||||
}
|
|
||||||
setIsSending(true);
|
|
||||||
const message =
|
|
||||||
isPrivate === false
|
|
||||||
? !htmlContent
|
|
||||||
? '<p></p>'
|
|
||||||
: editorRef.current.getJSON()
|
|
||||||
: htmlContent;
|
|
||||||
const secretKeyObject = await getSecretKey(false, true);
|
|
||||||
|
|
||||||
let repliedTo = replyMessage?.signature;
|
|
||||||
|
|
||||||
if (replyMessage?.chatReference) {
|
setIsSending(true)
|
||||||
repliedTo = replyMessage?.chatReference;
|
const message = isPrivate === false ? editorRef.current.getJSON() : htmlContent
|
||||||
}
|
const secretKeyObject = await getSecretKey(false, true)
|
||||||
|
|
||||||
const chatReference = onEditMessage?.signature;
|
let repliedTo = replyMessage?.signature
|
||||||
|
|
||||||
const publicData = isPrivate
|
if (replyMessage?.chatReference) {
|
||||||
? {}
|
repliedTo = replyMessage?.chatReference
|
||||||
: {
|
}
|
||||||
isEdited: chatReference ? true : false,
|
let chatReference = onEditMessage?.signature
|
||||||
};
|
|
||||||
|
|
||||||
interface ImageToPublish {
|
const publicData = isPrivate ? {} : {
|
||||||
service: string;
|
isEdited : chatReference ? true : false,
|
||||||
identifier: string;
|
}
|
||||||
name: string;
|
const otherData = {
|
||||||
base64: string;
|
repliedTo,
|
||||||
}
|
...(onEditMessage?.decryptedData || {}),
|
||||||
|
type: chatReference ? 'edit' : '',
|
||||||
|
specialId: uid.rnd(),
|
||||||
|
...publicData
|
||||||
|
}
|
||||||
|
const objectMessage = {
|
||||||
|
...(otherData || {}),
|
||||||
|
[isPrivate ? 'message' : 'messageText']: message,
|
||||||
|
version: 3
|
||||||
|
}
|
||||||
|
const message64: any = await objectToBase64(objectMessage)
|
||||||
|
|
||||||
const imagesToPublish: ImageToPublish[] = [];
|
const encryptSingle = isPrivate === false ? JSON.stringify(objectMessage) : await encryptChatMessage(message64, secretKeyObject)
|
||||||
|
// const res = await sendChatGroup({groupId: selectedGroup,messageText: encryptSingle})
|
||||||
|
|
||||||
if (deleteImage) {
|
const sendMessageFunc = async () => {
|
||||||
const fee = await getFee('ARBITRARY');
|
return await sendChatGroup({groupId: selectedGroup,messageText: encryptSingle, chatReference})
|
||||||
await show({
|
};
|
||||||
publishFee: fee.fee + ' QORT',
|
|
||||||
message: "Would you like to delete your previous chat image?",
|
|
||||||
});
|
|
||||||
|
|
||||||
// TODO magic string
|
// Add the function to the queue
|
||||||
await window.sendMessage('publishOnQDN', {
|
const messageObj = {
|
||||||
data: 'RA==',
|
message: {
|
||||||
identifier: onEditMessage?.images[0]?.identifier,
|
text: htmlContent,
|
||||||
service: onEditMessage?.images[0]?.service,
|
timestamp: Date.now(),
|
||||||
uploadType: 'base64',
|
senderName: myName,
|
||||||
});
|
sender: myAddress,
|
||||||
}
|
...(otherData || {})
|
||||||
|
},
|
||||||
if (chatImagesToSave?.length > 0) {
|
chatReference
|
||||||
const imageToSave = chatImagesToSave[0];
|
}
|
||||||
|
addToQueue(sendMessageFunc, messageObj, 'chat',
|
||||||
const base64ToSave = isPrivate
|
selectedGroup );
|
||||||
? await encryptChatMessage(imageToSave, secretKeyObject)
|
setTimeout(() => {
|
||||||
: imageToSave;
|
executeEvent("sent-new-message-group", {})
|
||||||
|
}, 150);
|
||||||
// 1 represents public group, 0 is private
|
clearEditorContent()
|
||||||
const identifier = `grp-q-manager_${isPrivate ? 0 : 1}_group_${selectedGroup}_${uidImages.rnd()}`;
|
setReplyMessage(null)
|
||||||
imagesToPublish.push({
|
setOnEditMessage(null)
|
||||||
service: 'IMAGE',
|
|
||||||
identifier,
|
|
||||||
name: myName,
|
|
||||||
base64: base64ToSave,
|
|
||||||
});
|
|
||||||
|
|
||||||
const res = await window.sendMessage(
|
|
||||||
'PUBLISH_MULTIPLE_QDN_RESOURCES',
|
|
||||||
{
|
|
||||||
resources: imagesToPublish,
|
|
||||||
},
|
|
||||||
240000,
|
|
||||||
true
|
|
||||||
);
|
|
||||||
if (res?.error)
|
|
||||||
throw new Error(
|
|
||||||
"Unable to publish image"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const images =
|
|
||||||
imagesToPublish?.length > 0
|
|
||||||
? imagesToPublish.map((item) => {
|
|
||||||
return {
|
|
||||||
name: item.name,
|
|
||||||
identifier: item.identifier,
|
|
||||||
service: item.service,
|
|
||||||
timestamp: Date.now(),
|
|
||||||
};
|
|
||||||
})
|
|
||||||
: chatReference
|
|
||||||
? isDeleteImage
|
|
||||||
? []
|
|
||||||
: onEditMessage?.images || []
|
|
||||||
: [];
|
|
||||||
|
|
||||||
const otherData = {
|
|
||||||
repliedTo,
|
|
||||||
...(onEditMessage?.decryptedData || {}),
|
|
||||||
type: chatReference ? 'edit' : '',
|
|
||||||
specialId: uid.rnd(),
|
|
||||||
images: images,
|
|
||||||
...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 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 || {}),
|
|
||||||
},
|
|
||||||
chatReference,
|
|
||||||
};
|
|
||||||
addToQueue(sendMessageFunc, messageObj, 'chat', selectedGroup);
|
|
||||||
setTimeout(() => {
|
|
||||||
executeEvent('sent-new-message-group', {});
|
|
||||||
}, 150);
|
|
||||||
clearEditorContent();
|
|
||||||
setReplyMessage(null);
|
|
||||||
setOnEditMessage(null);
|
|
||||||
setIsDeleteImage(false);
|
|
||||||
setChatImagesToSave([]);
|
|
||||||
}
|
}
|
||||||
// send chat message
|
// send chat message
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const errorMsg = error?.message || error;
|
const errorMsg = error?.message || error
|
||||||
setInfoSnack({
|
setInfoSnack({
|
||||||
type: 'error',
|
type: "error",
|
||||||
message: errorMsg,
|
message: errorMsg,
|
||||||
});
|
});
|
||||||
setOpenSnack(true);
|
setOpenSnack(true);
|
||||||
console.error(error);
|
console.error(error)
|
||||||
} finally {
|
} finally {
|
||||||
setIsSending(false);
|
setIsSending(false)
|
||||||
resumeAllQueues();
|
resumeAllQueues()
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (hide) {
|
if (hide) {
|
||||||
@ -854,8 +742,7 @@ const sendMessage = async () => {
|
|||||||
setReplyMessage(message)
|
setReplyMessage(message)
|
||||||
setOnEditMessage(null)
|
setOnEditMessage(null)
|
||||||
setIsFocusedParent(true);
|
setIsFocusedParent(true);
|
||||||
setIsDeleteImage(false);
|
|
||||||
setChatImagesToSave([]);
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
editorRef?.current?.chain().focus()
|
editorRef?.current?.chain().focus()
|
||||||
|
|
||||||
@ -868,7 +755,7 @@ const sendMessage = async () => {
|
|||||||
setReplyMessage(null)
|
setReplyMessage(null)
|
||||||
setIsFocusedParent(true);
|
setIsFocusedParent(true);
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
editorRef?.current?.chain().focus().setContent(message?.messageText || message?.text || '<p></p>').run();
|
editorRef.current.chain().focus().setContent(message?.messageText || message?.text).run();
|
||||||
}, 250);
|
}, 250);
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
@ -938,24 +825,6 @@ const sendMessage = async () => {
|
|||||||
}
|
}
|
||||||
}, [isPrivate])
|
}, [isPrivate])
|
||||||
|
|
||||||
const insertImage = useCallback(
|
|
||||||
(img) => {
|
|
||||||
if (
|
|
||||||
chatImagesToSave?.length > 0 ||
|
|
||||||
(messageHasImage(onEditMessage) && !isDeleteImage)
|
|
||||||
) {
|
|
||||||
setInfoSnack({
|
|
||||||
type: 'error',
|
|
||||||
message: 'This message already has an image',
|
|
||||||
});
|
|
||||||
setOpenSnack(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setChatImagesToSave((prev) => [...prev, img]);
|
|
||||||
},
|
|
||||||
[chatImagesToSave, onEditMessage?.images, isDeleteImage]
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{
|
<div style={{
|
||||||
height: isMobile ? '100%' : '100%',
|
height: isMobile ? '100%' : '100%',
|
||||||
@ -995,117 +864,6 @@ const sendMessage = async () => {
|
|||||||
overflow: !isMobile && "auto",
|
overflow: !isMobile && "auto",
|
||||||
flexShrink: 0
|
flexShrink: 0
|
||||||
}}>
|
}}>
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
alignItems: 'flex-start',
|
|
||||||
display: 'flex',
|
|
||||||
width: '100%',
|
|
||||||
gap: '10px',
|
|
||||||
flexWrap: 'wrap',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{!isDeleteImage &&
|
|
||||||
onEditMessage &&
|
|
||||||
messageHasImage(onEditMessage) &&
|
|
||||||
onEditMessage?.images?.map((_, index) => (
|
|
||||||
<div
|
|
||||||
key={index}
|
|
||||||
style={{
|
|
||||||
position: 'relative',
|
|
||||||
height: '50px',
|
|
||||||
width: '50px',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ImageIcon
|
|
||||||
|
|
||||||
sx={{
|
|
||||||
height: '100%',
|
|
||||||
width: '100%',
|
|
||||||
borderRadius: '3px',
|
|
||||||
color:'white'
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Tooltip title="Delete image">
|
|
||||||
<IconButton
|
|
||||||
onClick={() => setIsDeleteImage(true)}
|
|
||||||
size="small"
|
|
||||||
sx={{
|
|
||||||
position: 'absolute',
|
|
||||||
top: '50%',
|
|
||||||
left: '50%',
|
|
||||||
transform: 'translate(-50%, -50%)',
|
|
||||||
backgroundColor: (theme) =>
|
|
||||||
theme.palette.background.paper,
|
|
||||||
color: (theme) => theme.palette.text.primary,
|
|
||||||
borderRadius: '50%',
|
|
||||||
opacity: 0,
|
|
||||||
transition: 'opacity 0.2s',
|
|
||||||
boxShadow: (theme) => theme.shadows[2],
|
|
||||||
'&:hover': {
|
|
||||||
backgroundColor: (theme) =>
|
|
||||||
theme.palette.background.default,
|
|
||||||
opacity: 1,
|
|
||||||
},
|
|
||||||
pointerEvents: 'auto',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<CloseIcon fontSize="small" />
|
|
||||||
</IconButton>
|
|
||||||
</Tooltip>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
{chatImagesToSave.map((imgBase64, index) => (
|
|
||||||
<div
|
|
||||||
key={index}
|
|
||||||
style={{
|
|
||||||
position: 'relative',
|
|
||||||
height: '50px',
|
|
||||||
width: '50px',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
src={`data:image/webp;base64,${imgBase64}`}
|
|
||||||
style={{
|
|
||||||
height: '100%',
|
|
||||||
width: '100%',
|
|
||||||
objectFit: 'contain',
|
|
||||||
borderRadius: '3px',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Tooltip title="Remove image">
|
|
||||||
<IconButton
|
|
||||||
onClick={() =>
|
|
||||||
setChatImagesToSave((prev) =>
|
|
||||||
prev.filter((_, i) => i !== index)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
size="small"
|
|
||||||
sx={{
|
|
||||||
position: 'absolute',
|
|
||||||
top: '50%',
|
|
||||||
left: '50%',
|
|
||||||
transform: 'translate(-50%, -50%)',
|
|
||||||
backgroundColor: (theme) =>
|
|
||||||
theme.palette.background.paper,
|
|
||||||
color: (theme) => theme.palette.text.primary,
|
|
||||||
borderRadius: '50%',
|
|
||||||
opacity: 0,
|
|
||||||
transition: 'opacity 0.2s',
|
|
||||||
boxShadow: (theme) => theme.shadows[2],
|
|
||||||
'&:hover': {
|
|
||||||
backgroundColor: (theme) =>
|
|
||||||
theme.palette.background.default,
|
|
||||||
opacity: 1,
|
|
||||||
},
|
|
||||||
pointerEvents: 'auto',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<CloseIcon fontSize="small" />
|
|
||||||
</IconButton>
|
|
||||||
</Tooltip>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</Box>
|
|
||||||
{replyMessage && (
|
{replyMessage && (
|
||||||
<Box sx={{
|
<Box sx={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
@ -1137,7 +895,7 @@ const sendMessage = async () => {
|
|||||||
}}>
|
}}>
|
||||||
|
|
||||||
|
|
||||||
<Tiptap isReply={onEditMessage || replyMessage} enableMentions setEditorRef={setEditorRef} onEnter={sendMessage} isChat disableEnter={isMobile ? true : false} isFocusedParent={isFocusedParent} setIsFocusedParent={setIsFocusedParent} membersWithNames={members} insertImage={insertImage} />
|
<Tiptap isReply={onEditMessage || replyMessage} enableMentions setEditorRef={setEditorRef} onEnter={sendMessage} isChat disableEnter={isMobile ? true : false} isFocusedParent={isFocusedParent} setIsFocusedParent={setIsFocusedParent} membersWithNames={members} />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -261,18 +261,17 @@ export const ChatList = ({ initialMessages, myAddress, tempMessages, chatId, onR
|
|||||||
if (chatReferences?.[message.signature]) {
|
if (chatReferences?.[message.signature]) {
|
||||||
reactions = chatReferences[message.signature]?.reactions || null;
|
reactions = chatReferences[message.signature]?.reactions || null;
|
||||||
|
|
||||||
if (chatReferences[message.signature]?.edit) {
|
if (chatReferences[message.signature]?.edit?.message && message?.text) {
|
||||||
message.text =
|
message.text = chatReferences[message.signature]?.edit?.message;
|
||||||
chatReferences[message.signature]?.edit?.message;
|
message.isEdit = true
|
||||||
message.messageText =
|
message.editTimestamp = chatReferences[message.signature]?.edit?.timestamp
|
||||||
chatReferences[message.signature]?.edit?.messageText;
|
}
|
||||||
message.images =
|
if (chatReferences[message.signature]?.edit?.messageText && message?.messageText) {
|
||||||
chatReferences[message.signature]?.edit?.images;
|
message.messageText = chatReferences[message.signature]?.edit?.messageText;
|
||||||
|
message.isEdit = true
|
||||||
message.isEdit = true;
|
message.editTimestamp = chatReferences[message.signature]?.edit?.timestamp
|
||||||
message.editTimestamp =
|
|
||||||
chatReferences[message.signature]?.edit?.timestamp;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if message is updating
|
// Check if message is updating
|
||||||
|
@ -39,7 +39,6 @@ import StarterKit from "@tiptap/starter-kit";
|
|||||||
import Underline from "@tiptap/extension-underline";
|
import Underline from "@tiptap/extension-underline";
|
||||||
import { generateHTML } from "@tiptap/react";
|
import { generateHTML } from "@tiptap/react";
|
||||||
import ErrorBoundary from "../../common/ErrorBoundary";
|
import ErrorBoundary from "../../common/ErrorBoundary";
|
||||||
import { isHtmlString } from "../../utils/chat";
|
|
||||||
|
|
||||||
const extractTextFromHTML = (htmlString = '') => {
|
const extractTextFromHTML = (htmlString = '') => {
|
||||||
return convert(htmlString, {
|
return convert(htmlString, {
|
||||||
@ -60,30 +59,27 @@ export const ChatOptions = ({ messages : untransformedMessages, goToMessage, mem
|
|||||||
const parentRefMentions = useRef();
|
const parentRefMentions = useRef();
|
||||||
const [lastMentionTimestamp, setLastMentionTimestamp] = useState(null)
|
const [lastMentionTimestamp, setLastMentionTimestamp] = useState(null)
|
||||||
const [debouncedValue, setDebouncedValue] = useState(""); // Debounced value
|
const [debouncedValue, setDebouncedValue] = useState(""); // Debounced value
|
||||||
const messages = useMemo(() => {
|
const messages = useMemo(()=> {
|
||||||
return untransformedMessages?.map((item) => {
|
return untransformedMessages?.map((item)=> {
|
||||||
if (item?.messageText) {
|
if(item?.messageText){
|
||||||
let transformedMessage = item?.messageText;
|
let transformedMessage = item?.messageText
|
||||||
const isHtml = isHtmlString(item?.messageText);
|
|
||||||
try {
|
try {
|
||||||
transformedMessage = isHtml
|
transformedMessage = generateHTML(item?.messageText, [
|
||||||
? item?.messageText
|
StarterKit,
|
||||||
: generateHTML(item?.messageText, [
|
Underline,
|
||||||
StarterKit,
|
Highlight,
|
||||||
Underline,
|
Mention
|
||||||
Highlight,
|
])
|
||||||
Mention,
|
return {
|
||||||
]);
|
...item,
|
||||||
return {
|
messageText: transformedMessage
|
||||||
...item,
|
}
|
||||||
messageText: transformedMessage,
|
|
||||||
};
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
// error
|
||||||
}
|
}
|
||||||
} else return item;
|
} else return item
|
||||||
});
|
})
|
||||||
}, [untransformedMessages]);
|
}, [untransformedMessages])
|
||||||
|
|
||||||
const getTimestampMention = async () => {
|
const getTimestampMention = async () => {
|
||||||
try {
|
try {
|
||||||
|
@ -66,7 +66,7 @@ export const CreateCommonSecret = ({groupId, secretKey, isOwner, myAddress, sec
|
|||||||
const res = await fetch(
|
const res = await fetch(
|
||||||
`${getBaseApiReact()}/arbitrary/DOCUMENT_PRIVATE/${publish.name}/${
|
`${getBaseApiReact()}/arbitrary/DOCUMENT_PRIVATE/${publish.name}/${
|
||||||
publish.identifier
|
publish.identifier
|
||||||
}?encoding=base64&rebuild=true`
|
}?encoding=base64`
|
||||||
);
|
);
|
||||||
const data = await res.text();
|
const data = await res.text();
|
||||||
|
|
||||||
|
@ -33,9 +33,6 @@ import level7Img from "../../assets/badges/level-7.png"
|
|||||||
import level8Img from "../../assets/badges/level-8.png"
|
import level8Img from "../../assets/badges/level-8.png"
|
||||||
import level9Img from "../../assets/badges/level-9.png"
|
import level9Img from "../../assets/badges/level-9.png"
|
||||||
import level10Img from "../../assets/badges/level-10.png"
|
import level10Img from "../../assets/badges/level-10.png"
|
||||||
import { Embed } from "../Embeds/Embed";
|
|
||||||
import { buildImageEmbedLink, isHtmlString, messageHasImage } from "../../utils/chat";
|
|
||||||
import CommentsDisabledIcon from '@mui/icons-material/CommentsDisabled';
|
|
||||||
|
|
||||||
const getBadgeImg = (level)=> {
|
const getBadgeImg = (level)=> {
|
||||||
switch(level?.toString()){
|
switch(level?.toString()){
|
||||||
@ -105,33 +102,35 @@ useEffect(()=> {
|
|||||||
getInfo()
|
getInfo()
|
||||||
}, [message?.sender, getIndividualUserInfo])
|
}, [message?.sender, getIndividualUserInfo])
|
||||||
|
|
||||||
const htmlText = useMemo(() => {
|
const htmlText = useMemo(()=> {
|
||||||
if (message?.messageText) {
|
|
||||||
const isHtml = isHtmlString(message?.messageText);
|
if(message?.messageText){
|
||||||
if (isHtml) return message?.messageText;
|
|
||||||
return generateHTML(message?.messageText, [
|
return generateHTML(message?.messageText, [
|
||||||
StarterKit,
|
StarterKit,
|
||||||
Underline,
|
Underline,
|
||||||
Highlight,
|
Highlight,
|
||||||
Mention,
|
Mention,
|
||||||
TextStyle,
|
TextStyle
|
||||||
]);
|
])
|
||||||
}
|
}
|
||||||
}, [message?.editTimestamp]);
|
|
||||||
|
|
||||||
const htmlReply = useMemo(() => {
|
}, [message?.editTimestamp])
|
||||||
if (reply?.messageText) {
|
|
||||||
const isHtml = isHtmlString(reply?.messageText);
|
|
||||||
if (isHtml) return reply?.messageText;
|
|
||||||
|
const htmlReply = useMemo(()=> {
|
||||||
|
|
||||||
|
if(reply?.messageText){
|
||||||
return generateHTML(reply?.messageText, [
|
return generateHTML(reply?.messageText, [
|
||||||
StarterKit,
|
StarterKit,
|
||||||
Underline,
|
Underline,
|
||||||
Highlight,
|
Highlight,
|
||||||
Mention,
|
Mention,
|
||||||
TextStyle,
|
TextStyle
|
||||||
]);
|
])
|
||||||
}
|
}
|
||||||
}, [reply?.editTimestamp]);
|
|
||||||
|
}, [reply?.editTimestamp])
|
||||||
|
|
||||||
const userAvatarUrl = useMemo(()=> {
|
const userAvatarUrl = useMemo(()=> {
|
||||||
return message?.senderName ? `${getBaseApiReact()}/arbitrary/THUMBNAIL/${
|
return message?.senderName ? `${getBaseApiReact()}/arbitrary/THUMBNAIL/${
|
||||||
@ -143,13 +142,6 @@ const onSeenFunc = useCallback(()=> {
|
|||||||
onSeen(message.id);
|
onSeen(message.id);
|
||||||
}, [message?.id])
|
}, [message?.id])
|
||||||
|
|
||||||
const hasNoMessage =
|
|
||||||
(!message.decryptedData?.data?.message ||
|
|
||||||
message.decryptedData?.data?.message === '<p></p>') &&
|
|
||||||
(message?.images || [])?.length === 0 &&
|
|
||||||
(!message?.messageText || message?.messageText === '<p></p>') &&
|
|
||||||
(!message?.text || message?.text === '<p></p>');
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MessageWragger lastMessage={lastSignature === message?.signature} isLast={isLast} onSeen={onSeenFunc}>
|
<MessageWragger lastMessage={lastSignature === message?.signature} isLast={isLast} onSeen={onSeenFunc}>
|
||||||
{message?.divide && (
|
{message?.divide && (
|
||||||
@ -343,7 +335,7 @@ const hasNoMessage =
|
|||||||
</Box>
|
</Box>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{htmlText && !hasNoMessage && (
|
{message?.messageText && (
|
||||||
<MessageDisplay
|
<MessageDisplay
|
||||||
htmlContent={htmlText}
|
htmlContent={htmlText}
|
||||||
setMobileViewModeKeepOpen={setMobileViewModeKeepOpen}
|
setMobileViewModeKeepOpen={setMobileViewModeKeepOpen}
|
||||||
@ -351,30 +343,9 @@ const hasNoMessage =
|
|||||||
)}
|
)}
|
||||||
{message?.decryptedData?.type === "notification" ? (
|
{message?.decryptedData?.type === "notification" ? (
|
||||||
<MessageDisplay htmlContent={message.decryptedData?.data?.message} />
|
<MessageDisplay htmlContent={message.decryptedData?.data?.message} />
|
||||||
) : hasNoMessage ? null : (
|
) : (
|
||||||
<MessageDisplay setMobileViewModeKeepOpen={setMobileViewModeKeepOpen} htmlContent={message.text} />
|
<MessageDisplay setMobileViewModeKeepOpen={setMobileViewModeKeepOpen} htmlContent={message.text} />
|
||||||
)}
|
)}
|
||||||
{hasNoMessage && (
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
gap: '10px',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<CommentsDisabledIcon sx={{
|
|
||||||
color: 'white'
|
|
||||||
}} />
|
|
||||||
<Typography sx={{
|
|
||||||
color: 'white'
|
|
||||||
}}>
|
|
||||||
No Message
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
{message?.images && messageHasImage(message) && (
|
|
||||||
<Embed embedLink={buildImageEmbedLink(message.images[0])} />
|
|
||||||
)}
|
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
@ -548,19 +519,6 @@ const hasNoMessage =
|
|||||||
|
|
||||||
export const ReplyPreview = ({message, isEdit})=> {
|
export const ReplyPreview = ({message, isEdit})=> {
|
||||||
|
|
||||||
const replyMessageText = useMemo(() => {
|
|
||||||
if (!message?.messageText) return null;
|
|
||||||
const isHtml = isHtmlString(message?.messageText);
|
|
||||||
if (isHtml) return message?.messageText;
|
|
||||||
return generateHTML(message?.messageText, [
|
|
||||||
StarterKit,
|
|
||||||
Underline,
|
|
||||||
Highlight,
|
|
||||||
Mention,
|
|
||||||
TextStyle,
|
|
||||||
]);
|
|
||||||
}, [message?.messageText]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
@ -595,9 +553,15 @@ export const ReplyPreview = ({message, isEdit})=> {
|
|||||||
}}>Replied to {message?.senderName || message?.senderAddress}</Typography>
|
}}>Replied to {message?.senderName || message?.senderAddress}</Typography>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{replyMessageText && (
|
{message?.messageText && (
|
||||||
<MessageDisplay
|
<MessageDisplay
|
||||||
htmlContent={replyMessageText}
|
htmlContent={generateHTML(message?.messageText, [
|
||||||
|
StarterKit,
|
||||||
|
Underline,
|
||||||
|
Highlight,
|
||||||
|
Mention,
|
||||||
|
TextStyle
|
||||||
|
])}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{message?.decryptedData?.type === "notification" ? (
|
{message?.decryptedData?.type === "notification" ? (
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
import React, { useEffect, useMemo, useRef, useState } from "react";
|
||||||
import { EditorProvider, useCurrentEditor, useEditor } from "@tiptap/react";
|
import { EditorProvider, useCurrentEditor, useEditor } from "@tiptap/react";
|
||||||
import StarterKit from "@tiptap/starter-kit";
|
import StarterKit from "@tiptap/starter-kit";
|
||||||
import { Color } from "@tiptap/extension-color";
|
import { Color } from "@tiptap/extension-color";
|
||||||
@ -34,7 +34,6 @@ import ListItemButton from '@mui/material/ListItemButton';
|
|||||||
import ListItemText from '@mui/material/ListItemText';
|
import ListItemText from '@mui/material/ListItemText';
|
||||||
import { ReactRenderer } from '@tiptap/react'
|
import { ReactRenderer } from '@tiptap/react'
|
||||||
import MentionList from './MentionList.jsx'
|
import MentionList from './MentionList.jsx'
|
||||||
import { fileToBase64 } from "../../utils/fileReading/index.js";
|
|
||||||
|
|
||||||
function textMatcher(doc, from) {
|
function textMatcher(doc, from) {
|
||||||
const textBeforeCursor = doc.textBetween(0, from, ' ', ' ');
|
const textBeforeCursor = doc.textBetween(0, from, ' ', ' ');
|
||||||
@ -111,13 +110,13 @@ const MenuBar = ({ setEditorRef, isChat }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (editor && !isChat) {
|
if (editor) {
|
||||||
editor.view.dom.addEventListener("paste", handlePaste);
|
editor.view.dom.addEventListener("paste", handlePaste);
|
||||||
return () => {
|
return () => {
|
||||||
editor.view.dom.removeEventListener("paste", handlePaste);
|
editor.view.dom.removeEventListener("paste", handlePaste);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}, [editor, isChat]);
|
}, [editor]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="control-group">
|
<div className="control-group">
|
||||||
@ -300,8 +299,7 @@ export default ({
|
|||||||
customEditorHeight,
|
customEditorHeight,
|
||||||
membersWithNames,
|
membersWithNames,
|
||||||
enableMentions,
|
enableMentions,
|
||||||
isReply,
|
isReply
|
||||||
insertImage,
|
|
||||||
}) => {
|
}) => {
|
||||||
|
|
||||||
const extensionsFiltered = isChat
|
const extensionsFiltered = isChat
|
||||||
@ -331,35 +329,7 @@ export default ({
|
|||||||
}, [membersWithNames])
|
}, [membersWithNames])
|
||||||
|
|
||||||
|
|
||||||
const handleImageUpload = useCallback(async (file) => {
|
|
||||||
try {
|
|
||||||
if (!file.type.includes('image')) return;
|
|
||||||
let compressedFile = file;
|
|
||||||
if (file.type !== 'image/gif') {
|
|
||||||
await new Promise<void>((resolve) => {
|
|
||||||
new Compressor(file, {
|
|
||||||
quality: 0.6,
|
|
||||||
maxWidth: 1200,
|
|
||||||
mimeType: 'image/webp',
|
|
||||||
success(result) {
|
|
||||||
compressedFile = result;
|
|
||||||
resolve();
|
|
||||||
},
|
|
||||||
error(err) {
|
|
||||||
console.error('Image compression error:', err);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (compressedFile) {
|
|
||||||
const toBase64 = await fileToBase64(compressedFile);
|
|
||||||
insertImage(toBase64);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
}
|
|
||||||
}, [insertImage]);
|
|
||||||
|
|
||||||
|
|
||||||
const usersRef = useRef([]);
|
const usersRef = useRef([]);
|
||||||
@ -500,25 +470,6 @@ export default ({
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
handlePaste(view, event) {
|
|
||||||
if(!handleImageUpload) return
|
|
||||||
if (!isChat) return;
|
|
||||||
const items = event.clipboardData?.items;
|
|
||||||
if (!items) return false;
|
|
||||||
|
|
||||||
for (const item of items) {
|
|
||||||
if (item.type.startsWith('image/')) {
|
|
||||||
const file = item.getAsFile();
|
|
||||||
if (file) {
|
|
||||||
event.preventDefault(); // Block the default paste
|
|
||||||
handleImageUpload(file); // Custom handler
|
|
||||||
return true; // Let ProseMirror know we handled it
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false; // fallback to default behavior otherwise
|
|
||||||
},
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import React, { useCallback, useEffect, useRef } from "react";
|
import React, { useCallback, useEffect, useRef } from "react";
|
||||||
|
import { getBaseApiReact } from "../../App";
|
||||||
|
import { truncate } from "lodash";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export const useBlockedAddresses = () => {
|
||||||
export const useBlockedAddresses = (isAuthenticated: boolean) => {
|
|
||||||
const userBlockedRef = useRef({})
|
const userBlockedRef = useRef({})
|
||||||
const userNamesBlockedRef = useRef({})
|
const userNamesBlockedRef = useRef({})
|
||||||
|
|
||||||
@ -18,7 +19,7 @@ export const useBlockedAddresses = (isAuthenticated: boolean) => {
|
|||||||
const isUserBlocked = useCallback((address, name)=> {
|
const isUserBlocked = useCallback((address, name)=> {
|
||||||
try {
|
try {
|
||||||
if(!address) return false
|
if(!address) return false
|
||||||
if(userBlockedRef.current[address]) return true
|
if(userBlockedRef.current[address] || userNamesBlockedRef.current[name]) return true
|
||||||
return false
|
return false
|
||||||
|
|
||||||
|
|
||||||
@ -28,9 +29,6 @@ export const useBlockedAddresses = (isAuthenticated: boolean) => {
|
|||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
useEffect(()=> {
|
useEffect(()=> {
|
||||||
if (!isAuthenticated) return;
|
|
||||||
userBlockedRef.current = {};
|
|
||||||
userNamesBlockedRef.current = {};
|
|
||||||
const fetchBlockedList = async ()=> {
|
const fetchBlockedList = async ()=> {
|
||||||
try {
|
try {
|
||||||
const response = await new Promise((res, rej) => {
|
const response = await new Promise((res, rej) => {
|
||||||
@ -89,16 +87,46 @@ export const useBlockedAddresses = (isAuthenticated: boolean) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
fetchBlockedList()
|
fetchBlockedList()
|
||||||
}, [isAuthenticated])
|
}, [])
|
||||||
|
|
||||||
const removeBlockFromList = useCallback(async (address, name)=> {
|
const removeBlockFromList = useCallback(async (address, name)=> {
|
||||||
if(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]){
|
||||||
await new Promise((res, rej) => {
|
await new Promise((res, rej) => {
|
||||||
window.sendMessage("listActions", {
|
window.sendMessage("listActions", {
|
||||||
|
|
||||||
type: 'remove',
|
type: 'remove',
|
||||||
items: [name] ,
|
items: !name ? [name] : [address],
|
||||||
listName: 'blockedNames'
|
listName: !name ? 'blockedNames' : 'blockedAddresses'
|
||||||
|
|
||||||
})
|
})
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
@ -106,12 +134,9 @@ export const useBlockedAddresses = (isAuthenticated: boolean) => {
|
|||||||
rej(response?.message);
|
rej(response?.message);
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
|
const copyObject = {...userBlockedRef.current}
|
||||||
const copyObject = {...userNamesBlockedRef.current}
|
delete copyObject[address]
|
||||||
delete copyObject[name]
|
userBlockedRef.current = copyObject
|
||||||
userNamesBlockedRef.current = copyObject
|
|
||||||
|
|
||||||
|
|
||||||
res(response);
|
res(response);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -121,94 +146,41 @@ export const useBlockedAddresses = (isAuthenticated: boolean) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
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)=> {
|
const addToBlockList = useCallback(async (address, name)=> {
|
||||||
if(name){
|
await new Promise((res, rej) => {
|
||||||
await new Promise((res, rej) => {
|
window.sendMessage("listActions", {
|
||||||
window.sendMessage("listActions", {
|
|
||||||
|
|
||||||
type: 'add',
|
type: 'add',
|
||||||
items: [name],
|
items: name ? [name] : [address],
|
||||||
listName: 'blockedNames'
|
listName: name ? 'blockedNames' : 'blockedAddresses'
|
||||||
|
|
||||||
})
|
|
||||||
.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);
|
|
||||||
});
|
|
||||||
})
|
})
|
||||||
}
|
.then((response) => {
|
||||||
if(address){
|
if (response.error) {
|
||||||
await new Promise((res, rej) => {
|
rej(response?.message);
|
||||||
window.sendMessage("listActions", {
|
return;
|
||||||
|
} else {
|
||||||
|
if(name){
|
||||||
|
|
||||||
type: 'add',
|
const copyObject = {...userNamesBlockedRef.current}
|
||||||
items: [address],
|
copyObject[name] = true
|
||||||
listName: 'blockedAddresses'
|
userNamesBlockedRef.current = copyObject
|
||||||
|
}else {
|
||||||
|
const copyObject = {...userBlockedRef.current}
|
||||||
|
copyObject[address] = true
|
||||||
|
userBlockedRef.current = copyObject
|
||||||
|
|
||||||
})
|
|
||||||
.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);
|
|
||||||
});
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
|
res(response);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error("Failed qortalRequest", error);
|
||||||
|
});
|
||||||
|
})
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -52,8 +52,6 @@ export const ImageCard = ({
|
|||||||
backgroundColor: "#1F2023",
|
backgroundColor: "#1F2023",
|
||||||
height: height,
|
height: height,
|
||||||
transition: "height 0.6s ease-in-out",
|
transition: "height 0.6s ease-in-out",
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'column',
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box
|
<Box
|
||||||
@ -172,18 +170,8 @@ export const ImageCard = ({
|
|||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box
|
<Box>
|
||||||
sx={{
|
<CardContent>
|
||||||
maxHeight: '100%',
|
|
||||||
flexGrow: 1,
|
|
||||||
overflow: 'hidden',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<CardContent
|
|
||||||
sx={{
|
|
||||||
height: '100%',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ImageViewer src={image} />
|
<ImageViewer src={image} />
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Box>
|
</Box>
|
||||||
@ -215,7 +203,6 @@ export const ImageCard = ({
|
|||||||
display: "flex",
|
display: "flex",
|
||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
cursor: "pointer",
|
cursor: "pointer",
|
||||||
height: '100%',
|
|
||||||
}}
|
}}
|
||||||
onClick={handleOpenFullscreen}
|
onClick={handleOpenFullscreen}
|
||||||
>
|
>
|
||||||
@ -252,9 +239,6 @@ export const ImageCard = ({
|
|||||||
position: "relative",
|
position: "relative",
|
||||||
width: "100%",
|
width: "100%",
|
||||||
height: "100%",
|
height: "100%",
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'column',
|
|
||||||
justifyContent: 'center',
|
|
||||||
backgroundColor: "#000",
|
backgroundColor: "#000",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
@ -6,30 +6,19 @@ import {
|
|||||||
DialogContent,
|
DialogContent,
|
||||||
DialogContentText,
|
DialogContentText,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
IconButton,
|
|
||||||
TextField,
|
TextField,
|
||||||
Typography,
|
Typography,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import React, { useContext, useEffect, useState } from "react";
|
import React, { useContext, useEffect, useState } from "react";
|
||||||
import { getBaseApiReact, MyContext } from "../../App";
|
import { MyContext } from "../../App";
|
||||||
import { Spacer } from "../../common/Spacer";
|
import { Spacer } from "../../common/Spacer";
|
||||||
import { executeEvent, subscribeToEvent, unsubscribeFromEvent } from "../../utils/events";
|
import { executeEvent } 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 CloseIcon from '@mui/icons-material/Close';
|
|
||||||
|
|
||||||
import InfoIcon from '@mui/icons-material/Info';
|
export const BlockedUsersModal = ({ close }) => {
|
||||||
export const BlockedUsersModal = () => {
|
|
||||||
const [isOpenBlockedModal, setIsOpenBlockedModal] = useRecoilState(isOpenBlockedModalAtom)
|
|
||||||
const [hasChanged, setHasChanged] = useState(false);
|
const [hasChanged, setHasChanged] = useState(false);
|
||||||
const [value, setValue] = useState("");
|
const [value, setValue] = useState("");
|
||||||
const [addressesWithNames, setAddressesWithNames] = useState({})
|
|
||||||
const { isShow, onCancel, onOk, show, message } = useModal();
|
const { getAllBlockedUsers, removeBlockFromList, addToBlockList } = useContext(MyContext);
|
||||||
const { getAllBlockedUsers, removeBlockFromList, addToBlockList, setOpenSnackGlobal, setInfoSnackCustom } =
|
|
||||||
useContext(MyContext);
|
|
||||||
const [blockedUsers, setBlockedUsers] = useState({
|
const [blockedUsers, setBlockedUsers] = useState({
|
||||||
addresses: {},
|
addresses: {},
|
||||||
names: {},
|
names: {},
|
||||||
@ -39,162 +28,60 @@ export const BlockedUsersModal = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if(!isOpenBlockedModal) return
|
|
||||||
fetchBlockedUsers();
|
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) {
|
|
||||||
setOpenSnackGlobal(true);
|
|
||||||
|
|
||||||
setInfoSnackCustom({
|
|
||||||
type: "error",
|
|
||||||
message: error?.message || "Unable to block user",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const blockUserFromOutsideModalFunc = (e) => {
|
|
||||||
const user = e.detail?.user;
|
|
||||||
setIsOpenBlockedModal(true)
|
|
||||||
blockUser(null, user)
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
subscribeToEvent("blockUserFromOutside", blockUserFromOutsideModalFunc);
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
unsubscribeFromEvent("blockUserFromOutside", blockUserFromOutsideModalFunc);
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
return (
|
return (
|
||||||
<Dialog
|
<Dialog
|
||||||
open={isOpenBlockedModal}
|
open={true}
|
||||||
aria-labelledby="alert-dialog-title"
|
aria-labelledby="alert-dialog-title"
|
||||||
aria-describedby="alert-dialog-description"
|
aria-describedby="alert-dialog-description"
|
||||||
>
|
>
|
||||||
<DialogTitle>Blocked Users</DialogTitle>
|
<DialogTitle>Blocked Users</DialogTitle>
|
||||||
<DialogContent
|
<DialogContent sx={{
|
||||||
sx={{
|
padding: '20px'
|
||||||
padding: "20px",
|
}}>
|
||||||
|
<Box
|
||||||
|
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
gap: "10px",
|
||||||
}}
|
}}
|
||||||
>
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
gap: "10px",
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<TextField
|
<TextField
|
||||||
placeholder="Name or address"
|
placeholder="Name"
|
||||||
value={value}
|
value={value}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
setValue(e.target.value);
|
setValue(e.target.value);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button variant="contained" onClick={async ()=> {
|
||||||
sx={{
|
try {
|
||||||
flexShrink: 0,
|
if(!value) return
|
||||||
}}
|
await addToBlockList(undefined, value)
|
||||||
variant="contained"
|
fetchBlockedUsers()
|
||||||
onClick={blockUser}
|
setHasChanged(true)
|
||||||
>
|
} catch (error) {
|
||||||
Block
|
console.error(error)
|
||||||
</Button>
|
}
|
||||||
</Box>
|
}}>Block</Button>
|
||||||
|
</Box>
|
||||||
|
|
||||||
{Object.entries(blockedUsers?.addresses).length > 0 && (
|
{Object.entries(blockedUsers?.addresses).length > 0 && (
|
||||||
<>
|
<>
|
||||||
<Spacer height="20px" />
|
<Spacer height="20px" />
|
||||||
<DialogContentText id="alert-dialog-description">
|
<DialogContentText id="alert-dialog-description">
|
||||||
Blocked addresses- blocks processing of txs
|
Blocked Users for Chat ( addresses )
|
||||||
</DialogContentText>
|
</DialogContentText>
|
||||||
<Spacer height="10px" />
|
<Spacer height="10px" />
|
||||||
<Button variant="contained" size="small" onClick={getNames}>Fetch names</Button>
|
|
||||||
<Spacer height="10px" />
|
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Box
|
<Box sx={{
|
||||||
sx={{
|
display: 'flex',
|
||||||
display: "flex",
|
flexDirection: 'column',
|
||||||
flexDirection: "column",
|
gap: '10px'
|
||||||
gap: "10px",
|
}}>
|
||||||
}}
|
|
||||||
>
|
|
||||||
{Object.entries(blockedUsers?.addresses || {})?.map(
|
{Object.entries(blockedUsers?.addresses || {})?.map(
|
||||||
([key, value]) => {
|
([key, value]) => {
|
||||||
return (
|
return (
|
||||||
@ -203,22 +90,18 @@ export const BlockedUsersModal = () => {
|
|||||||
display: "flex",
|
display: "flex",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
gap: "10px",
|
gap: "10px",
|
||||||
width: "100%",
|
width: '100%',
|
||||||
justifyContent: "space-between",
|
justifyContent: 'space-between'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography>{addressesWithNames[key] || key}</Typography>
|
<Typography>{key}</Typography>
|
||||||
<Button
|
<Button
|
||||||
sx={{
|
|
||||||
flexShrink: 0,
|
|
||||||
}}
|
|
||||||
size="small"
|
|
||||||
variant="contained"
|
variant="contained"
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
try {
|
try {
|
||||||
await removeBlockFromList(key, undefined);
|
await removeBlockFromList(key, undefined);
|
||||||
setHasChanged(true);
|
setHasChanged(true);
|
||||||
setValue("");
|
setValue('')
|
||||||
fetchBlockedUsers();
|
fetchBlockedUsers();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
@ -236,19 +119,17 @@ export const BlockedUsersModal = () => {
|
|||||||
<>
|
<>
|
||||||
<Spacer height="20px" />
|
<Spacer height="20px" />
|
||||||
<DialogContentText id="alert-dialog-description">
|
<DialogContentText id="alert-dialog-description">
|
||||||
Blocked names for QDN
|
Blocked Users for QDN and Chat (names)
|
||||||
</DialogContentText>
|
</DialogContentText>
|
||||||
<Spacer height="10px" />
|
<Spacer height="10px" />
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Box
|
<Box sx={{
|
||||||
sx={{
|
display: 'flex',
|
||||||
display: "flex",
|
flexDirection: 'column',
|
||||||
flexDirection: "column",
|
gap: '10px'
|
||||||
gap: "10px",
|
}}>
|
||||||
}}
|
|
||||||
>
|
|
||||||
{Object.entries(blockedUsers?.names || {})?.map(([key, value]) => {
|
{Object.entries(blockedUsers?.names || {})?.map(([key, value]) => {
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
@ -256,16 +137,12 @@ export const BlockedUsersModal = () => {
|
|||||||
display: "flex",
|
display: "flex",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
gap: "10px",
|
gap: "10px",
|
||||||
width: "100%",
|
width: '100%',
|
||||||
justifyContent: "space-between",
|
justifyContent: 'space-between'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography>{key}</Typography>
|
<Typography>{key}</Typography>
|
||||||
<Button
|
<Button
|
||||||
size="small"
|
|
||||||
sx={{
|
|
||||||
flexShrink: 0,
|
|
||||||
}}
|
|
||||||
variant="contained"
|
variant="contained"
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
try {
|
try {
|
||||||
@ -298,78 +175,16 @@ export const BlockedUsersModal = () => {
|
|||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
variant="contained"
|
variant="contained"
|
||||||
onClick={() => {
|
onClick={()=> {
|
||||||
if (hasChanged) {
|
if(hasChanged){
|
||||||
executeEvent("updateChatMessagesWithBlocks", true);
|
executeEvent('updateChatMessagesWithBlocks', true)
|
||||||
}
|
}
|
||||||
setIsOpenBlockedModal(false);
|
close()
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
close
|
close
|
||||||
</Button>
|
</Button>
|
||||||
</DialogActions>
|
</DialogActions>
|
||||||
|
|
||||||
<Dialog
|
|
||||||
open={isShow}
|
|
||||||
aria-labelledby="alert-dialog-title"
|
|
||||||
aria-describedby="alert-dialog-description"
|
|
||||||
>
|
|
||||||
<DialogTitle id="alert-dialog-title">
|
|
||||||
{"Decide what to block"}
|
|
||||||
</DialogTitle>
|
|
||||||
<IconButton
|
|
||||||
onClick={onCancel}
|
|
||||||
sx={{
|
|
||||||
position: 'absolute',
|
|
||||||
right: 8,
|
|
||||||
top: 8,
|
|
||||||
color: 'white',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<CloseIcon />
|
|
||||||
</IconButton>
|
|
||||||
<DialogContent>
|
|
||||||
<DialogContentText id="alert-dialog-description">
|
|
||||||
Blocking {message?.userName || message?.userAddress}
|
|
||||||
</DialogContentText>
|
|
||||||
<Box sx={{
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
gap: '10px',
|
|
||||||
marginTop: '20px'
|
|
||||||
}}>
|
|
||||||
<InfoIcon sx={{
|
|
||||||
color: 'fff'
|
|
||||||
}}/> <Typography>Choose "block txs" or "all" to block chat messages </Typography>
|
|
||||||
</Box>
|
|
||||||
</DialogContent>
|
|
||||||
<DialogActions>
|
|
||||||
<Button
|
|
||||||
variant="contained"
|
|
||||||
onClick={() => {
|
|
||||||
onOk("address");
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Block txs
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
variant="contained"
|
|
||||||
onClick={() => {
|
|
||||||
onOk("name");
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Block QDN data
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
variant="contained"
|
|
||||||
onClick={() => {
|
|
||||||
onOk("both");
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Block All
|
|
||||||
</Button>
|
|
||||||
</DialogActions>
|
|
||||||
</Dialog>
|
|
||||||
</Dialog>
|
</Dialog>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -19,8 +19,7 @@ import React, {
|
|||||||
useRef,
|
useRef,
|
||||||
useState,
|
useState,
|
||||||
} from "react";
|
} from "react";
|
||||||
import PersonOffIcon from '@mui/icons-material/PersonOff';
|
import BlockIcon from '@mui/icons-material/Block';
|
||||||
|
|
||||||
import { WalletsAppWrapper } from "./WalletsAppWrapper";
|
import { WalletsAppWrapper } from "./WalletsAppWrapper";
|
||||||
|
|
||||||
import SettingsIcon from "@mui/icons-material/Settings";
|
import SettingsIcon from "@mui/icons-material/Settings";
|
||||||
@ -67,7 +66,7 @@ import HomeIcon from "@mui/icons-material/Home";
|
|||||||
import CloseIcon from "@mui/icons-material/Close";
|
import CloseIcon from "@mui/icons-material/Close";
|
||||||
|
|
||||||
import { ThingsToDoInitial } from "./ThingsToDoInitial";
|
import { ThingsToDoInitial } from "./ThingsToDoInitial";
|
||||||
import { GroupJoinRequests, requestQueueGroupJoinRequests } from "./GroupJoinRequests";
|
import { GroupJoinRequests } from "./GroupJoinRequests";
|
||||||
import { GroupForum } from "../Chat/GroupForum";
|
import { GroupForum } from "../Chat/GroupForum";
|
||||||
import { GroupInvites } from "./GroupInvites";
|
import { GroupInvites } from "./GroupInvites";
|
||||||
import {
|
import {
|
||||||
@ -100,7 +99,7 @@ import { formatEmailDate } from "./QMailMessages";
|
|||||||
import { useHandleMobileNativeBack } from "../../hooks/useHandleMobileNativeBack";
|
import { useHandleMobileNativeBack } from "../../hooks/useHandleMobileNativeBack";
|
||||||
import { AdminSpace } from "../Chat/AdminSpace";
|
import { AdminSpace } from "../Chat/AdminSpace";
|
||||||
import { useRecoilState, useSetRecoilState } from "recoil";
|
import { useRecoilState, useSetRecoilState } from "recoil";
|
||||||
import { addressInfoControllerAtom, groupsPropertiesAtom, isOpenBlockedModalAtom, lastEnteredGroupIdAtom, myGroupsWhereIAmAdminAtom, selectedGroupIdAtom } from "../../atoms/global";
|
import { addressInfoControllerAtom, groupsPropertiesAtom, lastEnteredGroupIdAtom, selectedGroupIdAtom } from "../../atoms/global";
|
||||||
import { sortArrayByTimestampAndGroupName } from "../../utils/time";
|
import { sortArrayByTimestampAndGroupName } from "../../utils/time";
|
||||||
import { BlockedUsersModal } from "./BlockedUsersModal";
|
import { BlockedUsersModal } from "./BlockedUsersModal";
|
||||||
import { GlobalTouchMenu } from "../GlobalTouchMenu";
|
import { GlobalTouchMenu } from "../GlobalTouchMenu";
|
||||||
@ -330,17 +329,16 @@ export const getDataPublishesFunc = async (groupId, type) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export async function getNameInfo(address: string) {
|
export async function getNameInfo(address: string) {
|
||||||
const response = await fetch(`${getBaseApiReact()}/names/primary/` + address);
|
const response = await fetch(`${getBaseApiReact()}/names/address/` + address);
|
||||||
const nameData = await response.json();
|
const nameData = await response.json();
|
||||||
|
|
||||||
if (nameData?.name) {
|
if (nameData?.length > 0) {
|
||||||
return nameData?.name;
|
return nameData[0]?.name;
|
||||||
} else {
|
} else {
|
||||||
return '';
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export const getGroupAdmins = async (groupNumber: number) => {
|
export const getGroupAdmins = async (groupNumber: number) => {
|
||||||
// const validApi = await findUsableApi();
|
// const validApi = await findUsableApi();
|
||||||
|
|
||||||
@ -472,9 +470,6 @@ export const Group = ({
|
|||||||
const { setMemberGroups, memberGroups, rootHeight, isRunningPublicNode } = useContext(MyContext);
|
const { setMemberGroups, memberGroups, rootHeight, isRunningPublicNode } = useContext(MyContext);
|
||||||
const lastGroupNotification = useRef<null | number>(null);
|
const lastGroupNotification = useRef<null | number>(null);
|
||||||
const [timestampEnterData, setTimestampEnterData] = useState({});
|
const [timestampEnterData, setTimestampEnterData] = useState({});
|
||||||
const groupsPropertiesRef = useRef({});
|
|
||||||
const setMyGroupsWhereIAmAdmin = useSetRecoilState(myGroupsWhereIAmAdminAtom);
|
|
||||||
|
|
||||||
const [chatMode, setChatMode] = useState("groups");
|
const [chatMode, setChatMode] = useState("groups");
|
||||||
const [newChat, setNewChat] = useState(false);
|
const [newChat, setNewChat] = useState(false);
|
||||||
const [openSnack, setOpenSnack] = React.useState(false);
|
const [openSnack, setOpenSnack] = React.useState(false);
|
||||||
@ -488,8 +483,6 @@ export const Group = ({
|
|||||||
const [groupAnnouncements, setGroupAnnouncements] = React.useState({});
|
const [groupAnnouncements, setGroupAnnouncements] = React.useState({});
|
||||||
const [defaultThread, setDefaultThread] = React.useState(null);
|
const [defaultThread, setDefaultThread] = React.useState(null);
|
||||||
const [isOpenDrawer, setIsOpenDrawer] = React.useState(false);
|
const [isOpenDrawer, setIsOpenDrawer] = React.useState(false);
|
||||||
const setIsOpenBlockedUserModal = useSetRecoilState(isOpenBlockedModalAtom)
|
|
||||||
|
|
||||||
const [hideCommonKeyPopup, setHideCommonKeyPopup] = React.useState(false);
|
const [hideCommonKeyPopup, setHideCommonKeyPopup] = React.useState(false);
|
||||||
const [isLoadingGroupMessage, setIsLoadingGroupMessage] = React.useState("");
|
const [isLoadingGroupMessage, setIsLoadingGroupMessage] = React.useState("");
|
||||||
const [drawerMode, setDrawerMode] = React.useState("groups");
|
const [drawerMode, setDrawerMode] = React.useState("groups");
|
||||||
@ -514,6 +507,7 @@ export const Group = ({
|
|||||||
const [isForceShowCreationKeyPopup, setIsForceShowCreationKeyPopup] = useState(false)
|
const [isForceShowCreationKeyPopup, setIsForceShowCreationKeyPopup] = useState(false)
|
||||||
const [groupsProperties, setGroupsProperties] = useRecoilState(groupsPropertiesAtom)
|
const [groupsProperties, setGroupsProperties] = useRecoilState(groupsPropertiesAtom)
|
||||||
const setUserInfoForLevels = useSetRecoilState(addressInfoControllerAtom);
|
const setUserInfoForLevels = useSetRecoilState(addressInfoControllerAtom);
|
||||||
|
const [isOpenBlockedUserModal, setIsOpenBlockedUserModal] = React.useState(false);
|
||||||
const setLastEnteredGroupIdAtom = useSetRecoilState(lastEnteredGroupIdAtom)
|
const setLastEnteredGroupIdAtom = useSetRecoilState(lastEnteredGroupIdAtom)
|
||||||
const isPrivate = useMemo(()=> {
|
const isPrivate = useMemo(()=> {
|
||||||
if(selectedGroup?.groupId === '0') return false
|
if(selectedGroup?.groupId === '0') return false
|
||||||
@ -539,9 +533,6 @@ export const Group = ({
|
|||||||
useEffect(()=> {
|
useEffect(()=> {
|
||||||
timestampEnterDataRef.current = timestampEnterData
|
timestampEnterDataRef.current = timestampEnterData
|
||||||
}, [timestampEnterData])
|
}, [timestampEnterData])
|
||||||
useEffect(() => {
|
|
||||||
groupsPropertiesRef.current = groupsProperties;
|
|
||||||
}, [groupsProperties]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
isFocusedRef.current = isFocused;
|
isFocusedRef.current = isFocused;
|
||||||
@ -581,7 +572,7 @@ export const Group = ({
|
|||||||
|
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.log("error", error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -848,7 +839,7 @@ export const Group = ({
|
|||||||
const res = await fetch(
|
const res = await fetch(
|
||||||
`${getBaseApiReact()}/arbitrary/DOCUMENT_PRIVATE/${publish.name}/${
|
`${getBaseApiReact()}/arbitrary/DOCUMENT_PRIVATE/${publish.name}/${
|
||||||
publish.identifier
|
publish.identifier
|
||||||
}?encoding=base64&rebuild=true`
|
}?encoding=base64`
|
||||||
);
|
);
|
||||||
data = await res.text();
|
data = await res.text();
|
||||||
}
|
}
|
||||||
@ -994,50 +985,15 @@ export const Group = ({
|
|||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const getGroupsWhereIAmAMember = useCallback(async (groups) => {
|
|
||||||
try {
|
|
||||||
let groupsAsAdmin = [];
|
|
||||||
const getAllGroupsAsAdmin = groups
|
|
||||||
.filter((item) => item.groupId !== '0')
|
|
||||||
.map(async (group) => {
|
|
||||||
const isAdminResponse = await requestQueueGroupJoinRequests.enqueue(
|
|
||||||
() => {
|
|
||||||
return fetch(
|
|
||||||
`${getBaseApiReact()}/groups/members/${group.groupId}?limit=0&onlyAdmins=true`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
const isAdminData = await isAdminResponse.json();
|
|
||||||
|
|
||||||
const findMyself = isAdminData?.members?.find(
|
useEffect(()=> {
|
||||||
(member) => member.member === myAddress
|
if(!myAddress) return
|
||||||
);
|
if(areKeysEqual(groups?.map((grp)=> grp?.groupId), Object.keys(groupsProperties))){
|
||||||
|
} else {
|
||||||
if (findMyself) {
|
getGroupsProperties(myAddress)
|
||||||
groupsAsAdmin.push(group);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
await Promise.all(getAllGroupsAsAdmin);
|
|
||||||
setMyGroupsWhereIAmAdmin(groupsAsAdmin);
|
|
||||||
} catch (error) {
|
|
||||||
console.error();
|
|
||||||
}
|
}
|
||||||
}, []);
|
}, [groups, myAddress])
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!myAddress) return;
|
|
||||||
if (
|
|
||||||
!areKeysEqual(
|
|
||||||
groups?.map((grp) => grp?.groupId),
|
|
||||||
Object.keys(groupsPropertiesRef.current)
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
getGroupsProperties(myAddress);
|
|
||||||
getGroupsWhereIAmAMember(groups);
|
|
||||||
}
|
|
||||||
}, [groups, myAddress]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Handler function for incoming messages
|
// Handler function for incoming messages
|
||||||
@ -1949,7 +1905,6 @@ export const Group = ({
|
|||||||
width: "100%",
|
width: "100%",
|
||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
padding: "10px",
|
padding: "10px",
|
||||||
gap: '10px'
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<CustomButton
|
<CustomButton
|
||||||
@ -1967,23 +1922,6 @@ export const Group = ({
|
|||||||
/>
|
/>
|
||||||
New Chat
|
New Chat
|
||||||
</CustomButton>
|
</CustomButton>
|
||||||
{!isRunningPublicNode && (
|
|
||||||
<CustomButton
|
|
||||||
onClick={() => {
|
|
||||||
setIsOpenBlockedUserModal(true);
|
|
||||||
}}
|
|
||||||
sx={{
|
|
||||||
minWidth: 'unset',
|
|
||||||
padding: '10px',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<PersonOffIcon
|
|
||||||
sx={{
|
|
||||||
color: 'white',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</CustomButton>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@ -2221,7 +2159,7 @@ export const Group = ({
|
|||||||
padding: '10px'
|
padding: '10px'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<PersonOffIcon
|
<BlockIcon
|
||||||
sx={{
|
sx={{
|
||||||
color: "white",
|
color: "white",
|
||||||
}}
|
}}
|
||||||
@ -2718,9 +2656,11 @@ export const Group = ({
|
|||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
{isOpenBlockedUserModal && (
|
||||||
<BlockedUsersModal />
|
<BlockedUsersModal close={()=> {
|
||||||
|
setIsOpenBlockedUserModal(false)
|
||||||
|
}} />
|
||||||
|
)}
|
||||||
{selectedDirect && !newChat && (
|
{selectedDirect && !newChat && (
|
||||||
<>
|
<>
|
||||||
<Box
|
<Box
|
||||||
@ -2816,7 +2756,7 @@ export const Group = ({
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{isMobile && (
|
{isMobile && (
|
||||||
<Apps mode={appsMode} setMode={setAppsMode} show={mobileViewMode === "apps"} myName={userInfo?.name} myAddress={userInfo?.address} />
|
<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}
|
<AppsDesktop toggleSideViewGroups={toggleSideViewGroups} toggleSideViewDirects={toggleSideViewDirects} goToHome={goToHome} mode={appsMode} setMode={setAppsMode} setDesktopSideView={setDesktopSideView} hasUnreadDirects={directChatHasUnread} show={desktopViewMode === "apps"} myName={userInfo?.name} isGroups={isOpenSideViewGroups}
|
||||||
|
@ -17,7 +17,7 @@ import { CustomLoader } from "../../common/CustomLoader";
|
|||||||
import { getBaseApi } from "../../background";
|
import { getBaseApi } from "../../background";
|
||||||
import { MyContext, getBaseApiReact, isMobile } from "../../App";
|
import { MyContext, getBaseApiReact, isMobile } from "../../App";
|
||||||
import { myGroupsWhereIAmAdminAtom } from "../../atoms/global";
|
import { myGroupsWhereIAmAdminAtom } from "../../atoms/global";
|
||||||
import { useRecoilState, useSetRecoilState } from "recoil";
|
import { useSetRecoilState } from "recoil";
|
||||||
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
|
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
|
||||||
import ExpandLessIcon from '@mui/icons-material/ExpandLess';
|
import ExpandLessIcon from '@mui/icons-material/ExpandLess';
|
||||||
export const requestQueueGroupJoinRequests = new RequestQueueWithPromise(2)
|
export const requestQueueGroupJoinRequests = new RequestQueueWithPromise(2)
|
||||||
@ -28,44 +28,66 @@ export const GroupJoinRequests = ({ myAddress, groups, setOpenManageMembers, get
|
|||||||
const [groupsWithJoinRequests, setGroupsWithJoinRequests] = React.useState([])
|
const [groupsWithJoinRequests, setGroupsWithJoinRequests] = React.useState([])
|
||||||
const [loading, setLoading] = React.useState(true)
|
const [loading, setLoading] = React.useState(true)
|
||||||
const {txList, setTxList} = React.useContext(MyContext)
|
const {txList, setTxList} = React.useContext(MyContext)
|
||||||
const [myGroupsWhereIAmAdmin] = useRecoilState(myGroupsWhereIAmAdminAtom);
|
const setMyGroupsWhereIAmAdmin = useSetRecoilState(
|
||||||
|
myGroupsWhereIAmAdminAtom
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
const getJoinRequests = async ()=> {
|
||||||
const getJoinRequests = async () => {
|
|
||||||
try {
|
try {
|
||||||
setLoading(true);
|
setLoading(true)
|
||||||
const res = await Promise.all(
|
|
||||||
myGroupsWhereIAmAdmin.map(async (group) => {
|
|
||||||
const joinRequestResponse =
|
|
||||||
await requestQueueGroupJoinRequests.enqueue(() => {
|
|
||||||
return fetch(
|
|
||||||
`${getBaseApiReact()}/groups/joinrequests/${group.groupId}`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
const joinRequestData = await joinRequestResponse.json();
|
let groupsAsAdmin = []
|
||||||
return {
|
const getAllGroupsAsAdmin = groups.filter((item)=> item.groupId !== '0').map(async (group)=> {
|
||||||
group,
|
|
||||||
data: joinRequestData,
|
const isAdminResponse = await requestQueueGroupJoinRequests.enqueue(()=> {
|
||||||
};
|
return fetch(
|
||||||
|
`${getBaseApiReact()}/groups/members/${group.groupId}?limit=0&onlyAdmins=true`
|
||||||
|
);
|
||||||
})
|
})
|
||||||
);
|
const isAdminData = await isAdminResponse.json()
|
||||||
setGroupsWithJoinRequests(res);
|
|
||||||
|
|
||||||
|
const findMyself = isAdminData?.members?.find((member)=> member.member === myAddress)
|
||||||
|
|
||||||
|
if(findMyself){
|
||||||
|
groupsAsAdmin.push(group)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
await Promise.all(getAllGroupsAsAdmin)
|
||||||
|
setMyGroupsWhereIAmAdmin(groupsAsAdmin)
|
||||||
|
const res = await Promise.all(groupsAsAdmin.map(async (group)=> {
|
||||||
|
|
||||||
|
const joinRequestResponse = await requestQueueGroupJoinRequests.enqueue(()=> {
|
||||||
|
return fetch(
|
||||||
|
`${getBaseApiReact()}/groups/joinrequests/${group.groupId}`
|
||||||
|
);
|
||||||
|
})
|
||||||
|
|
||||||
|
const joinRequestData = await joinRequestResponse.json()
|
||||||
|
return {
|
||||||
|
group,
|
||||||
|
data: joinRequestData
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
setGroupsWithJoinRequests(res)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false)
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (myAddress && myGroupsWhereIAmAdmin.length > 0) {
|
if (myAddress && groups.length > 0) {
|
||||||
getJoinRequests();
|
getJoinRequests()
|
||||||
} else {
|
} else {
|
||||||
setLoading(false);
|
setLoading(false)
|
||||||
}
|
}
|
||||||
}, [myAddress, myGroupsWhereIAmAdmin]);
|
}, [myAddress, groups]);
|
||||||
|
|
||||||
const filteredJoinRequests = React.useMemo(()=> {
|
const filteredJoinRequests = React.useMemo(()=> {
|
||||||
return groupsWithJoinRequests.map((group)=> {
|
return groupsWithJoinRequests.map((group)=> {
|
||||||
|
@ -226,7 +226,6 @@ export const ListOfGroupPromotions = () => {
|
|||||||
data: data,
|
data: data,
|
||||||
identifier: identifier,
|
identifier: identifier,
|
||||||
service: "DOCUMENT",
|
service: "DOCUMENT",
|
||||||
uploadType: 'base64',
|
|
||||||
})
|
})
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
if (!response?.error) {
|
if (!response?.error) {
|
||||||
|
@ -54,19 +54,35 @@ export const NewUsersCTA = ({ balance }) => {
|
|||||||
textDecoration: "underline",
|
textDecoration: "underline",
|
||||||
}}
|
}}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
window.open("https://link.qortal.dev/support", '_system')
|
if (chrome && chrome.tabs) {
|
||||||
|
chrome.tabs.create({ url: "https://link.qortal.dev/telegram-invite" }, (tab) => {
|
||||||
|
if (chrome.runtime.lastError) {
|
||||||
|
console.error("Error opening tab:", chrome.runtime.lastError);
|
||||||
|
} else {
|
||||||
|
console.log("Tab opened successfully:", tab);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Nextcloud
|
Telegram
|
||||||
</ButtonBase>
|
</ButtonBase>
|
||||||
<ButtonBase
|
<ButtonBase
|
||||||
sx={{
|
sx={{
|
||||||
textDecoration: "underline",
|
textDecoration: "underline",
|
||||||
}}
|
}}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
window.open("https://link.qortal.dev/discord-invite", '_system')
|
if (chrome && chrome.tabs) {
|
||||||
|
chrome.tabs.create({ url: "https://link.qortal.dev/discord-invite" }, (tab) => {
|
||||||
|
if (chrome.runtime.lastError) {
|
||||||
|
console.error("Error opening tab:", chrome.runtime.lastError);
|
||||||
|
} else {
|
||||||
|
console.log("Tab opened successfully:", tab);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
|
|
||||||
>
|
>
|
||||||
Discord
|
Discord
|
||||||
</ButtonBase>
|
</ButtonBase>
|
||||||
|
@ -67,7 +67,6 @@ const [isLoading, setIsLoading] = useState(false)
|
|||||||
data: avatarBase64,
|
data: avatarBase64,
|
||||||
identifier: "qortal_avatar",
|
identifier: "qortal_avatar",
|
||||||
service: "THUMBNAIL",
|
service: "THUMBNAIL",
|
||||||
uploadType: 'base64',
|
|
||||||
})
|
})
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
if (!response?.error) {
|
if (!response?.error) {
|
||||||
|
@ -89,14 +89,14 @@ export const Minting = ({
|
|||||||
const getName = async (address) => {
|
const getName = async (address) => {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
`${getBaseApiReact()}/names/primary/${address}`
|
`${getBaseApiReact()}/names/address/${address}`
|
||||||
);
|
);
|
||||||
const nameData = await response.json();
|
const nameData = await response.json();
|
||||||
if (nameData?.name) {
|
if (nameData?.length > 0) {
|
||||||
setNames((prev) => {
|
setNames((prev) => {
|
||||||
return {
|
return {
|
||||||
...prev,
|
...prev,
|
||||||
[address]: nameData?.name,
|
[address]: nameData[0].name,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@ -108,7 +108,7 @@ export const Minting = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
// error
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -57,13 +57,12 @@ export const RegisterName = ({setOpenSnack, setInfoSnack, userInfo, show, setTxL
|
|||||||
try {
|
try {
|
||||||
const res = await fetch(`${getBaseApiReact()}/names/` + name);
|
const res = await fetch(`${getBaseApiReact()}/names/` + name);
|
||||||
const data = await res.json()
|
const data = await res.json()
|
||||||
if(data?.message === 'name unknown' || data?.error){
|
if(data?.message === 'name unknown'){
|
||||||
setIsNameAvailable(Availability.AVAILABLE)
|
setIsNameAvailable(Availability.AVAILABLE)
|
||||||
} else {
|
} else {
|
||||||
setIsNameAvailable(Availability.NOT_AVAILABLE)
|
setIsNameAvailable(Availability.NOT_AVAILABLE)
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setIsNameAvailable(Availability.AVAILABLE)
|
|
||||||
console.error(error)
|
console.error(error)
|
||||||
} finally {
|
} finally {
|
||||||
}
|
}
|
||||||
|
@ -155,7 +155,6 @@ export const Save = ({ isDesktop, disableWidth, myName }) => {
|
|||||||
data: encryptData,
|
data: encryptData,
|
||||||
identifier: "ext_saved_settings",
|
identifier: "ext_saved_settings",
|
||||||
service: "DOCUMENT_PRIVATE",
|
service: "DOCUMENT_PRIVATE",
|
||||||
uploadType: 'base64',
|
|
||||||
})
|
})
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
if (!response?.error) {
|
if (!response?.error) {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { useCallback, useEffect, useMemo, useState } from "react";
|
import React, { useCallback, useEffect, useState } from "react";
|
||||||
import { DrawerUserLookup } from "../Drawer/DrawerUserLookup";
|
import { DrawerUserLookup } from "../Drawer/DrawerUserLookup";
|
||||||
import {
|
import {
|
||||||
Avatar,
|
Avatar,
|
||||||
@ -16,7 +16,6 @@ import {
|
|||||||
Typography,
|
Typography,
|
||||||
Table,
|
Table,
|
||||||
CircularProgress,
|
CircularProgress,
|
||||||
Autocomplete,
|
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import { getAddressInfo, getNameOrAddress } from "../../background";
|
import { getAddressInfo, getNameOrAddress } from "../../background";
|
||||||
import { getBaseApiReact } from "../../App";
|
import { getBaseApiReact } from "../../App";
|
||||||
@ -27,8 +26,6 @@ import { formatTimestamp } from "../../utils/time";
|
|||||||
import CloseFullscreenIcon from '@mui/icons-material/CloseFullscreen';
|
import CloseFullscreenIcon from '@mui/icons-material/CloseFullscreen';
|
||||||
import SearchIcon from '@mui/icons-material/Search';
|
import SearchIcon from '@mui/icons-material/Search';
|
||||||
import { executeEvent, subscribeToEvent, unsubscribeFromEvent } from "../../utils/events";
|
import { executeEvent, subscribeToEvent, unsubscribeFromEvent } from "../../utils/events";
|
||||||
import { useNameSearch } from "../../hooks/useNameSearch";
|
|
||||||
import { validateAddress } from "../../utils/validateAddress";
|
|
||||||
|
|
||||||
function formatAddress(str) {
|
function formatAddress(str) {
|
||||||
if (str.length <= 12) return str;
|
if (str.length <= 12) return str;
|
||||||
@ -41,13 +38,6 @@ function formatAddress(str) {
|
|||||||
|
|
||||||
export const UserLookup = ({ isOpenDrawerLookup, setIsOpenDrawerLookup }) => {
|
export const UserLookup = ({ isOpenDrawerLookup, setIsOpenDrawerLookup }) => {
|
||||||
const [nameOrAddress, setNameOrAddress] = useState("");
|
const [nameOrAddress, setNameOrAddress] = useState("");
|
||||||
const [inputValue, setInputValue] = useState('');
|
|
||||||
const { results, isLoading } = useNameSearch(inputValue);
|
|
||||||
const options = useMemo(() => {
|
|
||||||
const isAddress = validateAddress(inputValue);
|
|
||||||
if (isAddress) return [inputValue];
|
|
||||||
return results?.map((item) => item.name);
|
|
||||||
}, [results, inputValue]);
|
|
||||||
const [errorMessage, setErrorMessage] = useState("");
|
const [errorMessage, setErrorMessage] = useState("");
|
||||||
const [addressInfo, setAddressInfo] = useState(null);
|
const [addressInfo, setAddressInfo] = useState(null);
|
||||||
const [isLoadingUser, setIsLoadingUser] = useState(false);
|
const [isLoadingUser, setIsLoadingUser] = useState(false);
|
||||||
@ -68,10 +58,7 @@ export const UserLookup = ({ isOpenDrawerLookup, setIsOpenDrawerLookup }) => {
|
|||||||
if (!addressInfoRes?.publicKey) {
|
if (!addressInfoRes?.publicKey) {
|
||||||
throw new Error("Address does not exist on blockchain");
|
throw new Error("Address does not exist on blockchain");
|
||||||
}
|
}
|
||||||
const isAddress = validateAddress(messageAddressOrName);
|
const name = await getNameInfo(owner);
|
||||||
const name = !isAddress
|
|
||||||
? messageAddressOrName
|
|
||||||
: await getNameInfo(owner);
|
|
||||||
const balanceRes = await fetch(
|
const balanceRes = await fetch(
|
||||||
`${getBaseApiReact()}/addresses/balance/${owner}`
|
`${getBaseApiReact()}/addresses/balance/${owner}`
|
||||||
);
|
);
|
||||||
@ -119,7 +106,6 @@ export const UserLookup = ({ isOpenDrawerLookup, setIsOpenDrawerLookup }) => {
|
|||||||
setIsOpenDrawerLookup(false)
|
setIsOpenDrawerLookup(false)
|
||||||
setNameOrAddress('')
|
setNameOrAddress('')
|
||||||
setErrorMessage('')
|
setErrorMessage('')
|
||||||
setInputValue('');
|
|
||||||
setPayments([])
|
setPayments([])
|
||||||
setIsLoadingUser(false)
|
setIsLoadingUser(false)
|
||||||
setIsLoadingPayments(false)
|
setIsLoadingPayments(false)
|
||||||
@ -148,66 +134,27 @@ export const UserLookup = ({ isOpenDrawerLookup, setIsOpenDrawerLookup }) => {
|
|||||||
flexShrink: 0,
|
flexShrink: 0,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Autocomplete
|
<TextField
|
||||||
|
autoFocus
|
||||||
value={nameOrAddress}
|
value={nameOrAddress}
|
||||||
onChange={(event: any, newValue: string | null) => {
|
onChange={(e) => setNameOrAddress(e.target.value)}
|
||||||
if (!newValue) {
|
|
||||||
setNameOrAddress('');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setNameOrAddress(newValue);
|
|
||||||
lookupFunc(newValue);
|
|
||||||
}}
|
|
||||||
inputValue={inputValue}
|
|
||||||
onInputChange={(event, newInputValue) => {
|
|
||||||
setInputValue(newInputValue);
|
|
||||||
}}
|
|
||||||
id="controllable-states-demo"
|
|
||||||
loading={isLoading}
|
|
||||||
options={options}
|
|
||||||
sx={{ width: 300 }}
|
|
||||||
size="small"
|
size="small"
|
||||||
renderInput={(params) => (
|
placeholder="Address or Name"
|
||||||
<TextField
|
autoComplete="off"
|
||||||
autoFocus
|
onKeyDown={(e) => {
|
||||||
autoComplete="off"
|
if (e.key === "Enter" && nameOrAddress) {
|
||||||
{...params}
|
lookupFunc();
|
||||||
label="Address or Name"
|
}
|
||||||
onKeyDown={(e) => {
|
}}
|
||||||
if (e.key === 'Enter' && inputValue) {
|
|
||||||
lookupFunc(inputValue);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
|
|
||||||
sx={{
|
|
||||||
'& .MuiOutlinedInput-root': {
|
|
||||||
'& fieldset': {
|
|
||||||
borderColor: 'white',
|
|
||||||
},
|
|
||||||
'&:hover fieldset': {
|
|
||||||
borderColor: 'white',
|
|
||||||
},
|
|
||||||
'&.Mui-focused fieldset': {
|
|
||||||
borderColor: 'white',
|
|
||||||
},
|
|
||||||
'& input': {
|
|
||||||
color: 'white',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
'& .MuiInputLabel-root': {
|
|
||||||
color: 'white',
|
|
||||||
},
|
|
||||||
'& .MuiInputLabel-root.Mui-focused': {
|
|
||||||
color: 'white',
|
|
||||||
},
|
|
||||||
'& .MuiAutocomplete-endAdornment svg': {
|
|
||||||
color: 'white',
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
/>
|
||||||
|
<ButtonBase onClick={()=> {
|
||||||
|
lookupFunc();
|
||||||
|
}} >
|
||||||
|
<SearchIcon sx={{
|
||||||
|
color: 'white',
|
||||||
|
marginRight: '20px'
|
||||||
|
}} />
|
||||||
|
</ButtonBase>
|
||||||
<ButtonBase sx={{
|
<ButtonBase sx={{
|
||||||
marginLeft: 'auto',
|
marginLeft: 'auto',
|
||||||
|
|
||||||
|
@ -167,9 +167,12 @@ useEffect(()=> {
|
|||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
try {
|
try {
|
||||||
setIsLoading(true)
|
setIsLoading(true)
|
||||||
executeEvent("blockUserFromOutside", {
|
if(isAlreadyBlocked === true){
|
||||||
user: address
|
await removeBlockFromList(address, name)
|
||||||
})
|
} else if(isAlreadyBlocked === false) {
|
||||||
|
await addToBlockList(address, name)
|
||||||
|
}
|
||||||
|
executeEvent('updateChatMessagesWithBlocks', true)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error)
|
console.error(error)
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -173,6 +173,3 @@ const STATIC_BCRYPT_SALT = `$${BCRYPT_VERSION}$${BCRYPT_ROUNDS}$IxVE941tXVUD4cW0
|
|||||||
const KDF_THREADS = 16
|
const KDF_THREADS = 16
|
||||||
|
|
||||||
export { TX_TYPES, ERROR_CODES, QORT_DECIMALS, PROXY_URL, STATIC_SALT, ADDRESS_VERSION, KDF_THREADS, STATIC_BCRYPT_SALT, CHAT_REFERENCE_FEATURE_TRIGGER_TIMESTAMP, DYNAMIC_FEE_TIMESTAMP }
|
export { TX_TYPES, ERROR_CODES, QORT_DECIMALS, PROXY_URL, STATIC_SALT, ADDRESS_VERSION, KDF_THREADS, STATIC_BCRYPT_SALT, CHAT_REFERENCE_FEATURE_TRIGGER_TIMESTAMP, DYNAMIC_FEE_TIMESTAMP }
|
||||||
|
|
||||||
export const MAX_SIZE_PUBLIC_NODE = 500 * 1024 * 1024; // 500mb
|
|
||||||
export const MAX_SIZE_PUBLISH = 2000 * 1024 * 1024; // 2GB
|
|
@ -1,55 +0,0 @@
|
|||||||
import { useCallback, useEffect, useState } from 'react';
|
|
||||||
import { getBaseApiReact } from '../App';
|
|
||||||
|
|
||||||
interface NameListItem {
|
|
||||||
name: string;
|
|
||||||
address: string;
|
|
||||||
}
|
|
||||||
export const useNameSearch = (value: string, limit = 20) => {
|
|
||||||
const [nameList, setNameList] = useState<NameListItem[]>([]);
|
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
|
||||||
const checkIfNameExisits = useCallback(
|
|
||||||
async (name: string, listLimit: number) => {
|
|
||||||
try {
|
|
||||||
if (!name) {
|
|
||||||
setNameList([]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const res = await fetch(
|
|
||||||
`${getBaseApiReact()}/names/search?query=${name}&prefix=true&limit=${listLimit}`
|
|
||||||
);
|
|
||||||
const data = await res.json();
|
|
||||||
setNameList(
|
|
||||||
data?.map((item: any) => {
|
|
||||||
return {
|
|
||||||
name: item.name,
|
|
||||||
address: item.owner,
|
|
||||||
};
|
|
||||||
})
|
|
||||||
);
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
} finally {
|
|
||||||
setIsLoading(false);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[]
|
|
||||||
);
|
|
||||||
// Debounce logic
|
|
||||||
useEffect(() => {
|
|
||||||
setIsLoading(true);
|
|
||||||
const handler = setTimeout(() => {
|
|
||||||
checkIfNameExisits(value, limit);
|
|
||||||
}, 500);
|
|
||||||
|
|
||||||
// Cleanup timeout if searchValue changes before the timeout completes
|
|
||||||
return () => {
|
|
||||||
clearTimeout(handler);
|
|
||||||
};
|
|
||||||
}, [value, limit, checkIfNameExisits]);
|
|
||||||
return {
|
|
||||||
isLoading,
|
|
||||||
results: nameList,
|
|
||||||
};
|
|
||||||
};
|
|
@ -1,11 +0,0 @@
|
|||||||
import { useMemo } from 'react';
|
|
||||||
|
|
||||||
export function useSortedMyNames(names, myName) {
|
|
||||||
return useMemo(() => {
|
|
||||||
return [...names].sort((a, b) => {
|
|
||||||
if (a === myName) return -1;
|
|
||||||
if (b === myName) return 1;
|
|
||||||
return 0;
|
|
||||||
});
|
|
||||||
}, [names, myName]);
|
|
||||||
}
|
|
@ -24,7 +24,7 @@ window.addEventListener("message", (event) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
export const sendMessageBackground = (action, data = {}, timeout = 600000, isExtension, appInfo, skipAuth) => {
|
export const sendMessageBackground = (action, data = {}, timeout = 180000, isExtension, appInfo, skipAuth) => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const requestId = generateRequestId(); // Unique ID for each request
|
const requestId = generateRequestId(); // Unique ID for each request
|
||||||
callbackMap.set(requestId, { resolve, reject }); // Store both resolve and reject callbacks
|
callbackMap.set(requestId, { resolve, reject }); // Store both resolve and reject callbacks
|
||||||
|
@ -1,474 +1,265 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
|
|
||||||
import { Buffer } from 'buffer';
|
import { Buffer } from "buffer"
|
||||||
import Base58 from '../../deps/Base58';
|
import Base58 from "../../deps/Base58"
|
||||||
import nacl from '../../deps/nacl-fast';
|
import nacl from "../../deps/nacl-fast"
|
||||||
import utils from '../../utils/utils';
|
import utils from "../../utils/utils"
|
||||||
import { createEndpoint, getBaseApi } from '../../background';
|
import { createEndpoint, getBaseApi } from "../../background";
|
||||||
import { getData } from '../../utils/chromeStorage';
|
import { getData } from "../../utils/chromeStorage";
|
||||||
import { executeEvent } from '../../utils/events';
|
|
||||||
|
|
||||||
export async function reusableGet(endpoint) {
|
export async function reusableGet(endpoint){
|
||||||
const validApi = await getBaseApi();
|
const validApi = await getBaseApi();
|
||||||
|
|
||||||
const response = await fetch(validApi + endpoint);
|
const response = await fetch(validApi + endpoint);
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
return data;
|
return data
|
||||||
}
|
|
||||||
|
|
||||||
async function reusablePost(endpoint, _body) {
|
|
||||||
// const validApi = await findUsableApi();
|
|
||||||
const url = await createEndpoint(endpoint);
|
|
||||||
const response = await fetch(url, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
body: _body,
|
|
||||||
});
|
|
||||||
if (!response.ok) {
|
|
||||||
const errorText = await response.text();
|
|
||||||
throw new Error(errorText);
|
|
||||||
}
|
}
|
||||||
let data;
|
|
||||||
|
async function reusablePost(endpoint, _body){
|
||||||
|
// const validApi = await findUsableApi();
|
||||||
|
const url = await createEndpoint(endpoint)
|
||||||
|
const response = await fetch(url, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: _body
|
||||||
|
});
|
||||||
|
let data
|
||||||
try {
|
try {
|
||||||
data = await response.clone().json();
|
data = await response.clone().json()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
data = await response.text();
|
data = await response.text()
|
||||||
}
|
}
|
||||||
return data;
|
return data
|
||||||
}
|
|
||||||
|
|
||||||
async function reusablePostStream(endpoint, _body) {
|
|
||||||
const url = await createEndpoint(endpoint);
|
|
||||||
|
|
||||||
const headers = {};
|
|
||||||
|
|
||||||
const response = await fetch(url, {
|
|
||||||
method: 'POST',
|
|
||||||
headers,
|
|
||||||
body: _body,
|
|
||||||
});
|
|
||||||
|
|
||||||
return response; // return the actual response so calling code can use response.ok
|
|
||||||
}
|
|
||||||
|
|
||||||
async function uploadChunkWithRetry(endpoint, formData, index, maxRetries = 3) {
|
|
||||||
let attempt = 0;
|
|
||||||
while (attempt < maxRetries) {
|
|
||||||
try {
|
|
||||||
const response = await reusablePostStream(endpoint, formData);
|
|
||||||
if (!response.ok) {
|
|
||||||
const errorText = await response.text();
|
|
||||||
throw new Error(errorText);
|
|
||||||
}
|
|
||||||
return; // Success
|
|
||||||
} catch (err) {
|
|
||||||
attempt++;
|
|
||||||
console.warn(
|
|
||||||
`Chunk ${index} failed (attempt ${attempt}): ${err.message}`
|
|
||||||
);
|
|
||||||
if (attempt >= maxRetries) {
|
|
||||||
throw new Error(`Chunk ${index} failed after ${maxRetries} attempts`);
|
|
||||||
}
|
|
||||||
// Wait 10 seconds before next retry
|
|
||||||
await new Promise((res) => setTimeout(res, 25_000));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
async function resuablePostRetry(
|
|
||||||
endpoint,
|
|
||||||
body,
|
|
||||||
maxRetries = 3,
|
|
||||||
appInfo,
|
|
||||||
resourceInfo
|
|
||||||
) {
|
|
||||||
let attempt = 0;
|
|
||||||
while (attempt < maxRetries) {
|
|
||||||
try {
|
|
||||||
const response = await reusablePost(endpoint, body);
|
|
||||||
|
|
||||||
return response;
|
|
||||||
} catch (err) {
|
|
||||||
attempt++;
|
|
||||||
if (attempt >= maxRetries) {
|
|
||||||
throw new Error(
|
|
||||||
err instanceof Error
|
|
||||||
? err?.message || `Failed to make request`
|
|
||||||
: `Failed to make request`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (appInfo?.tabId && resourceInfo) {
|
|
||||||
executeEvent('receiveChunks', {
|
|
||||||
tabId: appInfo.tabId,
|
|
||||||
publishLocation: {
|
|
||||||
name: resourceInfo?.name,
|
|
||||||
identifier: resourceInfo?.identifier,
|
|
||||||
service: resourceInfo?.service,
|
|
||||||
},
|
|
||||||
retry: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// Wait 10 seconds before next retry
|
|
||||||
await new Promise((res) => setTimeout(res, 25_000));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getKeyPair() {
|
async function getKeyPair() {
|
||||||
const res = await getData<any>('keyPair').catch(() => null);
|
const res = await getData<any>("keyPair").catch(() => null);
|
||||||
if (res) {
|
if (res) {
|
||||||
return res;
|
return res
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Wallet not authenticated');
|
throw new Error("Wallet not authenticated");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export const publishData = async ({
|
export const publishData = async ({
|
||||||
registeredName,
|
registeredName,
|
||||||
data,
|
file,
|
||||||
service,
|
service,
|
||||||
identifier,
|
identifier,
|
||||||
uploadType,
|
uploadType,
|
||||||
filename,
|
isBase64,
|
||||||
withFee,
|
filename,
|
||||||
title,
|
withFee,
|
||||||
description,
|
title,
|
||||||
category,
|
description,
|
||||||
tag1,
|
category,
|
||||||
tag2,
|
tag1,
|
||||||
tag3,
|
tag2,
|
||||||
tag4,
|
tag3,
|
||||||
tag5,
|
tag4,
|
||||||
feeAmount,
|
tag5,
|
||||||
appInfo
|
feeAmount
|
||||||
}: any) => {
|
}: any) => {
|
||||||
const validateName = async (receiverName: string) => {
|
|
||||||
return await reusableGet(`/names/${receiverName}`);
|
|
||||||
};
|
|
||||||
|
|
||||||
const convertBytesForSigning = async (transactionBytesBase58: string) => {
|
const validateName = async (receiverName: string) => {
|
||||||
return await resuablePostRetry(
|
return await reusableGet(`/names/${receiverName}`)
|
||||||
'/transactions/convert',
|
}
|
||||||
transactionBytesBase58,
|
|
||||||
3,
|
|
||||||
appInfo,
|
|
||||||
{ identifier, name: registeredName, service }
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const getArbitraryFee = async () => {
|
const convertBytesForSigning = async (transactionBytesBase58: string) => {
|
||||||
const timestamp = Date.now();
|
return await reusablePost('/transactions/convert', transactionBytesBase58)
|
||||||
|
}
|
||||||
|
|
||||||
let fee = await reusableGet(
|
const getArbitraryFee = async () => {
|
||||||
`/transactions/unitfee?txType=ARBITRARY×tamp=${timestamp}`
|
const timestamp = Date.now()
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
let fee = await reusableGet(`/transactions/unitfee?txType=ARBITRARY×tamp=${timestamp}`)
|
||||||
timestamp,
|
|
||||||
fee: Number(fee),
|
|
||||||
feeToShow: (Number(fee) / 1e8).toFixed(8),
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const signArbitraryWithFee = (
|
return {
|
||||||
arbitraryBytesBase58,
|
timestamp,
|
||||||
arbitraryBytesForSigningBase58,
|
fee: Number(fee),
|
||||||
keyPair
|
feeToShow: (Number(fee) / 1e8).toFixed(8)
|
||||||
) => {
|
}
|
||||||
if (!arbitraryBytesBase58) {
|
}
|
||||||
throw new Error('ArbitraryBytesBase58 not defined');
|
|
||||||
|
const signArbitraryWithFee = (arbitraryBytesBase58, arbitraryBytesForSigningBase58, keyPair) => {
|
||||||
|
if (!arbitraryBytesBase58) {
|
||||||
|
throw new Error('ArbitraryBytesBase58 not defined')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!keyPair) {
|
||||||
|
throw new Error('keyPair not defined')
|
||||||
|
}
|
||||||
|
|
||||||
|
const arbitraryBytes = Base58.decode(arbitraryBytesBase58)
|
||||||
|
const _arbitraryBytesBuffer = Object.keys(arbitraryBytes).map(function (key) { return arbitraryBytes[key]; })
|
||||||
|
const arbitraryBytesBuffer = new Uint8Array(_arbitraryBytesBuffer)
|
||||||
|
const arbitraryBytesForSigning = Base58.decode(arbitraryBytesForSigningBase58)
|
||||||
|
const _arbitraryBytesForSigningBuffer = Object.keys(arbitraryBytesForSigning).map(function (key) { return arbitraryBytesForSigning[key]; })
|
||||||
|
const arbitraryBytesForSigningBuffer = new Uint8Array(_arbitraryBytesForSigningBuffer)
|
||||||
|
const signature = nacl.sign.detached(arbitraryBytesForSigningBuffer, keyPair.privateKey)
|
||||||
|
|
||||||
|
return utils.appendBuffer(arbitraryBytesBuffer, signature)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!keyPair) {
|
const processTransactionVersion2 = async (bytes) => {
|
||||||
throw new Error('keyPair not defined');
|
|
||||||
}
|
|
||||||
|
|
||||||
const arbitraryBytes = Base58.decode(arbitraryBytesBase58);
|
return await reusablePost('/transactions/process?apiVersion=2', Base58.encode(bytes))
|
||||||
const _arbitraryBytesBuffer = Object.keys(arbitraryBytes).map(
|
}
|
||||||
function (key) {
|
|
||||||
return arbitraryBytes[key];
|
|
||||||
}
|
|
||||||
);
|
|
||||||
const arbitraryBytesBuffer = new Uint8Array(_arbitraryBytesBuffer);
|
|
||||||
const arbitraryBytesForSigning = Base58.decode(
|
|
||||||
arbitraryBytesForSigningBase58
|
|
||||||
);
|
|
||||||
const _arbitraryBytesForSigningBuffer = Object.keys(
|
|
||||||
arbitraryBytesForSigning
|
|
||||||
).map(function (key) {
|
|
||||||
return arbitraryBytesForSigning[key];
|
|
||||||
});
|
|
||||||
const arbitraryBytesForSigningBuffer = new Uint8Array(
|
|
||||||
_arbitraryBytesForSigningBuffer
|
|
||||||
);
|
|
||||||
const signature = nacl.sign.detached(
|
|
||||||
arbitraryBytesForSigningBuffer,
|
|
||||||
keyPair.privateKey
|
|
||||||
);
|
|
||||||
|
|
||||||
return utils.appendBuffer(arbitraryBytesBuffer, signature);
|
const signAndProcessWithFee = async (transactionBytesBase58: string) => {
|
||||||
};
|
let convertedBytesBase58 = await convertBytesForSigning(
|
||||||
|
transactionBytesBase58
|
||||||
|
)
|
||||||
|
|
||||||
const processTransactionVersion2 = async (bytes) => {
|
if (convertedBytesBase58.error) {
|
||||||
return await resuablePostRetry(
|
throw new Error('Error when signing')
|
||||||
'/transactions/process?apiVersion=2',
|
}
|
||||||
Base58.encode(bytes),
|
|
||||||
3,
|
|
||||||
appInfo,
|
|
||||||
{ identifier, name: registeredName, service }
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const signAndProcessWithFee = async (transactionBytesBase58: string) => {
|
|
||||||
let convertedBytesBase58 = await convertBytesForSigning(
|
|
||||||
transactionBytesBase58
|
|
||||||
);
|
|
||||||
|
|
||||||
if (convertedBytesBase58.error) {
|
const resKeyPair = await getKeyPair()
|
||||||
throw new Error('Error when signing');
|
const parsedData = resKeyPair
|
||||||
}
|
const uint8PrivateKey = Base58.decode(parsedData.privateKey);
|
||||||
|
const uint8PublicKey = Base58.decode(parsedData.publicKey);
|
||||||
|
const keyPair = {
|
||||||
|
privateKey: uint8PrivateKey,
|
||||||
|
publicKey: uint8PublicKey
|
||||||
|
};
|
||||||
|
|
||||||
const resKeyPair = await getKeyPair();
|
let signedArbitraryBytes = signArbitraryWithFee(transactionBytesBase58, convertedBytesBase58, keyPair)
|
||||||
const parsedData = resKeyPair;
|
const response = await processTransactionVersion2(signedArbitraryBytes)
|
||||||
const uint8PrivateKey = Base58.decode(parsedData.privateKey);
|
|
||||||
const uint8PublicKey = Base58.decode(parsedData.publicKey);
|
|
||||||
const keyPair = {
|
|
||||||
privateKey: uint8PrivateKey,
|
|
||||||
publicKey: uint8PublicKey,
|
|
||||||
};
|
|
||||||
|
|
||||||
let signedArbitraryBytes = signArbitraryWithFee(
|
let myResponse = { error: '' }
|
||||||
transactionBytesBase58,
|
|
||||||
convertedBytesBase58,
|
|
||||||
keyPair
|
|
||||||
);
|
|
||||||
const response = await processTransactionVersion2(signedArbitraryBytes);
|
|
||||||
|
|
||||||
let myResponse = { error: '' };
|
if (response === false) {
|
||||||
|
throw new Error('Error when signing')
|
||||||
|
} else {
|
||||||
|
myResponse = response
|
||||||
|
}
|
||||||
|
|
||||||
if (response === false) {
|
return myResponse
|
||||||
throw new Error('Error when signing');
|
}
|
||||||
} else {
|
|
||||||
myResponse = response;
|
|
||||||
}
|
|
||||||
if (appInfo?.tabId) {
|
|
||||||
executeEvent('receiveChunks', {
|
|
||||||
tabId: appInfo.tabId,
|
|
||||||
publishLocation: {
|
|
||||||
name: registeredName,
|
|
||||||
identifier,
|
|
||||||
service,
|
|
||||||
},
|
|
||||||
processed: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return myResponse;
|
|
||||||
};
|
|
||||||
|
|
||||||
const validate = async () => {
|
const validate = async () => {
|
||||||
let validNameRes = await validateName(registeredName);
|
let validNameRes = await validateName(registeredName)
|
||||||
|
|
||||||
if (validNameRes.error) {
|
if (validNameRes.error) {
|
||||||
throw new Error('Name not found');
|
throw new Error('Name not found')
|
||||||
}
|
}
|
||||||
|
|
||||||
let fee = null;
|
let fee = null
|
||||||
|
|
||||||
if (withFee && feeAmount) {
|
if (withFee && feeAmount) {
|
||||||
fee = feeAmount;
|
fee = feeAmount
|
||||||
} else if (withFee) {
|
} else if (withFee) {
|
||||||
const res = await getArbitraryFee();
|
const res = await getArbitraryFee()
|
||||||
if (res.fee) {
|
if (res.fee) {
|
||||||
fee = res.fee;
|
fee = res.fee
|
||||||
} else {
|
} else {
|
||||||
throw new Error('unable to get fee');
|
throw new Error('unable to get fee')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let transactionBytes = await uploadData(registeredName, data, fee);
|
let transactionBytes = await uploadData(registeredName, file, fee)
|
||||||
if (!transactionBytes || transactionBytes.error) {
|
if (!transactionBytes || transactionBytes.error) {
|
||||||
throw new Error(transactionBytes?.message || 'Error when uploading');
|
throw new Error(transactionBytes?.message || 'Error when uploading')
|
||||||
} else if (transactionBytes.includes('Error 500 Internal Server Error')) {
|
} else if (transactionBytes.includes('Error 500 Internal Server Error')) {
|
||||||
throw new Error('Error when uploading');
|
throw new Error('Error when uploading')
|
||||||
}
|
}
|
||||||
|
|
||||||
let signAndProcessRes;
|
let signAndProcessRes
|
||||||
|
|
||||||
if (withFee) {
|
if (withFee) {
|
||||||
signAndProcessRes = await signAndProcessWithFee(transactionBytes);
|
signAndProcessRes = await signAndProcessWithFee(transactionBytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (signAndProcessRes?.error) {
|
if (signAndProcessRes?.error) {
|
||||||
throw new Error('Error when signing');
|
throw new Error('Error when signing')
|
||||||
}
|
}
|
||||||
|
|
||||||
return signAndProcessRes;
|
return signAndProcessRes
|
||||||
};
|
}
|
||||||
|
|
||||||
const uploadData = async (registeredName: string, data: any, fee: number) => {
|
const uploadData = async (registeredName: string, file:any, fee: number) => {
|
||||||
let postBody = '';
|
|
||||||
let urlSuffix = '';
|
|
||||||
|
|
||||||
if (data != null) {
|
let postBody = ''
|
||||||
if (uploadType === 'base64') {
|
let urlSuffix = ''
|
||||||
urlSuffix = '/base64';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (uploadType === 'base64') {
|
if (file != null) {
|
||||||
postBody = data;
|
// If we're sending zipped data, make sure to use the /zip version of the POST /arbitrary/* API
|
||||||
}
|
if (uploadType === 'zip') {
|
||||||
} else {
|
urlSuffix = '/zip'
|
||||||
throw new Error('No data provided');
|
}
|
||||||
}
|
|
||||||
|
|
||||||
let uploadDataUrl = `/arbitrary/${service}/${registeredName}`;
|
// If we're sending file data, use the /base64 version of the POST /arbitrary/* API
|
||||||
let paramQueries = '';
|
else if (uploadType === 'file') {
|
||||||
if (identifier?.trim().length > 0) {
|
urlSuffix = '/base64'
|
||||||
uploadDataUrl = `/arbitrary/${service}/${registeredName}/${identifier}`;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
paramQueries = paramQueries + `?fee=${fee}`;
|
// Base64 encode the file to work around compatibility issues between javascript and java byte arrays
|
||||||
|
if (isBase64) {
|
||||||
|
postBody = file
|
||||||
|
}
|
||||||
|
|
||||||
if (filename != null && filename != 'undefined') {
|
if (!isBase64) {
|
||||||
paramQueries = paramQueries + '&filename=' + encodeURIComponent(filename);
|
let fileBuffer = new Uint8Array(await file.arrayBuffer())
|
||||||
}
|
postBody = Buffer.from(fileBuffer).toString("base64")
|
||||||
|
}
|
||||||
|
|
||||||
if (title != null && title != 'undefined') {
|
}
|
||||||
paramQueries = paramQueries + '&title=' + encodeURIComponent(title);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (description != null && description != 'undefined') {
|
let uploadDataUrl = `/arbitrary/${service}/${registeredName}${urlSuffix}`
|
||||||
paramQueries =
|
if (identifier?.trim().length > 0) {
|
||||||
paramQueries + '&description=' + encodeURIComponent(description);
|
uploadDataUrl = `/arbitrary/${service}/${registeredName}/${identifier}${urlSuffix}`
|
||||||
}
|
}
|
||||||
|
|
||||||
if (category != null && category != 'undefined') {
|
uploadDataUrl = uploadDataUrl + `?fee=${fee}`
|
||||||
paramQueries = paramQueries + '&category=' + encodeURIComponent(category);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tag1 != null && tag1 != 'undefined') {
|
|
||||||
paramQueries = paramQueries + '&tags=' + encodeURIComponent(tag1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tag2 != null && tag2 != 'undefined') {
|
if (filename != null && filename != 'undefined') {
|
||||||
paramQueries = paramQueries + '&tags=' + encodeURIComponent(tag2);
|
uploadDataUrl = uploadDataUrl + '&filename=' + encodeURIComponent(filename)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tag3 != null && tag3 != 'undefined') {
|
if (title != null && title != 'undefined') {
|
||||||
paramQueries = paramQueries + '&tags=' + encodeURIComponent(tag3);
|
uploadDataUrl = uploadDataUrl + '&title=' + encodeURIComponent(title)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tag4 != null && tag4 != 'undefined') {
|
if (description != null && description != 'undefined') {
|
||||||
paramQueries = paramQueries + '&tags=' + encodeURIComponent(tag4);
|
uploadDataUrl = uploadDataUrl + '&description=' + encodeURIComponent(description)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tag5 != null && tag5 != 'undefined') {
|
if (category != null && category != 'undefined') {
|
||||||
paramQueries = paramQueries + '&tags=' + encodeURIComponent(tag5);
|
uploadDataUrl = uploadDataUrl + '&category=' + encodeURIComponent(category)
|
||||||
}
|
}
|
||||||
if (uploadType === 'zip') {
|
|
||||||
paramQueries = paramQueries + '&isZip=' + true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (uploadType === 'base64') {
|
if (tag1 != null && tag1 != 'undefined') {
|
||||||
if (urlSuffix) {
|
uploadDataUrl = uploadDataUrl + '&tags=' + encodeURIComponent(tag1)
|
||||||
uploadDataUrl = uploadDataUrl + urlSuffix;
|
}
|
||||||
}
|
|
||||||
uploadDataUrl = uploadDataUrl + paramQueries;
|
|
||||||
if (appInfo?.tabId) {
|
|
||||||
executeEvent('receiveChunks', {
|
|
||||||
tabId: appInfo.tabId,
|
|
||||||
publishLocation: {
|
|
||||||
name: registeredName,
|
|
||||||
identifier,
|
|
||||||
service,
|
|
||||||
},
|
|
||||||
chunksSubmitted: 1,
|
|
||||||
totalChunks: 1,
|
|
||||||
processed: false,
|
|
||||||
filename: filename || title || `${service}-${identifier || ''}`,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return await resuablePostRetry(uploadDataUrl, postBody, 3, appInfo, {
|
|
||||||
identifier,
|
|
||||||
name: registeredName,
|
|
||||||
service,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const file = data;
|
if (tag2 != null && tag2 != 'undefined') {
|
||||||
const urlCheck = `/arbitrary/check/tmp?totalSize=${file.size}`;
|
uploadDataUrl = uploadDataUrl + '&tags=' + encodeURIComponent(tag2)
|
||||||
|
}
|
||||||
|
|
||||||
const checkEndpoint = await createEndpoint(urlCheck);
|
if (tag3 != null && tag3 != 'undefined') {
|
||||||
const checkRes = await fetch(checkEndpoint);
|
uploadDataUrl = uploadDataUrl + '&tags=' + encodeURIComponent(tag3)
|
||||||
if (!checkRes.ok) {
|
}
|
||||||
throw new Error('Not enough space on your hard drive');
|
|
||||||
}
|
|
||||||
|
|
||||||
const chunkUrl = uploadDataUrl + `/chunk`;
|
if (tag4 != null && tag4 != 'undefined') {
|
||||||
const chunkSize = 5 * 1024 * 1024; // 5MB
|
uploadDataUrl = uploadDataUrl + '&tags=' + encodeURIComponent(tag4)
|
||||||
|
}
|
||||||
|
|
||||||
const totalChunks = Math.ceil(file.size / chunkSize);
|
if (tag5 != null && tag5 != 'undefined') {
|
||||||
if (appInfo?.tabId) {
|
uploadDataUrl = uploadDataUrl + '&tags=' + encodeURIComponent(tag5)
|
||||||
executeEvent('receiveChunks', {
|
}
|
||||||
tabId: appInfo.tabId,
|
|
||||||
publishLocation: {
|
|
||||||
name: registeredName,
|
|
||||||
identifier,
|
|
||||||
service,
|
|
||||||
},
|
|
||||||
chunksSubmitted: 0,
|
|
||||||
totalChunks,
|
|
||||||
processed: false,
|
|
||||||
filename:
|
|
||||||
file?.name || filename || title || `${service}-${identifier || ''}`,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
for (let index = 0; index < totalChunks; index++) {
|
|
||||||
const start = index * chunkSize;
|
|
||||||
const end = Math.min(start + chunkSize, file.size);
|
|
||||||
const chunk = file.slice(start, end);
|
|
||||||
const formData = new FormData();
|
|
||||||
formData.append('chunk', chunk, file.name); // Optional: include filename
|
|
||||||
formData.append('index', index);
|
|
||||||
|
|
||||||
await uploadChunkWithRetry(chunkUrl, formData, index);
|
return await reusablePost(uploadDataUrl, postBody)
|
||||||
if (appInfo?.tabId) {
|
|
||||||
executeEvent('receiveChunks', {
|
|
||||||
tabId: appInfo.tabId,
|
|
||||||
publishLocation: {
|
|
||||||
name: registeredName,
|
|
||||||
identifier,
|
|
||||||
service,
|
|
||||||
},
|
|
||||||
chunksSubmitted: index + 1,
|
|
||||||
totalChunks,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
const finalizeUrl = uploadDataUrl + `/finalize` + paramQueries;
|
|
||||||
|
|
||||||
const finalizeEndpoint = await createEndpoint(finalizeUrl);
|
try {
|
||||||
|
return await validate()
|
||||||
const response = await fetch(finalizeEndpoint, {
|
} catch (error: any) {
|
||||||
method: 'POST',
|
throw new Error(error?.message)
|
||||||
headers: {},
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
if (!response?.ok) {
|
|
||||||
const errorText = await response.text();
|
|
||||||
throw new Error(`Finalize failed: ${errorText}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = await response.text(); // Base58-encoded unsigned transaction
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
|
||||||
return await validate();
|
|
||||||
} catch (error: any) {
|
|
||||||
throw new Error(error?.message);
|
|
||||||
}
|
|
||||||
};
|
|
@ -1,10 +1,9 @@
|
|||||||
import { gateways, getApiKeyFromStorage, getNameInfoForOthers } from "./background";
|
import { gateways, getApiKeyFromStorage } from "./background";
|
||||||
import { listOfAllQortalRequests } from "./components/Apps/useQortalMessageListener";
|
import { listOfAllQortalRequests } from "./components/Apps/useQortalMessageListener";
|
||||||
import { addForeignServer, addGroupAdminRequest, addListItems, adminAction, banFromGroupRequest, buyNameRequest, cancelGroupBanRequest, cancelGroupInviteRequest, cancelSellNameRequest, cancelSellOrder, createAndCopyEmbedLink, createBuyOrder, createGroupRequest, createPoll, createSellOrder, decryptAESGCMRequest, decryptData, decryptDataWithSharingKey, decryptQortalGroupData, deleteHostedData, deleteListItems, deployAt, encryptData, encryptDataWithSharingKey, encryptQortalGroupData, getArrrSyncStatus, getCrossChainServerInfo, getDaySummary, getForeignFee, getHostedData, getListItems, getNodeInfo, getNodeStatus, getServerConnectionHistory, getTxActivitySummary, getUserAccount, getUserWallet, getUserWalletInfo, getUserWalletTransactions, getWalletBalance, inviteToGroupRequest, joinGroup, kickFromGroupRequest, leaveGroupRequest, multiPaymentWithPrivateData, openNewTab, publishMultipleQDNResources, publishQDNResource, registerNameRequest, removeForeignServer, removeGroupAdminRequest, saveFile, sellNameRequest, sendChatMessage, sendCoin, setCurrentForeignServer, signForeignFees, signTransaction, transferAssetRequest, updateForeignFee, updateGroupRequest, updateNameRequest, voteOnPoll } from "./qortalRequests/get";
|
import { addForeignServer, addGroupAdminRequest, addListItems, adminAction, banFromGroupRequest, cancelGroupBanRequest, cancelGroupInviteRequest, cancelSellOrder, createAndCopyEmbedLink, createBuyOrder, createGroupRequest, createPoll, decryptAESGCMRequest, decryptData, decryptDataWithSharingKey, decryptQortalGroupData, deleteHostedData, deleteListItems, deployAt, encryptData, encryptDataWithSharingKey, encryptQortalGroupData, getArrrSyncStatus, getCrossChainServerInfo, getDaySummary, getForeignFee, getHostedData, getListItems, getNodeInfo, getNodeStatus, getServerConnectionHistory, getTxActivitySummary, getUserAccount, getUserWallet, getUserWalletInfo, getUserWalletTransactions, getWalletBalance, inviteToGroupRequest, joinGroup, kickFromGroupRequest, leaveGroupRequest, openNewTab, publishMultipleQDNResources, publishQDNResource, registerNameRequest, removeForeignServer, removeGroupAdminRequest, saveFile, sendChatMessage, sendCoin, setCurrentForeignServer, signTransaction, updateForeignFee, updateNameRequest, voteOnPoll } from "./qortalRequests/get";
|
||||||
import { getData, storeData } from "./utils/chromeStorage";
|
import { getData, storeData } from "./utils/chromeStorage";
|
||||||
import { executeEvent } from "./utils/events";
|
import { executeEvent } from "./utils/events";
|
||||||
|
|
||||||
import { ScreenOrientation } from '@capacitor/screen-orientation';
|
|
||||||
|
|
||||||
|
|
||||||
function getLocalStorage(key) {
|
function getLocalStorage(key) {
|
||||||
@ -201,7 +200,7 @@ export const isRunningGateway = async ()=> {
|
|||||||
|
|
||||||
case "PUBLISH_QDN_RESOURCE": {
|
case "PUBLISH_QDN_RESOURCE": {
|
||||||
try {
|
try {
|
||||||
const res = await publishQDNResource(request.payload, event.source, isFromExtension, appInfo);
|
const res = await publishQDNResource(request.payload, event.source, isFromExtension);
|
||||||
event.source.postMessage({
|
event.source.postMessage({
|
||||||
requestId: request.requestId,
|
requestId: request.requestId,
|
||||||
action: request.action,
|
action: request.action,
|
||||||
@ -221,7 +220,7 @@ export const isRunningGateway = async ()=> {
|
|||||||
|
|
||||||
case "PUBLISH_MULTIPLE_QDN_RESOURCES": {
|
case "PUBLISH_MULTIPLE_QDN_RESOURCES": {
|
||||||
try {
|
try {
|
||||||
const res = await publishMultipleQDNResources(request.payload, event.source, isFromExtension, appInfo);
|
const res = await publishMultipleQDNResources(request.payload, event.source, isFromExtension);
|
||||||
event.source.postMessage({
|
event.source.postMessage({
|
||||||
requestId: request.requestId,
|
requestId: request.requestId,
|
||||||
action: request.action,
|
action: request.action,
|
||||||
@ -463,7 +462,7 @@ export const isRunningGateway = async ()=> {
|
|||||||
|
|
||||||
case "UPDATE_FOREIGN_FEE": {
|
case "UPDATE_FOREIGN_FEE": {
|
||||||
try {
|
try {
|
||||||
const res = await updateForeignFee(request.payload, isFromExtension);
|
const res = await updateForeignFee(request.payload);
|
||||||
event.source.postMessage({
|
event.source.postMessage({
|
||||||
requestId: request.requestId,
|
requestId: request.requestId,
|
||||||
action: request.action,
|
action: request.action,
|
||||||
@ -503,7 +502,7 @@ export const isRunningGateway = async ()=> {
|
|||||||
|
|
||||||
case "SET_CURRENT_FOREIGN_SERVER": {
|
case "SET_CURRENT_FOREIGN_SERVER": {
|
||||||
try {
|
try {
|
||||||
const res = await setCurrentForeignServer(request.payload, isFromExtension);
|
const res = await setCurrentForeignServer(request.payload);
|
||||||
event.source.postMessage({
|
event.source.postMessage({
|
||||||
requestId: request.requestId,
|
requestId: request.requestId,
|
||||||
action: request.action,
|
action: request.action,
|
||||||
@ -523,7 +522,7 @@ export const isRunningGateway = async ()=> {
|
|||||||
|
|
||||||
case "ADD_FOREIGN_SERVER": {
|
case "ADD_FOREIGN_SERVER": {
|
||||||
try {
|
try {
|
||||||
const res = await addForeignServer(request.payload, isFromExtension);
|
const res = await addForeignServer(request.payload);
|
||||||
event.source.postMessage({
|
event.source.postMessage({
|
||||||
requestId: request.requestId,
|
requestId: request.requestId,
|
||||||
action: request.action,
|
action: request.action,
|
||||||
@ -543,7 +542,7 @@ export const isRunningGateway = async ()=> {
|
|||||||
|
|
||||||
case "REMOVE_FOREIGN_SERVER": {
|
case "REMOVE_FOREIGN_SERVER": {
|
||||||
try {
|
try {
|
||||||
const res = await removeForeignServer(request.payload, isFromExtension);
|
const res = await removeForeignServer(request.payload);
|
||||||
event.source.postMessage({
|
event.source.postMessage({
|
||||||
requestId: request.requestId,
|
requestId: request.requestId,
|
||||||
action: request.action,
|
action: request.action,
|
||||||
@ -621,32 +620,6 @@ export const isRunningGateway = async ()=> {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'CREATE_TRADE_SELL_ORDER': {
|
|
||||||
try {
|
|
||||||
const res = await createSellOrder(request.payload, isFromExtension);
|
|
||||||
event.source.postMessage(
|
|
||||||
{
|
|
||||||
requestId: request.requestId,
|
|
||||||
action: request.action,
|
|
||||||
payload: res,
|
|
||||||
type: 'backgroundMessageResponse',
|
|
||||||
},
|
|
||||||
event.origin
|
|
||||||
);
|
|
||||||
} catch (error) {
|
|
||||||
event.source.postMessage(
|
|
||||||
{
|
|
||||||
requestId: request.requestId,
|
|
||||||
action: request.action,
|
|
||||||
error: error.message,
|
|
||||||
type: 'backgroundMessageResponse',
|
|
||||||
},
|
|
||||||
event.origin
|
|
||||||
);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case "CANCEL_TRADE_SELL_ORDER": {
|
case "CANCEL_TRADE_SELL_ORDER": {
|
||||||
try {
|
try {
|
||||||
const res = await cancelSellOrder(request.payload, isFromExtension);
|
const res = await cancelSellOrder(request.payload, isFromExtension);
|
||||||
@ -1233,206 +1206,6 @@ export const isRunningGateway = async ()=> {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "UPDATE_GROUP" : {
|
|
||||||
try {
|
|
||||||
const res = await updateGroupRequest(request.payload, isFromExtension)
|
|
||||||
event.source.postMessage({
|
|
||||||
requestId: request.requestId,
|
|
||||||
action: request.action,
|
|
||||||
payload: res,
|
|
||||||
type: "backgroundMessageResponse",
|
|
||||||
}, event.origin);
|
|
||||||
} catch (error) {
|
|
||||||
event.source.postMessage({
|
|
||||||
requestId: request.requestId,
|
|
||||||
action: request.action,
|
|
||||||
error: error?.message,
|
|
||||||
type: "backgroundMessageResponse",
|
|
||||||
}, event.origin);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "BUY_NAME": {
|
|
||||||
try {
|
|
||||||
const res = await buyNameRequest(request.payload, isFromExtension);
|
|
||||||
event.source.postMessage({
|
|
||||||
requestId: request.requestId,
|
|
||||||
action: request.action,
|
|
||||||
payload: res,
|
|
||||||
type: "backgroundMessageResponse",
|
|
||||||
}, event.origin);
|
|
||||||
} catch (error) {
|
|
||||||
event.source.postMessage({
|
|
||||||
requestId: request.requestId,
|
|
||||||
action: request.action,
|
|
||||||
error: error.message,
|
|
||||||
type: "backgroundMessageResponse",
|
|
||||||
}, event.origin);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "SELL_NAME": {
|
|
||||||
try {
|
|
||||||
const res = await sellNameRequest(request.payload, isFromExtension);
|
|
||||||
event.source.postMessage({
|
|
||||||
requestId: request.requestId,
|
|
||||||
action: request.action,
|
|
||||||
payload: res,
|
|
||||||
type: "backgroundMessageResponse",
|
|
||||||
}, event.origin);
|
|
||||||
} catch (error) {
|
|
||||||
event.source.postMessage({
|
|
||||||
requestId: request.requestId,
|
|
||||||
action: request.action,
|
|
||||||
error: error.message,
|
|
||||||
type: "backgroundMessageResponse",
|
|
||||||
}, event.origin);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "CANCEL_SELL_NAME": {
|
|
||||||
try {
|
|
||||||
const res = await cancelSellNameRequest(request.payload, isFromExtension);
|
|
||||||
event.source.postMessage({
|
|
||||||
requestId: request.requestId,
|
|
||||||
action: request.action,
|
|
||||||
payload: res,
|
|
||||||
type: "backgroundMessageResponse",
|
|
||||||
}, event.origin);
|
|
||||||
} catch (error) {
|
|
||||||
event.source.postMessage({
|
|
||||||
requestId: request.requestId,
|
|
||||||
action: request.action,
|
|
||||||
error: error.message,
|
|
||||||
type: "backgroundMessageResponse",
|
|
||||||
}, event.origin);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "MULTI_ASSET_PAYMENT_WITH_PRIVATE_DATA" : {
|
|
||||||
try {
|
|
||||||
const res = await multiPaymentWithPrivateData(request.payload, isFromExtension)
|
|
||||||
event.source.postMessage({
|
|
||||||
requestId: request.requestId,
|
|
||||||
action: request.action,
|
|
||||||
payload: res,
|
|
||||||
type: "backgroundMessageResponse",
|
|
||||||
}, event.origin);
|
|
||||||
} catch (error) {
|
|
||||||
event.source.postMessage({
|
|
||||||
requestId: request.requestId,
|
|
||||||
action: request.action,
|
|
||||||
error: error?.message,
|
|
||||||
type: "backgroundMessageResponse",
|
|
||||||
}, event.origin);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "TRANSFER_ASSET" : {
|
|
||||||
try {
|
|
||||||
const res = await transferAssetRequest(request.payload, isFromExtension)
|
|
||||||
event.source.postMessage({
|
|
||||||
requestId: request.requestId,
|
|
||||||
action: request.action,
|
|
||||||
payload: res,
|
|
||||||
type: "backgroundMessageResponse",
|
|
||||||
}, event.origin);
|
|
||||||
} catch (error) {
|
|
||||||
event.source.postMessage({
|
|
||||||
requestId: request.requestId,
|
|
||||||
action: request.action,
|
|
||||||
error: error?.message,
|
|
||||||
type: "backgroundMessageResponse",
|
|
||||||
}, event.origin);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'SIGN_FOREIGN_FEES': {
|
|
||||||
try {
|
|
||||||
const res = await signForeignFees(request.payload, isFromExtension);
|
|
||||||
event.source.postMessage(
|
|
||||||
{
|
|
||||||
requestId: request.requestId,
|
|
||||||
action: request.action,
|
|
||||||
payload: res,
|
|
||||||
type: 'backgroundMessageResponse',
|
|
||||||
},
|
|
||||||
event.origin
|
|
||||||
);
|
|
||||||
} catch (error) {
|
|
||||||
event.source.postMessage(
|
|
||||||
{
|
|
||||||
requestId: request.requestId,
|
|
||||||
action: request.action,
|
|
||||||
error: error.message,
|
|
||||||
type: 'backgroundMessageResponse',
|
|
||||||
},
|
|
||||||
event.origin
|
|
||||||
);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'GET_PRIMARY_NAME': {
|
|
||||||
try {
|
|
||||||
const res = await getNameInfoForOthers(request.payload?.address);
|
|
||||||
const resData = res ? res : "";
|
|
||||||
event.source.postMessage(
|
|
||||||
{
|
|
||||||
requestId: request.requestId,
|
|
||||||
action: request.action,
|
|
||||||
payload: resData,
|
|
||||||
type: 'backgroundMessageResponse',
|
|
||||||
},
|
|
||||||
event.origin
|
|
||||||
);
|
|
||||||
} catch (error) {
|
|
||||||
event.source.postMessage(
|
|
||||||
{
|
|
||||||
requestId: request.requestId,
|
|
||||||
action: request.action,
|
|
||||||
error: error.message,
|
|
||||||
type: 'backgroundMessageResponse',
|
|
||||||
},
|
|
||||||
event.origin
|
|
||||||
);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'SCREEN_ORIENTATION': {
|
|
||||||
try {
|
|
||||||
const mode = request.payload?.mode
|
|
||||||
if(mode === 'unlock'){
|
|
||||||
await ScreenOrientation.unlock();
|
|
||||||
|
|
||||||
} else {
|
|
||||||
await ScreenOrientation.lock({ orientation: mode });
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
event.source.postMessage(
|
|
||||||
{
|
|
||||||
requestId: request.requestId,
|
|
||||||
action: request.action,
|
|
||||||
payload: true,
|
|
||||||
type: 'backgroundMessageResponse',
|
|
||||||
},
|
|
||||||
event.origin
|
|
||||||
);
|
|
||||||
} catch (error) {
|
|
||||||
event.source.postMessage(
|
|
||||||
{
|
|
||||||
requestId: request.requestId,
|
|
||||||
action: request.action,
|
|
||||||
error: error.message,
|
|
||||||
type: 'backgroundMessageResponse',
|
|
||||||
},
|
|
||||||
event.origin
|
|
||||||
);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,45 +0,0 @@
|
|||||||
// @ts-nocheck
|
|
||||||
|
|
||||||
import { QORT_DECIMALS } from "../constants/constants"
|
|
||||||
import TransactionBase from "./TransactionBase"
|
|
||||||
|
|
||||||
|
|
||||||
export default class BuyNameTransacion extends TransactionBase {
|
|
||||||
constructor() {
|
|
||||||
super()
|
|
||||||
this.type = 7
|
|
||||||
}
|
|
||||||
|
|
||||||
set fee(fee) {
|
|
||||||
this._fee = fee * QORT_DECIMALS
|
|
||||||
this._feeBytes = this.constructor.utils.int64ToBytes(this._fee)
|
|
||||||
}
|
|
||||||
|
|
||||||
set name(name) {
|
|
||||||
this.nameText = name
|
|
||||||
this._nameBytes = this.constructor.utils.stringtoUTF8Array(name)
|
|
||||||
this._nameLength = this.constructor.utils.int32ToBytes(this._nameBytes.length)
|
|
||||||
}
|
|
||||||
|
|
||||||
set sellPrice(sellPrice) {
|
|
||||||
this._sellPrice = sellPrice * QORT_DECIMALS
|
|
||||||
this._sellPriceBytes = this.constructor.utils.int64ToBytes(this._sellPrice)
|
|
||||||
}
|
|
||||||
|
|
||||||
set recipient(recipient) {
|
|
||||||
this._recipient = recipient instanceof Uint8Array ? recipient : this.constructor.Base58.decode(recipient)
|
|
||||||
this.theRecipient = recipient
|
|
||||||
}
|
|
||||||
|
|
||||||
get params() {
|
|
||||||
const params = super.params
|
|
||||||
params.push(
|
|
||||||
this._nameLength,
|
|
||||||
this._nameBytes,
|
|
||||||
this._sellPriceBytes,
|
|
||||||
this._recipient,
|
|
||||||
this._feeBytes
|
|
||||||
)
|
|
||||||
return params
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,33 +0,0 @@
|
|||||||
// @ts-nocheck
|
|
||||||
|
|
||||||
import { QORT_DECIMALS } from "../constants/constants"
|
|
||||||
import TransactionBase from "./TransactionBase"
|
|
||||||
|
|
||||||
|
|
||||||
export default class CancelSellNameTransacion extends TransactionBase {
|
|
||||||
constructor() {
|
|
||||||
super()
|
|
||||||
this.type = 6
|
|
||||||
}
|
|
||||||
|
|
||||||
set fee(fee) {
|
|
||||||
this._fee = fee * QORT_DECIMALS
|
|
||||||
this._feeBytes = this.constructor.utils.int64ToBytes(this._fee)
|
|
||||||
}
|
|
||||||
|
|
||||||
set name(name) {
|
|
||||||
this.nameText = name
|
|
||||||
this._nameBytes = this.constructor.utils.stringtoUTF8Array(name)
|
|
||||||
this._nameLength = this.constructor.utils.int32ToBytes(this._nameBytes.length)
|
|
||||||
}
|
|
||||||
|
|
||||||
get params() {
|
|
||||||
const params = super.params
|
|
||||||
params.push(
|
|
||||||
this._nameLength,
|
|
||||||
this._nameBytes,
|
|
||||||
this._feeBytes
|
|
||||||
)
|
|
||||||
return params
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,40 +0,0 @@
|
|||||||
// @ts-nocheck
|
|
||||||
|
|
||||||
import { QORT_DECIMALS } from "../constants/constants"
|
|
||||||
import TransactionBase from "./TransactionBase"
|
|
||||||
|
|
||||||
|
|
||||||
export default class SellNameTransacion extends TransactionBase {
|
|
||||||
constructor() {
|
|
||||||
super()
|
|
||||||
this.type = 5
|
|
||||||
}
|
|
||||||
|
|
||||||
set fee(fee) {
|
|
||||||
this._fee = fee * QORT_DECIMALS
|
|
||||||
this._feeBytes = this.constructor.utils.int64ToBytes(this._fee)
|
|
||||||
}
|
|
||||||
|
|
||||||
set name(name) {
|
|
||||||
this.nameText = name
|
|
||||||
this._nameBytes = this.constructor.utils.stringtoUTF8Array(name)
|
|
||||||
this._nameLength = this.constructor.utils.int32ToBytes(this._nameBytes.length)
|
|
||||||
}
|
|
||||||
|
|
||||||
set sellPrice(sellPrice) {
|
|
||||||
this.showSellPrice = sellPrice
|
|
||||||
this._sellPrice = sellPrice * QORT_DECIMALS
|
|
||||||
this._sellPriceBytes = this.constructor.utils.int64ToBytes(this._sellPrice)
|
|
||||||
}
|
|
||||||
|
|
||||||
get params() {
|
|
||||||
const params = super.params
|
|
||||||
params.push(
|
|
||||||
this._nameLength,
|
|
||||||
this._nameBytes,
|
|
||||||
this._sellPriceBytes,
|
|
||||||
this._feeBytes
|
|
||||||
)
|
|
||||||
return params
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,35 +0,0 @@
|
|||||||
// @ts-nocheck
|
|
||||||
|
|
||||||
import { QORT_DECIMALS } from '../constants/constants'
|
|
||||||
import TransactionBase from './TransactionBase'
|
|
||||||
|
|
||||||
export default class TransferAssetTransaction extends TransactionBase {
|
|
||||||
constructor() {
|
|
||||||
super()
|
|
||||||
this.type = 12
|
|
||||||
}
|
|
||||||
|
|
||||||
set recipient(recipient) {
|
|
||||||
this._recipient = recipient instanceof Uint8Array ? recipient : this.constructor.Base58.decode(recipient)
|
|
||||||
}
|
|
||||||
|
|
||||||
set amount(amount) {
|
|
||||||
this._amount = Math.round(amount * QORT_DECIMALS)
|
|
||||||
this._amountBytes = this.constructor.utils.int64ToBytes(this._amount)
|
|
||||||
}
|
|
||||||
|
|
||||||
set assetId(assetId) {
|
|
||||||
this._assetId = this.constructor.utils.int64ToBytes(assetId)
|
|
||||||
}
|
|
||||||
|
|
||||||
get params() {
|
|
||||||
const params = super.params
|
|
||||||
params.push(
|
|
||||||
this._recipient,
|
|
||||||
this._assetId,
|
|
||||||
this._amountBytes,
|
|
||||||
this._feeBytes
|
|
||||||
)
|
|
||||||
return params
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,62 +0,0 @@
|
|||||||
// @ts-nocheck
|
|
||||||
|
|
||||||
|
|
||||||
import { QORT_DECIMALS } from "../constants/constants";
|
|
||||||
import TransactionBase from "./TransactionBase";
|
|
||||||
|
|
||||||
export default class UpdateGroupTransaction extends TransactionBase {
|
|
||||||
constructor() {
|
|
||||||
super()
|
|
||||||
this.type = 23
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
set fee(fee) {
|
|
||||||
this._fee = fee * QORT_DECIMALS
|
|
||||||
this._feeBytes = this.constructor.utils.int64ToBytes(this._fee)
|
|
||||||
}
|
|
||||||
set newOwner(newOwner) {
|
|
||||||
this._newOwner = newOwner instanceof Uint8Array ? newOwner : this.constructor.Base58.decode(newOwner)
|
|
||||||
}
|
|
||||||
set newIsOpen(newIsOpen) {
|
|
||||||
|
|
||||||
this._rGroupType = new Uint8Array(1)
|
|
||||||
this._rGroupType[0] = newIsOpen
|
|
||||||
}
|
|
||||||
set newDescription(newDescription) {
|
|
||||||
this._rGroupDescBytes = this.constructor.utils.stringtoUTF8Array(newDescription.toLocaleLowerCase())
|
|
||||||
this._rGroupDescLength = this.constructor.utils.int32ToBytes(this._rGroupDescBytes.length)
|
|
||||||
}
|
|
||||||
set newApprovalThreshold(newApprovalThreshold) {
|
|
||||||
this._rGroupApprovalThreshold = new Uint8Array(1)
|
|
||||||
this._rGroupApprovalThreshold[0] = newApprovalThreshold;
|
|
||||||
}
|
|
||||||
set newMinimumBlockDelay(newMinimumBlockDelay) {
|
|
||||||
this._rGroupMinimumBlockDelayBytes = this.constructor.utils.int32ToBytes(newMinimumBlockDelay)
|
|
||||||
}
|
|
||||||
set newMaximumBlockDelay(newMaximumBlockDelay) {
|
|
||||||
|
|
||||||
this._rGroupMaximumBlockDelayBytes = this.constructor.utils.int32ToBytes(newMaximumBlockDelay)
|
|
||||||
}
|
|
||||||
|
|
||||||
set _groupId(_groupId){
|
|
||||||
this._groupBytes = this.constructor.utils.int32ToBytes(_groupId)
|
|
||||||
}
|
|
||||||
get params() {
|
|
||||||
const params = super.params
|
|
||||||
params.push(
|
|
||||||
this._groupBytes,
|
|
||||||
this._newOwner,
|
|
||||||
this._rGroupDescLength,
|
|
||||||
this._rGroupDescBytes,
|
|
||||||
this._rGroupType,
|
|
||||||
this._rGroupApprovalThreshold,
|
|
||||||
this._rGroupMinimumBlockDelayBytes,
|
|
||||||
this._rGroupMaximumBlockDelayBytes,
|
|
||||||
this._feeBytes
|
|
||||||
)
|
|
||||||
return params
|
|
||||||
}
|
|
||||||
}
|
|
@ -20,27 +20,17 @@ import DeployAtTransaction from './DeployAtTransaction.js'
|
|||||||
import RewardShareTransaction from './RewardShareTransaction.js'
|
import RewardShareTransaction from './RewardShareTransaction.js'
|
||||||
import RemoveRewardShareTransaction from './RemoveRewardShareTransaction.js'
|
import RemoveRewardShareTransaction from './RemoveRewardShareTransaction.js'
|
||||||
import UpdateNameTransaction from './UpdateNameTransaction.js'
|
import UpdateNameTransaction from './UpdateNameTransaction.js'
|
||||||
import UpdateGroupTransaction from './UpdateGroupTransaction.js'
|
|
||||||
import SellNameTransacion from './SellNameTransacion.js'
|
|
||||||
import CancelSellNameTransacion from './CancelSellNameTransacion.js'
|
|
||||||
import BuyNameTransacion from './BuyNameTransacion.js'
|
|
||||||
import TransferAssetTransaction from './TransferAssetTransaction.js'
|
|
||||||
|
|
||||||
export const transactionTypes = {
|
export const transactionTypes = {
|
||||||
3: RegisterNameTransaction,
|
3: RegisterNameTransaction,
|
||||||
4: UpdateNameTransaction,
|
4: UpdateNameTransaction,
|
||||||
2: PaymentTransaction,
|
2: PaymentTransaction,
|
||||||
5: SellNameTransacion,
|
|
||||||
6: CancelSellNameTransacion,
|
|
||||||
7: BuyNameTransacion,
|
|
||||||
8: CreatePollTransaction,
|
8: CreatePollTransaction,
|
||||||
9: VoteOnPollTransaction,
|
9: VoteOnPollTransaction,
|
||||||
12: TransferAssetTransaction,
|
|
||||||
16: DeployAtTransaction,
|
16: DeployAtTransaction,
|
||||||
18: ChatTransaction,
|
18: ChatTransaction,
|
||||||
181: GroupChatTransaction,
|
181: GroupChatTransaction,
|
||||||
22: CreateGroupTransaction,
|
22: CreateGroupTransaction,
|
||||||
23: UpdateGroupTransaction,
|
|
||||||
24: AddGroupAdminTransaction,
|
24: AddGroupAdminTransaction,
|
||||||
25: RemoveGroupAdminTransaction,
|
25: RemoveGroupAdminTransaction,
|
||||||
26: GroupBanTransaction,
|
26: GroupBanTransaction,
|
||||||
|
@ -1,26 +0,0 @@
|
|||||||
export function buildImageEmbedLink(image?: {
|
|
||||||
name?: string;
|
|
||||||
identifier?: string;
|
|
||||||
service?: string;
|
|
||||||
timestamp?: number;
|
|
||||||
}): string | null {
|
|
||||||
if (!image?.name || !image.identifier || !image.service) return null;
|
|
||||||
|
|
||||||
const base = `qortal://use-embed/IMAGE?name=${image.name}&identifier=${image.identifier}&service=${image.service}&mimeType=image%2Fpng×tamp=${image?.timestamp || ''}`;
|
|
||||||
|
|
||||||
const isEncrypted = image.identifier.startsWith('grp-q-manager_0');
|
|
||||||
return isEncrypted ? `${base}&encryptionType=group` : base;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const messageHasImage = (message) => {
|
|
||||||
return (
|
|
||||||
Array.isArray(message?.images) &&
|
|
||||||
message.images[0]?.identifier &&
|
|
||||||
message.images[0]?.name &&
|
|
||||||
message.images[0]?.service
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export function isHtmlString(value) {
|
|
||||||
return typeof value === 'string' && /<[^>]+>/.test(value.trim());
|
|
||||||
}
|
|
@ -14,18 +14,3 @@ export function decodeIfEncoded(input) {
|
|||||||
// Return input as-is if not URI-encoded
|
// Return input as-is if not URI-encoded
|
||||||
return input;
|
return input;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const isValidBase64 = (str: string): boolean => {
|
|
||||||
if (typeof str !== "string" || str.length % 4 !== 0) return false;
|
|
||||||
|
|
||||||
const base64Regex = /^[A-Za-z0-9+/]*={0,2}$/;
|
|
||||||
return base64Regex.test(str);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const isValidBase64WithDecode = (str: string): boolean => {
|
|
||||||
try {
|
|
||||||
return isValidBase64(str) && Boolean(atob(str));
|
|
||||||
} catch {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
@ -43,15 +43,14 @@ export function formatTimestamp(timestamp: number): string {
|
|||||||
// Both have timestamp, sort by timestamp descending
|
// Both have timestamp, sort by timestamp descending
|
||||||
return b.timestamp - a.timestamp;
|
return b.timestamp - a.timestamp;
|
||||||
} else if (a.timestamp) {
|
} else if (a.timestamp) {
|
||||||
|
// Only `a` has timestamp, it comes first
|
||||||
return -1;
|
return -1;
|
||||||
} else if (b.timestamp) {
|
} else if (b.timestamp) {
|
||||||
|
// Only `b` has timestamp, it comes first
|
||||||
return 1;
|
return 1;
|
||||||
} else {
|
} else {
|
||||||
// Neither has timestamp, sort alphabetically by groupName (with fallback)
|
// Neither has timestamp, sort alphabetically by groupName
|
||||||
const nameA = a.groupName || '';
|
return a.groupName.localeCompare(b.groupName);
|
||||||
const nameB = b.groupName || '';
|
|
||||||
return nameA.localeCompare(nameB);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user