Merge pull request #418 from kinode-dao/tm/app-store-mobile-ui

app store mobile ui
This commit is contained in:
doria 2024-06-21 23:00:14 +09:00 committed by GitHub
commit cf1811fa18
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
21 changed files with 109 additions and 89 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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