finished desktop view

This commit is contained in:
2024-10-22 20:12:54 +03:00
parent 6b3a1f51ba
commit bec51a07bb
21 changed files with 1747 additions and 508 deletions

View File

@@ -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>
);
};

View File

@@ -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>

View File

@@ -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">

View File

@@ -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'

View 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>
);
};

View 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%'
}));

View 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>
);
};

View 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>
);
};

View 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>
);
};

View 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>
);
};

View File

@@ -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>

View File

@@ -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>
);
};

View File

@@ -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>
// )}

View File

@@ -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}