mirror of
https://github.com/uqbar-dao/nectar.git
synced 2024-12-23 16:43:24 +03:00
Merge pull request #418 from kinode-dao/tm/app-store-mobile-ui
app store mobile ui
This commit is contained in:
commit
cf1811fa18
@ -60,6 +60,7 @@ pub fn init_frontend(our: &Address) {
|
||||
fn make_widget() -> String {
|
||||
return r#"<html>
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<style>
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
@ -84,7 +85,7 @@ fn make_widget() -> String {
|
||||
gap: 0.5rem;
|
||||
align-items: center;
|
||||
backdrop-filter: saturate(1.25);
|
||||
border-radius: 0.75rem;
|
||||
border-radius: 1rem;
|
||||
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
@ -98,7 +99,7 @@ fn make_widget() -> String {
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
align-items: stretch;
|
||||
border-radius: 0.75rem;
|
||||
border-radius: 0.5rem;
|
||||
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
cursor: pointer;
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -14,8 +14,8 @@
|
||||
<meta httpEquiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport"
|
||||
content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1.00001, viewport-fit=cover" />
|
||||
<script type="module" crossorigin src="/main:app_store:sys/assets/index-I7H8IZID.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/main:app_store:sys/assets/index-fGthT1qI.css">
|
||||
<script type="module" crossorigin src="/main:app_store:sys/assets/index-B8jjW9yS.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/main:app_store:sys/assets/index-zU7UyELC.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
@ -37,12 +37,13 @@ export default function AppEntry({ app, size = "medium", overrideImageSize, show
|
||||
}}
|
||||
>
|
||||
<AppHeader app={app} size={size} overrideImageSize={overrideImageSize} />
|
||||
<div className={classNames("flex items-center", {
|
||||
<div className={classNames("flex", {
|
||||
'items-center': size !== 'large',
|
||||
'items-start': size === 'large',
|
||||
'absolute': size !== 'large',
|
||||
'top-2 right-2': size !== 'large' && showMoreActions,
|
||||
'top-0 right-0': size !== 'large' && !showMoreActions,
|
||||
'ml-auto': size === 'large' && isMobile,
|
||||
'min-w-1/5': size === 'large'
|
||||
})}>
|
||||
<ActionButton
|
||||
app={app}
|
||||
@ -54,7 +55,13 @@ export default function AppEntry({ app, size = "medium", overrideImageSize, show
|
||||
'w-full': size === 'large'
|
||||
})}
|
||||
/>
|
||||
{showMoreActions && <MoreActions app={app} className="self-stretch" />}
|
||||
{showMoreActions && <MoreActions
|
||||
app={app}
|
||||
className={classNames("self-stretch", {
|
||||
'self-start': size === 'large',
|
||||
|
||||
})}
|
||||
/>}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -13,8 +13,8 @@ export default function Dropdown({ ...props }: DropdownProps) {
|
||||
unmountOnClose={true}
|
||||
className={classNames("relative", props.className)}
|
||||
direction='left'
|
||||
menuButton={<MenuButton className="small">
|
||||
<FaEllipsisH className='-mb-1' />
|
||||
menuButton={<MenuButton>
|
||||
<FaEllipsisH className='mb-[3px]' />
|
||||
</MenuButton>}
|
||||
>
|
||||
{props.children}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import classNames from 'classnames'
|
||||
import React, { MouseEvent } from 'react'
|
||||
import { FaX } from 'react-icons/fa6'
|
||||
import { isMobileCheck } from '../utils/dimensions'
|
||||
|
||||
export interface ModalProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
show: boolean
|
||||
@ -25,10 +26,17 @@ const Modal: React.FC<ModalProps> = ({
|
||||
return null
|
||||
}
|
||||
|
||||
const isMobile = isMobileCheck()
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames(`bg-black/25 backdrop-blur-lg fixed top-0 bottom-0 left-0 right-0 flex flex-col c z-30 min-h-[10em] min-w-[30em]`,
|
||||
{ show }
|
||||
className={classNames(`bg-black/25 backdrop-blur-lg fixed top-0 bottom-0 left-0 right-0 flex flex-col c z-30 min-h-[10em] isMobile-${isMobile}`,
|
||||
{
|
||||
isMobile,
|
||||
show,
|
||||
'min-w-[30em]': !isMobile,
|
||||
'min-w-[75vw]': isMobile,
|
||||
}
|
||||
)}
|
||||
onClick={hide}
|
||||
>
|
||||
|
@ -17,13 +17,15 @@ export default function MoreActions({ app, className }: MoreActionsProps) {
|
||||
const navigate = useNavigate();
|
||||
|
||||
const downloaded = Boolean(app.state);
|
||||
const menuClass = "flex flex-col bg-black p-2 rounded-lg relative z-10 border border-orange -mr-[1px]"
|
||||
|
||||
if (!downloaded) {
|
||||
if (!app.metadata) return <></>;
|
||||
|
||||
|
||||
return (
|
||||
<Dropdown className={className}>
|
||||
<div className="flex flex-col backdrop-blur-lg bg-black/10 p-2 rounded-lg relative z-10">
|
||||
<div className={menuClass}>
|
||||
{app.metadata?.description && (
|
||||
<button
|
||||
className="my-1 whitespace-nowrap clear"
|
||||
@ -48,7 +50,7 @@ export default function MoreActions({ app, className }: MoreActionsProps) {
|
||||
|
||||
return (
|
||||
<Dropdown className={className}>
|
||||
<div className="flex flex-col p-2 rounded-lg backdrop-blur-lg relative z-10">
|
||||
<div className={menuClass}>
|
||||
<button
|
||||
className="my-1 whitespace-nowrap clear"
|
||||
onClick={() => navigate(`/${APP_DETAILS_PATH}/${appId(app)}`)}
|
||||
|
@ -99,8 +99,8 @@ export default function SearchHeader({
|
||||
})}
|
||||
onClick={() => (isMyAppsPage ? navigate(-1) : navigate(MY_APPS_PATH))}
|
||||
>
|
||||
{!isMobile && <span>My Apps</span>}
|
||||
<FaDownload />
|
||||
<span>My Apps</span>
|
||||
{!isMobile && <FaDownload />}
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
|
@ -5,7 +5,7 @@ import Modal from "./Modal";
|
||||
import { getAppName } from "../utils/app";
|
||||
import Loader from "./Loader";
|
||||
import classNames from "classnames";
|
||||
import { FaU } from "react-icons/fa6";
|
||||
import { FaExclamation } from "react-icons/fa6";
|
||||
|
||||
interface UpdateButtonProps extends React.HTMLAttributes<HTMLButtonElement> {
|
||||
app: AppInfo;
|
||||
@ -61,7 +61,7 @@ export default function UpdateButton({ app, isIcon = false, ...props }: UpdateBu
|
||||
})}
|
||||
onClick={onClick}
|
||||
>
|
||||
{isIcon ? <FaU /> : 'Update'}
|
||||
{isIcon ? <FaExclamation /> : 'Update'}
|
||||
</button>
|
||||
<Modal show={showModal} hide={() => setShowModal(false)}>
|
||||
{loading ? (
|
||||
|
@ -63,11 +63,11 @@ export default function MyAppsPage() { // eslint-disable-line
|
||||
'gap-4 max-w-screen': isMobile,
|
||||
'gap-8 max-w-[900px]': !isMobile,
|
||||
})}>
|
||||
<HomeButton />
|
||||
{!isMobile && <HomeButton />}
|
||||
<SearchHeader value={searchQuery} onChange={searchMyApps} />
|
||||
<div className="flex justify-between items-center mt-2">
|
||||
<h3>My Packages</h3>
|
||||
<button onClick={() => navigate(PUBLISH_PATH)}>
|
||||
<button className="alt" onClick={() => navigate(PUBLISH_PATH)}>
|
||||
<FaUpload className="mr-2" />
|
||||
Publish Package
|
||||
</button>
|
||||
|
@ -11,6 +11,8 @@ import classNames from 'classnames';
|
||||
import { FaArrowRotateRight } from "react-icons/fa6";
|
||||
import { isMobileCheck } from "../utils/dimensions";
|
||||
import HomeButton from "../components/HomeButton";
|
||||
import Modal from "../components/Modal";
|
||||
import Loader from "../components/Loader";
|
||||
|
||||
interface StorePageProps extends PageProps { }
|
||||
|
||||
@ -24,6 +26,7 @@ export default function StorePage() {
|
||||
const [page, setPage] = useState(1);
|
||||
const [tags, setTags] = useState<string[]>([])
|
||||
const [launchPaths, setLaunchPaths] = useState<{ [package_name: string]: string }>({})
|
||||
const [isRebuildingIndex, setIsRebuildingIndex] = useState(false);
|
||||
|
||||
const pages = useMemo(
|
||||
() =>
|
||||
@ -97,12 +100,18 @@ export default function StorePage() {
|
||||
);
|
||||
|
||||
const tryRebuildIndex = useCallback(async () => {
|
||||
if (!window.confirm('Are you sure you want to rebuild the app index? This may take a few seconds.')) {
|
||||
return;
|
||||
}
|
||||
|
||||
setIsRebuildingIndex(true);
|
||||
try {
|
||||
await rebuildIndex();
|
||||
alert("Index rebuilt successfully.");
|
||||
await getListedApps();
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
} finally {
|
||||
setIsRebuildingIndex(false);
|
||||
}
|
||||
}, [rebuildIndex]);
|
||||
|
||||
@ -127,6 +136,7 @@ export default function StorePage() {
|
||||
|
||||
return (
|
||||
<div className={classNames("flex flex-col w-full max-h-screen p-2", {
|
||||
isMobile,
|
||||
'gap-4 max-w-screen': isMobile,
|
||||
'gap-6 max-w-[900px]': !isMobile
|
||||
})}>
|
||||
@ -174,41 +184,44 @@ export default function StorePage() {
|
||||
</div>
|
||||
{!searchQuery && <div className={classNames("flex flex-col", {
|
||||
'gap-4': !isMobile,
|
||||
'grow overflow-y-auto gap-2 items-center px-2': isMobile
|
||||
'gap-2 items-center': isMobile
|
||||
})}>
|
||||
<h2>Featured Apps</h2>
|
||||
<div className={classNames("flex gap-2", {
|
||||
'flex-col': isMobile
|
||||
'flex-wrap': isMobile
|
||||
})}>
|
||||
{listedApps.filter(app => {
|
||||
return featuredPackageNames.indexOf(app.package) !== -1
|
||||
}).map((app) => (
|
||||
<AppEntry
|
||||
key={appId(app) + (app.state?.our_version || "")}
|
||||
size={'medium'}
|
||||
size={isMobile ? 'small' : 'medium'}
|
||||
app={app}
|
||||
launchPath={launchPaths[app.package]}
|
||||
className={classNames("grow", {
|
||||
'w-1/4': !isMobile,
|
||||
'w-full': isMobile
|
||||
'w-1/3': isMobile
|
||||
})}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>}
|
||||
<h2>{searchQuery ? 'Search Results' : 'All Apps'}</h2>
|
||||
<div className={classNames("flex flex-col grow overflow-y-auto", {
|
||||
<h2 className={classNames({
|
||||
'text-center': isMobile
|
||||
})}>{searchQuery ? 'Search Results' : 'All Apps'}</h2>
|
||||
<div className={classNames("flex flex-col grow", {
|
||||
'gap-2': isMobile,
|
||||
'gap-4': !isMobile,
|
||||
'gap-4 overflow-y-auto': !isMobile,
|
||||
})}>
|
||||
{displayedApps
|
||||
.filter(app => searchQuery ? true : featuredPackageNames.indexOf(app.package) === -1)
|
||||
.map(app => <AppEntry
|
||||
key={appId(app) + (app.state?.our_version || "")}
|
||||
size='large'
|
||||
size={isMobile ? 'medium' : 'large'}
|
||||
app={app}
|
||||
className="self-stretch"
|
||||
overrideImageSize="medium"
|
||||
showMoreActions={!isMobile}
|
||||
/>)}
|
||||
</div>
|
||||
{pages.length > 1 && <div className="flex flex-wrap self-center gap-2">
|
||||
@ -234,6 +247,9 @@ export default function StorePage() {
|
||||
<FaChevronRight />
|
||||
</button>
|
||||
</div>}
|
||||
<Modal title="Rebuilding index..." show={isRebuildingIndex} hide={() => { }}>
|
||||
<Loader msg="This may take a few seconds." />
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -82,6 +82,11 @@ export default defineConfig({
|
||||
target: PROXY_URL,
|
||||
changeOrigin: true,
|
||||
},
|
||||
'/api/*': {
|
||||
target: PROXY_URL,
|
||||
changeOrigin: true,
|
||||
rewrite: (path) => path.replace('/api', ''),
|
||||
},
|
||||
// '/example': {
|
||||
// target: PROXY_URL,
|
||||
// changeOrigin: true,
|
||||
|
5
kinode/packages/chess/chess/Cargo.lock
generated
5
kinode/packages/chess/chess/Cargo.lock
generated
@ -222,13 +222,8 @@ checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
|
||||
|
||||
[[package]]
|
||||
name = "kinode_process_lib"
|
||||
<<<<<<< HEAD:modules/chess/chess/Cargo.lock
|
||||
version = "0.5.7"
|
||||
source = "git+https://github.com/kinode-dao/process_lib?tag=v0.5.9-alpha#c1ac7227951fbd8cabf6568704f0ce11e8558c8a"
|
||||
=======
|
||||
version = "0.5.6"
|
||||
source = "git+https://github.com/kinode-dao/process_lib?rev=fccb6a0#fccb6a0c07ebda3e385bff7f76e4984b741f01c7"
|
||||
>>>>>>> develop:kinode/packages/chess/chess/Cargo.lock
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bincode",
|
||||
|
File diff suppressed because one or more lines are too long
@ -9,7 +9,7 @@
|
||||
<meta httpEquiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport"
|
||||
content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1.00001, viewport-fit=cover" />
|
||||
<script type="module" crossorigin src="/assets/index-BLQ3kP3C.js"></script>
|
||||
<script type="module" crossorigin src="/assets/index-DRR7woJo.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-BS5LP50I.css">
|
||||
</head>
|
||||
|
||||
|
2
kinode/packages/homepage/ui/dist/index.html
vendored
2
kinode/packages/homepage/ui/dist/index.html
vendored
@ -9,7 +9,7 @@
|
||||
<meta httpEquiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport"
|
||||
content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1.00001, viewport-fit=cover" />
|
||||
<script type="module" crossorigin src="/assets/index-BLQ3kP3C.js"></script>
|
||||
<script type="module" crossorigin src="/assets/index-DRR7woJo.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-BS5LP50I.css">
|
||||
</head>
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
import classNames from "classnames"
|
||||
import { FaEye, FaEyeSlash } from "react-icons/fa6"
|
||||
import { useEffect, useState } from "react"
|
||||
import usePersistentStore from "../store/persistentStore"
|
||||
import useHomepageStore from "../store/homepageStore"
|
||||
@ -13,8 +12,7 @@ interface WidgetProps {
|
||||
|
||||
const Widget: React.FC<WidgetProps> = ({ package_name, widget, forceLarge }) => {
|
||||
const { apps } = useHomepageStore()
|
||||
const { widgetSettings, toggleWidgetVisibility } = usePersistentStore()
|
||||
const [isHovered, setIsHovered] = useState(false)
|
||||
const { widgetSettings } = usePersistentStore()
|
||||
const isMobile = isMobileCheck()
|
||||
const isLarge = forceLarge || widgetSettings[package_name]?.size === "large"
|
||||
const isSmall = !widgetSettings[package_name]?.size || widgetSettings[package_name]?.size === "small"
|
||||
@ -31,8 +29,6 @@ const Widget: React.FC<WidgetProps> = ({ package_name, widget, forceLarge }) =>
|
||||
"max-w-1/4": isSmall && !tallScreen,
|
||||
'w-full': isMobile
|
||||
})}
|
||||
onMouseEnter={() => setIsHovered(true)}
|
||||
onMouseLeave={() => setIsHovered(false)}
|
||||
>
|
||||
<h6 className="flex-center my-2">
|
||||
{apps.find(app => app.package_name === package_name)?.label || package_name}
|
||||
@ -42,12 +38,6 @@ const Widget: React.FC<WidgetProps> = ({ package_name, widget, forceLarge }) =>
|
||||
className="grow self-stretch"
|
||||
data-widget-code={widget}
|
||||
/>
|
||||
{isHovered && <button
|
||||
className="absolute top-0 left-0 icon"
|
||||
onClick={() => toggleWidgetVisibility(package_name)}
|
||||
>
|
||||
{widgetSettings[package_name]?.hide ? <FaEye /> : <FaEyeSlash />}
|
||||
</button>}
|
||||
</div>
|
||||
}
|
||||
|
||||
|
@ -49,6 +49,7 @@ fn create_widget(posts: Vec<KinodeBlogPost>) -> String {
|
||||
return format!(
|
||||
r#"<html>
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<style>
|
||||
* {{
|
||||
box-sizing: border-box;
|
||||
|
5
kinode/packages/settings/settings/Cargo.lock
generated
5
kinode/packages/settings/settings/Cargo.lock
generated
@ -222,13 +222,8 @@ checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
|
||||
|
||||
[[package]]
|
||||
name = "kinode_process_lib"
|
||||
<<<<<<< HEAD:modules/chess/chess/Cargo.lock
|
||||
version = "0.5.7"
|
||||
source = "git+https://github.com/kinode-dao/process_lib?tag=v0.5.9-alpha#c1ac7227951fbd8cabf6568704f0ce11e8558c8a"
|
||||
=======
|
||||
version = "0.5.6"
|
||||
source = "git+https://github.com/kinode-dao/process_lib?rev=fccb6a0#fccb6a0c07ebda3e385bff7f76e4984b741f01c7"
|
||||
>>>>>>> develop:kinode/packages/chess/chess/Cargo.lock
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bincode",
|
||||
|
Loading…
Reference in New Issue
Block a user