fix: Ingress, should probably add more event listeners and more ingress types for better user feedback
This commit is contained in:
parent
a158f5a058
commit
f00db32cab
8 changed files with 98 additions and 110 deletions
|
|
@ -23,6 +23,7 @@
|
|||
"@leeoniya/ufuzzy": "^1.0.14",
|
||||
"@onelauncher/client": "workspace:*",
|
||||
"@solid-primitives/i18n": "^2.1.1",
|
||||
"@solid-primitives/map": "^0.4.13",
|
||||
"@solid-primitives/refs": "^1.0.8",
|
||||
"@solidjs/router": "^0.14.7",
|
||||
"@t3-oss/env-core": "^0.11.0",
|
||||
|
|
|
|||
|
|
@ -1,45 +1,21 @@
|
|||
import type { IngressPayload, IngressType } from '@onelauncher/client/bindings';
|
||||
import { PausableTimer } from '@onelauncher/client';
|
||||
import { InfoCircleIcon } from '@untitled-theme/icons-solid';
|
||||
import { type ComponentProps, createEffect, createSignal, type JSX, Match, Show, Switch } from 'solid-js';
|
||||
import { createEffect, createSignal, type JSX, Match, Show, Switch } from 'solid-js';
|
||||
|
||||
type NotificationComponentProps = IngressPayload & {
|
||||
export interface NotificationData {
|
||||
title: string;
|
||||
message: string;
|
||||
fraction?: number | undefined;
|
||||
}
|
||||
|
||||
export type NotificationComponentProps = NotificationData & {
|
||||
icon?: () => JSX.Element;
|
||||
overlay: boolean;
|
||||
};
|
||||
|
||||
function IconFromNotificationType(type: IngressType['type']): (props: ComponentProps<'svg'>) => JSX.Element {
|
||||
switch (type) {
|
||||
case 'initialize':
|
||||
case 'download_java':
|
||||
case 'download_loader':
|
||||
case 'sync_cluster':
|
||||
case 'copy_cluster':
|
||||
case 'sync_config':
|
||||
case 'archival':
|
||||
case 'download_package':
|
||||
case 'download_pack':
|
||||
default:
|
||||
return InfoCircleIcon;
|
||||
}
|
||||
}
|
||||
|
||||
function ColorFromNotificationType(type: IngressType['type']): string {
|
||||
switch (type) {
|
||||
case 'initialize':
|
||||
case 'download_java':
|
||||
case 'download_loader':
|
||||
case 'sync_cluster':
|
||||
case 'copy_cluster':
|
||||
case 'sync_config':
|
||||
case 'archival':
|
||||
case 'download_package':
|
||||
case 'download_pack':
|
||||
default:
|
||||
return 'text-fg-primary';
|
||||
}
|
||||
}
|
||||
|
||||
const TOTAL_SECONDS = 7;
|
||||
const fractionEnded = (fraction: number | undefined) => fraction !== undefined && fraction >= 0.97;
|
||||
|
||||
function NotificationOverlayComponent(props: NotificationComponentProps) {
|
||||
const [disappearing, setDisappearing] = createSignal<boolean>(true);
|
||||
const [timer, setTimer] = createSignal<PausableTimer | undefined>();
|
||||
|
|
@ -49,7 +25,7 @@ function NotificationOverlayComponent(props: NotificationComponentProps) {
|
|||
createEffect(() => {
|
||||
setDisappearing(props.fraction === undefined);
|
||||
|
||||
if (props.fraction !== null && props.fraction >= 1) {
|
||||
if (fractionEnded(props.fraction)) {
|
||||
setVisible(false);
|
||||
return;
|
||||
}
|
||||
|
|
@ -65,10 +41,6 @@ function NotificationOverlayComponent(props: NotificationComponentProps) {
|
|||
}
|
||||
});
|
||||
|
||||
// onMount(() => {
|
||||
// setVisible(true);
|
||||
// });
|
||||
|
||||
function onInterval() {
|
||||
if (secondsLeft() <= 0) {
|
||||
hide();
|
||||
|
|
@ -93,14 +65,6 @@ function NotificationOverlayComponent(props: NotificationComponentProps) {
|
|||
}
|
||||
|
||||
return (
|
||||
// <Transition
|
||||
// enterClass="noti-animation-enter"
|
||||
// enterActiveClass="noti-animation-enter-active"
|
||||
// enterToClass="noti-animation-enter-to"
|
||||
// exitClass="noti-animation-leave"
|
||||
// exitActiveClass="noti-animation-leave-active"
|
||||
// exitToClass="noti-animation-leave-to"
|
||||
// >
|
||||
<Show when={visible()}>
|
||||
<div
|
||||
class="flex flex-col overflow-hidden rounded-lg bg-component-bg"
|
||||
|
|
@ -111,7 +75,7 @@ function NotificationOverlayComponent(props: NotificationComponentProps) {
|
|||
<NotificationPopupComponent {...props} />
|
||||
</div>
|
||||
|
||||
<Show when={disappearing() === true && props.fraction === undefined}>
|
||||
<Show when={disappearing() === true}>
|
||||
<div class="h-1.5 w-full bg-brand-disabled">
|
||||
<div
|
||||
class="h-1.5 rounded-lg bg-brand transition-width"
|
||||
|
|
@ -123,21 +87,21 @@ function NotificationOverlayComponent(props: NotificationComponentProps) {
|
|||
</Show>
|
||||
</div>
|
||||
</Show>
|
||||
// </Transition>
|
||||
);
|
||||
}
|
||||
|
||||
function NotificationPopupComponent(props: NotificationComponentProps) {
|
||||
// eslint-disable-next-line solid/reactivity -- Doesn't really matter here
|
||||
const Icon = props.icon ?? InfoCircleIcon;
|
||||
|
||||
return (
|
||||
<div class="flex flex-col gap-y-1 p-2">
|
||||
<div class="grid grid-cols-[24px_1fr_auto] min-h-10 place-items-center gap-3">
|
||||
{IconFromNotificationType(props.event.type)({
|
||||
class: `w-6 h-6 ${ColorFromNotificationType(props.event.type)}`,
|
||||
})}
|
||||
<Icon class="h-6 w-6 text-fg-primary" />
|
||||
|
||||
<div class="w-full flex flex-col">
|
||||
<span class={`font-medium ${ColorFromNotificationType(props.event.type)}`}>{props.message}</span>
|
||||
<span class="text-sm text-white/60">{props.message}</span>
|
||||
<div class="w-full flex flex-col gap-y-1">
|
||||
<span class="text-fg-primary font-medium capitalize">{props.title}</span>
|
||||
<span class="text-sm text-white/60 capitalize">{props.message}</span>
|
||||
</div>
|
||||
|
||||
<Show when={props.overlay !== true}>
|
||||
|
|
@ -147,12 +111,12 @@ function NotificationPopupComponent(props: NotificationComponentProps) {
|
|||
</Show>
|
||||
</div>
|
||||
|
||||
<Show when={props.fraction !== null}>
|
||||
<Show when={props.fraction !== undefined && props.fraction !== null && !fractionEnded(props.fraction)}>
|
||||
<div class="h-1.5 w-full overflow-hidden rounded-full bg-brand-disabled">
|
||||
<div
|
||||
class="h-full max-w-full min-w-0 rounded-full bg-brand transition-width"
|
||||
style={{
|
||||
width: `${Math.floor(props.fraction!)}%`,
|
||||
width: `${Math.floor(props.fraction! * 100)}%`,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import useIngress from '~ui/hooks/useIngress';
|
||||
import useNotifications from '~ui/hooks/useNotifications';
|
||||
import { For } from 'solid-js';
|
||||
import Popup from '../Popup';
|
||||
import NotificationComponent from './NotificationComponent';
|
||||
|
||||
export default function NotificationOverlay() {
|
||||
const [ingress] = useIngress();
|
||||
const [notifications] = useNotifications();
|
||||
|
||||
return (
|
||||
<Popup
|
||||
|
|
@ -14,7 +14,7 @@ export default function NotificationOverlay() {
|
|||
visible={() => true}
|
||||
>
|
||||
<div class="flex flex-col-reverse gap-2">
|
||||
<For each={ingress()}>
|
||||
<For each={Object.values(notifications())}>
|
||||
{notification => (
|
||||
<NotificationComponent overlay {...notification} />
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -1,37 +1,29 @@
|
|||
import { Settings01Icon, Trash01Icon } from '@untitled-theme/icons-solid';
|
||||
import useIngress from '~ui/hooks/useIngress';
|
||||
import useNotifications from '~ui/hooks/useNotifications';
|
||||
import { OverlayScrollbarsComponent } from 'overlayscrollbars-solid';
|
||||
import { For, Match, Switch } from 'solid-js';
|
||||
import { createEffect, createMemo, For, Match, on, Switch } from 'solid-js';
|
||||
import Button from '../../base/Button';
|
||||
import Popup, { type PopupProps } from '../Popup';
|
||||
import NotificationComponent from './NotificationComponent';
|
||||
|
||||
function NotificationPopup(props: PopupProps) {
|
||||
const [ingress] = useIngress(updateSize);
|
||||
const [notifications, setNotifications] = useNotifications();
|
||||
|
||||
let inner!: HTMLDivElement;
|
||||
let parent!: HTMLDivElement;
|
||||
|
||||
function updateSize() {
|
||||
createEffect(on(notifications, () => {
|
||||
if (inner && parent) {
|
||||
const rect = inner.getBoundingClientRect();
|
||||
parent.style.height = `${rect.height}px`;
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
// onMount(() => {
|
||||
// document.addEventListener('keypress', async (e) => {
|
||||
// if (e.key === 'n') {
|
||||
// const progress = Math.random() > 0.0 ? { progress: 0.39 } : {};
|
||||
// const id = await manager.addNotification({
|
||||
// title: 'Test Notification',
|
||||
// message: 'This is a test notification',
|
||||
// notification_type: manager.NotificationType.Download,
|
||||
// ...(progress),
|
||||
// });
|
||||
// }
|
||||
// });
|
||||
// });
|
||||
const memoedNotifications = createMemo(() => Object.values(notifications()));
|
||||
|
||||
function clearNotifications() {
|
||||
setNotifications({});
|
||||
}
|
||||
|
||||
return (
|
||||
<Popup {...props}>
|
||||
|
|
@ -40,10 +32,10 @@ function NotificationPopup(props: PopupProps) {
|
|||
<div class="flex flex-col items-stretch justify-start gap-2 text-start" ref={inner}>
|
||||
<p class="px-2 pt-1 text-2lg">Notifications</p>
|
||||
<Switch>
|
||||
<Match when={ingress().length > 0}>
|
||||
<Match when={memoedNotifications().length > 0}>
|
||||
<OverlayScrollbarsComponent class="max-h-[min(500px,60vh)] overflow-auto">
|
||||
<div class="flex flex-col-reverse items-stretch justify-center">
|
||||
<For each={ingress()}>
|
||||
<For each={memoedNotifications()}>
|
||||
{noti => (
|
||||
<div class="w-full flex flex-col">
|
||||
<NotificationComponent {...noti} overlay={false} />
|
||||
|
|
@ -54,14 +46,14 @@ function NotificationPopup(props: PopupProps) {
|
|||
</div>
|
||||
</OverlayScrollbarsComponent>
|
||||
</Match>
|
||||
<Match when={ingress().length === 0}>
|
||||
<Match when>
|
||||
<span class="px-2">You have no notifications</span>
|
||||
<span class="h-px bg-gray-05" />
|
||||
</Match>
|
||||
</Switch>
|
||||
|
||||
<div class="flex flex-row items-end justify-between">
|
||||
<Button buttonStyle="ghost" iconLeft={<Trash01Icon />} onClick={() => {}}>
|
||||
<Button buttonStyle="ghost" iconLeft={<Trash01Icon />} onClick={clearNotifications}>
|
||||
Clear Notifications
|
||||
</Button>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,26 +0,0 @@
|
|||
import { events, type IngressPayload, type IngressType } from '@onelauncher/client/bindings';
|
||||
import { createSignal, onMount, type Signal } from 'solid-js';
|
||||
|
||||
type Ingresses = IngressPayload[];
|
||||
type OnUpdateFn = (event: IngressType['type']) => unknown;
|
||||
|
||||
function useIngress(onUpdate?: OnUpdateFn): Signal<Ingresses> {
|
||||
const [ingresses, setIngresses] = createSignal<Ingresses>([]);
|
||||
|
||||
async function update(event: IngressType['type']) {
|
||||
const ingress: Ingresses = []; // await manager.getNotifications();
|
||||
setIngresses(ingress);
|
||||
|
||||
if (onUpdate)
|
||||
onUpdate(event);
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
update('initialize');
|
||||
events.ingressPayload.listen(e => update(e.payload.event.type));
|
||||
});
|
||||
|
||||
return [ingresses, setIngresses];
|
||||
}
|
||||
|
||||
export default useIngress;
|
||||
34
apps/frontend/src/ui/hooks/useNotifications.ts
Normal file
34
apps/frontend/src/ui/hooks/useNotifications.ts
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
import type { NotificationData } from '~ui/components/overlay/notifications/NotificationComponent';
|
||||
import { events } from '@onelauncher/client/bindings';
|
||||
import { createSignal, onMount, type Signal } from 'solid-js';
|
||||
|
||||
type Notifications = Record<string, NotificationData>;
|
||||
type OnUpdateFn = () => unknown;
|
||||
|
||||
function useNotifications(onUpdate?: OnUpdateFn): Signal<Notifications> {
|
||||
const [notifications, setNotifications] = createSignal<Notifications>({});
|
||||
|
||||
async function update(id: string, data: NotificationData) {
|
||||
setNotifications((notifications) => {
|
||||
notifications[id] = data;
|
||||
return {
|
||||
...notifications,
|
||||
};
|
||||
});
|
||||
|
||||
if (onUpdate)
|
||||
onUpdate();
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
events.ingressPayload.listen(e => update(e.payload.ingress_uuid, {
|
||||
title: e.payload.event.type.replaceAll('_', ' '),
|
||||
message: e.payload.message,
|
||||
fraction: e.payload.fraction ?? undefined,
|
||||
}));
|
||||
});
|
||||
|
||||
return [notifications, setNotifications];
|
||||
}
|
||||
|
||||
export default useNotifications;
|
||||
|
|
@ -235,7 +235,7 @@ pub async fn install_minecraft(
|
|||
})
|
||||
.await?;
|
||||
State::sync().await?;
|
||||
send_ingress(&ingress, 1.0, Some("installed minecraft successfully")).await?;
|
||||
send_ingress(&ingress, 100.0, Some("installed minecraft successfully")).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
23
pnpm-lock.yaml
generated
23
pnpm-lock.yaml
generated
|
|
@ -100,6 +100,9 @@ importers:
|
|||
'@solid-primitives/i18n':
|
||||
specifier: ^2.1.1
|
||||
version: 2.1.1(solid-js@1.9.1)
|
||||
'@solid-primitives/map':
|
||||
specifier: ^0.4.13
|
||||
version: 0.4.13(solid-js@1.9.1)
|
||||
'@solid-primitives/refs':
|
||||
specifier: ^1.0.8
|
||||
version: 1.0.8(solid-js@1.9.1)
|
||||
|
|
@ -1265,6 +1268,11 @@ packages:
|
|||
peerDependencies:
|
||||
solid-js: ^1.6.12
|
||||
|
||||
'@solid-primitives/map@0.4.13':
|
||||
resolution: {integrity: sha512-B1zyFbsiTQvqPr+cuPCXO72sRuczG9Swncqk5P74NCGw1VE8qa/Ry9GlfI1e/VdeQYHjan+XkbE3rO2GW/qKew==}
|
||||
peerDependencies:
|
||||
solid-js: ^1.6.12
|
||||
|
||||
'@solid-primitives/memo@1.3.9':
|
||||
resolution: {integrity: sha512-OIlGcsEPXr6U23fDaSEm8iqezWDT4SNSjLTekWQVOu4qHostnMPlqU8xo1Jf0I7OgRuqA0wTMQ5cu0bYjeR/ZQ==}
|
||||
peerDependencies:
|
||||
|
|
@ -1285,6 +1293,11 @@ packages:
|
|||
peerDependencies:
|
||||
solid-js: ^1.6.12
|
||||
|
||||
'@solid-primitives/trigger@1.1.0':
|
||||
resolution: {integrity: sha512-00BbAiXV66WwjHuKZc3wr0+GLb9C24mMUmi3JdTpNFgHBbrQGrIHubmZDg36c5/7wH+E0GQtOOanwQS063PO+A==}
|
||||
peerDependencies:
|
||||
solid-js: ^1.6.12
|
||||
|
||||
'@solid-primitives/utils@6.2.3':
|
||||
resolution: {integrity: sha512-CqAwKb2T5Vi72+rhebSsqNZ9o67buYRdEJrIFzRXz3U59QqezuuxPsyzTSVCacwS5Pf109VRsgCJQoxKRoECZQ==}
|
||||
peerDependencies:
|
||||
|
|
@ -5061,6 +5074,11 @@ snapshots:
|
|||
dependencies:
|
||||
solid-js: 1.9.1
|
||||
|
||||
'@solid-primitives/map@0.4.13(solid-js@1.9.1)':
|
||||
dependencies:
|
||||
'@solid-primitives/trigger': 1.1.0(solid-js@1.9.1)
|
||||
solid-js: 1.9.1
|
||||
|
||||
'@solid-primitives/memo@1.3.9(solid-js@1.9.1)':
|
||||
dependencies:
|
||||
'@solid-primitives/scheduled': 1.4.3(solid-js@1.9.1)
|
||||
|
|
@ -5080,6 +5098,11 @@ snapshots:
|
|||
dependencies:
|
||||
solid-js: 1.9.1
|
||||
|
||||
'@solid-primitives/trigger@1.1.0(solid-js@1.9.1)':
|
||||
dependencies:
|
||||
'@solid-primitives/utils': 6.2.3(solid-js@1.9.1)
|
||||
solid-js: 1.9.1
|
||||
|
||||
'@solid-primitives/utils@6.2.3(solid-js@1.9.1)':
|
||||
dependencies:
|
||||
solid-js: 1.9.1
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue