diff --git a/src/App.tsx b/src/App.tsx
index f190e0a..de83cfe 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -134,6 +134,7 @@ import BoundedNumericTextField from "./common/BoundedNumericTextField";
import { useHandleUserInfo } from "./components/Group/useHandleUserInfo";
import { Minting } from "./components/Minting/Minting";
import { isRunningGateway } from "./qortalRequests";
+import { GlobalActions } from "./components/GlobalActions/GlobalActions";
type extStates =
@@ -1817,6 +1818,7 @@ function App() {
>
+
)}
{isOpenSendQort && isMainWindow && (
diff --git a/src/components/Chat/MessageDisplay.tsx b/src/components/Chat/MessageDisplay.tsx
index 3418eaf..62529c7 100644
--- a/src/components/Chat/MessageDisplay.tsx
+++ b/src/components/Chat/MessageDisplay.tsx
@@ -97,6 +97,27 @@ export const MessageDisplay = ({ htmlContent, isReply, setMobileViewModeKeepOpen
window.open(href, '_system');
} 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/Embeds/VideoPlayer.tsx b/src/components/Embeds/VideoPlayer.tsx
index 9f9c84d..9138fcf 100644
--- a/src/components/Embeds/VideoPlayer.tsx
+++ b/src/components/Embeds/VideoPlayer.tsx
@@ -541,7 +541,7 @@ export const VideoPlayer: React.FC = ({
id={identifier}
ref={videoRef}
src={!startPlay ? '' : resourceStatus?.status === 'READY' ? src : ''}
- poster="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw=="
+ poster={poster ? poster : "data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw=="}
onTimeUpdate={updateProgress}
autoPlay={autoplay}
onClick={togglePlay}
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..65da592
--- /dev/null
+++ b/src/components/GlobalActions/JoinGroup.tsx
@@ -0,0 +1,286 @@
+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) => {
+ window
+ .sendMessage("joinGroup", {
+ groupId,
+ })
+ .then((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);
+ }
+ })
+ .catch((error) => {
+ setInfoSnack({
+ type: "error",
+ message: error.message || "An error occurred",
+ });
+ setOpenSnack(true);
+ rej(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..3747645
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..d120287
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..329336b
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..82680b5
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..8b6d67b
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..5198fe1
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..7162518
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 dc2f19b..1e5fe79 100644
--- a/src/components/Tutorials/useHandleTutorials.tsx
+++ b/src/components/Tutorials/useHandleTutorials.tsx
@@ -2,6 +2,13 @@ import React, { useCallback, useEffect, useState } from "react";
import { saveToLocalStorage } from "../Apps/AppsNavBar";
import { getData, storeData } from "../../utils/chromeStorage";
+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 {
@@ -80,6 +87,7 @@ useEffect(()=> {
name: "a-test",
service: "VIDEO",
identifier: "account-creation-go",
+ poster: creationImg
},
});
}
@@ -95,6 +103,7 @@ useEffect(()=> {
name: "a-test",
service: "VIDEO",
identifier: "important-information-go",
+ poster: importantImg
},
});
}
@@ -114,6 +123,7 @@ useEffect(()=> {
name: "a-test",
service: "VIDEO",
identifier: "getting-started-go",
+ poster: startedImg
},
},
{
@@ -122,6 +132,7 @@ useEffect(()=> {
name: "a-test",
service: "VIDEO",
identifier: "overview-go",
+ poster: overviewImg
},
},
{
@@ -130,6 +141,7 @@ useEffect(()=> {
name: "a-test",
service: "VIDEO",
identifier: "groups-go",
+ poster: groupsImg
},
},
],
@@ -149,6 +161,7 @@ useEffect(()=> {
name: "a-test",
service: "VIDEO",
identifier: "apps-dashboard-go",
+ poster: dashboardImg
},
},
{
@@ -157,6 +170,7 @@ useEffect(()=> {
name: "a-test",
service: "VIDEO",
identifier: "apps-navigation-go",
+ poster: navigationImg
},
}
diff --git a/src/components/WrapperUserAction.tsx b/src/components/WrapperUserAction.tsx
index aa0d19a..8758c99 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
+
>