mirror of
https://github.com/Qortal/Qortal-Hub.git
synced 2025-07-24 10:41:24 +00:00
finished desktop view
This commit is contained in:
@@ -22,7 +22,7 @@ import {
|
||||
} from "./Apps-styles";
|
||||
import { Avatar, Box, ButtonBase, InputBase } from "@mui/material";
|
||||
import { Add } from "@mui/icons-material";
|
||||
import { getBaseApiReact } from "../../App";
|
||||
import { getBaseApiReact, isMobile } from "../../App";
|
||||
import LogoSelected from "../../assets/svgs/LogoSelected.svg";
|
||||
|
||||
import { Spacer } from "../../common/Spacer";
|
||||
@@ -32,7 +32,21 @@ import { AppRating } from "./AppRating";
|
||||
export const AppInfo = ({ app, myName }) => {
|
||||
const isInstalled = app?.status?.status === "READY";
|
||||
return (
|
||||
<AppsLibraryContainer>
|
||||
<AppsLibraryContainer
|
||||
sx={{
|
||||
height: !isMobile && "100%",
|
||||
justifyContent: !isMobile && "flex-start",
|
||||
alignItems: isMobile && 'center'
|
||||
}}
|
||||
>
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
maxWidth: "500px",
|
||||
}}>
|
||||
|
||||
|
||||
{!isMobile && <Spacer height="30px" />}
|
||||
<AppsWidthLimiter>
|
||||
<AppInfoSnippetContainer>
|
||||
<AppInfoSnippetLeft
|
||||
@@ -108,30 +122,28 @@ export const AppInfo = ({ app, myName }) => {
|
||||
</AppDownloadButton>
|
||||
</AppsWidthLimiter>
|
||||
<Spacer height="20px" />
|
||||
<AppsWidthLimiter>
|
||||
|
||||
<AppsCategoryInfo>
|
||||
<AppRating ratingCountPosition="top" myName={myName} app={app} />
|
||||
<Spacer width="16px" />
|
||||
<Spacer height="40px" width="1px" backgroundColor="white" />
|
||||
<Spacer width="16px" />
|
||||
<AppsCategoryInfoSub>
|
||||
<AppsCategoryInfoLabel>Category:</AppsCategoryInfoLabel>
|
||||
<Spacer height="4px" />
|
||||
<AppsCategoryInfoValue>
|
||||
{app?.metadata?.categoryName || "none"}
|
||||
</AppsCategoryInfoValue>
|
||||
</AppsCategoryInfoSub>
|
||||
</AppsCategoryInfo>
|
||||
<Spacer height="30px" />
|
||||
<AppInfoAppName>
|
||||
About this Q-App
|
||||
</AppInfoAppName>
|
||||
<AppsWidthLimiter>
|
||||
<AppsCategoryInfo>
|
||||
<AppRating ratingCountPosition="top" myName={myName} app={app} />
|
||||
<Spacer width="16px" />
|
||||
<Spacer height="40px" width="1px" backgroundColor="white" />
|
||||
<Spacer width="16px" />
|
||||
<AppsCategoryInfoSub>
|
||||
<AppsCategoryInfoLabel>Category:</AppsCategoryInfoLabel>
|
||||
<Spacer height="4px" />
|
||||
<AppsCategoryInfoValue>
|
||||
{app?.metadata?.categoryName || "none"}
|
||||
</AppsCategoryInfoValue>
|
||||
</AppsCategoryInfoSub>
|
||||
</AppsCategoryInfo>
|
||||
<Spacer height="30px" />
|
||||
<AppInfoAppName>About this Q-App</AppInfoAppName>
|
||||
</AppsWidthLimiter>
|
||||
<Spacer height="20px" />
|
||||
<AppsInfoDescription>
|
||||
{app?.metadata?.description || "No description"}
|
||||
{app?.metadata?.description || "No description"}
|
||||
</AppsInfoDescription>
|
||||
</Box>
|
||||
</AppsLibraryContainer>
|
||||
);
|
||||
};
|
||||
|
@@ -39,7 +39,7 @@ import { Option as BaseOption, optionClasses } from "@mui/base/Option";
|
||||
import { styled } from "@mui/system";
|
||||
import UnfoldMoreRoundedIcon from "@mui/icons-material/UnfoldMoreRounded";
|
||||
import { Add } from "@mui/icons-material";
|
||||
import { MyContext, getBaseApiReact } from "../../App";
|
||||
import { MyContext, getBaseApiReact, isMobile } from "../../App";
|
||||
import LogoSelected from "../../assets/svgs/LogoSelected.svg";
|
||||
|
||||
import { Spacer } from "../../common/Spacer";
|
||||
@@ -257,8 +257,14 @@ export const AppPublish = ({ names, categories }) => {
|
||||
}
|
||||
};
|
||||
return (
|
||||
<AppsLibraryContainer>
|
||||
<AppsWidthLimiter>
|
||||
<AppsLibraryContainer sx={{
|
||||
height: !isMobile && '100%',
|
||||
paddingTop: !isMobile && '30px',
|
||||
alignItems: !isMobile && 'center'
|
||||
}}>
|
||||
<AppsWidthLimiter sx={{
|
||||
width: !isMobile && 'auto'
|
||||
}}>
|
||||
<AppLibrarySubTitle>Create Apps!</AppLibrarySubTitle>
|
||||
<Spacer height="18px" />
|
||||
<PublishQAppInfo>
|
||||
|
@@ -16,7 +16,7 @@ import {
|
||||
} from "./Apps-styles";
|
||||
import { Avatar, Box, ButtonBase, InputBase } from "@mui/material";
|
||||
import { Add } from "@mui/icons-material";
|
||||
import { MyContext, getBaseApiReact } from "../../App";
|
||||
import { MyContext, getBaseApiReact, isMobile } from "../../App";
|
||||
import LogoSelected from "../../assets/svgs/LogoSelected.svg";
|
||||
|
||||
import { Spacer } from "../../common/Spacer";
|
||||
@@ -61,7 +61,7 @@ export const AppViewer = ({ app }) => {
|
||||
|
||||
return (
|
||||
<iframe ref={iframeRef} style={{
|
||||
height: `calc(${rootHeight} - 60px - 45px - 20px)`,
|
||||
height: !isMobile ? '100vh' : `calc(${rootHeight} - 60px - 45px - 20px)`,
|
||||
border: 'none',
|
||||
width: '100%'
|
||||
}} id="browser-iframe" src={defaultUrl} sandbox="allow-scripts allow-same-origin allow-forms allow-downloads allow-modals" allow="fullscreen">
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import React, { useContext, useEffect, useRef } from 'react'
|
||||
import { AppViewer } from './AppViewer'
|
||||
import Frame from 'react-frame-component';
|
||||
import { MyContext } from '../../App';
|
||||
import { MyContext, isMobile } from '../../App';
|
||||
import { subscribeToEvent, unsubscribeFromEvent } from '../../utils/events';
|
||||
|
||||
const AppViewerContainer = ({app, isSelected, hide}) => {
|
||||
@@ -32,7 +32,7 @@ const AppViewerContainer = ({app, isSelected, hide}) => {
|
||||
</style>
|
||||
</>
|
||||
} style={{
|
||||
height: `calc(${rootHeight} - 60px - 45px - 20px)`,
|
||||
height: !isMobile ? '100vh' : `calc(${rootHeight} - 60px - 45px - 20px)`,
|
||||
border: 'none',
|
||||
width: '100%',
|
||||
display: (!isSelected || hide) && 'none'
|
||||
|
223
src/components/Apps/AppsCategoryDesktop.tsx
Normal file
223
src/components/Apps/AppsCategoryDesktop.tsx
Normal file
@@ -0,0 +1,223 @@
|
||||
import React, {
|
||||
useCallback,
|
||||
useContext,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
} from "react";
|
||||
import {
|
||||
AppCircle,
|
||||
AppCircleContainer,
|
||||
AppCircleLabel,
|
||||
AppLibrarySubTitle,
|
||||
AppsContainer,
|
||||
AppsLibraryContainer,
|
||||
AppsParent,
|
||||
AppsSearchContainer,
|
||||
AppsSearchLeft,
|
||||
AppsSearchRight,
|
||||
AppsWidthLimiter,
|
||||
PublishQAppCTAButton,
|
||||
PublishQAppCTALeft,
|
||||
PublishQAppCTAParent,
|
||||
PublishQAppCTARight,
|
||||
PublishQAppDotsBG,
|
||||
} from "./Apps-styles";
|
||||
import { Avatar, Box, ButtonBase, InputBase, styled } from "@mui/material";
|
||||
import { Add } from "@mui/icons-material";
|
||||
import { MyContext, getBaseApiReact } from "../../App";
|
||||
import LogoSelected from "../../assets/svgs/LogoSelected.svg";
|
||||
import IconSearch from "../../assets/svgs/Search.svg";
|
||||
import IconClearInput from "../../assets/svgs/ClearInput.svg";
|
||||
import qappDevelopText from "../../assets/svgs/qappDevelopText.svg";
|
||||
import qappDots from "../../assets/svgs/qappDots.svg";
|
||||
|
||||
import { Spacer } from "../../common/Spacer";
|
||||
import { AppInfoSnippet } from "./AppInfoSnippet";
|
||||
import { Virtuoso } from "react-virtuoso";
|
||||
import { executeEvent } from "../../utils/events";
|
||||
import { AppsDesktopLibraryBody, AppsDesktopLibraryHeader } from "./AppsDesktop-styles";
|
||||
const officialAppList = [
|
||||
"q-tube",
|
||||
"q-blog",
|
||||
"q-share",
|
||||
"q-support",
|
||||
"q-mail",
|
||||
"qombo",
|
||||
"q-fund",
|
||||
"q-shop",
|
||||
];
|
||||
|
||||
const ScrollerStyled = styled("div")({
|
||||
// Hide scrollbar for WebKit browsers (Chrome, Safari)
|
||||
"::-webkit-scrollbar": {
|
||||
width: "0px",
|
||||
height: "0px",
|
||||
},
|
||||
|
||||
// Hide scrollbar for Firefox
|
||||
scrollbarWidth: "none",
|
||||
|
||||
// Hide scrollbar for IE and older Edge
|
||||
"-ms-overflow-style": "none",
|
||||
});
|
||||
|
||||
const StyledVirtuosoContainer = styled("div")({
|
||||
position: "relative",
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
|
||||
// Hide scrollbar for WebKit browsers (Chrome, Safari)
|
||||
"::-webkit-scrollbar": {
|
||||
width: "0px",
|
||||
height: "0px",
|
||||
},
|
||||
|
||||
// Hide scrollbar for Firefox
|
||||
scrollbarWidth: "none",
|
||||
|
||||
// Hide scrollbar for IE and older Edge
|
||||
"-ms-overflow-style": "none",
|
||||
});
|
||||
|
||||
export const AppsCategoryDesktop = ({
|
||||
availableQapps,
|
||||
myName,
|
||||
category,
|
||||
isShow,
|
||||
}) => {
|
||||
const [searchValue, setSearchValue] = useState("");
|
||||
const virtuosoRef = useRef();
|
||||
const { rootHeight } = useContext(MyContext);
|
||||
|
||||
const categoryList = useMemo(() => {
|
||||
return availableQapps.filter(
|
||||
(app) => app?.metadata?.category === category?.id
|
||||
);
|
||||
}, [availableQapps, category]);
|
||||
|
||||
const [debouncedValue, setDebouncedValue] = useState(""); // Debounced value
|
||||
|
||||
// Debounce logic
|
||||
useEffect(() => {
|
||||
const handler = setTimeout(() => {
|
||||
setDebouncedValue(searchValue);
|
||||
}, 350);
|
||||
|
||||
// Cleanup timeout if searchValue changes before the timeout completes
|
||||
return () => {
|
||||
clearTimeout(handler);
|
||||
};
|
||||
}, [searchValue]); // Runs effect when searchValue changes
|
||||
|
||||
// Example: Perform search or other actions based on debouncedValue
|
||||
|
||||
const searchedList = useMemo(() => {
|
||||
if (!debouncedValue) return categoryList;
|
||||
return categoryList.filter((app) =>
|
||||
app.name.toLowerCase().includes(debouncedValue.toLowerCase())
|
||||
);
|
||||
}, [debouncedValue, categoryList]);
|
||||
|
||||
const rowRenderer = (index) => {
|
||||
let app = searchedList[index];
|
||||
return (
|
||||
<AppInfoSnippet
|
||||
key={`${app?.service}-${app?.name}`}
|
||||
app={app}
|
||||
myName={myName}
|
||||
isFromCategory={true}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<AppsLibraryContainer
|
||||
sx={{
|
||||
display: !isShow && "none",
|
||||
padding: "0px",
|
||||
height: "100vh",
|
||||
overflow: "hidden",
|
||||
paddingTop: "30px",
|
||||
}}
|
||||
>
|
||||
<AppsDesktopLibraryHeader
|
||||
sx={{
|
||||
maxWidth: "1500px",
|
||||
width: "90%",
|
||||
}}
|
||||
>
|
||||
<AppsWidthLimiter
|
||||
sx={{
|
||||
alignItems: "flex-end",
|
||||
}}
|
||||
>
|
||||
<AppsSearchContainer sx={{
|
||||
width: "412px",
|
||||
}}>
|
||||
<AppsSearchLeft>
|
||||
<img src={IconSearch} />
|
||||
<InputBase
|
||||
value={searchValue}
|
||||
onChange={(e) => setSearchValue(e.target.value)}
|
||||
sx={{ ml: 1, flex: 1 }}
|
||||
placeholder="Search for apps"
|
||||
inputProps={{
|
||||
"aria-label": "Search for apps",
|
||||
fontSize: "16px",
|
||||
fontWeight: 400,
|
||||
}}
|
||||
/>
|
||||
</AppsSearchLeft>
|
||||
<AppsSearchRight>
|
||||
{searchValue && (
|
||||
<ButtonBase
|
||||
onClick={() => {
|
||||
setSearchValue("");
|
||||
}}
|
||||
>
|
||||
<img src={IconClearInput} />
|
||||
</ButtonBase>
|
||||
)}
|
||||
</AppsSearchRight>
|
||||
</AppsSearchContainer>
|
||||
</AppsWidthLimiter>
|
||||
</AppsDesktopLibraryHeader>
|
||||
<AppsDesktopLibraryBody
|
||||
sx={{
|
||||
height: `calc(100vh - 36px)`,
|
||||
overflow: "auto",
|
||||
padding: "0px",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<Spacer height="25px" />
|
||||
<AppsWidthLimiter>
|
||||
<AppLibrarySubTitle>{`Category: ${category?.name}`}</AppLibrarySubTitle>
|
||||
|
||||
<Spacer height="25px" />
|
||||
</AppsWidthLimiter>
|
||||
<AppsWidthLimiter>
|
||||
<StyledVirtuosoContainer
|
||||
sx={{
|
||||
height: rootHeight,
|
||||
}}
|
||||
>
|
||||
<Virtuoso
|
||||
ref={virtuosoRef}
|
||||
data={searchedList}
|
||||
itemContent={rowRenderer}
|
||||
atBottomThreshold={50}
|
||||
followOutput="smooth"
|
||||
components={{
|
||||
Scroller: ScrollerStyled, // Use the styled scroller component
|
||||
}}
|
||||
/>
|
||||
</StyledVirtuosoContainer>
|
||||
</AppsWidthLimiter>
|
||||
</AppsDesktopLibraryBody>
|
||||
</AppsLibraryContainer>
|
||||
);
|
||||
};
|
24
src/components/Apps/AppsDesktop-styles.tsx
Normal file
24
src/components/Apps/AppsDesktop-styles.tsx
Normal file
@@ -0,0 +1,24 @@
|
||||
import {
|
||||
AppBar,
|
||||
Button,
|
||||
Toolbar,
|
||||
Typography,
|
||||
Box,
|
||||
TextField,
|
||||
InputLabel,
|
||||
ButtonBase,
|
||||
} from "@mui/material";
|
||||
import { styled } from "@mui/system";
|
||||
|
||||
export const AppsDesktopLibraryHeader = styled(Box)(({ theme }) => ({
|
||||
display: "flex",
|
||||
flexDirection: 'column',
|
||||
flexShrink: 0,
|
||||
width: '100%'
|
||||
}));
|
||||
export const AppsDesktopLibraryBody = styled(Box)(({ theme }) => ({
|
||||
display: "flex",
|
||||
flexDirection: 'column',
|
||||
flexGrow: 1,
|
||||
width: '100%'
|
||||
}));
|
429
src/components/Apps/AppsDesktop.tsx
Normal file
429
src/components/Apps/AppsDesktop.tsx
Normal file
@@ -0,0 +1,429 @@
|
||||
import React, { useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { AppsHomeDesktop } from "./AppsHomeDesktop";
|
||||
import { Spacer } from "../../common/Spacer";
|
||||
import { MyContext, getBaseApiReact } from "../../App";
|
||||
import { AppInfo } from "./AppInfo";
|
||||
import {
|
||||
executeEvent,
|
||||
subscribeToEvent,
|
||||
unsubscribeFromEvent,
|
||||
} from "../../utils/events";
|
||||
import { AppsNavBar } from "./AppsNavBar";
|
||||
import { AppsParent } from "./Apps-styles";
|
||||
import { AppViewer } from "./AppViewer";
|
||||
import AppViewerContainer from "./AppViewerContainer";
|
||||
import ShortUniqueId from "short-unique-id";
|
||||
import { AppPublish } from "./AppPublish";
|
||||
import { useRecoilState } from "recoil";
|
||||
import { AppsCategory } from "./AppsCategory";
|
||||
import { AppsLibrary } from "./AppsLibrary";
|
||||
import { AppsLibraryDesktop } from "./AppsLibraryDesktop";
|
||||
import { AppsCategoryDesktop } from "./AppsCategoryDesktop";
|
||||
import { AppsNavBarDesktop } from "./AppsNavBarDesktop";
|
||||
import { Box, ButtonBase } from "@mui/material";
|
||||
import { HomeIcon } from "../../assets/Icons/HomeIcon";
|
||||
import { MessagingIcon } from "../../assets/Icons/MessagingIcon";
|
||||
import { Save } from "../Save/Save";
|
||||
import { HubsIcon } from "../../assets/Icons/HubsIcon";
|
||||
|
||||
const uid = new ShortUniqueId({ length: 8 });
|
||||
|
||||
export const AppsDesktop = ({ mode, setMode, show , myName, goToHome, setDesktopSideView, hasUnreadDirects, isDirects, isGroups, hasUnreadGroups, toggleSideViewGroups, toggleSideViewDirects}) => {
|
||||
const [availableQapps, setAvailableQapps] = useState([]);
|
||||
const [selectedAppInfo, setSelectedAppInfo] = useState(null);
|
||||
const [selectedCategory, setSelectedCategory] = useState(null)
|
||||
const [tabs, setTabs] = useState([]);
|
||||
const [selectedTab, setSelectedTab] = useState(null);
|
||||
const [isNewTabWindow, setIsNewTabWindow] = useState(false);
|
||||
const [categories, setCategories] = useState([])
|
||||
|
||||
|
||||
const myApp = useMemo(()=> {
|
||||
|
||||
return availableQapps.find((app)=> app.name === myName && app.service === 'APP')
|
||||
}, [myName, availableQapps])
|
||||
const myWebsite = useMemo(()=> {
|
||||
|
||||
return availableQapps.find((app)=> app.name === myName && app.service === 'WEBSITE')
|
||||
}, [myName, availableQapps])
|
||||
|
||||
useEffect(() => {
|
||||
setTimeout(() => {
|
||||
executeEvent("setTabsToNav", {
|
||||
data: {
|
||||
tabs: tabs,
|
||||
selectedTab: selectedTab,
|
||||
isNewTabWindow: isNewTabWindow,
|
||||
},
|
||||
});
|
||||
}, 100);
|
||||
}, [show, tabs, selectedTab, isNewTabWindow]);
|
||||
|
||||
const getCategories = React.useCallback(async () => {
|
||||
try {
|
||||
const url = `${getBaseApiReact()}/arbitrary/categories`;
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
if (!response?.ok) return;
|
||||
const responseData = await response.json();
|
||||
|
||||
setCategories(responseData);
|
||||
|
||||
} catch (error) {
|
||||
} finally {
|
||||
// dispatch(setIsLoadingGlobal(false))
|
||||
}
|
||||
}, []);
|
||||
|
||||
const getQapps = React.useCallback(async () => {
|
||||
try {
|
||||
let apps = [];
|
||||
let websites = [];
|
||||
// dispatch(setIsLoadingGlobal(true))
|
||||
const url = `${getBaseApiReact()}/arbitrary/resources/search?service=APP&mode=ALL&limit=0&includestatus=true&includemetadata=true`;
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
if (!response?.ok) return;
|
||||
const responseData = await response.json();
|
||||
const urlWebsites = `${getBaseApiReact()}/arbitrary/resources/search?service=WEBSITE&mode=ALL&limit=0&includestatus=true&includemetadata=true`;
|
||||
|
||||
const responseWebsites = await fetch(urlWebsites, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
if (!responseWebsites?.ok) return;
|
||||
const responseDataWebsites = await responseWebsites.json();
|
||||
|
||||
apps = responseData;
|
||||
websites = responseDataWebsites;
|
||||
const combine = [...apps, ...websites];
|
||||
setAvailableQapps(combine);
|
||||
} catch (error) {
|
||||
} finally {
|
||||
// dispatch(setIsLoadingGlobal(false))
|
||||
}
|
||||
}, []);
|
||||
useEffect(() => {
|
||||
getQapps();
|
||||
getCategories()
|
||||
}, [getQapps, getCategories]);
|
||||
|
||||
const selectedAppInfoFunc = (e) => {
|
||||
const data = e.detail?.data;
|
||||
setSelectedAppInfo(data);
|
||||
setMode("appInfo");
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
subscribeToEvent("selectedAppInfo", selectedAppInfoFunc);
|
||||
|
||||
return () => {
|
||||
unsubscribeFromEvent("selectedAppInfo", selectedAppInfoFunc);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const selectedAppInfoCategoryFunc = (e) => {
|
||||
const data = e.detail?.data;
|
||||
setSelectedAppInfo(data);
|
||||
setMode("appInfo-from-category");
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
subscribeToEvent("selectedAppInfoCategory", selectedAppInfoCategoryFunc);
|
||||
|
||||
return () => {
|
||||
unsubscribeFromEvent("selectedAppInfoCategory", selectedAppInfoCategoryFunc);
|
||||
};
|
||||
}, []);
|
||||
|
||||
|
||||
|
||||
const selectedCategoryFunc = (e) => {
|
||||
const data = e.detail?.data;
|
||||
setSelectedCategory(data);
|
||||
setMode("category");
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
subscribeToEvent("selectedCategory", selectedCategoryFunc);
|
||||
|
||||
return () => {
|
||||
unsubscribeFromEvent("selectedCategory", selectedCategoryFunc);
|
||||
};
|
||||
}, []);
|
||||
|
||||
|
||||
const navigateBackFunc = (e) => {
|
||||
if(mode === 'category'){
|
||||
setMode("library");
|
||||
setSelectedCategory(null)
|
||||
} else if (mode === "appInfo-from-category") {
|
||||
setMode("category");
|
||||
} else if (mode === "appInfo") {
|
||||
setMode("library");
|
||||
} else if (mode === "library") {
|
||||
if (isNewTabWindow) {
|
||||
setMode("viewer");
|
||||
} else {
|
||||
setMode("home");
|
||||
}
|
||||
} else if(mode === 'publish'){
|
||||
setMode('library')
|
||||
} else {
|
||||
const iframeId = `browser-iframe-${selectedTab?.tabId}`;
|
||||
const iframe = document.getElementById(iframeId);
|
||||
// Go Back in the iframe's history
|
||||
if (iframe) {
|
||||
if (iframe && iframe.contentWindow) {
|
||||
const iframeWindow = iframe.contentWindow;
|
||||
if (iframeWindow && iframeWindow.history) {
|
||||
iframeWindow.history.back();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
subscribeToEvent("navigateBack", navigateBackFunc);
|
||||
|
||||
return () => {
|
||||
unsubscribeFromEvent("navigateBack", navigateBackFunc);
|
||||
};
|
||||
}, [mode, selectedTab]);
|
||||
|
||||
const addTabFunc = (e) => {
|
||||
const data = e.detail?.data;
|
||||
const newTab = {
|
||||
...data,
|
||||
tabId: uid.rnd(),
|
||||
};
|
||||
setTabs((prev) => [...prev, newTab]);
|
||||
setSelectedTab(newTab);
|
||||
setMode("viewer");
|
||||
|
||||
setIsNewTabWindow(false);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
subscribeToEvent("addTab", addTabFunc);
|
||||
|
||||
return () => {
|
||||
unsubscribeFromEvent("addTab", addTabFunc);
|
||||
};
|
||||
}, [tabs]);
|
||||
|
||||
const setSelectedTabFunc = (e) => {
|
||||
const data = e.detail?.data;
|
||||
|
||||
setSelectedTab(data);
|
||||
setTimeout(() => {
|
||||
executeEvent("setTabsToNav", {
|
||||
data: {
|
||||
tabs: tabs,
|
||||
selectedTab: data,
|
||||
isNewTabWindow: isNewTabWindow,
|
||||
},
|
||||
});
|
||||
}, 100);
|
||||
setIsNewTabWindow(false);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
subscribeToEvent("setSelectedTab", setSelectedTabFunc);
|
||||
|
||||
return () => {
|
||||
unsubscribeFromEvent("setSelectedTab", setSelectedTabFunc);
|
||||
};
|
||||
}, [tabs, isNewTabWindow]);
|
||||
|
||||
const removeTabFunc = (e) => {
|
||||
const data = e.detail?.data;
|
||||
const copyTabs = [...tabs].filter((tab) => tab?.tabId !== data?.tabId);
|
||||
if (copyTabs?.length === 0) {
|
||||
setMode("home");
|
||||
} else {
|
||||
setSelectedTab(copyTabs[0]);
|
||||
}
|
||||
setTabs(copyTabs);
|
||||
setSelectedTab(copyTabs[0]);
|
||||
setTimeout(() => {
|
||||
executeEvent("setTabsToNav", {
|
||||
data: {
|
||||
tabs: copyTabs,
|
||||
selectedTab: copyTabs[0],
|
||||
},
|
||||
});
|
||||
}, 400);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
subscribeToEvent("removeTab", removeTabFunc);
|
||||
|
||||
return () => {
|
||||
unsubscribeFromEvent("removeTab", removeTabFunc);
|
||||
};
|
||||
}, [tabs]);
|
||||
|
||||
const setNewTabWindowFunc = (e) => {
|
||||
setIsNewTabWindow(true);
|
||||
setSelectedTab(null)
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
subscribeToEvent("newTabWindow", setNewTabWindowFunc);
|
||||
|
||||
return () => {
|
||||
unsubscribeFromEvent("newTabWindow", setNewTabWindowFunc);
|
||||
};
|
||||
}, [tabs]);
|
||||
|
||||
|
||||
return (
|
||||
<AppsParent
|
||||
sx={{
|
||||
display: !show && "none",
|
||||
flexDirection: 'row'
|
||||
}}
|
||||
>
|
||||
|
||||
<Box sx={{
|
||||
width: '60px',
|
||||
flexDirection: 'column',
|
||||
height: '100vh',
|
||||
alignItems: 'center',
|
||||
display: 'flex',
|
||||
gap: '30px'
|
||||
}}>
|
||||
<ButtonBase
|
||||
sx={{
|
||||
width: '60px',
|
||||
height: '60px',
|
||||
paddingTop: '23px'
|
||||
}}
|
||||
onClick={() => {
|
||||
goToHome();
|
||||
|
||||
}}
|
||||
>
|
||||
|
||||
<HomeIcon
|
||||
height={34}
|
||||
color="rgba(250, 250, 250, 0.5)"
|
||||
/>
|
||||
|
||||
</ButtonBase>
|
||||
<ButtonBase
|
||||
onClick={() => {
|
||||
setDesktopSideView("directs");
|
||||
toggleSideViewDirects()
|
||||
}}
|
||||
>
|
||||
|
||||
<MessagingIcon
|
||||
height={30}
|
||||
color={
|
||||
hasUnreadDirects
|
||||
? "var(--unread)"
|
||||
: isDirects
|
||||
? "white"
|
||||
: "rgba(250, 250, 250, 0.5)"
|
||||
}
|
||||
/>
|
||||
|
||||
</ButtonBase>
|
||||
<ButtonBase
|
||||
onClick={() => {
|
||||
setDesktopSideView("groups");
|
||||
toggleSideViewGroups()
|
||||
}}
|
||||
>
|
||||
<HubsIcon
|
||||
height={30}
|
||||
color={
|
||||
hasUnreadGroups
|
||||
? "var(--unread)"
|
||||
: isGroups
|
||||
? "white"
|
||||
: "rgba(250, 250, 250, 0.5)"
|
||||
}
|
||||
/>
|
||||
|
||||
</ButtonBase>
|
||||
<Save isDesktop />
|
||||
{mode !== 'home' && (
|
||||
<AppsNavBarDesktop />
|
||||
|
||||
)}
|
||||
|
||||
</Box>
|
||||
|
||||
|
||||
{mode === "home" && (
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
width: '100%',
|
||||
flexDirection: 'column',
|
||||
height: '100vh',
|
||||
overflow: 'auto'
|
||||
}}>
|
||||
|
||||
<Spacer height="30px" />
|
||||
<AppsHomeDesktop availableQapps={availableQapps} setMode={setMode} myApp={myApp} myWebsite={myWebsite} />
|
||||
</Box>
|
||||
)}
|
||||
|
||||
<AppsLibraryDesktop
|
||||
isShow={mode === "library" && !selectedTab}
|
||||
availableQapps={availableQapps}
|
||||
setMode={setMode}
|
||||
myName={myName}
|
||||
hasPublishApp={!!(myApp || myWebsite)}
|
||||
categories={categories}
|
||||
/>
|
||||
|
||||
{mode === "appInfo" && <AppInfo app={selectedAppInfo} myName={myName} />}
|
||||
{mode === "appInfo-from-category" && <AppInfo app={selectedAppInfo} myName={myName} />}
|
||||
<AppsCategoryDesktop availableQapps={availableQapps} isShow={mode === 'category' && !selectedTab} category={selectedCategory} myName={myName} />
|
||||
{mode === "publish" && <AppPublish names={myName ? [myName] : []} categories={categories} />}
|
||||
|
||||
{tabs.map((tab) => {
|
||||
return (
|
||||
<AppViewerContainer
|
||||
hide={isNewTabWindow}
|
||||
isSelected={tab?.tabId === selectedTab?.tabId}
|
||||
app={tab}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
|
||||
{isNewTabWindow && mode === "viewer" && (
|
||||
<>
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
width: '100%',
|
||||
flexDirection: 'column',
|
||||
height: '100vh',
|
||||
overflow: 'auto'
|
||||
}}>
|
||||
|
||||
<Spacer height="30px" />
|
||||
<AppsHomeDesktop availableQapps={availableQapps} setMode={setMode} myApp={myApp} myWebsite={myWebsite} />
|
||||
</Box>
|
||||
</>
|
||||
)}
|
||||
</AppsParent>
|
||||
);
|
||||
};
|
39
src/components/Apps/AppsHomeDesktop.tsx
Normal file
39
src/components/Apps/AppsHomeDesktop.tsx
Normal file
@@ -0,0 +1,39 @@
|
||||
import React, { useMemo, useState } from "react";
|
||||
import {
|
||||
AppCircle,
|
||||
AppCircleContainer,
|
||||
AppCircleLabel,
|
||||
AppsContainer,
|
||||
AppsParent,
|
||||
} from "./Apps-styles";
|
||||
import { Avatar, ButtonBase } from "@mui/material";
|
||||
import { Add } from "@mui/icons-material";
|
||||
import { getBaseApiReact } from "../../App";
|
||||
import LogoSelected from "../../assets/svgs/LogoSelected.svg";
|
||||
import { executeEvent } from "../../utils/events";
|
||||
import { SortablePinnedApps } from "./SortablePinnedApps";
|
||||
|
||||
export const AppsHomeDesktop = ({ setMode, myApp, myWebsite, availableQapps }) => {
|
||||
return (
|
||||
<AppsContainer sx={{
|
||||
gap: '75px',
|
||||
justifyContent: 'flex-start'
|
||||
}}>
|
||||
<ButtonBase
|
||||
onClick={() => {
|
||||
setMode("library");
|
||||
}}
|
||||
>
|
||||
<AppCircleContainer>
|
||||
<AppCircle>
|
||||
<Add>+</Add>
|
||||
</AppCircle>
|
||||
<AppCircleLabel>Add</AppCircleLabel>
|
||||
</AppCircleContainer>
|
||||
</ButtonBase>
|
||||
|
||||
<SortablePinnedApps isDesktop={true} availableQapps={availableQapps} myWebsite={myWebsite} myApp={myApp} />
|
||||
|
||||
</AppsContainer>
|
||||
);
|
||||
};
|
411
src/components/Apps/AppsLibraryDesktop.tsx
Normal file
411
src/components/Apps/AppsLibraryDesktop.tsx
Normal file
@@ -0,0 +1,411 @@
|
||||
import React, {
|
||||
useCallback,
|
||||
useContext,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
} from "react";
|
||||
import {
|
||||
AppCircle,
|
||||
AppCircleContainer,
|
||||
AppCircleLabel,
|
||||
AppLibrarySubTitle,
|
||||
AppsContainer,
|
||||
AppsLibraryContainer,
|
||||
AppsParent,
|
||||
AppsSearchContainer,
|
||||
AppsSearchLeft,
|
||||
AppsSearchRight,
|
||||
AppsWidthLimiter,
|
||||
PublishQAppCTAButton,
|
||||
PublishQAppCTALeft,
|
||||
PublishQAppCTAParent,
|
||||
PublishQAppCTARight,
|
||||
PublishQAppDotsBG,
|
||||
} from "./Apps-styles";
|
||||
import { Avatar, Box, ButtonBase, InputBase, styled } from "@mui/material";
|
||||
import { Add } from "@mui/icons-material";
|
||||
import { MyContext, getBaseApiReact } from "../../App";
|
||||
import LogoSelected from "../../assets/svgs/LogoSelected.svg";
|
||||
import IconSearch from "../../assets/svgs/Search.svg";
|
||||
import IconClearInput from "../../assets/svgs/ClearInput.svg";
|
||||
import qappDevelopText from "../../assets/svgs/qappDevelopText.svg";
|
||||
import qappLibraryText from "../../assets/svgs/qappLibraryText.svg";
|
||||
|
||||
import qappDots from "../../assets/svgs/qappDots.svg";
|
||||
|
||||
import { Spacer } from "../../common/Spacer";
|
||||
import { AppInfoSnippet } from "./AppInfoSnippet";
|
||||
import { Virtuoso } from "react-virtuoso";
|
||||
import { executeEvent } from "../../utils/events";
|
||||
import {
|
||||
AppsDesktopLibraryBody,
|
||||
AppsDesktopLibraryHeader,
|
||||
} from "./AppsDesktop-styles";
|
||||
import { AppsNavBarDesktop } from "./AppsNavBarDesktop";
|
||||
const officialAppList = [
|
||||
"q-tube",
|
||||
"q-blog",
|
||||
"q-share",
|
||||
"q-support",
|
||||
"q-mail",
|
||||
"qombo",
|
||||
"q-fund",
|
||||
"q-shop",
|
||||
];
|
||||
|
||||
const ScrollerStyled = styled("div")({
|
||||
// Hide scrollbar for WebKit browsers (Chrome, Safari)
|
||||
"::-webkit-scrollbar": {
|
||||
width: "0px",
|
||||
height: "0px",
|
||||
},
|
||||
|
||||
// Hide scrollbar for Firefox
|
||||
scrollbarWidth: "none",
|
||||
|
||||
// Hide scrollbar for IE and older Edge
|
||||
"-ms-overflow-style": "none",
|
||||
});
|
||||
|
||||
const StyledVirtuosoContainer = styled("div")({
|
||||
position: "relative",
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
|
||||
// Hide scrollbar for WebKit browsers (Chrome, Safari)
|
||||
"::-webkit-scrollbar": {
|
||||
width: "0px",
|
||||
height: "0px",
|
||||
},
|
||||
|
||||
// Hide scrollbar for Firefox
|
||||
scrollbarWidth: "none",
|
||||
|
||||
// Hide scrollbar for IE and older Edge
|
||||
"-ms-overflow-style": "none",
|
||||
});
|
||||
|
||||
export const AppsLibraryDesktop = ({
|
||||
availableQapps,
|
||||
setMode,
|
||||
myName,
|
||||
hasPublishApp,
|
||||
isShow,
|
||||
categories = { categories },
|
||||
}) => {
|
||||
const [searchValue, setSearchValue] = useState("");
|
||||
const virtuosoRef = useRef();
|
||||
|
||||
const officialApps = useMemo(() => {
|
||||
return availableQapps.filter(
|
||||
(app) =>
|
||||
app.service === "APP" &&
|
||||
officialAppList.includes(app?.name?.toLowerCase())
|
||||
);
|
||||
}, [availableQapps]);
|
||||
|
||||
const [debouncedValue, setDebouncedValue] = useState(""); // Debounced value
|
||||
|
||||
// Debounce logic
|
||||
useEffect(() => {
|
||||
const handler = setTimeout(() => {
|
||||
setDebouncedValue(searchValue);
|
||||
}, 350);
|
||||
|
||||
// Cleanup timeout if searchValue changes before the timeout completes
|
||||
return () => {
|
||||
clearTimeout(handler);
|
||||
};
|
||||
}, [searchValue]); // Runs effect when searchValue changes
|
||||
|
||||
// Example: Perform search or other actions based on debouncedValue
|
||||
|
||||
const searchedList = useMemo(() => {
|
||||
if (!debouncedValue) return [];
|
||||
return availableQapps.filter((app) =>
|
||||
app.name.toLowerCase().includes(debouncedValue.toLowerCase())
|
||||
);
|
||||
}, [debouncedValue]);
|
||||
|
||||
const rowRenderer = (index) => {
|
||||
let app = searchedList[index];
|
||||
return (
|
||||
<AppInfoSnippet
|
||||
key={`${app?.service}-${app?.name}`}
|
||||
app={app}
|
||||
myName={myName}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<AppsLibraryContainer
|
||||
sx={{
|
||||
display: !isShow && "none",
|
||||
padding: "0px",
|
||||
height: "100vh",
|
||||
overflow: "hidden",
|
||||
paddingTop: '30px'
|
||||
}}
|
||||
>
|
||||
|
||||
<AppsDesktopLibraryHeader
|
||||
sx={{
|
||||
maxWidth: "1500px",
|
||||
width: "90%",
|
||||
}}
|
||||
>
|
||||
<AppsWidthLimiter>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
width: "100%",
|
||||
justifyContent: "space-between",
|
||||
}}
|
||||
>
|
||||
<img src={qappLibraryText} />
|
||||
<AppsSearchContainer
|
||||
sx={{
|
||||
width: "412px",
|
||||
}}
|
||||
>
|
||||
<AppsSearchLeft>
|
||||
<img src={IconSearch} />
|
||||
<InputBase
|
||||
value={searchValue}
|
||||
onChange={(e) => setSearchValue(e.target.value)}
|
||||
sx={{ ml: 1, flex: 1 }}
|
||||
placeholder="Search for apps"
|
||||
inputProps={{
|
||||
"aria-label": "Search for apps",
|
||||
fontSize: "16px",
|
||||
fontWeight: 400,
|
||||
}}
|
||||
/>
|
||||
</AppsSearchLeft>
|
||||
<AppsSearchRight>
|
||||
{searchValue && (
|
||||
<ButtonBase
|
||||
onClick={() => {
|
||||
setSearchValue("");
|
||||
}}
|
||||
>
|
||||
<img src={IconClearInput} />
|
||||
</ButtonBase>
|
||||
)}
|
||||
</AppsSearchRight>
|
||||
</AppsSearchContainer>
|
||||
</Box>
|
||||
</AppsWidthLimiter>
|
||||
</AppsDesktopLibraryHeader>
|
||||
<AppsDesktopLibraryBody
|
||||
sx={{
|
||||
height: `calc(100vh - 36px)`,
|
||||
overflow: "auto",
|
||||
padding: "0px",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<AppsDesktopLibraryBody
|
||||
sx={{
|
||||
height: `calc(100vh - 36px)`,
|
||||
flexGrow: "unset",
|
||||
maxWidth: "1500px",
|
||||
width: "90%",
|
||||
}}
|
||||
>
|
||||
<Spacer height="90px" />
|
||||
{searchedList?.length > 0 ? (
|
||||
<AppsWidthLimiter>
|
||||
<StyledVirtuosoContainer
|
||||
sx={{
|
||||
height: `calc(100vh - 36px - 90px)`,
|
||||
}}
|
||||
>
|
||||
<Virtuoso
|
||||
ref={virtuosoRef}
|
||||
data={searchedList}
|
||||
itemContent={rowRenderer}
|
||||
atBottomThreshold={50}
|
||||
followOutput="smooth"
|
||||
components={{
|
||||
Scroller: ScrollerStyled, // Use the styled scroller component
|
||||
}}
|
||||
/>
|
||||
</StyledVirtuosoContainer>
|
||||
</AppsWidthLimiter>
|
||||
) : (
|
||||
<>
|
||||
<AppLibrarySubTitle
|
||||
sx={{
|
||||
fontSize: "30px",
|
||||
}}
|
||||
>
|
||||
Official Apps
|
||||
</AppLibrarySubTitle>
|
||||
<Spacer height="45px" />
|
||||
<AppsContainer>
|
||||
{officialApps?.map((qapp) => {
|
||||
return (
|
||||
<ButtonBase
|
||||
sx={{
|
||||
height: "80px",
|
||||
width: "60px",
|
||||
}}
|
||||
onClick={() => {
|
||||
// executeEvent("addTab", {
|
||||
// data: qapp
|
||||
// })
|
||||
executeEvent("selectedAppInfo", {
|
||||
data: qapp,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<AppCircleContainer
|
||||
sx={{
|
||||
gap: "10px",
|
||||
}}
|
||||
>
|
||||
<AppCircle
|
||||
sx={{
|
||||
border: "none",
|
||||
}}
|
||||
>
|
||||
<Avatar
|
||||
sx={{
|
||||
height: "31px",
|
||||
width: "31px",
|
||||
}}
|
||||
alt={qapp?.name}
|
||||
src={`${getBaseApiReact()}/arbitrary/THUMBNAIL/${
|
||||
qapp?.name
|
||||
}/qortal_avatar?async=true`}
|
||||
>
|
||||
<img
|
||||
style={{
|
||||
width: "31px",
|
||||
height: "auto",
|
||||
}}
|
||||
src={LogoSelected}
|
||||
alt="center-icon"
|
||||
/>
|
||||
</Avatar>
|
||||
</AppCircle>
|
||||
<AppCircleLabel>
|
||||
{qapp?.metadata?.title || qapp?.name}
|
||||
</AppCircleLabel>
|
||||
</AppCircleContainer>
|
||||
</ButtonBase>
|
||||
);
|
||||
})}
|
||||
</AppsContainer>
|
||||
<Spacer height="80px" />
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
gap: "250px",
|
||||
display: "flex",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
}}
|
||||
>
|
||||
<AppLibrarySubTitle
|
||||
sx={{
|
||||
fontSize: "30px",
|
||||
width: "100%",
|
||||
textAlign: "start",
|
||||
}}
|
||||
>
|
||||
{hasPublishApp ? "Update Apps!" : "Create Apps!"}
|
||||
</AppLibrarySubTitle>
|
||||
<Spacer height="18px" />
|
||||
<PublishQAppCTAParent
|
||||
sx={{
|
||||
gap: "25px",
|
||||
}}
|
||||
>
|
||||
<PublishQAppCTALeft>
|
||||
<PublishQAppDotsBG>
|
||||
<img src={qappDots} />
|
||||
</PublishQAppDotsBG>
|
||||
<Spacer width="29px" />
|
||||
<img src={qappDevelopText} />
|
||||
</PublishQAppCTALeft>
|
||||
<PublishQAppCTARight
|
||||
onClick={() => {
|
||||
setMode("publish");
|
||||
}}
|
||||
>
|
||||
<PublishQAppCTAButton>
|
||||
{hasPublishApp ? "Update" : "Publish"}
|
||||
</PublishQAppCTAButton>
|
||||
<Spacer width="20px" />
|
||||
</PublishQAppCTARight>
|
||||
</PublishQAppCTAParent>
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
}}
|
||||
>
|
||||
<AppLibrarySubTitle
|
||||
sx={{
|
||||
fontSize: "30px",
|
||||
}}
|
||||
>
|
||||
Categories
|
||||
</AppLibrarySubTitle>
|
||||
<Spacer height="18px" />
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
gap: "20px",
|
||||
flexWrap: "wrap",
|
||||
}}
|
||||
>
|
||||
{categories?.map((category) => {
|
||||
return (
|
||||
<ButtonBase
|
||||
key={category?.id}
|
||||
onClick={() => {
|
||||
executeEvent("selectedCategory", {
|
||||
data: category,
|
||||
});
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
height: "60px",
|
||||
padding: "0px 24px",
|
||||
border: "4px solid #10242F",
|
||||
borderRadius: "6px",
|
||||
boxShadow: "2px 4px 0px 0px #000000",
|
||||
}}
|
||||
>
|
||||
{category?.name}
|
||||
</Box>
|
||||
</ButtonBase>
|
||||
);
|
||||
})}
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
</>
|
||||
)}
|
||||
</AppsDesktopLibraryBody>
|
||||
</AppsDesktopLibraryBody>
|
||||
</AppsLibraryContainer>
|
||||
);
|
||||
};
|
347
src/components/Apps/AppsNavBarDesktop.tsx
Normal file
347
src/components/Apps/AppsNavBarDesktop.tsx
Normal file
@@ -0,0 +1,347 @@
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import {
|
||||
AppsNavBarLeft,
|
||||
AppsNavBarParent,
|
||||
AppsNavBarRight,
|
||||
} from "./Apps-styles";
|
||||
import NavBack from "../../assets/svgs/NavBack.svg";
|
||||
import NavAdd from "../../assets/svgs/NavAdd.svg";
|
||||
import NavMoreMenu from "../../assets/svgs/NavMoreMenu.svg";
|
||||
import {
|
||||
ButtonBase,
|
||||
ListItemIcon,
|
||||
ListItemText,
|
||||
Menu,
|
||||
MenuItem,
|
||||
Tab,
|
||||
Tabs,
|
||||
} from "@mui/material";
|
||||
import {
|
||||
executeEvent,
|
||||
subscribeToEvent,
|
||||
unsubscribeFromEvent,
|
||||
} from "../../utils/events";
|
||||
import TabComponent from "./TabComponent";
|
||||
import PushPinIcon from "@mui/icons-material/PushPin";
|
||||
import RefreshIcon from "@mui/icons-material/Refresh";
|
||||
import { useRecoilState, useSetRecoilState } from "recoil";
|
||||
import {
|
||||
settingsLocalLastUpdatedAtom,
|
||||
sortablePinnedAppsAtom,
|
||||
} from "../../atoms/global";
|
||||
|
||||
export function saveToLocalStorage(key, subKey, newValue) {
|
||||
try {
|
||||
// Fetch existing data
|
||||
const existingData = localStorage.getItem(key);
|
||||
let combinedData = {};
|
||||
|
||||
if (existingData) {
|
||||
// Parse the existing data
|
||||
const parsedData = JSON.parse(existingData);
|
||||
// Merge with the new data under the subKey
|
||||
combinedData = {
|
||||
...parsedData,
|
||||
timestamp: Date.now(), // Update the root timestamp
|
||||
[subKey]: newValue, // Assuming the data is an array
|
||||
};
|
||||
} else {
|
||||
// If no existing data, just use the new data under the subKey
|
||||
combinedData = {
|
||||
timestamp: Date.now(), // Set the initial root timestamp
|
||||
[subKey]: newValue,
|
||||
};
|
||||
}
|
||||
|
||||
// Save combined data back to localStorage
|
||||
const serializedValue = JSON.stringify(combinedData);
|
||||
localStorage.setItem(key, serializedValue);
|
||||
} catch (error) {
|
||||
console.error("Error saving to localStorage:", error);
|
||||
}
|
||||
}
|
||||
|
||||
export const AppsNavBarDesktop = () => {
|
||||
const [tabs, setTabs] = useState([]);
|
||||
const [selectedTab, setSelectedTab] = useState(null);
|
||||
const [isNewTabWindow, setIsNewTabWindow] = useState(false);
|
||||
const tabsRef = useRef(null);
|
||||
const [anchorEl, setAnchorEl] = useState(null);
|
||||
const open = Boolean(anchorEl);
|
||||
const [sortablePinnedApps, setSortablePinnedApps] = useRecoilState(
|
||||
sortablePinnedAppsAtom
|
||||
);
|
||||
|
||||
const setSettingsLocalLastUpdated = useSetRecoilState(
|
||||
settingsLocalLastUpdatedAtom
|
||||
);
|
||||
|
||||
const handleClick = (event) => {
|
||||
setAnchorEl(event.currentTarget);
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
setAnchorEl(null);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
// Scroll to the last tab whenever the tabs array changes (e.g., when a new tab is added)
|
||||
if (tabsRef.current) {
|
||||
const tabElements = tabsRef.current.querySelectorAll(".MuiTab-root");
|
||||
if (tabElements.length > 0) {
|
||||
const lastTab = tabElements[tabElements.length - 1];
|
||||
lastTab.scrollIntoView({
|
||||
behavior: "smooth",
|
||||
block: "nearest",
|
||||
inline: "end",
|
||||
});
|
||||
}
|
||||
}
|
||||
}, [tabs.length]); // Dependency on the number of tabs
|
||||
|
||||
const setTabsToNav = (e) => {
|
||||
const { tabs, selectedTab, isNewTabWindow } = e.detail?.data;
|
||||
|
||||
setTabs([...tabs]);
|
||||
setSelectedTab(!selectedTab ? nulll : { ...selectedTab });
|
||||
setIsNewTabWindow(isNewTabWindow);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
subscribeToEvent("setTabsToNav", setTabsToNav);
|
||||
|
||||
return () => {
|
||||
unsubscribeFromEvent("setTabsToNav", setTabsToNav);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const isSelectedAppPinned = !!sortablePinnedApps?.find(
|
||||
(item) =>
|
||||
item?.name === selectedTab?.name && item?.service === selectedTab?.service
|
||||
);
|
||||
return (
|
||||
<AppsNavBarParent
|
||||
sx={{
|
||||
position: "relative",
|
||||
flexDirection: "column",
|
||||
width: "60px",
|
||||
height: "unset",
|
||||
maxHeight: "70vh",
|
||||
borderRadius: "0px 30px 30px 0px",
|
||||
padding: "10px",
|
||||
}}
|
||||
>
|
||||
<AppsNavBarLeft
|
||||
sx={{
|
||||
flexDirection: "column",
|
||||
}}
|
||||
>
|
||||
<ButtonBase
|
||||
onClick={() => {
|
||||
executeEvent("navigateBack", {});
|
||||
}}
|
||||
>
|
||||
<img src={NavBack} />
|
||||
</ButtonBase>
|
||||
<Tabs
|
||||
orientation="vertical"
|
||||
ref={tabsRef}
|
||||
aria-label="basic tabs example"
|
||||
variant="scrollable" // Make tabs scrollable
|
||||
scrollButtons={true}
|
||||
sx={{
|
||||
"& .MuiTabs-indicator": {
|
||||
backgroundColor: "white",
|
||||
},
|
||||
maxHeight: `320px`, // Ensure the tabs container fits within the available space
|
||||
overflow: "hidden", // Prevents overflow on small screens
|
||||
}}
|
||||
>
|
||||
{tabs?.map((tab) => (
|
||||
<Tab
|
||||
key={tab.tabId}
|
||||
label={
|
||||
<TabComponent
|
||||
isSelected={
|
||||
tab?.tabId === selectedTab?.tabId && !isNewTabWindow
|
||||
}
|
||||
app={tab}
|
||||
/>
|
||||
} // Pass custom component
|
||||
sx={{
|
||||
"&.Mui-selected": {
|
||||
color: "white",
|
||||
},
|
||||
padding: "0px",
|
||||
margin: "0px",
|
||||
minWidth: "0px",
|
||||
width: "50px",
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</Tabs>
|
||||
</AppsNavBarLeft>
|
||||
<AppsNavBarRight
|
||||
sx={{
|
||||
gap: "10px",
|
||||
flexDirection: "column",
|
||||
}}
|
||||
>
|
||||
<ButtonBase
|
||||
onClick={() => {
|
||||
setSelectedTab(null);
|
||||
executeEvent("newTabWindow", {});
|
||||
}}
|
||||
>
|
||||
<img
|
||||
style={{
|
||||
height: "40px",
|
||||
width: "40px",
|
||||
}}
|
||||
src={NavAdd}
|
||||
/>
|
||||
</ButtonBase>
|
||||
<ButtonBase
|
||||
onClick={(e) => {
|
||||
if (!selectedTab) return;
|
||||
handleClick(e);
|
||||
}}
|
||||
>
|
||||
<img
|
||||
style={{
|
||||
height: "34px",
|
||||
width: "34px",
|
||||
}}
|
||||
src={NavMoreMenu}
|
||||
/>
|
||||
</ButtonBase>
|
||||
</AppsNavBarRight>
|
||||
<Menu
|
||||
id="navbar-more-mobile"
|
||||
anchorEl={anchorEl}
|
||||
open={open}
|
||||
onClose={handleClose}
|
||||
MenuListProps={{
|
||||
"aria-labelledby": "basic-button",
|
||||
}}
|
||||
anchorOrigin={{
|
||||
vertical: "bottom",
|
||||
horizontal: "center",
|
||||
}}
|
||||
transformOrigin={{
|
||||
vertical: "top",
|
||||
horizontal: "center",
|
||||
}}
|
||||
slotProps={{
|
||||
paper: {
|
||||
sx: {
|
||||
backgroundColor: "var(--bg-primary)",
|
||||
color: "#fff",
|
||||
width: "148px",
|
||||
borderRadius: "5px",
|
||||
},
|
||||
},
|
||||
}}
|
||||
sx={{
|
||||
marginTop: "10px",
|
||||
}}
|
||||
>
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
if (!selectedTab) return;
|
||||
|
||||
setSortablePinnedApps((prev) => {
|
||||
let updatedApps;
|
||||
|
||||
if (isSelectedAppPinned) {
|
||||
// Remove the selected app if it is pinned
|
||||
updatedApps = prev.filter(
|
||||
(item) =>
|
||||
!(
|
||||
item?.name === selectedTab?.name &&
|
||||
item?.service === selectedTab?.service
|
||||
)
|
||||
);
|
||||
} else {
|
||||
// Add the selected app if it is not pinned
|
||||
updatedApps = [
|
||||
...prev,
|
||||
{
|
||||
name: selectedTab?.name,
|
||||
service: selectedTab?.service,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
saveToLocalStorage(
|
||||
"ext_saved_settings",
|
||||
"sortablePinnedApps",
|
||||
updatedApps
|
||||
);
|
||||
return updatedApps;
|
||||
});
|
||||
setSettingsLocalLastUpdated(Date.now());
|
||||
|
||||
handleClose();
|
||||
}}
|
||||
>
|
||||
<ListItemIcon
|
||||
sx={{
|
||||
minWidth: "24px !important",
|
||||
marginRight: "5px",
|
||||
}}
|
||||
>
|
||||
<PushPinIcon
|
||||
height={20}
|
||||
sx={{
|
||||
color: isSelectedAppPinned ? "red" : "rgba(250, 250, 250, 0.5)",
|
||||
}}
|
||||
/>
|
||||
</ListItemIcon>
|
||||
<ListItemText
|
||||
sx={{
|
||||
"& .MuiTypography-root": {
|
||||
fontSize: "12px",
|
||||
fontWeight: 600,
|
||||
color: isSelectedAppPinned ? "red" : "rgba(250, 250, 250, 0.5)",
|
||||
},
|
||||
}}
|
||||
primary={`${isSelectedAppPinned ? "Unpin app" : "Pin app"}`}
|
||||
/>
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
executeEvent("refreshApp", {
|
||||
tabId: selectedTab?.tabId,
|
||||
});
|
||||
handleClose();
|
||||
}}
|
||||
>
|
||||
<ListItemIcon
|
||||
sx={{
|
||||
minWidth: "24px !important",
|
||||
marginRight: "5px",
|
||||
}}
|
||||
>
|
||||
<RefreshIcon
|
||||
height={20}
|
||||
sx={{
|
||||
color: "rgba(250, 250, 250, 0.5)",
|
||||
}}
|
||||
/>
|
||||
</ListItemIcon>
|
||||
<ListItemText
|
||||
sx={{
|
||||
"& .MuiTypography-root": {
|
||||
fontSize: "12px",
|
||||
fontWeight: 600,
|
||||
color: "rgba(250, 250, 250, 0.5)",
|
||||
},
|
||||
}}
|
||||
primary="Refresh"
|
||||
/>
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
</AppsNavBarParent>
|
||||
);
|
||||
};
|
@@ -12,7 +12,7 @@ import { useRecoilState, useSetRecoilState } from 'recoil';
|
||||
import { saveToLocalStorage } from './AppsNavBar';
|
||||
import { ContextMenuPinnedApps } from '../ContextMenuPinnedApps';
|
||||
|
||||
const SortableItem = ({ id, name, app }) => {
|
||||
const SortableItem = ({ id, name, app, isDesktop }) => {
|
||||
const { attributes, listeners, setNodeRef, transform, transition } = useSortable({ id });
|
||||
const style = {
|
||||
transform: CSS.Transform.toString(transform),
|
||||
@@ -42,7 +42,10 @@ const SortableItem = ({ id, name, app }) => {
|
||||
})
|
||||
}}
|
||||
>
|
||||
<AppCircleContainer>
|
||||
<AppCircleContainer sx={{
|
||||
border: "none",
|
||||
gap: isDesktop ? '10px': '5px'
|
||||
}}>
|
||||
<AppCircle
|
||||
sx={{
|
||||
border: "none",
|
||||
@@ -80,7 +83,7 @@ const SortableItem = ({ id, name, app }) => {
|
||||
);
|
||||
};
|
||||
|
||||
export const SortablePinnedApps = ({ myWebsite, myApp, availableQapps = [] }) => {
|
||||
export const SortablePinnedApps = ({ isDesktop, myWebsite, myApp, availableQapps = [] }) => {
|
||||
const [pinnedApps, setPinnedApps] = useRecoilState(sortablePinnedAppsAtom);
|
||||
const setSettingsLocalLastUpdated = useSetRecoilState(settingsLocalLastUpdatedAtom);
|
||||
|
||||
@@ -164,7 +167,7 @@ export const SortablePinnedApps = ({ myWebsite, myApp, availableQapps = [] }) =
|
||||
<DndContext sensors={sensors} collisionDetection={closestCenter} onDragEnd={handleDragEnd}>
|
||||
<SortableContext items={transformPinnedApps.map((app) => `${app?.service}-${app?.name}`)}>
|
||||
{transformPinnedApps.map((app) => (
|
||||
<SortableItem key={`${app?.service}-${app?.name}`} id={`${app?.service}-${app?.name}`} name={app?.name} app={app} />
|
||||
<SortableItem isDesktop={isDesktop} key={`${app?.service}-${app?.name}`} id={`${app?.service}-${app?.name}`} name={app?.name} app={app} />
|
||||
))}
|
||||
</SortableContext>
|
||||
</DndContext>
|
||||
|
@@ -13,21 +13,24 @@ import { WalletIcon } from "../../assets/Icons/WalletIcon";
|
||||
import { HubsIcon } from "../../assets/Icons/HubsIcon";
|
||||
import { TradingIcon } from "../../assets/Icons/TradingIcon";
|
||||
import { MessagingIcon } from "../../assets/Icons/MessagingIcon";
|
||||
import { HomeIcon } from "../../assets/Icons/HomeIcon";
|
||||
import AppIcon from "../../assets/svgs/AppIcon.svg";
|
||||
|
||||
const IconWrapper = ({ children, label, color, selected }) => {
|
||||
import { HomeIcon } from "../../assets/Icons/HomeIcon";
|
||||
import { Save } from "../Save/Save";
|
||||
|
||||
export const IconWrapper = ({ children, label, color, selected }) => {
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
gap: "5px",
|
||||
gap: "5px",
|
||||
flexDirection: "column",
|
||||
height: '89px',
|
||||
width: '89px',
|
||||
borderRadius: '50%',
|
||||
backgroundColor: selected ? 'rgba(28, 29, 32, 1)' : 'transparent'
|
||||
height: "89px",
|
||||
width: "89px",
|
||||
borderRadius: "50%",
|
||||
backgroundColor: selected ? "rgba(28, 29, 32, 1)" : "transparent",
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
@@ -69,9 +72,17 @@ export const DesktopFooter = ({
|
||||
isHome,
|
||||
isGroups,
|
||||
isDirects,
|
||||
setDesktopSideView
|
||||
setDesktopSideView,
|
||||
isApps,
|
||||
setDesktopViewMode,
|
||||
desktopViewMode,
|
||||
hide,
|
||||
setIsOpenSideViewDirects,
|
||||
setisOpenSideViewDirects
|
||||
|
||||
}) => {
|
||||
const [value, setValue] = React.useState(0);
|
||||
|
||||
if(hide) return
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
@@ -82,37 +93,93 @@ export const DesktopFooter = ({
|
||||
alignItems: "center",
|
||||
height: "100px", // Footer height
|
||||
zIndex: 1,
|
||||
justifyContent: 'center'
|
||||
justifyContent: "center",
|
||||
}}
|
||||
>
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
gap: '20px'
|
||||
}}>
|
||||
<ButtonBase onClick={()=> {
|
||||
goToHome()
|
||||
}}>
|
||||
<IconWrapper color="rgba(250, 250, 250, 0.5)" label="Home" selected={isHome}>
|
||||
<HomeIcon height={30} color={isHome ? "white" : "rgba(250, 250, 250, 0.5)"} />
|
||||
</IconWrapper>
|
||||
</ButtonBase>
|
||||
<ButtonBase onClick={()=> {
|
||||
setDesktopSideView('groups')
|
||||
}}>
|
||||
<IconWrapper color="rgba(250, 250, 250, 0.5)" label="Hubs" selected={isGroups}>
|
||||
<HubsIcon height={30} color={hasUnreadGroups ? "var(--unread)" : isGroups ? 'white' : "rgba(250, 250, 250, 0.5)"} />
|
||||
</IconWrapper>
|
||||
</ButtonBase>
|
||||
<ButtonBase onClick={()=> {
|
||||
setDesktopSideView('directs')
|
||||
}}>
|
||||
|
||||
<IconWrapper color="rgba(250, 250, 250, 0.5)" label="Messaging" selected={isDirects}>
|
||||
<MessagingIcon height={30} color={hasUnreadDirects ? "var(--unread)" : isDirects ? 'white' : "rgba(250, 250, 250, 0.5)"} />
|
||||
</IconWrapper>
|
||||
</ButtonBase>
|
||||
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
gap: "20px",
|
||||
}}
|
||||
>
|
||||
<ButtonBase
|
||||
onClick={() => {
|
||||
goToHome();
|
||||
}}
|
||||
>
|
||||
<IconWrapper
|
||||
color="rgba(250, 250, 250, 0.5)"
|
||||
label="Home"
|
||||
selected={isHome}
|
||||
>
|
||||
<HomeIcon
|
||||
height={30}
|
||||
color={isHome ? "white" : "rgba(250, 250, 250, 0.5)"}
|
||||
/>
|
||||
</IconWrapper>
|
||||
</ButtonBase>
|
||||
<ButtonBase
|
||||
onClick={() => {
|
||||
setDesktopViewMode('apps')
|
||||
setIsOpenSideViewDirects(false)
|
||||
setIsOpenSideViewGroups(false)
|
||||
}}
|
||||
>
|
||||
<IconWrapper
|
||||
color="rgba(250, 250, 250, 0.5)"
|
||||
label="Apps"
|
||||
selected={isApps}
|
||||
>
|
||||
<img src={AppIcon} />
|
||||
</IconWrapper>
|
||||
</ButtonBase>
|
||||
<ButtonBase
|
||||
onClick={() => {
|
||||
setDesktopSideView("groups");
|
||||
}}
|
||||
>
|
||||
<IconWrapper
|
||||
color="rgba(250, 250, 250, 0.5)"
|
||||
label="Hubs"
|
||||
selected={isGroups}
|
||||
>
|
||||
<HubsIcon
|
||||
height={30}
|
||||
color={
|
||||
hasUnreadGroups
|
||||
? "var(--unread)"
|
||||
: isGroups
|
||||
? "white"
|
||||
: "rgba(250, 250, 250, 0.5)"
|
||||
}
|
||||
/>
|
||||
</IconWrapper>
|
||||
</ButtonBase>
|
||||
<ButtonBase
|
||||
onClick={() => {
|
||||
setDesktopSideView("directs");
|
||||
}}
|
||||
>
|
||||
<IconWrapper
|
||||
color="rgba(250, 250, 250, 0.5)"
|
||||
label="Messaging"
|
||||
selected={isDirects}
|
||||
>
|
||||
<MessagingIcon
|
||||
height={30}
|
||||
color={
|
||||
hasUnreadDirects
|
||||
? "var(--unread)"
|
||||
: isDirects
|
||||
? "white"
|
||||
: "rgba(250, 250, 250, 0.5)"
|
||||
}
|
||||
/>
|
||||
</IconWrapper>
|
||||
</ButtonBase>
|
||||
|
||||
<Save isDesktop />
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
@@ -90,6 +90,7 @@ import { DesktopFooter } from "../Desktop/DesktopFooter";
|
||||
import { DesktopHeader } from "../Desktop/DesktopHeader";
|
||||
import { Apps } from "../Apps/Apps";
|
||||
import { AppsNavBar } from "../Apps/AppsNavBar";
|
||||
import { AppsDesktop } from "../Apps/AppsDesktop";
|
||||
|
||||
// let touchStartY = 0;
|
||||
// let disablePullToRefresh = false;
|
||||
@@ -375,7 +376,11 @@ export const Group = ({
|
||||
isOpenDrawerProfile,
|
||||
setIsOpenDrawerProfile,
|
||||
logoutFunc,
|
||||
setDesktopViewMode,
|
||||
desktopViewMode
|
||||
}: GroupProps) => {
|
||||
const [desktopSideView, setDesktopSideView] = useState('groups')
|
||||
|
||||
const [secretKey, setSecretKey] = useState(null);
|
||||
const [secretKeyPublishDate, setSecretKeyPublishDate] = useState(null);
|
||||
const lastFetchedSecretKey = useRef(null);
|
||||
@@ -420,7 +425,6 @@ export const Group = ({
|
||||
const [mutedGroups, setMutedGroups] = useState([]);
|
||||
const [mobileViewMode, setMobileViewMode] = useState("home");
|
||||
const [mobileViewModeKeepOpen, setMobileViewModeKeepOpen] = useState("");
|
||||
const [desktopSideView, setDesktopSideView] = useState('groups')
|
||||
const isFocusedRef = useRef(true);
|
||||
const timestampEnterDataRef = useRef({});
|
||||
const selectedGroupRef = useRef(null);
|
||||
@@ -434,7 +438,21 @@ export const Group = ({
|
||||
const initiatedGetMembers = useRef(false);
|
||||
const [groupChatTimestamps, setGroupChatTimestamps] = React.useState({});
|
||||
const [appsMode, setAppsMode] = useState('home')
|
||||
const [isOpenSideViewDirects, setIsOpenSideViewDirects] = useState(false)
|
||||
const [isOpenSideViewGroups, setIsOpenSideViewGroups] = useState(false)
|
||||
|
||||
const toggleSideViewDirects = ()=> {
|
||||
if(isOpenSideViewGroups){
|
||||
setIsOpenSideViewGroups(false)
|
||||
}
|
||||
setIsOpenSideViewDirects((prev)=> !prev)
|
||||
}
|
||||
const toggleSideViewGroups = ()=> {
|
||||
if(isOpenSideViewDirects){
|
||||
setIsOpenSideViewDirects(false)
|
||||
}
|
||||
setIsOpenSideViewGroups((prev)=> !prev)
|
||||
}
|
||||
useEffect(()=> {
|
||||
timestampEnterDataRef.current = timestampEnterData
|
||||
}, [timestampEnterData])
|
||||
@@ -501,6 +519,7 @@ export const Group = ({
|
||||
});
|
||||
} catch (error) {}
|
||||
};
|
||||
console.log('desktopViewMode', desktopViewMode)
|
||||
const getGroupDataSingle = async (groupId) => {
|
||||
try {
|
||||
return new Promise((res, rej) => {
|
||||
@@ -1314,6 +1333,7 @@ export const Group = ({
|
||||
setTimeout(() => {
|
||||
setSelectedGroup(findGroup);
|
||||
setMobileViewMode("group");
|
||||
setDesktopSideView('home')
|
||||
getTimestampEnterChat();
|
||||
isLoadingOpenSectionFromNotification.current = false;
|
||||
}, 200);
|
||||
@@ -1361,7 +1381,7 @@ export const Group = ({
|
||||
setTimeout(() => {
|
||||
setSelectedGroup(findGroup);
|
||||
setMobileViewMode("group");
|
||||
|
||||
setDesktopSideView('home')
|
||||
getGroupAnnouncements();
|
||||
}, 200);
|
||||
}
|
||||
@@ -1416,6 +1436,7 @@ export const Group = ({
|
||||
setTimeout(() => {
|
||||
setSelectedGroup(findGroup);
|
||||
setMobileViewMode("group");
|
||||
setDesktopSideView('home')
|
||||
getGroupAnnouncements();
|
||||
}, 200);
|
||||
}
|
||||
@@ -1439,6 +1460,8 @@ export const Group = ({
|
||||
}
|
||||
if (!isMobile) {
|
||||
}
|
||||
setDesktopViewMode('home')
|
||||
|
||||
setGroupSection("default");
|
||||
clearAllQueues();
|
||||
await new Promise((res) => {
|
||||
@@ -1462,6 +1485,8 @@ export const Group = ({
|
||||
setMemberCountFromSecretKeyData(null);
|
||||
setTriedToFetchSecretKey(false);
|
||||
setFirstSecretKeyInCreation(false);
|
||||
setIsOpenSideViewDirects(false)
|
||||
setIsOpenSideViewGroups(false)
|
||||
};
|
||||
|
||||
const goToAnnouncements = async () => {
|
||||
@@ -1937,6 +1962,7 @@ export const Group = ({
|
||||
// }
|
||||
onClick={() => {
|
||||
setMobileViewMode("group");
|
||||
setDesktopSideView('home')
|
||||
initiatedGetMembers.current = false;
|
||||
clearAllQueues();
|
||||
setSelectedDirect(null);
|
||||
@@ -2165,8 +2191,8 @@ export const Group = ({
|
||||
alignItems: "flex-start",
|
||||
}}
|
||||
>
|
||||
{!isMobile && desktopSideView === 'groups' && renderGroups()}
|
||||
{!isMobile && desktopSideView === 'directs' && renderDirects()}
|
||||
{!isMobile && ((desktopSideView === 'groups' && desktopViewMode !== 'apps') || isOpenSideViewGroups) && renderGroups()}
|
||||
{!isMobile && ((desktopSideView === 'directs' && desktopViewMode !== 'apps') || isOpenSideViewDirects) && renderDirects()}
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
@@ -2623,10 +2649,16 @@ export const Group = ({
|
||||
groupsAnnHasUnread}
|
||||
hasUnreadDirects={directChatHasUnread}
|
||||
myName={userInfo?.name || null}
|
||||
isHome={groupSection === "home"}
|
||||
isGroups={desktopSideView === 'groups'}
|
||||
isDirects={desktopSideView === 'directs'}
|
||||
isHome={groupSection === "home" && desktopViewMode === 'home'}
|
||||
isGroups={desktopSideView === 'groups' && desktopViewMode !== 'apps'}
|
||||
isDirects={desktopSideView === 'directs' && desktopViewMode !== 'apps'}
|
||||
setDesktopViewMode={setDesktopViewMode}
|
||||
isApps={desktopViewMode === 'apps'}
|
||||
setDesktopSideView={setDesktopSideView}
|
||||
desktopViewMode={desktopViewMode}
|
||||
hide={desktopViewMode === 'apps'}
|
||||
setIsOpenSideViewDirects={setIsOpenSideViewDirects}
|
||||
setIsOpenSideViewGroups={setIsOpenSideViewGroups}
|
||||
/>
|
||||
)}
|
||||
{isMobile && mobileViewMode === "home" && (
|
||||
@@ -2648,11 +2680,16 @@ export const Group = ({
|
||||
{isMobile && (
|
||||
<Apps mode={appsMode} setMode={setAppsMode} show={mobileViewMode === "apps"} myName={userInfo?.name} />
|
||||
)}
|
||||
{
|
||||
!isMobile && !selectedGroup &&
|
||||
groupSection === "home" && (
|
||||
|
||||
<HomeDesktop
|
||||
{!isMobile && (
|
||||
<AppsDesktop toggleSideViewGroups={toggleSideViewGroups} toggleSideViewDirects={toggleSideViewDirects} goToHome={goToHome} mode={appsMode} setMode={setAppsMode} setDesktopSideView={setDesktopSideView} hasUnreadDirects={directChatHasUnread} show={desktopViewMode === "apps"} myName={userInfo?.name} isGroups={isOpenSideViewGroups}
|
||||
isDirects={isOpenSideViewDirects} hasUnreadGroups={groupChatHasUnread ||
|
||||
groupsAnnHasUnread} />
|
||||
)}
|
||||
|
||||
|
||||
{!isMobile && !selectedGroup &&
|
||||
groupSection === "home" && desktopViewMode !== "apps" && (
|
||||
<HomeDesktop
|
||||
refreshHomeDataFunc={refreshHomeDataFunc}
|
||||
myAddress={myAddress}
|
||||
isLoadingGroups={isLoadingGroups}
|
||||
@@ -2666,7 +2703,9 @@ export const Group = ({
|
||||
setOpenAddGroup={setOpenAddGroup}
|
||||
setMobileViewMode={setMobileViewMode}
|
||||
/>
|
||||
)}
|
||||
)}
|
||||
|
||||
|
||||
</Box>
|
||||
<AuthenticatedContainerInnerRight
|
||||
sx={{
|
||||
@@ -2674,188 +2713,10 @@ export const Group = ({
|
||||
width: "31px",
|
||||
// minWidth: "135px",
|
||||
padding: "5px",
|
||||
display: isMobile ? "none" : "flex",
|
||||
display: (isMobile || desktopViewMode === 'apps') ? "none" : "flex",
|
||||
}}
|
||||
>
|
||||
{/* <Spacer height="20px" />
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
gap: "3px",
|
||||
alignItems: "center",
|
||||
justifyContent: "flex-start",
|
||||
width: "100%",
|
||||
cursor: "pointer",
|
||||
}}
|
||||
onClick={goToHome}
|
||||
>
|
||||
<HomeIcon
|
||||
sx={{
|
||||
cursor: "pointer",
|
||||
color: groupSection === "home" ? "#1444c7" : "white",
|
||||
opacity: groupSection === "home" ? 1 : 0.4,
|
||||
}}
|
||||
/>
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: "12px",
|
||||
color: groupSection === "home" ? "#1444c7" : "white",
|
||||
opacity: groupSection === "home" ? 1 : 0.4,
|
||||
}}
|
||||
>
|
||||
Home
|
||||
</Typography>
|
||||
</Box>
|
||||
{selectedGroup && (
|
||||
<>
|
||||
<Spacer height="20px" />
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
gap: "3px",
|
||||
alignItems: "center",
|
||||
justifyContent: "flex-start",
|
||||
width: "100%",
|
||||
cursor: "pointer",
|
||||
}}
|
||||
onClick={goToAnnouncements}
|
||||
>
|
||||
<CampaignIcon
|
||||
sx={{
|
||||
cursor: "pointer",
|
||||
color: isUnread
|
||||
? "red"
|
||||
: groupSection === "announcement"
|
||||
? "#1444c7"
|
||||
: "white",
|
||||
opacity: groupSection === "announcement" ? 1 : 0.4,
|
||||
}}
|
||||
/>
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: "12px",
|
||||
color: isUnread
|
||||
? "red"
|
||||
: groupSection === "announcement"
|
||||
? "#1444c7"
|
||||
: "white",
|
||||
opacity: groupSection === "announcement" ? 1 : 0.4,
|
||||
}}
|
||||
>
|
||||
Announcements
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Spacer height="20px" />
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
gap: "3px",
|
||||
alignItems: "center",
|
||||
justifyContent: "flex-start",
|
||||
width: "100%",
|
||||
cursor: "pointer",
|
||||
}}
|
||||
onClick={goToChat}
|
||||
>
|
||||
<ChatIcon
|
||||
sx={{
|
||||
cursor: "pointer",
|
||||
color: isUnreadChat
|
||||
? "red"
|
||||
: groupSection === "chat"
|
||||
? "#1444c7"
|
||||
: "white",
|
||||
opacity: groupSection === "chat" ? 1 : 0.4,
|
||||
}}
|
||||
/>
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: "12px",
|
||||
color: isUnreadChat
|
||||
? "red"
|
||||
: groupSection === "chat"
|
||||
? "#1444c7"
|
||||
: "white",
|
||||
opacity: groupSection === "chat" ? 1 : 0.4,
|
||||
}}
|
||||
>
|
||||
Chat
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Spacer height="20px" />
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
gap: "3px",
|
||||
alignItems: "center",
|
||||
justifyContent: "flex-start",
|
||||
width: "100%",
|
||||
cursor: "pointer",
|
||||
}}
|
||||
onClick={() => {
|
||||
setGroupSection("forum");
|
||||
setSelectedDirect(null);
|
||||
setNewChat(false);
|
||||
}}
|
||||
>
|
||||
<ForumIcon
|
||||
sx={{
|
||||
cursor: "pointer",
|
||||
color: groupSection === "forum" ? "#1444c7" : "white",
|
||||
opacity: groupSection === "forum" ? 1 : 0.4,
|
||||
}}
|
||||
/>
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: "12px",
|
||||
color: groupSection === "forum" ? "#1444c7" : "white",
|
||||
opacity: groupSection === "forum" ? 1 : 0.4,
|
||||
}}
|
||||
>
|
||||
Forum
|
||||
</Typography>
|
||||
</Box>
|
||||
<Spacer height="20px" />
|
||||
<Box
|
||||
onClick={() => setOpenManageMembers(true)}
|
||||
sx={{
|
||||
display: "flex",
|
||||
gap: "3px",
|
||||
alignItems: "center",
|
||||
justifyContent: "flex-start",
|
||||
width: "100%",
|
||||
cursor: "pointer",
|
||||
}}
|
||||
>
|
||||
<PeopleIcon
|
||||
sx={{
|
||||
cursor: "pointer",
|
||||
color: "white",
|
||||
opacity: 0.4,
|
||||
}}
|
||||
/>
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: "12px",
|
||||
color: "white",
|
||||
opacity: 0.4,
|
||||
}}
|
||||
>
|
||||
Members
|
||||
</Typography>
|
||||
</Box>
|
||||
<Spacer height="20px" />
|
||||
</>
|
||||
)} */}
|
||||
|
||||
{/* <SettingsIcon
|
||||
sx={{
|
||||
cursor: "pointer",
|
||||
color: "white",
|
||||
}}
|
||||
/> */}
|
||||
|
||||
</AuthenticatedContainerInnerRight>
|
||||
<LoadingSnackbar
|
||||
open={isLoadingGroup}
|
||||
@@ -2915,7 +2776,7 @@ export const Group = ({
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{(isMobile && isMobile && mobileViewMode === "apps" && appsMode !== 'home') && !mobileViewModeKeepOpen && (
|
||||
{(isMobile && mobileViewMode === "apps" && appsMode !== 'home') && !mobileViewModeKeepOpen && (
|
||||
<>
|
||||
<AppsNavBar />
|
||||
</>
|
||||
@@ -2924,236 +2785,4 @@ export const Group = ({
|
||||
);
|
||||
};
|
||||
|
||||
// {isMobile && (
|
||||
// <Box
|
||||
// sx={{
|
||||
// display: "flex",
|
||||
// alignItems: "center",
|
||||
// justifyContent: "center",
|
||||
// flexDirection: "column",
|
||||
// width: "100%",
|
||||
// height: "75px", // Keep the height at 75px
|
||||
// background: "rgba(0, 0, 0, 0.1)",
|
||||
// padding: "0px", // Remove unnecessary padding
|
||||
// }}
|
||||
// >
|
||||
// <Grid
|
||||
// container
|
||||
// spacing={0.5}
|
||||
// sx={{ width: "100%", justifyContent: "space-around" }}
|
||||
// >
|
||||
// {selectedGroup && (
|
||||
// <>
|
||||
// <Grid item xs={4} sx={{
|
||||
// display: 'flex'
|
||||
// }}>
|
||||
// <Button
|
||||
// fullWidth
|
||||
// size="small"
|
||||
// variant="contained"
|
||||
// startIcon={<AnnouncementsIcon />}
|
||||
// sx={{
|
||||
// padding: "4px 6px",
|
||||
// color:
|
||||
// groupSection === "announcement" ? "black" : "white",
|
||||
// backgroundColor: isUnread
|
||||
// ? "red"
|
||||
// : groupSection === "announcement"
|
||||
// ? "white"
|
||||
// : "black",
|
||||
// "&:hover": {
|
||||
// backgroundColor: isUnread
|
||||
// ? "red"
|
||||
// : groupSection === "announcement"
|
||||
// ? "white"
|
||||
// : "black",
|
||||
// },
|
||||
// "&:active": {
|
||||
// backgroundColor: isUnread
|
||||
// ? "red"
|
||||
// : groupSection === "announcement"
|
||||
// ? "white"
|
||||
// : "black",
|
||||
// },
|
||||
// "&:focus": {
|
||||
// backgroundColor: isUnread
|
||||
// ? "red"
|
||||
// : groupSection === "announcement"
|
||||
// ? "white"
|
||||
// : "black",
|
||||
// },
|
||||
// }}
|
||||
// onClick={goToAnnouncements}
|
||||
// >
|
||||
// ANN
|
||||
// </Button>
|
||||
// </Grid>
|
||||
// <Grid item xs={4} sx={{
|
||||
// display: 'flex'
|
||||
// }}>
|
||||
// <Button
|
||||
// fullWidth
|
||||
// size="small"
|
||||
// variant="contained"
|
||||
// startIcon={<ChatIcon />}
|
||||
// sx={{
|
||||
// padding: "4px 6px",
|
||||
// color: groupSection === "chat" ? "black" : "white",
|
||||
// backgroundColor: isUnreadChat
|
||||
// ? "red"
|
||||
// : groupSection === "chat"
|
||||
// ? "white"
|
||||
// : "black",
|
||||
// "&:hover": {
|
||||
// backgroundColor: isUnreadChat
|
||||
// ? "red"
|
||||
// : groupSection === "chat"
|
||||
// ? "white"
|
||||
// : "black", // Same logic for hover
|
||||
// },
|
||||
// "&:active": {
|
||||
// backgroundColor: isUnreadChat
|
||||
// ? "red"
|
||||
// : groupSection === "chat"
|
||||
// ? "white"
|
||||
// : "black", // Same logic for active
|
||||
// },
|
||||
// "&:focus": {
|
||||
// backgroundColor: isUnreadChat
|
||||
// ? "red"
|
||||
// : groupSection === "chat"
|
||||
// ? "white"
|
||||
// : "black", // Same logic for focus
|
||||
// },
|
||||
// }}
|
||||
// onClick={goToChat}
|
||||
// >
|
||||
// Chat
|
||||
// </Button>
|
||||
// </Grid>
|
||||
// <Grid item xs={4} sx={{
|
||||
// display: 'flex'
|
||||
// }}>
|
||||
// <Button
|
||||
// fullWidth
|
||||
// size="small"
|
||||
// variant="contained"
|
||||
// startIcon={<ForumIcon />}
|
||||
// sx={{
|
||||
// padding: "4px 6px",
|
||||
// color: groupSection === "forum" ? "black" : "white",
|
||||
// backgroundColor:
|
||||
// groupSection === "forum" ? "white" : "black",
|
||||
// "&:hover": {
|
||||
// backgroundColor: groupSection === "forum" ? "white" : "black", // Hover state
|
||||
// },
|
||||
// "&:active": {
|
||||
// backgroundColor: groupSection === "forum" ? "white" : "black", // Active state
|
||||
// },
|
||||
// "&:focus": {
|
||||
// backgroundColor: groupSection === "forum" ? "white" : "black", // Focus state
|
||||
// },
|
||||
// }}
|
||||
// onClick={() => {
|
||||
// setSelectedDirect(null);
|
||||
// setNewChat(false)
|
||||
// setGroupSection("forum")
|
||||
// } }
|
||||
// >
|
||||
// Forum
|
||||
// </Button>
|
||||
// </Grid>
|
||||
// <Grid item xs={4} sx={{
|
||||
// display: 'flex'
|
||||
// }}>
|
||||
// <Button
|
||||
// fullWidth
|
||||
// size="small"
|
||||
// variant="contained"
|
||||
// startIcon={<GroupIcon />}
|
||||
// sx={{ padding: "4px 6px", backgroundColor: "black", "&:hover": {
|
||||
// backgroundColor: "black", // Hover state
|
||||
// },
|
||||
// "&:active": {
|
||||
// backgroundColor: "black", // Active state
|
||||
// },
|
||||
// "&:focus": {
|
||||
// backgroundColor: "black", // Focus state
|
||||
// }, }}
|
||||
// onClick={() => setOpenManageMembers(true)}
|
||||
// >
|
||||
// Members
|
||||
// </Button>
|
||||
// </Grid>
|
||||
// </>
|
||||
// )}
|
||||
|
||||
// {/* Second row: Groups, Home, Profile */}
|
||||
// <Grid item xs={4} sx={{
|
||||
// display: 'flex',
|
||||
// }}>
|
||||
// <Button
|
||||
// fullWidth
|
||||
// size="small"
|
||||
// variant="contained"
|
||||
// startIcon={<GroupIcon />}
|
||||
// sx={{
|
||||
// padding: "2px 4px",
|
||||
// backgroundColor:
|
||||
// groupChatHasUnread ||
|
||||
// groupsAnnHasUnread ||
|
||||
// directChatHasUnread
|
||||
// ? "red"
|
||||
// : "black",
|
||||
// "&:hover": {
|
||||
// backgroundColor:
|
||||
// groupChatHasUnread || groupsAnnHasUnread || directChatHasUnread
|
||||
// ? "red"
|
||||
// : "black", // Hover state follows the same logic
|
||||
// },
|
||||
// "&:active": {
|
||||
// backgroundColor:
|
||||
// groupChatHasUnread || groupsAnnHasUnread || directChatHasUnread
|
||||
// ? "red"
|
||||
// : "black", // Active state follows the same logic
|
||||
// },
|
||||
// "&:focus": {
|
||||
// backgroundColor:
|
||||
// groupChatHasUnread || groupsAnnHasUnread || directChatHasUnread
|
||||
// ? "red"
|
||||
// : "black", // Focus state follows the same logic
|
||||
// },
|
||||
// }}
|
||||
// onClick={() => {
|
||||
// setIsOpenDrawer(true);
|
||||
// setDrawerMode("groups");
|
||||
// }}
|
||||
// >
|
||||
// {chatMode === "groups" ? "Groups" : "Direct"}
|
||||
// </Button>
|
||||
// </Grid>
|
||||
// <Grid item xs={2} sx={{
|
||||
// display: 'flex',
|
||||
// justifyContent: 'center'
|
||||
// }}>
|
||||
// <IconButton
|
||||
// sx={{ padding: "0", color: "white" }} // Reduce padding for icons
|
||||
// onClick={goToHome}
|
||||
// >
|
||||
// <HomeIcon />
|
||||
// </IconButton>
|
||||
// </Grid>
|
||||
// <Grid item xs={2} sx={{
|
||||
// display: 'flex',
|
||||
// justifyContent: 'center'
|
||||
// }}>
|
||||
// <IconButton
|
||||
// sx={{ padding: "0", color: "white" }} // Reduce padding for icons
|
||||
// onClick={() => setIsOpenDrawerProfile(true)}
|
||||
// >
|
||||
// <PersonIcon />
|
||||
// </IconButton>
|
||||
// </Grid>
|
||||
// </Grid>
|
||||
// </Box>
|
||||
// )}
|
||||
|
@@ -8,7 +8,8 @@ import { MyContext } from '../../App';
|
||||
import { getFee } from '../../background';
|
||||
import { CustomizedSnackbars } from '../Snackbar/Snackbar';
|
||||
import { SaveIcon } from '../../assets/svgs/SaveIcon';
|
||||
export const Save = () => {
|
||||
import { IconWrapper } from '../Desktop/DesktopFooter';
|
||||
export const Save = ({isDesktop}) => {
|
||||
const [pinnedApps, setPinnedApps] = useRecoilState(sortablePinnedAppsAtom);
|
||||
const [settingsQdnLastUpdated, setSettingsQdnLastUpdated] = useRecoilState(settingsQDNLastUpdatedAtom);
|
||||
const [settingsLocalLastUpdated] = useRecoilState(settingsLocalLastUpdatedAtom);
|
||||
@@ -130,9 +131,22 @@ export const Save = () => {
|
||||
return (
|
||||
<>
|
||||
<ButtonBase onClick={saveToQdn} disabled={!hasChanged || !canSave || isLoading || settingsQdnLastUpdated === -100}>
|
||||
<SaveIcon
|
||||
{isDesktop ? (
|
||||
<IconWrapper
|
||||
color="rgba(250, 250, 250, 0.5)"
|
||||
label="Save"
|
||||
selected={false}
|
||||
>
|
||||
<SaveIcon
|
||||
color={settingsQdnLastUpdated === -100 ? '#8F8F91' : (hasChanged && !isLoading) ? '#5EB049' : '#8F8F91'}
|
||||
/>
|
||||
</IconWrapper>
|
||||
) : (
|
||||
<SaveIcon
|
||||
color={settingsQdnLastUpdated === -100 ? '#8F8F91' : (hasChanged && !isLoading) ? '#5EB049' : '#8F8F91'}
|
||||
/>
|
||||
)}
|
||||
|
||||
</ButtonBase>
|
||||
<CustomizedSnackbars
|
||||
duration={3500}
|
||||
|
Reference in New Issue
Block a user