diff --git a/src/App.tsx b/src/App.tsx
index 310dc21..eb80967 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -124,6 +124,7 @@ import { useHandleUserInfo } from "./components/Group/useHandleUserInfo";
import { Minting } from "./components/Minting/Minting";
import { isRunningGateway } from "./qortalRequests";
import { QMailStatus } from "./components/QMailStatus";
+import { GlobalActions } from "./components/GlobalActions/GlobalActions";
type extStates =
| "not-authenticated"
@@ -1686,6 +1687,7 @@ function App() {
}}
>
+
)}
diff --git a/src/components/Chat/MessageDisplay.tsx b/src/components/Chat/MessageDisplay.tsx
index 394a0bf..b78cbc9 100644
--- a/src/components/Chat/MessageDisplay.tsx
+++ b/src/components/Chat/MessageDisplay.tsx
@@ -97,6 +97,28 @@ export const MessageDisplay = ({ htmlContent, isReply }) => {
window.electronAPI.openExternal(href);
} else if (target.getAttribute('data-url')) {
const url = target.getAttribute('data-url');
+
+ let copyUrl = url
+
+ try {
+ copyUrl = copyUrl.replace(/^(qortal:\/\/)/, '')
+ if (copyUrl.startsWith('use-')) {
+ // Handle the new 'use' format
+ const parts = copyUrl.split('/')
+ const type = parts[0].split('-')[1] // e.g., 'group' from 'use-group'
+ parts.shift()
+ const action = parts.length > 0 ? parts[0].split('-')[1] : null // e.g., 'invite' from 'action-invite'
+ parts.shift()
+ const idPrefix = parts.length > 0 ? parts[0].split('-')[0] : null // e.g., 'groupid' from 'groupid-321'
+ const id = parts.length > 0 ? parts[0].split('-')[1] : null // e.g., '321' from 'groupid-321'
+ if(action === 'join'){
+ executeEvent("globalActionJoinGroup", { groupId: id});
+ return
+ }
+ }
+ } catch (error) {
+ //error
+ }
const res = extractComponents(url);
if (res) {
const { service, name, identifier, path } = res;
diff --git a/src/components/GlobalActions/GlobalActions.tsx b/src/components/GlobalActions/GlobalActions.tsx
new file mode 100644
index 0000000..6dd845c
--- /dev/null
+++ b/src/components/GlobalActions/GlobalActions.tsx
@@ -0,0 +1,10 @@
+import React from 'react'
+import { JoinGroup } from './JoinGroup'
+
+export const GlobalActions = ({memberGroups}) => {
+ return (
+ <>
+
+ >
+ )
+}
diff --git a/src/components/GlobalActions/JoinGroup.tsx b/src/components/GlobalActions/JoinGroup.tsx
new file mode 100644
index 0000000..3eedd6c
--- /dev/null
+++ b/src/components/GlobalActions/JoinGroup.tsx
@@ -0,0 +1,272 @@
+import React, { useContext, useEffect, useMemo, useState } from "react";
+import { subscribeToEvent, unsubscribeFromEvent } from "../../utils/events";
+import {
+ Box,
+ Button,
+ ButtonBase,
+ CircularProgress,
+ Dialog,
+ DialogActions,
+ DialogContent,
+ Typography,
+} from "@mui/material";
+import { CustomButton, CustomButtonAccept } from "../../App-styles";
+import { getBaseApiReact, MyContext } from "../../App";
+import { getFee } from "../../background";
+import { CustomizedSnackbars } from "../Snackbar/Snackbar";
+import { FidgetSpinner } from "react-loader-spinner";
+
+export const JoinGroup = ({ memberGroups }) => {
+ const { show, setTxList } = useContext(MyContext);
+ const [openSnack, setOpenSnack] = useState(false);
+ const [infoSnack, setInfoSnack] = useState(null);
+ const [groupInfo, setGroupInfo] = useState(null);
+ const [isLoadingInfo, setIsLoadingInfo] = useState(false);
+ const [isOpen, setIsOpen] = useState(false);
+ const [isLoadingJoinGroup, setIsLoadingJoinGroup] = useState(false);
+ const handleJoinGroup = async (e) => {
+ setGroupInfo(null);
+ const groupId = e?.detail?.groupId;
+ if (groupId) {
+ try {
+ setIsOpen(true);
+ setIsLoadingInfo(true);
+ const response = await fetch(`${getBaseApiReact()}/groups/${groupId}`);
+ const groupData = await response.json();
+ setGroupInfo(groupData);
+ } catch (error) {
+ } finally {
+ setIsLoadingInfo(false);
+ }
+ }
+ };
+
+ useEffect(() => {
+ subscribeToEvent("globalActionJoinGroup", handleJoinGroup);
+
+ return () => {
+ unsubscribeFromEvent("globalActionJoinGroup", handleJoinGroup);
+ };
+ }, []);
+
+ const isInGroup = useMemo(()=> {
+ return !!memberGroups.find((item)=> +item?.groupId === +groupInfo?.groupId)
+ }, [memberGroups, groupInfo])
+ const joinGroup = async (group, isOpen) => {
+ try {
+ const groupId = group.groupId;
+ const fee = await getFee("JOIN_GROUP");
+ await show({
+ message: "Would you like to perform an JOIN_GROUP transaction?",
+ publishFee: fee.fee + " QORT",
+ });
+ setIsLoadingJoinGroup(true);
+ await new Promise((res, rej) => {
+ chrome?.runtime?.sendMessage(
+ {
+ action: "joinGroup",
+ payload: {
+ groupId,
+ },
+ },
+ (response) => {
+
+ if (!response?.error) {
+ setInfoSnack({
+ type: "success",
+ message: "Successfully requested to join group. It may take a couple of minutes for the changes to propagate",
+ });
+ if(isOpen){
+ setTxList((prev)=> [{
+ ...response,
+ type: 'joined-group',
+ label: `Joined Group ${group?.groupName}: awaiting confirmation`,
+ labelDone: `Joined Group ${group?.groupName}: success !`,
+ done: false,
+ groupId,
+ }, ...prev])
+ } else {
+ setTxList((prev)=> [{
+ ...response,
+ type: 'joined-group-request',
+ label: `Requested to join Group ${group?.groupName}: awaiting confirmation`,
+ labelDone: `Requested to join Group ${group?.groupName}: success !`,
+ done: false,
+ groupId,
+ }, ...prev])
+ }
+ setOpenSnack(true);
+ res(response);
+ return;
+ } else {
+ setInfoSnack({
+ type: "error",
+ message: response?.error,
+ });
+ setOpenSnack(true);
+ rej(response.error);
+ }
+ }
+ );
+ });
+ setIsLoadingJoinGroup(false);
+ } catch (error) {
+ } finally {
+ setIsLoadingJoinGroup(false);
+ }
+ };
+ return (
+ <>
+
+
+
+ {isLoadingJoinGroup && (
+
+
+
+ )}
+ >
+ );
+};
diff --git a/src/components/Tutorials/img/creation.webp b/src/components/Tutorials/img/creation.webp
new file mode 100644
index 0000000..46310e0
Binary files /dev/null and b/src/components/Tutorials/img/creation.webp differ
diff --git a/src/components/Tutorials/img/dashboard.webp b/src/components/Tutorials/img/dashboard.webp
new file mode 100644
index 0000000..6e33ffd
Binary files /dev/null and b/src/components/Tutorials/img/dashboard.webp differ
diff --git a/src/components/Tutorials/img/groups.webp b/src/components/Tutorials/img/groups.webp
new file mode 100644
index 0000000..8a0ad22
Binary files /dev/null and b/src/components/Tutorials/img/groups.webp differ
diff --git a/src/components/Tutorials/img/important.webp b/src/components/Tutorials/img/important.webp
new file mode 100644
index 0000000..880a84e
Binary files /dev/null and b/src/components/Tutorials/img/important.webp differ
diff --git a/src/components/Tutorials/img/navigation.webp b/src/components/Tutorials/img/navigation.webp
new file mode 100644
index 0000000..a568960
Binary files /dev/null and b/src/components/Tutorials/img/navigation.webp differ
diff --git a/src/components/Tutorials/img/overview.webp b/src/components/Tutorials/img/overview.webp
new file mode 100644
index 0000000..bc413b4
Binary files /dev/null and b/src/components/Tutorials/img/overview.webp differ
diff --git a/src/components/Tutorials/img/started.webp b/src/components/Tutorials/img/started.webp
new file mode 100644
index 0000000..76099a5
Binary files /dev/null and b/src/components/Tutorials/img/started.webp differ
diff --git a/src/components/Tutorials/useHandleTutorials.tsx b/src/components/Tutorials/useHandleTutorials.tsx
index 468dc4c..decf6f0 100644
--- a/src/components/Tutorials/useHandleTutorials.tsx
+++ b/src/components/Tutorials/useHandleTutorials.tsx
@@ -1,6 +1,12 @@
import React, { useCallback, useEffect, useState } from "react";
import { saveToLocalStorage } from "../Apps/AppsNavBar";
-
+import creationImg from './img/creation.webp'
+import dashboardImg from './img/dashboard.webp'
+import groupsImg from './img/groups.webp'
+import importantImg from './img/important.webp'
+import navigationImg from './img/navigation.webp'
+import overviewImg from './img/overview.webp'
+import startedImg from './img/started.webp'
const checkIfGatewayIsOnline = async () => {
try {
@@ -61,102 +67,108 @@ useEffect(()=> {
const isOnline = await checkIfGatewayIsOnline()
if(!isOnline) return
switch (type) {
- case "create-account":
- {
- if((shownTutorials || {})['create-account'] && !isForce) return
- saveShowTutorial('create-account')
- setOpenTutorialModal({
- title: "Account Creation",
- resource: {
- name: "a-test",
- service: "VIDEO",
- identifier: "account-creation-hub",
- },
- });
- }
- break;
- case "important-information":
- {
- if((shownTutorials || {})['important-information'] && !isForce) return
- saveShowTutorial('important-information')
+ case "create-account":
+ {
+ if((shownTutorials || {})['create-account'] && !isForce) return
+ saveShowTutorial('create-account')
+ setOpenTutorialModal({
+ title: "Account Creation",
+ resource: {
+ name: "a-test",
+ service: "VIDEO",
+ identifier: "account-creation-hub",
+ poster: creationImg
+ },
+ });
+ }
+ break;
+ case "important-information":
+ {
+ if((shownTutorials || {})['important-information'] && !isForce) return
+ saveShowTutorial('important-information')
- setOpenTutorialModal({
- title: "Important Information!",
- resource: {
- name: "a-test",
- service: "VIDEO",
- identifier: "important-information-hub",
- },
- });
- }
- break;
- case "getting-started":
- {
- if((shownTutorials || {})['getting-started'] && !isForce) return
- saveShowTutorial('getting-started')
+ setOpenTutorialModal({
+ title: "Important Information!",
+ resource: {
+ name: "a-test",
+ service: "VIDEO",
+ identifier: "important-information-hub",
+ poster: importantImg
+ },
+ });
+ }
+ break;
+ case "getting-started":
+ {
+ if((shownTutorials || {})['getting-started'] && !isForce) return
+ saveShowTutorial('getting-started')
- setOpenTutorialModal({
- multi: [
-
- {
- title: "1. Getting Started",
+ setOpenTutorialModal({
+ multi: [
+
+ {
+ title: "1. Getting Started",
+ resource: {
+ name: "a-test",
+ service: "VIDEO",
+ identifier: "getting-started-hub",
+ poster: startedImg
+ },
+ },
+ {
+ title: "2. Overview",
resource: {
name: "a-test",
service: "VIDEO",
- identifier: "getting-started-hub",
+ identifier: "overview-hub",
+ poster: overviewImg
},
},
- {
- title: "2. Overview",
+ {
+ title: "3. Qortal Groups",
+ resource: {
+ name: "a-test",
+ service: "VIDEO",
+ identifier: "groups-hub",
+ poster: groupsImg
+ },
+ },
+ ],
+ });
+ }
+ break;
+ case "qapps":
+ {
+ if((shownTutorials || {})['qapps'] && !isForce) return
+ saveShowTutorial('qapps')
+
+ setOpenTutorialModal({
+ multi: [
+ {
+ title: "1. Apps Dashboard",
resource: {
name: "a-test",
service: "VIDEO",
- identifier: "overview-hub",
+ identifier: "apps-dashboard-hub",
+ poster: dashboardImg
},
},
- {
- title: "3. Qortal Groups",
- resource: {
- name: "a-test",
- service: "VIDEO",
- identifier: "groups-hub",
- },
- },
- ],
- });
- }
- break;
- case "qapps":
- {
- if((shownTutorials || {})['qapps'] && !isForce) return
- saveShowTutorial('qapps')
-
- setOpenTutorialModal({
- multi: [
- {
- title: "1. Apps Dashboard",
- resource: {
- name: "a-test",
- service: "VIDEO",
- identifier: "apps-dashboard-hub",
- },
+ {
+ title: "2. Apps Navigation",
+ resource: {
+ name: "a-test",
+ service: "VIDEO",
+ identifier: "apps-navigation-hub",
+ poster: navigationImg
},
- {
- title: "2. Apps Navigation",
- resource: {
- name: "a-test",
- service: "VIDEO",
- identifier: "apps-navigation-hub",
- },
- }
-
- ],
- });
- }
- break;
- default:
- break;
- }
+ }
+ ],
+ });
+ }
+ break;
+ default:
+ break;
+ }
} catch (error) {
//error
}
diff --git a/src/components/WrapperUserAction.tsx b/src/components/WrapperUserAction.tsx
index aa0d19a..8337943 100644
--- a/src/components/WrapperUserAction.tsx
+++ b/src/components/WrapperUserAction.tsx
@@ -80,7 +80,8 @@ export const WrapperUserAction = ({ children, address, name, disabled }) => {
}, 200);
}}
sx={{
- color: 'white'
+ color: 'white',
+ justifyContent: 'flex-start'
}}
>
Message
@@ -98,11 +99,26 @@ export const WrapperUserAction = ({ children, address, name, disabled }) => {
}}
sx={{
- color: 'white'
+ color: 'white',
+ justifyContent: 'flex-start'
}}
>
Send QORT
+
>