Merge branch 'feature/multi-names' into feature/large-files-and-names

This commit is contained in:
PhilReact 2025-05-17 23:49:45 +03:00
commit 933c6622fb
8 changed files with 1029 additions and 865 deletions

File diff suppressed because it is too large Load Diff

View File

@ -66,6 +66,15 @@ export async function getNameInfo() {
}
}
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() {
const res = await getData<any>('keyPair').catch(() => null);
if (res) {
@ -285,9 +294,10 @@ export const publishOnQDN = async ({
tag4,
tag5,
uploadType = 'file',
name,
}) => {
if (data && service) {
const registeredName = await getNameInfo();
const registeredName = name || (await getNameInfo());
if (!registeredName) throw new Error('You need a name to publish');
const res = await publishData({

View File

@ -1,4 +1,4 @@
import React, { useContext, useEffect, useState } from 'react';
import React, { useCallback, useContext, useEffect, useState } from 'react';
import {
AppCircle,
AppCircleContainer,
@ -72,7 +72,8 @@ const CustomMenuItem = styled(MenuItem)({
// },
});
export const AppPublish = ({ names, categories }) => {
export const AppPublish = ({ categories, myAddress, myName }) => {
const [names, setNames] = useState([]);
const [name, setName] = useState('');
const [title, setTitle] = useState('');
const [description, setDescription] = useState('');
@ -152,6 +153,25 @@ export const AppPublish = ({ names, categories }) => {
getQapp(name, appType);
}, [name, appType]);
const getNames = useCallback(async () => {
if (!myAddress) return;
try {
setIsLoading('Loading names');
const res = await fetch(
`${getBaseApiReact()}/names/address/${myAddress}`
);
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 () => {
try {
const data = {
@ -195,6 +215,7 @@ export const AppPublish = ({ names, categories }) => {
data: file,
service: appType,
title,
name,
description,
category,
tag1,

View File

@ -38,6 +38,7 @@ export const AppsDesktop = ({
hasUnreadGroups,
setDesktopViewMode,
desktopViewMode,
myAddress,
}) => {
const [availableQapps, setAvailableQapps] = useState([]);
const [selectedAppInfo, setSelectedAppInfo] = useState(null);
@ -458,6 +459,7 @@ export const AppsDesktop = ({
setMode={setMode}
myApp={myApp}
myWebsite={myWebsite}
myAddress={myAddress}
/>
</Box>
)}
@ -485,7 +487,11 @@ export const AppsDesktop = ({
myName={myName}
/>
{mode === 'publish' && !selectedTab && (
<AppPublish names={myName ? [myName] : []} categories={categories} />
<AppPublish
names={myName ? [myName] : []}
categories={categories}
myAddress={myAddress}
/>
)}
{tabs.map((tab) => {
if (!iframeRefs.current[tab.tabId]) {
@ -521,6 +527,7 @@ export const AppsDesktop = ({
setMode={setMode}
myApp={myApp}
myWebsite={myWebsite}
myAddress={myAddress}
/>
</Box>
</>

View File

@ -23,6 +23,7 @@ export const AppsHomeDesktop = ({
myWebsite,
availableQapps,
myName,
myAddress,
}) => {
const [qortalUrl, setQortalUrl] = useState('');
const theme = useTheme();
@ -147,7 +148,7 @@ export const AppsHomeDesktop = ({
</AppCircleContainer>
</ButtonBase>
<AppsPrivate myName={myName} />
<AppsPrivate myName={myName} myAddress={myAddress} />
<SortablePinnedApps
isDesktop={true}

View File

@ -1,4 +1,10 @@
import React, { useContext, useMemo, useState } from 'react';
import React, {
useCallback,
useContext,
useEffect,
useMemo,
useState,
} from 'react';
import {
Box,
Button,
@ -31,7 +37,7 @@ import {
} from './Apps-styles';
import AddIcon from '@mui/icons-material/Add';
import ImageUploader from '../../common/ImageUploader';
import { MyContext } from '../../App';
import { getBaseApiReact, MyContext } from '../../App';
import { fileToBase64 } from '../../utils/fileReading';
import { objectToBase64 } from '../../qdn/encryption/group-encryption';
import { getFee } from '../../background';
@ -39,7 +45,10 @@ import { useAtom } from 'jotai';
const maxFileSize = 50 * 1024 * 1024; // 50MB
export const AppsPrivate = ({ myName }) => {
export const AppsPrivate = ({ myName, myAddress }) => {
const [names, setNames] = useState([]);
const [name, setName] = useState(0);
const { openApp } = useHandlePrivateApps();
const [file, setFile] = useState(null);
const [logo, setLogo] = useState(null);
@ -140,7 +149,7 @@ export const AppsPrivate = ({ myName }) => {
try {
if (selectedGroup === 0) return;
if (!logo) throw new Error('Please select an image for a logo');
if (!myName) throw new Error('You need a Qortal name to publish');
if (!name) throw new Error('Please select a Qortal name');
if (!newPrivateAppValues?.name) throw new Error('Your app needs a name');
const base64Logo = await fileToBase64(logo);
const base64App = await fileToBase64(file);
@ -178,6 +187,7 @@ export const AppsPrivate = ({ myName }) => {
identifier: newPrivateAppValues?.identifier,
service: newPrivateAppValues?.service,
uploadType: 'base64',
name,
})
.then((response) => {
if (!response?.error) {
@ -195,7 +205,7 @@ export const AppsPrivate = ({ myName }) => {
{
identifier: newPrivateAppValues?.identifier,
service: newPrivateAppValues?.service,
name: myName,
name,
groupId: selectedGroup,
},
true
@ -221,6 +231,22 @@ export const AppsPrivate = ({ myName }) => {
};
}
const getNames = useCallback(async () => {
if (!myAddress) return;
try {
const res = await fetch(
`${getBaseApiReact()}/names/address/${myAddress}`
);
const data = await res.json();
setNames(data?.map((item) => item.name));
} catch (error) {
console.error(error);
}
}, [myAddress]);
useEffect(() => {
getNames();
}, [getNames]);
return (
<>
<ButtonBase
@ -458,7 +484,34 @@ export const AppsPrivate = ({ myName }) => {
<input {...getInputProps()} />
{file ? 'Change' : 'Choose'} File
</PublishQAppChoseFile>
<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>
{names.map((name) => {
return (
<MenuItem key={name} value={name}>
{name}
</MenuItem>
);
})}
</Select>
</Box>
<Spacer height="20px" />
<Box

View File

@ -519,7 +519,7 @@ export const Group = ({
});
});
} catch (error) {
console.log('error', error);
console.error(error);
}
}, [setMutedGroups]);
@ -2317,6 +2317,7 @@ export const Group = ({
hasUnreadDirects={directChatHasUnread}
show={desktopViewMode === 'apps'}
myName={userInfo?.name}
myAddress={userInfo?.address}
isGroups={isOpenSideViewGroups}
isDirects={isOpenSideViewDirects}
hasUnreadGroups={groupChatHasUnread || groupsAnnHasUnread}

View File

@ -39,6 +39,7 @@ import {
transferAsset,
} from '../background';
import {
getAllUserNames,
getNameInfo,
uint8ArrayToObject,
} from '../backgroundFunctions/encryption';
@ -1065,7 +1066,8 @@ export const publishQDNResource = async (
if (appFee && appFee > 0 && appFeeRecipient) {
hasAppFee = true;
}
const registeredName = await getNameInfo();
const registeredName = data?.name || (await getNameInfo());
const name = registeredName;
if (!name) {
throw new Error('User has no Qortal name');
@ -1145,6 +1147,7 @@ export const publishQDNResource = async (
text1: 'Do you give this application permission to publish to QDN?',
text2: `service: ${service}`,
text3: `identifier: ${identifier || null}`,
text4: `name: ${registeredName}`,
fee: fee.fee,
...handleDynamicValues,
},
@ -1265,10 +1268,19 @@ export const publishMultipleQDNResources = async (
const fee = await getFee('ARBITRARY');
const registeredName = await getNameInfo();
const name = registeredName;
if (!name) {
throw new Error('You need a Qortal name to publish.');
}
const userNames = await getAllUserNames();
data.resources?.forEach((item) => {
if (item?.name && !userNames?.includes(item.name))
throw new Error(
`The name ${item.name}, does not belong to the publisher.`
);
});
const appFee = data?.appFee ? +data.appFee : undefined;
const appFeeRecipient = data?.appFeeRecipient;
let hasAppFee = false;
@ -1336,7 +1348,7 @@ export const publishMultipleQDNResources = async (
<div class="resource-detail"><span>Service:</span> ${
resource.service
}</div>
<div class="resource-detail"><span>Name:</span> ${name}</div>
<div class="resource-detail"><span>Name:</span> ${resource?.name || name}</div>
<div class="resource-detail"><span>Identifier:</span> ${
resource.identifier
}</div>
@ -1377,6 +1389,7 @@ export const publishMultipleQDNResources = async (
reason: errorMsg,
identifier: resource.identifier,
service: resource.service,
name: resource?.name || name,
});
continue;
}
@ -1386,6 +1399,7 @@ export const publishMultipleQDNResources = async (
reason: errorMsg,
identifier: resource.identifier,
service: resource.service,
name: resource?.name || name,
});
continue;
}
@ -1415,6 +1429,7 @@ export const publishMultipleQDNResources = async (
reason: errorMsg,
identifier: resource.identifier,
service: resource.service,
name: resource?.name || name,
});
continue;
}
@ -1448,6 +1463,7 @@ export const publishMultipleQDNResources = async (
reason: errorMsg,
identifier: resource.identifier,
service: resource.service,
name: resource?.name || name,
});
continue;
}
@ -1463,8 +1479,8 @@ export const publishMultipleQDNResources = async (
publishData,
[
{
registeredName: encodeURIComponent(name),
data: rawData,
registeredName: encodeURIComponent(resource?.name || name),
service: service,
identifier: encodeURIComponent(identifier),
uploadType: dataType,
@ -1495,6 +1511,7 @@ export const publishMultipleQDNResources = async (
reason: errorMsg,
identifier: resource.identifier,
service: resource.service,
name: resource?.name || name,
});
}
} catch (error) {
@ -1502,6 +1519,7 @@ export const publishMultipleQDNResources = async (
reason: error?.message || 'Unknown error',
identifier: resource.identifier,
service: resource.service,
name: resource?.name || name,
});
}
}
@ -4311,9 +4329,10 @@ export const updateNameRequest = async (data, isFromExtension) => {
const fee = await getFee('UPDATE_NAME');
const resPermission = await getUserPermission(
{
text1: `Do you give this application permission to register this name?`,
highlightedText: data.newName,
text2: data?.description,
text1: `Do you give this application permission to update this name?`,
text2: `previous name: ${oldName}`,
text3: `new name: ${newName}`,
text4: data?.description,
fee: fee.fee,
},
isFromExtension
@ -4747,7 +4766,7 @@ export const createGroupRequest = async (data, isFromExtension) => {
];
const missingFields: string[] = [];
requiredFields.forEach((field) => {
if (data[field] !== undefined && data[field] !== null) {
if (data[field] === undefined || data[field] === null) {
missingFields.push(field);
}
});
@ -4799,7 +4818,7 @@ export const updateGroupRequest = async (data, isFromExtension) => {
];
const missingFields: string[] = [];
requiredFields.forEach((field) => {
if (data[field] !== undefined && data[field] !== null) {
if (data[field] === undefined || data[field] === null) {
missingFields.push(field);
}
});
@ -4934,7 +4953,7 @@ export const sellNameRequest = async (data, isFromExtension) => {
const requiredFields = ['salePrice', 'nameForSale'];
const missingFields: string[] = [];
requiredFields.forEach((field) => {
if (data[field] !== undefined && data[field] !== null) {
if (data[field] === undefined || data[field] === null) {
missingFields.push(field);
}
});
@ -4978,7 +4997,7 @@ export const cancelSellNameRequest = async (data, isFromExtension) => {
const requiredFields = ['nameForSale'];
const missingFields: string[] = [];
requiredFields.forEach((field) => {
if (data[field] !== undefined && data[field] !== null) {
if (data[field] === undefined || data[field] === null) {
missingFields.push(field);
}
});
@ -5018,7 +5037,7 @@ export const buyNameRequest = async (data, isFromExtension) => {
const requiredFields = ['nameForSale'];
const missingFields: string[] = [];
requiredFields.forEach((field) => {
if (data[field] !== undefined && data[field] !== null) {
if (data[field] === undefined || data[field] === null) {
missingFields.push(field);
}
});