Merge pull request #375 from kinode-dao/tm/appstore-paging-fix

Appstore paging fix
This commit is contained in:
doria 2024-06-04 07:46:57 +09:00 committed by GitHub
commit fc5e60f67e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 178 additions and 143 deletions

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

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-GPWa2djr.js"></script>
<link rel="stylesheet" crossorigin href="/main:app_store:sys/assets/index-EAwGkdAI.css">
<script type="module" crossorigin src="/main:app_store:sys/assets/index-Mr04YvPM.js"></script>
<link rel="stylesheet" crossorigin href="/main:app_store:sys/assets/index-fGthT1qI.css">
</head>
<body>

View File

@ -11,9 +11,10 @@ interface ActionButtonProps extends React.HTMLAttributes<HTMLButtonElement> {
app: AppInfo;
isIcon?: boolean;
permitMultiButton?: boolean;
launchPath?: string
}
export default function ActionButton({ app, isIcon = false, permitMultiButton = false, ...props }: ActionButtonProps) {
export default function ActionButton({ app, launchPath = '', isIcon = false, permitMultiButton = false, ...props }: ActionButtonProps) {
const { installed, downloaded, updatable } = useMemo(() => {
const versions = Object.entries(app?.metadata?.properties?.code_hashes || {});
const latestHash = (versions.find(([v]) => v === app.metadata?.properties?.current_version) || [])[1];
@ -32,21 +33,6 @@ export default function ActionButton({ app, isIcon = false, permitMultiButton =
};
}, [app]);
const [launchPath, setLaunchPath] = useState('');
useEffect(() => {
fetch('/apps').then(data => data.json())
.then((data: Array<{ package_name: string, path: string }>) => {
if (Array.isArray(data)) {
const homepageAppData = data.find(otherApp => app.package === otherApp.package_name)
if (homepageAppData) {
setLaunchPath(homepageAppData.path)
}
}
})
}, [app])
return (
<>
{/* if it's got a UI and it's updatable, show both buttons if we have space (launch will otherwise push out update) */}

View File

@ -15,9 +15,10 @@ interface AppEntryProps extends React.HTMLAttributes<HTMLDivElement> {
size?: "small" | "medium" | "large";
overrideImageSize?: "small" | "medium" | "large";
showMoreActions?: boolean;
launchPath?: string;
}
export default function AppEntry({ app, size = "medium", overrideImageSize, showMoreActions, ...props }: AppEntryProps) {
export default function AppEntry({ app, size = "medium", overrideImageSize, showMoreActions, launchPath, ...props }: AppEntryProps) {
const isMobile = isMobileCheck()
const navigate = useNavigate()
@ -45,6 +46,7 @@ export default function AppEntry({ app, size = "medium", overrideImageSize, show
})}>
<ActionButton
app={app}
launchPath={launchPath}
isIcon={!showMoreActions && size !== 'large'}
className={classNames({
'bg-orange text-lg': size === 'large',

View File

@ -22,6 +22,7 @@ export default function AppPage() {
const navigate = useNavigate();
const params = useParams();
const [app, setApp] = useState<AppInfo | undefined>(undefined);
const [launchPath, setLaunchPath] = useState('');
useEffect(() => {
const myApp = myApps.local.find((a) => appId(a) === params.id);
@ -91,6 +92,18 @@ export default function AppPage() {
}
]
useEffect(() => {
fetch('/apps').then(data => data.json())
.then((data: Array<{ package_name: string, path: string }>) => {
if (Array.isArray(data)) {
const homepageAppData = data.find(otherApp => app?.package === otherApp.package_name)
if (homepageAppData) {
setLaunchPath(homepageAppData.path)
}
}
})
}, [app])
return (
<div className={classNames("flex flex-col w-full p-2",
{
@ -143,7 +156,12 @@ export default function AppPage() {
<div className={classNames("flex-center gap-2", {
'flex-col': isMobile,
})}>
<ActionButton app={app} className={classNames("self-center bg-orange text-lg px-12")} permitMultiButton />
<ActionButton
app={app}
launchPath={launchPath}
className={classNames("self-center bg-orange text-lg px-12")}
permitMultiButton
/>
</div>
{app.installed && app.state?.mirroring && (
<button type="button" onClick={goToPublish}>

View File

@ -19,21 +19,25 @@ export default function StorePage() {
const { listedApps, getListedApps, rebuildIndex } = useAppsStore();
const [resultsSort, setResultsSort] = useState<string>("Recently published");
const [searchQuery, setSearchQuery] = useState<string>("");
const [displayedApps, setDisplayedApps] = useState<AppInfo[]>(listedApps);
const [page, setPage] = useState(1);
const [tags, setTags] = useState<string[]>([])
const [launchPaths, setLaunchPaths] = useState<{ [package_name: string]: string }>({})
const pages = useMemo(
() =>
Array.from(
{ length: Math.ceil(displayedApps.length / 10) },
{
length: Math.ceil(listedApps.length / 10)
},
(_, index) => index + 1
),
[displayedApps]
[listedApps]
);
const featuredPackageNames = ['dartfrog', 'kcal', 'memedeck', 'filter'];
useEffect(() => {
const start = (page - 1) * 10;
const end = start + 10;
@ -104,6 +108,23 @@ export default function StorePage() {
const isMobile = isMobileCheck()
useEffect(() => {
fetch('/apps').then(data => data.json())
.then((data: Array<{ package_name: string, path: string }>) => {
if (Array.isArray(data)) {
listedApps.forEach(app => {
const homepageAppData = data.find(otherApp => app.package === otherApp.package_name)
if (homepageAppData) {
setLaunchPaths({
...launchPaths,
[app.package]: homepageAppData.path
})
}
})
}
})
}, [listedApps])
return (
<div className={classNames("flex flex-col w-full max-h-screen p-2", {
'gap-4 max-w-screen': isMobile,
@ -159,13 +180,14 @@ export default function StorePage() {
<div className={classNames("flex gap-2", {
'flex-col': isMobile
})}>
{displayedApps.filter(app => {
return ['kcal', 'command_center', 'memedeck', 'filter'].indexOf(app.package) !== -1
{listedApps.filter(app => {
return featuredPackageNames.indexOf(app.package) !== -1
}).map((app) => (
<AppEntry
key={appId(app) + (app.state?.our_version || "")}
size={'medium'}
app={app}
launchPath={launchPaths[app.package]}
className={classNames("grow", {
'w-1/4': !isMobile,
'w-full': isMobile
@ -179,34 +201,38 @@ export default function StorePage() {
'gap-2': isMobile,
'gap-4': !isMobile,
})}>
{displayedApps.map(app => <AppEntry
key={appId(app) + (app.state?.our_version || "")}
size='large'
app={app}
className="self-stretch"
overrideImageSize="medium"
/>)}
{displayedApps
.filter(app => searchQuery ? true : featuredPackageNames.indexOf(app.package) === -1)
.map(app => <AppEntry
key={appId(app) + (app.state?.our_version || "")}
size='large'
app={app}
className="self-stretch"
overrideImageSize="medium"
/>)}
</div>
{pages.length > 1 && <div className="flex self-center">
{page !== pages[0] && (
<button className="icon" onClick={() => setPage(page - 1)}>
<FaChevronLeft />
</button>
)}
{pages.length > 1 && <div className="flex flex-wrap self-center gap-2">
<button
className="icon"
onClick={() => page !== pages[0] && setPage(page - 1)}
>
<FaChevronLeft />
</button>
{pages.map((p) => (
<button
key={`page-${p}`}
className={classNames('my-1 mx-2 clear', { "font-bold": p === page })}
className={classNames('icon', { "!bg-white/10": p === page })}
onClick={() => setPage(p)}
>
{p}
</button>
))}
{page !== pages[pages.length - 1] && (
<button className="icon" onClick={() => setPage(page + 1)}>
<FaChevronRight />
</button>
)}
<button
className="icon"
onClick={() => page !== pages[pages.length - 1] && setPage(page + 1)}
>
<FaChevronRight />
</button>
</div>}
</div>
);

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-Gt0JAj27.js"></script>
<script type="module" crossorigin src="/assets/index-DqBTDSfz.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-Gt0JAj27.js"></script>
<script type="module" crossorigin src="/assets/index-DqBTDSfz.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-BS5LP50I.css">
</head>

View File

@ -34,6 +34,9 @@ const usePersistentStore = create<PersistentStore>()(
setFavoriteApps: (favoriteApps: PersistentStore['favoriteApps']) => set({ favoriteApps }),
toggleWidgetVisibility: (package_name: string) => {
const { widgetSettings } = get()
if (!window.confirm(`Really hide this widget?`)) {
return
}
set({
widgetSettings: {
...widgetSettings,

View File

@ -93,7 +93,7 @@ impl process::ProcessState {
}
}
/// Convert a message from the main event loop into a result for the process to receive.
/// Forward the message from the main event loop to the process in the form of a result.
/// If the message is a response or error, get context if we have one.
fn kernel_message_to_process_receive(
&mut self,