Remove files for mobile

This commit is contained in:
Nicola Benaglia 2025-04-20 13:49:42 +02:00
parent e1bb064d1a
commit de0f52311e
7 changed files with 0 additions and 2094 deletions

View File

@ -1,357 +0,0 @@
import React, { useEffect, useMemo, useRef, useState } from 'react';
import { AppsHome } from './AppsHome';
import { Spacer } from '../../common/Spacer';
import { getBaseApiReact } from '../../App';
import { AppInfo } from './AppInfo';
import {
executeEvent,
subscribeToEvent,
unsubscribeFromEvent,
} from '../../utils/events';
import { AppsParent } from './Apps-styles';
import AppViewerContainer from './AppViewerContainer';
import ShortUniqueId from 'short-unique-id';
import { AppPublish } from './AppPublish';
import { AppsCategory } from './AppsCategory';
import { AppsLibrary } from './AppsLibrary';
const uid = new ShortUniqueId({ length: 8 });
export const Apps = ({ mode, setMode, show, myName }) => {
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 iframeRefs = useRef({});
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) {
console.log(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) {
console.log(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 (
[
'category',
'appInfo-from-category',
'appInfo',
'library',
'publish',
].includes(mode)
) {
// Handle the various modes as needed
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 if (selectedTab?.tabId) {
executeEvent(`navigateBackApp-${selectedTab?.tabId}`, {});
}
};
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',
}}
>
{mode !== 'viewer' && !selectedTab && <Spacer height="30px" />}
{mode === 'home' && (
<AppsHome
availableQapps={availableQapps}
setMode={setMode}
myApp={myApp}
myWebsite={myWebsite}
/>
)}
<AppsLibrary
isShow={mode === 'library' && !selectedTab}
availableQapps={availableQapps}
setMode={setMode}
myName={myName}
hasPublishApp={!!(myApp || myWebsite)}
categories={categories}
/>
{mode === 'appInfo' && !selectedTab && (
<AppInfo app={selectedAppInfo} myName={myName} />
)}
{mode === 'appInfo-from-category' && !selectedTab && (
<AppInfo app={selectedAppInfo} myName={myName} />
)}
<AppsCategory
availableQapps={availableQapps}
isShow={mode === 'category' && !selectedTab}
category={selectedCategory}
myName={myName}
/>
{mode === 'publish' && !selectedTab && (
<AppPublish names={myName ? [myName] : []} categories={categories} />
)}
{tabs.map((tab) => {
if (!iframeRefs.current[tab.tabId]) {
iframeRefs.current[tab.tabId] = React.createRef();
}
return (
<AppViewerContainer
key={tab?.tabId}
hide={isNewTabWindow}
isSelected={tab?.tabId === selectedTab?.tabId}
app={tab}
ref={iframeRefs.current[tab.tabId]}
/>
);
})}
{isNewTabWindow && mode === 'viewer' && (
<>
<Spacer height="30px" />
<AppsHome
availableQapps={availableQapps}
setMode={setMode}
myApp={myApp}
myWebsite={myWebsite}
/>
</>
)}
{mode !== 'viewer' && !selectedTab && <Spacer height="180px" />}
</AppsParent>
);
};

View File

@ -1,204 +0,0 @@
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';
const officialAppList = [
'q-tube',
'q-blog',
'q-share',
'q-support',
'q-mail',
'q-fund',
'q-shop',
'q-trade',
'q-support',
'q-manager',
'q-wallets',
'q-search',
'q-nodecontrol',
];
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
'-msOverflowStyle': '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
'-msOverflowStyle': 'none',
});
export const AppsCategory = ({ 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',
}}
>
<AppsWidthLimiter>
<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>
</AppsWidthLimiter>
<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>
</AppsLibraryContainer>
);
};

View File

@ -1,51 +0,0 @@
import {
AppCircle,
AppCircleContainer,
AppCircleLabel,
AppLibrarySubTitle,
AppsContainer,
} from './Apps-styles';
import { ButtonBase } from '@mui/material';
import { Add } from '@mui/icons-material';
import { SortablePinnedApps } from './SortablePinnedApps';
import { Spacer } from '../../common/Spacer';
export const AppsHome = ({ setMode, myApp, myWebsite, availableQapps }) => {
return (
<>
<AppsContainer
sx={{
justifyContent: 'flex-start',
}}
>
<AppLibrarySubTitle>Apps Dashboard</AppLibrarySubTitle>
</AppsContainer>
<Spacer height="20px" />
<AppsContainer>
<ButtonBase
onClick={() => {
setMode('library');
}}
>
<AppCircleContainer
sx={{
gap: '10px',
}}
>
<AppCircle>
<Add>+</Add>
</AppCircle>
<AppCircleLabel>Library</AppCircleLabel>
</AppCircleContainer>
</ButtonBase>
<SortablePinnedApps
availableQapps={availableQapps}
myWebsite={myWebsite}
myApp={myApp}
/>
</AppsContainer>
</>
);
};

View File

@ -1,384 +0,0 @@
import { useContext, useEffect, useMemo, useRef, useState } from 'react';
import {
AppCircle,
AppCircleContainer,
AppCircleLabel,
AppLibrarySubTitle,
AppsContainer,
AppsLibraryContainer,
AppsSearchContainer,
AppsSearchLeft,
AppsSearchRight,
AppsWidthLimiter,
PublishQAppCTAButton,
PublishQAppCTALeft,
PublishQAppCTAParent,
PublishQAppCTARight,
PublishQAppDotsBG,
} from './Apps-styles';
import {
Avatar,
Box,
ButtonBase,
InputBase,
styled,
useTheme,
} from '@mui/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 { Return } from './assets/svgs/Return.tsx';
import ReturnSVG from '../../assets/svgs/Return.svg';
import { Spacer } from '../../common/Spacer';
import { AppInfoSnippet } from './AppInfoSnippet';
import { Virtuoso } from 'react-virtuoso';
import { executeEvent } from '../../utils/events';
import {
ComposeP,
MailIconImg,
ShowMessageReturnButton,
} from '../Group/Forum/Mail-styles';
const officialAppList = [
'q-tube',
'q-blog',
'q-share',
'q-support',
'q-mail',
'q-fund',
'q-shop',
'q-trade',
'q-support',
'q-manager',
'q-wallets',
'q-search',
'q-nodecontrol',
];
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
'-msOverflowStyle': 'none',
});
const StyledVirtuosoContainer = styled('div')({
display: 'flex',
flexDirection: 'column',
position: 'relative',
width: '100%',
// 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
'-msOverflowStyle': 'none',
});
export const AppsLibrary = ({
availableQapps,
setMode,
myName,
hasPublishApp,
isShow,
categories = { categories },
}) => {
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())
);
}, [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}
/>
);
};
const theme = useTheme();
return (
<AppsLibraryContainer
sx={{
display: !isShow && 'none',
}}
>
<AppsWidthLimiter>
<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>
</AppsWidthLimiter>
<Spacer height="25px" />
<ShowMessageReturnButton
sx={{
padding: '2px',
}}
onClick={() => {
executeEvent('navigateBack', {});
}}
>
<MailIconImg src={ReturnSVG} /> // TODO return icon
<ComposeP>Return to Apps Dashboard</ComposeP>
</ShowMessageReturnButton>
<Spacer height="25px" />
{searchedList?.length > 0 ? (
<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>
) : (
<>
<AppsWidthLimiter>
<AppLibrarySubTitle>Official Apps</AppLibrarySubTitle>
<Spacer height="18px" />
<AppsContainer>
{officialApps?.map((qapp) => {
return (
<ButtonBase
sx={{
height: '80px',
width: '60px',
}}
onClick={() => {
// executeEvent("addTab", {
// data: qapp
// })
executeEvent('selectedAppInfo', {
data: qapp,
});
}}
>
<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>
<Spacer height="30px" />
<AppLibrarySubTitle>
{hasPublishApp ? 'Update Apps!' : 'Create Apps!'}
</AppLibrarySubTitle>
<Spacer height="18px" />
</AppsWidthLimiter>
<PublishQAppCTAParent>
<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>
<AppsWidthLimiter>
<Spacer height="18px" />
<AppLibrarySubTitle>Categories</AppLibrarySubTitle>
<Spacer height="18px" />
<AppsWidthLimiter
sx={{
flexDirection: 'row',
overflowX: 'auto',
width: '100%',
gap: '5px',
'::-webkit-scrollbar': {
width: '0px',
height: '0px',
},
// Hide scrollbar for Firefox
scrollbarWidth: 'none',
// Hide scrollbar for IE and older Edge
'-msOverflowStyle': 'none',
}}
>
{categories?.map((category) => {
return (
<ButtonBase
key={category?.id}
onClick={() => {
executeEvent('selectedCategory', {
data: category,
});
}}
>
<Box
sx={{
alignItems: 'center',
background: theme.palette.background.default,
borderRadius: '11px',
color: theme.palette.text.primary,
display: 'flex',
flexShrink: 0,
fontSize: '16px',
fontWeight: 700,
height: '110px',
justifyContent: 'center',
width: '110px',
}}
>
{category?.name}
</Box>
</ButtonBase>
);
})}
</AppsWidthLimiter>
</AppsWidthLimiter>
</>
)}
</AppsLibraryContainer>
);
};

View File

@ -1,367 +0,0 @@
import { useEffect, useMemo, useRef, useState } from 'react';
import {
AppsNavBarLeft,
AppsNavBarParent,
AppsNavBarRight,
} from './Apps-styles';
import { NavBack } from '../../assets/Icons/NavBack.tsx';
import { NavAdd } from '../../assets/Icons/NavAdd.tsx';
import { NavMoreMenu } from '../../assets/Icons/NavMoreMenu.tsx';
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 {
navigationControllerAtom,
settingsLocalLastUpdatedAtom,
sortablePinnedAppsAtom,
} from '../../atoms/global';
export function saveToLocalStorage(
key,
subKey,
newValue,
otherRootData = {},
deleteWholeKey
) {
try {
if (deleteWholeKey) {
localStorage.setItem(key, null);
return;
}
// 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,
...otherRootData,
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 = {
...otherRootData,
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 AppsNavBar = () => {
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 [navigationController, setNavigationController] = useRecoilState(
navigationControllerAtom
);
const isDisableBackButton = useMemo(() => {
if (selectedTab && navigationController[selectedTab?.tabId]?.hasBack)
return false;
if (selectedTab && !navigationController[selectedTab?.tabId]?.hasBack)
return true;
return false;
}, [navigationController, selectedTab]);
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 ? null : { ...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>
<AppsNavBarLeft>
<ButtonBase
onClick={() => {
executeEvent('navigateBack', selectedTab?.tabId);
}}
disabled={isDisableBackButton}
sx={{
opacity: !isDisableBackButton ? 1 : 0.3,
cursor: !isDisableBackButton ? 'pointer' : 'default',
}}
>
<NavBack />
</ButtonBase>
<Tabs
ref={tabsRef}
aria-label="basic tabs example"
variant="scrollable" // Make tabs scrollable
scrollButtons={false}
sx={{
'& .MuiTabs-indicator': {
backgroundColor: 'white',
},
maxWidth: `calc(100vw - 150px)`, // 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>
{selectedTab && (
<AppsNavBarRight
sx={{
gap: '10px',
}}
>
<ButtonBase
onClick={() => {
setSelectedTab(null);
executeEvent('newTabWindow', {});
}}
>
<NavAdd
style={{
height: '40px',
width: '40px',
}}
/>
</ButtonBase>
<ButtonBase
onClick={(e) => {
if (!selectedTab) return;
handleClick(e);
}}
>
<NavMoreMenu
style={{
height: '34px',
width: '34px',
}}
/>
</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

@ -1,203 +0,0 @@
import * as React from "react";
import {
BottomNavigation,
BottomNavigationAction,
ButtonBase,
Typography,
} from "@mui/material";
import { Home, Groups, Message, ShowChart } from "@mui/icons-material";
import Box from "@mui/material/Box";
import BottomLogo from "../../assets/svgs/BottomLogo5.svg";
import LogoSelected from "../../assets/svgs/LogoSelected.svg";
import { Browser } from '@capacitor/browser';
import { CustomSvg } from "../../common/CustomSvg";
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 { executeEvent } from "../../utils/events";
const IconWrapper = ({ children, label, color }) => {
return (
<Box
sx={{
display: "flex",
justifyContent: "center",
alignItems: "center",
gap: "5px",
flexDirection: "column",
}}
>
{children}
<Typography
sx={{
fontFamily: "Inter",
fontSize: "12px",
fontWeight: 500,
color: color,
wordBreak: 'normal'
}}
>
{label}
</Typography>
</Box>
);
};
export const MobileFooter = ({
selectedGroup,
groupSection,
isUnread,
goToAnnouncements,
isUnreadChat,
goToChat,
goToThreads,
setOpenManageMembers,
groupChatHasUnread,
groupsAnnHasUnread,
directChatHasUnread,
chatMode,
openDrawerGroups,
goToHome,
setIsOpenDrawerProfile,
mobileViewMode,
setMobileViewMode,
setMobileViewModeKeepOpen,
hasUnreadGroups,
hasUnreadDirects
}) => {
const [value, setValue] = React.useState(0);
return (
<Box
sx={{
width: "100%",
position: "fixed",
bottom: 0,
backgroundColor: "var(--bg-primary)",
display: "flex",
alignItems: "center",
height: "67px", // Footer height
zIndex: 1,
borderTopRightRadius: "25px",
borderTopLeftRadius: "25px",
boxShadow: '0px -2px 10px rgba(0, 0, 0, 0.1)',
}}
>
<BottomNavigation
showLabels
value={value}
onChange={(event, newValue) => setValue(newValue)}
sx={{ backgroundColor: "transparent", flexGrow: 1 }}
>
<BottomNavigationAction
onClick={() => {
// setMobileViewMode('wallet')
setIsOpenDrawerProfile(true);
}}
icon={
<IconWrapper color="rgba(250, 250, 250, 0.5)" label="Wallet">
<WalletIcon color="rgba(250, 250, 250, 0.5)" />
</IconWrapper>
}
sx={{ color: value === 0 ? "white" : "gray", padding: "0px 10px" }}
/>
<BottomNavigationAction
onClick={() => {
setMobileViewMode("groups");
}}
icon={
<IconWrapper color="rgba(250, 250, 250, 0.5)" label="Groups">
<HubsIcon color={hasUnreadGroups ? "var(--danger)" : "rgba(250, 250, 250, 0.5)"} />
</IconWrapper>
}
sx={{
color: value === 0 ? "white" : "gray",
paddingLeft: "10px",
paddingRight: "42px",
}}
/>
</BottomNavigation>
{/* Floating Center Button */}
<Box
sx={{
position: "absolute",
bottom: "34px", // Adjusted to float properly based on footer height
left: "50%",
transform: "translateX(-50%)", // Center horizontally
width: "59px",
height: "59px",
backgroundColor: "var(--bg-primary)",
borderRadius: "50%",
display: "flex",
justifyContent: "center",
alignItems: "center",
boxShadow: "0 4px 10px rgba(0, 0, 0, 0.3)", // Subtle shadow for the floating effect
zIndex: 3,
}}
>
<ButtonBase onClick={()=> {
if(mobileViewMode === 'home'){
setMobileViewMode('apps')
} else {
setMobileViewMode('home')
}
}}>
<Box
sx={{
width: "49px", // Slightly smaller inner circle
height: "49px",
backgroundColor: "var(--bg-primary)",
borderRadius: "50%",
display: "flex",
justifyContent: "center",
alignItems: "center",
}}
>
{/* Custom Center Icon */}
<img src={mobileViewMode === 'apps' ? LogoSelected : BottomLogo} alt="center-icon" />
</Box>
</ButtonBase>
</Box>
<BottomNavigation
showLabels
value={value}
onChange={(event, newValue) => setValue(newValue)}
sx={{ backgroundColor: "transparent", flexGrow: 1 }}
>
<BottomNavigationAction
onClick={() => {
setMobileViewModeKeepOpen("messaging");
}}
icon={
<IconWrapper label="Messaging" color="rgba(250, 250, 250, 0.5)">
<MessagingIcon color={hasUnreadDirects ? "var(--danger)" :"rgba(250, 250, 250, 0.5)"} />
</IconWrapper>
}
sx={{
color: value === 2 ? "white" : "gray",
paddingLeft: "55px",
paddingRight: "10px",
}}
/>
<BottomNavigationAction
onClick={async () => {
executeEvent("addTab", { data: { service: 'APP', name: 'q-trade' } });
executeEvent("open-apps-mode", { });
}}
icon={
<IconWrapper label="Trading" color="rgba(250, 250, 250, 0.5)">
<TradingIcon color="rgba(250, 250, 250, 0.5)" />
</IconWrapper>
}
sx={{ color: value === 3 ? "white" : "gray", padding: "0px 10px" }}
/>
</BottomNavigation>
</Box>
);
};

View File

@ -1,528 +0,0 @@
import React, { useState } from "react";
import {
AppBar,
Toolbar,
IconButton,
Typography,
Box,
MenuItem,
Select,
ButtonBase,
Menu,
ListItemIcon,
ListItemText,
} from "@mui/material";
import { HomeIcon } from "../../assets/Icons/HomeIcon";
import { LogoutIcon } from "../../assets/Icons/LogoutIcon";
import { NotificationIcon } from "../../assets/Icons/NotificationIcon";
import { ArrowDownIcon } from "../../assets/Icons/ArrowDownIcon";
import { MessagingIcon } from "../../assets/Icons/MessagingIcon";
import { MessagingIcon2 } from "../../assets/Icons/MessagingIcon2";
import { HubsIcon } from "../../assets/Icons/HubsIcon";
import { Save } from "../Save/Save";
import CloseFullscreenIcon from "@mui/icons-material/CloseFullscreen";
import { useRecoilState } from "recoil";
import { fullScreenAtom, hasSettingsChangedAtom } from "../../atoms/global";
import { useAppFullScreen } from "../../useAppFullscreen";
const Header = ({
logoutFunc,
goToHome,
setIsOpenDrawerProfile,
isThin,
setMobileViewModeKeepOpen,
hasUnreadGroups,
hasUnreadDirects,
setMobileViewMode,
myName,
setSelectedDirect,
setNewChat,
}) => {
const [anchorEl, setAnchorEl] = useState(null);
const open = Boolean(anchorEl);
const [fullScreen, setFullScreen] = useRecoilState(fullScreenAtom);
const { exitFullScreen } = useAppFullScreen(setFullScreen);
const handleClick = (event) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
if (isThin) {
return (
<AppBar
position="static"
sx={{
backgroundColor: "background: rgba(0, 0, 0, 0.2)",
boxShadow: "none",
}}
>
<Toolbar
sx={{
justifyContent: "space-between",
padding: "0 16px",
height: "45px",
minHeight: "45px",
}}
>
{/* Left Home Icon */}
<Box
sx={{
display: "flex",
alignItems: "center",
gap: "18px",
width: "75px",
}}
>
<ButtonBase
onClick={() => {
setMobileViewModeKeepOpen("");
goToHome();
}}
// onClick={onHomeClick}
>
<HomeIcon height={20} width={27} />
</ButtonBase>
<ButtonBase onClick={handleClick}>
<NotificationIcon
height={20}
width={21}
color={
hasUnreadDirects || hasUnreadGroups
? "var(--danger)"
: "rgba(145, 145, 147, 1)"
}
/>
</ButtonBase>
{fullScreen && (
<ButtonBase
onClick={() => {
exitFullScreen();
setFullScreen(false);
}}
>
<CloseFullscreenIcon
sx={{
color: "rgba(145, 145, 147, 1)",
}}
/>
</ButtonBase>
)}
</Box>
{/* Center Title */}
<Typography
variant="h6"
sx={{
color: "rgba(255, 255, 255, 1)",
fontWeight: 700,
letterSpacing: "2px",
fontSize: "13px",
}}
>
QORTAL
</Typography>
<Box
sx={{
display: "flex",
alignItems: "center",
gap: "18px",
width: "75px",
justifyContent: "flex-end",
}}
>
{/* Right Logout Icon */}
<ButtonBase
onClick={() => {
setMobileViewModeKeepOpen("messaging");
}}
>
<MessagingIcon2
height={20}
color={
hasUnreadDirects ? "var(--danger)" : "rgba(145, 145, 147, 1)"
}
/>
</ButtonBase>
<Save />
<ButtonBase onClick={logoutFunc}>
<LogoutIcon
height={20}
width={21}
color="rgba(145, 145, 147, 1)"
/>
</ButtonBase>
</Box>
</Toolbar>
<Menu
id="home-menu"
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={() => {
setSelectedDirect(null);
setNewChat(false);
setMobileViewMode("groups");
setMobileViewModeKeepOpen("");
handleClose();
}}
>
<ListItemIcon
sx={{
minWidth: "24px !important",
}}
>
<HubsIcon
height={20}
color={
hasUnreadGroups ? "var(--danger)" : "rgba(250, 250, 250, 0.5)"
}
/>
</ListItemIcon>
<ListItemText
sx={{
"& .MuiTypography-root": {
fontSize: "12px",
fontWeight: 600,
color: hasUnreadGroups
? "var(--danger)"
: "rgba(250, 250, 250, 0.5)",
},
}}
primary="Groups"
/>
</MenuItem>
<MenuItem
onClick={() => {
setMobileViewModeKeepOpen("messaging");
handleClose();
}}
>
<ListItemIcon
sx={{
minWidth: "24px !important",
}}
>
<MessagingIcon
height={20}
color={
hasUnreadDirects
? "var(--danger)"
: "rgba(250, 250, 250, 0.5)"
}
/>
</ListItemIcon>
<ListItemText
sx={{
"& .MuiTypography-root": {
fontSize: "12px",
fontWeight: 600,
color: hasUnreadDirects
? "var(--danger)"
: "rgba(250, 250, 250, 0.5)",
},
}}
primary="Messaging"
/>
</MenuItem>
</Menu>
</AppBar>
);
}
return (
<>
{/* Main Header */}
<AppBar
position="static"
sx={{ backgroundColor: "var(--bg-primary)", boxShadow: "none" }}
>
<Toolbar
sx={{
justifyContent: "space-between",
padding: "0 16px",
height: "60px",
}}
>
{/* Left Home Icon */}
<Box
sx={{
display: "flex",
alignItems: "center",
gap: "18px",
width: "75px",
}}
>
<ButtonBase
onClick={goToHome}
// onClick={onHomeClick}
>
<HomeIcon />
</ButtonBase>
{fullScreen && (
<ButtonBase
onClick={() => {
exitFullScreen();
setFullScreen(false);
}}
>
<CloseFullscreenIcon
sx={{
color: "rgba(145, 145, 147, 1)",
}}
/>
</ButtonBase>
)}
</Box>
{/* Center Title */}
<Typography
variant="h6"
sx={{
color: "rgba(255, 255, 255, 1)",
fontWeight: 700,
letterSpacing: "2px",
fontSize: "13px",
}}
>
QORTAL
</Typography>
<Box
sx={{
display: "flex",
alignItems: "center",
gap: "30px",
width: "75px",
justifyContent: "flex-end",
}}
>
{/* Right Logout Icon */}
<Save />
<ButtonBase
onClick={logoutFunc}
// onClick={onLogoutClick}
>
<LogoutIcon color="rgba(145, 145, 147, 1)" />
</ButtonBase>
</Box>
</Toolbar>
</AppBar>
{/* Secondary Section */}
<Box
sx={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
backgroundColor: "var(--bg-3)",
padding: "8px 16px",
position: "relative",
height: "27px",
}}
>
<Box
sx={{
display: "flex",
gap: "10px",
alignItems: "center",
userSelect: "none",
}}
>
<Typography
sx={{
color: "rgba(255, 255, 255, 1)",
fontWeight: 400,
fontSize: "11px",
}}
>
{myName}
</Typography>
{/*
<ArrowDownIcon /> */}
</Box>
<Box
sx={{
position: "absolute",
left: "50%",
transform: "translate(-50%, 50%)",
display: "flex",
justifyContent: "center",
alignItems: "center",
zIndex: 6,
width: "30px", // Adjust as needed
height: "30px", // Adjust as needed
backgroundColor: "#232428", // Circle background
borderRadius: "50%",
boxShadow: "0px 4px 10px rgba(0, 0, 0, 0.3)", // Optional shadow for the circle
}}
>
<IconButton onClick={handleClick} color="inherit">
<NotificationIcon
color={
hasUnreadDirects || hasUnreadGroups
? "var(--danger)"
: "rgba(255, 255, 255, 1)"
}
/>
</IconButton>
</Box>
{/* Right Dropdown */}
{/* <ButtonBase
onClick={() => {
setIsOpenDrawerProfile(true);
}}
>
<Box
sx={{
display: "flex",
gap: "10px",
alignItems: "center",
}}
>
<Typography
sx={{
color: "rgba(255, 255, 255, 1)",
fontWeight: 400,
fontSize: "11px",
}}
>
View Wallet
</Typography>
<ArrowDownIcon />
</Box>
</ButtonBase> */}
</Box>
<Menu
id="home-menu"
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={() => {
setMobileViewMode("groups");
setMobileViewModeKeepOpen("");
handleClose();
}}
>
<ListItemIcon
sx={{
minWidth: "24px !important",
}}
>
<HubsIcon
height={20}
color={
hasUnreadGroups ? "var(--danger)" : "rgba(250, 250, 250, 0.5)"
}
/>
</ListItemIcon>
<ListItemText
sx={{
"& .MuiTypography-root": {
fontSize: "12px",
fontWeight: 600,
color: hasUnreadDirects
? "var(--danger)"
: "rgba(250, 250, 250, 0.5)",
},
}}
primary="Groups"
/>
</MenuItem>
<MenuItem
onClick={() => {
setMobileViewModeKeepOpen("messaging");
handleClose();
}}
>
<ListItemIcon
sx={{
minWidth: "24px !important",
}}
>
<MessagingIcon
height={20}
color={
hasUnreadDirects ? "var(--danger)" : "rgba(250, 250, 250, 0.5)"
}
/>
</ListItemIcon>
<ListItemText
sx={{
"& .MuiTypography-root": {
fontSize: "12px",
fontWeight: 600,
color: hasUnreadDirects
? "var(--danger)"
: "rgba(250, 250, 250, 0.5)",
},
}}
primary="Messaging"
/>
</MenuItem>
</Menu>
</>
);
};
export default Header;