started app browser

This commit is contained in:
2024-10-18 02:50:19 +03:00
parent bd170d8481
commit 28c2dfbb7a
16 changed files with 795 additions and 172 deletions

View File

@@ -1,22 +1,102 @@
import React, { useEffect, useMemo, useState } from "react";
import {
AppCircle,
AppCircleContainer,
AppCircleLabel,
AppDownloadButton,
AppDownloadButtonText,
AppInfoAppName,
AppInfoSnippetContainer,
AppInfoSnippetLeft,
AppInfoSnippetMiddle,
AppInfoSnippetRight,
AppInfoUserName,
AppsLibraryContainer,
AppsParent,
} from "./Apps-styles";
import { Avatar, Box, ButtonBase, InputBase } from "@mui/material";
import { Add } from "@mui/icons-material";
import { getBaseApiReact } from "../../App";
import LogoSelected from "../../assets/svgs/LogoSelected.svg";
import { Spacer } from "../../common/Spacer";
import { executeEvent } from "../../utils/events";
export const AppInfo = ({ app }) => {
const isInstalled = app?.status?.status === 'READY'
return (
<AppsLibraryContainer>
<AppInfoSnippetContainer>
<AppInfoSnippetLeft sx={{
flexGrow: 1,
gap: '18px'
}}>
<AppCircleContainer sx={{
width: 'auto'
}}>
<AppCircle
sx={{
border: "none",
height: '100px',
width: '100px'
}}
>
<Avatar
sx={{
height: "43px",
width: "43px",
'& img': {
objectFit: 'fill',
}
}}
alt={app?.name}
src={`${getBaseApiReact()}/arbitrary/THUMBNAIL/${
app?.name
}/qortal_avatar?async=true`}
>
<img
style={{
width: "43px",
height: "auto",
}}
src={LogoSelected}
alt="center-icon"
/>
</Avatar>
</AppCircle>
</AppCircleContainer>
<AppInfoSnippetMiddle>
</AppsLibraryContainer>
<AppInfoAppName >
{app?.metadata?.title || app?.name}
</AppInfoAppName>
<Spacer height="6px" />
<AppInfoUserName>
{ app?.name}
</AppInfoUserName>
<Spacer height="3px" />
</AppInfoSnippetMiddle>
</AppInfoSnippetLeft>
<AppInfoSnippetRight>
</AppInfoSnippetRight>
</AppInfoSnippetContainer>
<Spacer height="11px" />
<AppDownloadButton sx={{
backgroundColor: isInstalled ? '#0091E1' : '#247C0E',
width: '100%',
maxWidth: '320px',
height: '29px'
}}>
<AppDownloadButtonText>{isInstalled ? 'Open' : 'Download'}</AppDownloadButtonText>
</AppDownloadButton>
</AppsLibraryContainer>
);
};

View File

@@ -0,0 +1,100 @@
import React, { useEffect, useMemo, useState } from "react";
import {
AppCircle,
AppCircleContainer,
AppCircleLabel,
AppDownloadButton,
AppDownloadButtonText,
AppInfoAppName,
AppInfoSnippetContainer,
AppInfoSnippetLeft,
AppInfoSnippetMiddle,
AppInfoSnippetRight,
AppInfoUserName,
AppsLibraryContainer,
} from "./Apps-styles";
import { Avatar, Box, ButtonBase, InputBase } from "@mui/material";
import { Add } from "@mui/icons-material";
import { getBaseApiReact } from "../../App";
import LogoSelected from "../../assets/svgs/LogoSelected.svg";
import { Spacer } from "../../common/Spacer";
import { executeEvent } from "../../utils/events";
export const AppInfoSnippet = ({ app }) => {
const isInstalled = app?.status?.status === 'READY'
return (
<AppInfoSnippetContainer>
<AppInfoSnippetLeft>
<ButtonBase
sx={{
height: "80px",
width: "60px",
}}
onClick={()=> {
executeEvent("selectedAppInfo", {
data: app,
});
}}
>
<AppCircleContainer>
<AppCircle
sx={{
border: "none",
}}
>
<Avatar
sx={{
height: "31px",
width: "31px",
'& img': {
objectFit: 'fill',
}
}}
alt={app?.name}
src={`${getBaseApiReact()}/arbitrary/THUMBNAIL/${
app?.name
}/qortal_avatar?async=true`}
>
<img
style={{
width: "31px",
height: "auto",
}}
src={LogoSelected}
alt="center-icon"
/>
</Avatar>
</AppCircle>
</AppCircleContainer>
</ButtonBase>
<AppInfoSnippetMiddle>
<ButtonBase onClick={()=> {
executeEvent("selectedAppInfo", {
data: app,
});
}}>
<AppInfoAppName >
{app?.metadata?.title || app?.name}
</AppInfoAppName>
</ButtonBase>
<Spacer height="6px" />
<AppInfoUserName>
{ app?.name}
</AppInfoUserName>
<Spacer height="3px" />
</AppInfoSnippetMiddle>
</AppInfoSnippetLeft>
<AppInfoSnippetRight>
<AppDownloadButton sx={{
backgroundColor: isInstalled ? '#0091E1' : '#247C0E',
}}>
<AppDownloadButtonText>{isInstalled ? 'Open' : 'Download'}</AppDownloadButtonText>
</AppDownloadButton>
</AppInfoSnippetRight>
</AppInfoSnippetContainer>
);
};

View File

@@ -0,0 +1,46 @@
import React, { useContext, useEffect, useMemo, useRef, useState } from "react";
import {
AppCircle,
AppCircleContainer,
AppCircleLabel,
AppDownloadButton,
AppDownloadButtonText,
AppInfoAppName,
AppInfoSnippetContainer,
AppInfoSnippetLeft,
AppInfoSnippetMiddle,
AppInfoSnippetRight,
AppInfoUserName,
AppsLibraryContainer,
AppsParent,
} from "./Apps-styles";
import { Avatar, Box, ButtonBase, InputBase } from "@mui/material";
import { Add } from "@mui/icons-material";
import { MyContext, getBaseApiReact } from "../../App";
import LogoSelected from "../../assets/svgs/LogoSelected.svg";
import { Spacer } from "../../common/Spacer";
import { executeEvent } from "../../utils/events";
export const AppViewer = ({ app }) => {
const { rootHeight } = useContext(MyContext);
const iframeRef = useRef(null);
const url = useMemo(()=> {
return `${getBaseApiReact()}/render/${app?.service}/${app?.name}${app?.path != null ? app?.path : ''}?theme=dark&identifier=${(app?.identifier != null && app?.identifier != 'null') ? app?.identifier : ''}`
}, [app?.service, app?.name, app?.identifier, app?.path])
return (
<iframe ref={iframeRef} style={{
height: `calc(${rootHeight} - 60px - 45px)`,
border: 'none',
width: '100%'
}} id="browser-iframe" src={url} sandbox="allow-scripts allow-same-origin allow-forms allow-downloads allow-modals" allow="fullscreen">
</iframe>
);
};

View File

@@ -0,0 +1,40 @@
import React, { useContext, useRef } from 'react'
import { AppViewer } from './AppViewer'
import Frame from 'react-frame-component';
import { MyContext } from '../../App';
const AppViewerContainer = ({app, isSelected}) => {
const { rootHeight } = useContext(MyContext);
const frameRef = useRef(null);
return (
<Frame id={`browser-iframe-${app?.tabId}` } ref={frameRef} head={
<>
{/* Inject styles directly into the iframe */}
<style>
{`
body {
margin: 0;
padding: 0;
}
/* Hide scrollbars for all elements */
* {
-ms-overflow-style: none; /* IE and Edge */
scrollbar-width: none; /* Firefox */
}
*::-webkit-scrollbar {
display: none; /* Chrome, Safari, Opera */
}
`}
</style>
</>
} style={{
height: `calc(${rootHeight} - 60px - 45px)`,
border: 'none',
width: '100%',
display: !isSelected && 'none'
}} ><AppViewer app={app} /></Frame>
)
}
export default AppViewerContainer

View File

@@ -6,6 +6,7 @@ import {
Box,
TextField,
InputLabel,
ButtonBase,
} from "@mui/material";
import { styled } from "@mui/system";
@@ -15,7 +16,7 @@ import {
flexDirection: "column",
height: "100%",
alignItems: "center",
overflow: 'auto',
overflow: 'auto',
// For WebKit-based browsers (Chrome, Safari, etc.)
"::-webkit-scrollbar": {
width: "0px", // Set the width to 0 to hide the scrollbar
@@ -35,6 +36,7 @@ import {
gap: '24px',
flexWrap: 'wrap',
alignItems: 'flex-start',
alignSelf: 'center'
}));
export const AppsLibraryContainer = styled(Box)(({ theme }) => ({
@@ -59,13 +61,16 @@ import {
width: "90%",
justifyContent: 'flex-start',
alignItems: 'center',
gap: '10px'
gap: '10px',
flexGrow: 1,
flexShrink: 0
}));
export const AppsSearchRight = styled(Box)(({ theme }) => ({
display: "flex",
width: "90%",
justifyContent: 'flex-end',
alignItems: 'center',
flexShrink: 1
}));
export const AppCircleContainer = styled(Box)(({ theme }) => ({
display: "flex",
@@ -105,4 +110,85 @@ import {
borderRadius: '50%',
backgroundColor: "var(--apps-circle)",
border: '1px solid #FFFFFF'
}));
export const AppInfoSnippetContainer = styled(Box)(({ theme }) => ({
display: "flex",
justifyContent: 'space-between',
alignItems: 'center',
width: '100%'
}));
export const AppInfoSnippetLeft = styled(Box)(({ theme }) => ({
display: "flex",
justifyContent: 'flex-start',
alignItems: 'center',
gap: '12px'
}));
export const AppInfoSnippetRight = styled(Box)(({ theme }) => ({
display: "flex",
justifyContent: 'flex-end',
alignItems: 'center',
}));
export const AppDownloadButton = styled(ButtonBase)(({ theme }) => ({
backgroundColor: "#247C0E",
width: '101px',
height: '29px',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
borderRadius: '25px'
}));
export const AppDownloadButtonText = styled(Typography)(({ theme }) => ({
fontSize: '14px',
fontWeight: 500,
lineHeight: 1.2,
}));
export const AppInfoSnippetMiddle = styled(Box)(({ theme }) => ({
display: "flex",
flexDirection: "column",
justifyContent: 'center',
alignItems: 'flex-start',
}));
export const AppInfoAppName = styled(Typography)(({ theme }) => ({
fontSize: '16px',
fontWeight: 500,
lineHeight: 1.2,
}));
export const AppInfoUserName = styled(Typography)(({ theme }) => ({
fontSize: '13px',
fontWeight: 400,
lineHeight: 1.2,
color: '#8D8F93'
}));
export const AppsNavBarParent = styled(Box)(({ theme }) => ({
display: "flex",
justifyContent: 'space-between',
alignItems: 'center',
width: '100%',
height: '60px',
backgroundColor: '#1F2023',
padding: '0px 10px',
position: "fixed",
bottom: 0,
zIndex: 1,
}));
export const AppsNavBarLeft = styled(Box)(({ theme }) => ({
display: "flex",
justifyContent: 'flex-start',
alignItems: 'center',
}));
export const AppsNavBarRight = styled(Box)(({ theme }) => ({
display: "flex",
justifyContent: 'flex-end',
alignItems: 'center',
}));

View File

@@ -1,14 +1,26 @@
import React, { useEffect, useState } from 'react'
import React, { useContext, useEffect, useRef, useState } from 'react'
import { AppsHome } from './AppsHome'
import { Spacer } from '../../common/Spacer'
import { getBaseApiReact } from '../../App'
import { MyContext, getBaseApiReact } from '../../App'
import { AppsLibrary } from './AppsLibrary'
import { AppInfo } from './AppInfo'
import { 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";
export const Apps = () => {
const [mode, setMode] = useState('home')
const uid = new ShortUniqueId({ length: 8 });
export const Apps = ({mode, setMode}) => {
const [availableQapps, setAvailableQapps] = useState([])
const [downloadedQapps, setDownloadedQapps] = useState([])
const [selectedAppInfo, setSelectedAppInfo] = useState(null)
const [tabs, setTabs] = useState([])
const [selectedTab, setSelectedTab] = useState(null)
const getQapps = React.useCallback(
async () => {
@@ -52,11 +64,95 @@ export const Apps = () => {
getQapps()
}, [getQapps])
const selectedAppInfoFunc = (e) => {
const data = e.detail?.data;
setSelectedAppInfo(data)
setMode('appInfo')
};
useEffect(() => {
subscribeToEvent("selectedAppInfo", selectedAppInfoFunc);
return () => {
unsubscribeFromEvent("selectedAppInfo", selectedAppInfoFunc);
};
}, []);
const navigateBackFunc = (e) => {
if(mode === 'appInfo'){
setMode('library')
} else if(mode === 'library'){
setMode('home')
} else {
const iframe = document.getElementById('browser-iframe2');
console.log('iframe', iframe)
// Go Back in the iframe's history
if (iframe) {
console.log(iframe.contentWindow)
if (iframe && iframe.contentWindow) {
const iframeWindow = iframe.contentWindow;
if (iframeWindow && iframeWindow.history) {
iframeWindow.history.back();
}
}
}
}
};
useEffect(() => {
subscribeToEvent("navigateBack", navigateBackFunc);
return () => {
unsubscribeFromEvent("navigateBack", navigateBackFunc);
};
}, [mode]);
const addTabFunc = (e) => {
const data = e.detail?.data;
const newTab = {
...data,
tabId: uid.rnd()
}
setTabs((prev)=> [...prev, newTab])
setSelectedTab(newTab)
};
useEffect(() => {
subscribeToEvent("addTab", addTabFunc);
return () => {
unsubscribeFromEvent("addTab", addTabFunc);
};
}, [mode]);
return (
<>
<Spacer height="30px" />
<AppsParent>
{mode !== 'viewer' && (
<Spacer height="30px" />
)}
{mode === 'home' && <AppsHome downloadedQapps={downloadedQapps} setMode={setMode} />}
{mode === 'library' && <AppsLibrary downloadedQapps={downloadedQapps} availableQapps={availableQapps} />}
</>
{mode === 'appInfo' && <AppInfo app={selectedAppInfo} />}
{mode === 'viewer' && (
<>
{tabs.map((tab)=> {
return <AppViewerContainer isSelected={tab?.tabId === selectedTab?.tabId} app={tab} />
})}
</>
) }
{mode !== 'viewer' && (
<Spacer height="180px" />
)}
</AppsParent>
)
}

View File

@@ -1,56 +1,81 @@
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 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";
export const AppsHome = ({downloadedQapps, setMode}) => {
export const AppsHome = ({ downloadedQapps, setMode }) => {
return (
<AppsParent>
<AppsContainer>
<ButtonBase onClick={()=> {
setMode('library')
}}>
<AppCircleContainer>
<AppsContainer>
<ButtonBase
onClick={() => {
setMode("library");
}}
>
<AppCircleContainer>
<AppCircle>
<Add>+</Add>
<Add>+</Add>
</AppCircle>
<AppCircleLabel>Add</AppCircleLabel>
</AppCircleContainer>
</AppCircleContainer>
</ButtonBase>
{downloadedQapps?.map((app) => {
return (
<ButtonBase
sx={{
height: "80px",
width: "60px",
}}
onClick={()=> {
executeEvent("addTab", {
data: app
})
}}
>
<AppCircleContainer>
<AppCircle
sx={{
border: "none",
}}
>
<Avatar
sx={{
height: "31px",
width: "31px",
'& img': {
objectFit: 'fill',
}
}}
alt={app?.name}
src={`${getBaseApiReact()}/arbitrary/THUMBNAIL/${
app?.name
}/qortal_avatar?async=true`}
>
<img
style={{
width: "31px",
height: "auto",
}}
src={LogoSelected}
alt="center-icon"
/>
</Avatar>
</AppCircle>
<AppCircleLabel>
{app?.name}
</AppCircleLabel>
</AppCircleContainer>
</ButtonBase>
{downloadedQapps?.map((qapp)=> {
return (
<ButtonBase sx={{
height: '80px',
width: '60px'
}}>
<AppCircleContainer>
<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>
</AppsParent>
)
}
);
})}
</AppsContainer>
);
};

View File

@@ -1,4 +1,4 @@
import React, { useEffect, useMemo, useState } from "react";
import React, { useContext, useEffect, useMemo, useRef, useState } from "react";
import {
AppCircle,
AppCircleContainer,
@@ -11,14 +11,16 @@ import {
AppsSearchLeft,
AppsSearchRight,
} from "./Apps-styles";
import { Avatar, Box, ButtonBase, InputBase } from "@mui/material";
import { Avatar, Box, ButtonBase, InputBase, styled } from "@mui/material";
import { Add } from "@mui/icons-material";
import { getBaseApiReact } from "../../App";
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 { Spacer } from "../../common/Spacer";
import { AppInfoSnippet } from "./AppInfoSnippet";
import { Virtuoso } from "react-virtuoso";
const officialAppList = [
"q-tube",
"q-blog",
@@ -30,21 +32,41 @@ const officialAppList = [
"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",
});
export const AppsLibrary = ({ downloadedQapps, availableQapps }) => {
const [searchValue, setSearchValue] = useState('')
const [searchValue, setSearchValue] = useState("");
const virtuosoRef = useRef();
const { rootHeight } = useContext(MyContext);
const officialApps = useMemo(() => {
return availableQapps.filter((app) => app.service === 'APP' &&
officialAppList.includes(app?.name?.toLowerCase())
return availableQapps.filter(
(app) =>
app.service === "APP" &&
officialAppList.includes(app?.name?.toLowerCase())
);
}, [availableQapps]);
const [debouncedValue, setDebouncedValue] = useState(''); // Debounced value
const [debouncedValue, setDebouncedValue] = useState(""); // Debounced value
// Debounce logic
useEffect(() => {
const handler = setTimeout(() => {
setDebouncedValue(searchValue); // Update debounced value after delay
}, 500); // 500ms debounce time (adjustable)
setDebouncedValue(searchValue);
}, 250);
// Cleanup timeout if searchValue changes before the timeout completes
return () => {
@@ -53,111 +75,147 @@ export const AppsLibrary = ({ downloadedQapps, availableQapps }) => {
}, [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])
console.log('officialApps', searchedList)
return (
<AppsParent>
<AppsLibraryContainer>
<Box sx={{
display: 'flex',
width: '100%',
justifyContent: 'center'
}}>
<AppsSearchContainer>
<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>
<Spacer height="25px" />
{searchedList?.length > 0 ? (
<>
{searchedList.map((app)=> {
return (
<AppInfo app={app} />
)
})}
</>
) : (
<>
<AppLibrarySubTitle>Official Apps</AppLibrarySubTitle>
<Spacer height="18px" />
<AppsContainer>
{officialApps?.map((qapp) => {
return (
<ButtonBase
sx={{
height: "80px",
width: "60px",
const searchedList = useMemo(() => {
if (!debouncedValue) return [];
return availableQapps.filter((app) =>
app.name.toLowerCase().includes(debouncedValue.toLowerCase())
);
}, [debouncedValue]);
console.log("officialApps", searchedList);
const rowRenderer = (index) => {
let app = searchedList[index];
console.log('appi', app)
return <AppInfoSnippet key={`${app?.service}-${app?.name}`} app={app} />;
};
const StyledVirtuosoContainer = styled('div')({
position: 'relative',
height: rootHeight,
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",
});
return (
<AppsLibraryContainer>
<Box
sx={{
display: "flex",
width: "100%",
justifyContent: "center",
}}
>
<AppsSearchContainer>
<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,
}}
>
<AppCircleContainer>
<AppCircle
/>
</AppsSearchLeft>
<AppsSearchRight>
{searchValue && (
<ButtonBase
onClick={() => {
setSearchValue("");
}}
>
<img src={IconClearInput} />
</ButtonBase>
)}
</AppsSearchRight>
</AppsSearchContainer>
</Box>
<Spacer height="25px" />
{searchedList?.length > 0 ? (
<StyledVirtuosoContainer>
<Virtuoso
ref={virtuosoRef}
data={searchedList}
itemContent={rowRenderer}
atBottomThreshold={50}
followOutput="smooth"
components={{
Scroller: ScrollerStyled // Use the styled scroller component
}}
/>
</StyledVirtuosoContainer>
) : (
<>
<AppLibrarySubTitle>Official Apps</AppLibrarySubTitle>
<Spacer height="18px" />
<AppsContainer>
{officialApps?.map((qapp) => {
return (
<ButtonBase
sx={{
border: "none",
height: "80px",
width: "60px",
}}
>
<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",
<AppCircleContainer>
<AppCircle
sx={{
border: "none",
}}
src={LogoSelected}
alt="center-icon"
/>
</Avatar>
</AppCircle>
<AppCircleLabel>
{qapp?.metadata?.title || qapp?.name}
</AppCircleLabel>
</AppCircleContainer>
</ButtonBase>
);
})}
</AppsContainer>
<Spacer height="18px" />
<AppLibrarySubTitle>Featured</AppLibrarySubTitle>
>
<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="18px" />
<AppLibrarySubTitle>Featured</AppLibrarySubTitle>
<Spacer height="18px" />
<AppLibrarySubTitle>Categories</AppLibrarySubTitle>
</>
<Spacer height="18px" />
<AppLibrarySubTitle>Categories</AppLibrarySubTitle>
</>
)}
</AppsLibraryContainer>
</AppsParent>
);
};

View File

@@ -0,0 +1,43 @@
import React from "react";
import {
AppsNavBarLeft,
AppsNavBarParent,
AppsNavBarRight,
} from "./Apps-styles";
import NavBack from "../../assets/svgs/NavBack.svg";
import NavCloseTab from "../../assets/svgs/NavCloseTab.svg";
import NavAdd from "../../assets/svgs/NavAdd.svg";
import NavMoreMenu from "../../assets/svgs/NavMoreMenu.svg";
import { ButtonBase } from "@mui/material";
import { executeEvent } from "../../utils/events";
export const AppsNavBar = () => {
return (
<AppsNavBarParent>
<AppsNavBarLeft>
<ButtonBase onClick={()=> {
executeEvent("navigateBack", {
});
}}>
<img src={NavBack} />
</ButtonBase>
</AppsNavBarLeft>
<AppsNavBarRight sx={{
gap: '10px'
}}>
<ButtonBase>
<img style={{
height: '40px',
width: '40px'
}} src={NavAdd} />
</ButtonBase>
<ButtonBase>
<img style={{
height: '34px',
width: '34px'
}} src={NavMoreMenu} />
</ButtonBase>
</AppsNavBarRight>
</AppsNavBarParent>
);
};

View File

@@ -89,6 +89,7 @@ import { HomeDesktop } from "./HomeDesktop";
import { DesktopFooter } from "../Desktop/DesktopFooter";
import { DesktopHeader } from "../Desktop/DesktopHeader";
import { Apps } from "../Apps/Apps";
import { AppsNavBar } from "../Apps/AppsNavBar";
// let touchStartY = 0;
// let disablePullToRefresh = false;
@@ -432,6 +433,7 @@ export const Group = ({
const { clearStatesMessageQueueProvider } = useMessageQueue();
const initiatedGetMembers = useRef(false);
const [groupChatTimestamps, setGroupChatTimestamps] = React.useState({});
const [appsMode, setAppsMode] = useState('viewer')
useEffect(()=> {
timestampEnterDataRef.current = timestampEnterData
@@ -2229,7 +2231,7 @@ export const Group = ({
isThin={
mobileViewMode === "groups" ||
mobileViewMode === "group" ||
mobileViewModeKeepOpen === "messaging"
mobileViewModeKeepOpen === "messaging" || (mobileViewMode === "apps" && appsMode !== 'home')
}
logoutFunc={logoutFunc}
goToHome={goToHome}
@@ -2735,7 +2737,7 @@ export const Group = ({
/>
)}
{isMobile && mobileViewMode === "apps" && (
<Apps />
<Apps mode={appsMode} setMode={setAppsMode} />
)}
{
!isMobile && !selectedGroup &&
@@ -2962,7 +2964,7 @@ export const Group = ({
/>
</div>
{(isMobile && mobileViewMode === "home" || isMobile && mobileViewMode === "apps") && !mobileViewModeKeepOpen && (
{(isMobile && mobileViewMode === "home" || (isMobile && mobileViewMode === "apps" && appsMode === 'home')) && !mobileViewModeKeepOpen && (
<>
<div
style={{
@@ -3004,6 +3006,11 @@ export const Group = ({
)}
</>
)}
{(isMobile && isMobile && mobileViewMode === "apps" && appsMode !== 'home') && !mobileViewModeKeepOpen && (
<>
<AppsNavBar />
</>
)}
</>
);
};