refactor: Remove all traces of the old bridge + types

This commit is contained in:
LynithDev 2024-06-02 15:21:23 +02:00
parent 8c7a2f4b9b
commit ae49d82d9e
18 changed files with 86 additions and 473 deletions

View file

@ -93,5 +93,5 @@
"package.json": "package-lock.json, yarn.lock, pnpm-lock.yaml, pnpm-workspace.yaml, .pnp.cjs, .pnp.loader.mjs",
"tsconfig.json": "tsconfig.*.json",
"flake.nix": "shell.nix, flake.lock, Cargo.nix"
}
}
}

View file

@ -1,21 +0,0 @@
import { invoke } from '@tauri-apps/api/core';
import { listen } from '@tauri-apps/api/event';
type MicrosoftLoginCallback = (msg: string, stage: number) => never;
export async function loginMicrosoft(cb?: MicrosoftLoginCallback): Promise<Core.Account> {
if (cb) {
const unlisten = await listen<[string, number, boolean]>('msa:status', (event) => {
cb(event.payload[0], event.payload[1]);
if (event.payload[2])
unlisten();
});
}
return await invoke('plugin:auth|login_msa');
}
export async function getUuidHeadSrc(uuid: string): Promise<string> {
// TODO implement some kind of caching + fallback
return `https://crafatar.com/avatars/${uuid}?size=32`;
}

View file

@ -1,157 +0,0 @@
import { invoke } from '@tauri-apps/api/core';
import { type UnlistenFn, listen } from '@tauri-apps/api/event';
const _test_cluster = {
cluster: {
id: 'd09575a6-7c17-4399-a7a3-a117fffa5420',
name: 'Placeholder Name',
createdAt: 1715454720,
cover: null,
group: null,
client: {
type: 'Vanilla',
},
},
manifest: {
id: '1.0 cluster',
manifest: {
id: '1.8.9',
javaVersion: {
majorVersion: 8,
},
},
},
} as Core.ClusterWithManifest;
export async function getCluster(uuid: string): Promise<Core.ClusterWithManifest> {
return _test_cluster;
return await invoke<Core.ClusterWithManifest>(
'plugin:onelauncher|get_cluster',
{ uuid },
);
}
export async function getClusterLogs(uuid: string): Promise<string[]> {
return ['test log'];
try {
return await invoke<string[]>(
'plugin:onelauncher|get_cluster_logs',
{ uuid },
);
}
catch (e) {
console.error(e);
return [];
}
}
export async function getClusterLog(uuid: string, log: string): Promise<string> {
return 'test log';
return await invoke<string>(
'plugin:onelauncher|get_cluster_log',
{ uuid, log },
);
}
export async function getClustersGrouped(): Promise<Map<string, WithIndex<Core.ClusterWithManifest>[]>> {
const clusters = await getClusters();
const map = new Map<string, WithIndex<Core.ClusterWithManifest>[]>();
clusters.forEach((cluster, index) => {
const groupName = cluster.cluster.group || 'Unnamed';
const value: WithIndex<Core.ClusterWithManifest> = {
...cluster,
index,
};
if (map.has(groupName))
map.get(groupName)!.push(value);
else
map.set(groupName, [value]);
});
return map;
}
export async function getClusters(): Promise<Core.ClusterWithManifest[]> {
return [_test_cluster];
return await invoke<Core.ClusterWithManifest[]>('plugin:onelauncher|get_clusters');
}
export async function createCluster(cluster: Omit<Core.Cluster, 'id' | 'createdAt'> & { version: string }): Promise<void> {
return await invoke(
'plugin:onelauncher|create_cluster',
{
name: cluster.name,
version: cluster.version,
client: cluster.client,
// cover: cluster.cover,
// group: cluster.group,
},
);
}
export async function refreshClientManager(): Promise<void> {
return await invoke('plugin:onelauncher|refresh_client_manager');
}
interface LaunchCallbacks {
on_launch: (pid: number) => any;
on_stdout: (line: string) => any;
on_stderr: (line: string) => any;
};
export function launchCluster(uuid: string, callbacks?: LaunchCallbacks): Promise<number> {
return;
// eslint-disable-next-line no-async-promise-executor
return new Promise(async (resolve) => {
let unlisten_stdout: UnlistenFn | undefined;
let unlisten_stderr: UnlistenFn | undefined;
let pid: number = -1;
function guard(passed_pid: number, fn: () => any | undefined) {
if (passed_pid !== pid)
return;
if (fn)
fn();
}
const unlisten_launch = await listen<number>('game:launch', async (e) => {
pid = e.payload;
if (callbacks)
callbacks.on_launch(pid);
unlisten_launch!();
});
const unlisten_exit = await listen<[number, number]>(`game:exit`, e => guard(e.payload[0], () => {
if (callbacks) {
unlisten_stdout!();
unlisten_stderr!();
}
unlisten_exit!();
resolve(e.payload[1]);
}));
if (callbacks) {
unlisten_stdout = await listen<[number, string]>(`game:stdout`, e => guard(
e.payload[0],
callbacks.on_stdout(e.payload[1]),
));
unlisten_stderr = await listen<[number, string]>(`game:stderr`, e => guard(
e.payload[0],
callbacks.on_stderr(e.payload[1]),
));
}
await invoke<number>(
'plugin:onelauncher|launch_cluster',
{ uuid },
);
});
}

View file

@ -1,121 +0,0 @@
import { invoke } from '@tauri-apps/api/core';
import type { UnlistenFn } from '@tauri-apps/api/event';
import { listen } from '@tauri-apps/api/event';
// TODO: Refactor notifications
// - getNotifications should return a list of notifications
// - event "update" + "add" + "remove" should emit the event AS WELL AS emitting
// the ID of the notification / the entire notification data
// so that the frontend can easily find the notification reference and update it without losing reactivity
export enum NotificationType {
Info = 'Info',
Alert = 'Alert',
Success = 'Success',
Download = 'Download',
DownloadSuccess = 'DownloadSuccess',
DownloadError = 'DownloadError',
Refresh = 'Refresh',
}
let _notifications: Core.Notification[] = [];
class _Emitter {
private listeners: ((event: string) => unknown)[] = [];
public emit(event: Core.NotificationEvents) {
this.listeners.forEach(value => value(event));
}
public on(callback: (event: string) => unknown) {
this.listeners.push(callback);
}
}
const emitter = new _Emitter();
export async function updateNotification(id: number, notification: Partial<Core.Notification>): Promise<void> {
const index = _notifications.findIndex(noti => noti.id === id);
if (index < 0) {
console.log('no index');
return;
}
const found = _notifications[index];
if (!found) {
console.log('not found');
return;
}
const newNoti = {
...found,
...notification,
};
_notifications[index] = newNoti;
emitter.emit('modified');
}
export async function addNotification(notification: MakeOptional<Core.Notification, 'id' | 'created_at'>): Promise<number> {
const id = notification.id || _notifications.length + 1;
_notifications.push({
id,
created_at: Math.floor(Date.now() / 1000),
...notification,
});
emitter.emit('added');
return id;
// await invoke('plugin:onelauncher|notifications_add', {
// notification: {
// created_at: Math.floor(Date.now() / 1000),
// ...notification,
// },
// });
}
export async function removeNotificationById(id: number): Promise<void> {
const index = _notifications.findIndex(noti => noti.id === id);
if (index > -1) {
_notifications.splice(index, 1);
emitter.emit('removed');
}
}
export async function removeNotificationByIndex(index: number): Promise<void> {
_notifications.splice(index, 1);
emitter.emit('removed');
// await invoke('plugin:onelauncher|notifications_remove_by_index', { index });
}
export async function clearNotifications(): Promise<void> {
_notifications = [];
emitter.emit('cleared');
// await invoke('plugin:onelauncher|notifications_clear');
}
export async function getNotifications(): Promise<Core.Notification[]> {
return [..._notifications]; // Destructured to mock the way backend <-> frontend works. (new list gets sent)
// return await invoke('plugin:onelauncher|notifications_get');
}
export async function getNotificationById(id: number): Promise<Core.Notification | undefined> {
return _notifications.find(noti => noti.id === id);
}
export async function on<
T extends Core.NotificationEvents,
>(
event: T,
callback: () => void,
): Promise<UnlistenFn> {
emitter.on((e) => {
if (event === e)
callback();
});
return () => {};
// return await listen<string>(`notifications_event`, (e) => {
// if (e.payload.toLowerCase() === event)
// callback();
// });
};

View file

@ -11,6 +11,8 @@ import UpdatesPage from './ui/pages/Updates';
import ClusterRoot from '~ui/pages/cluster/ClusterRoot';
import SettingsRoot from '~ui/pages/settings/SettingsRoot';
export * as bridge from "./bindings";
render(() => (
<Router root={App}>
<Route path="/" component={HomePage} />

View file

@ -1,14 +0,0 @@
declare namespace Core {
export interface Account {
username: string;
uuid: string;
skins: AccountSkin[];
}
export interface AccountSkin {
id: string;
state: 'active' | 'something';
url: string;
variant: 'CLASSIC' | 'SLIM';
}
}

View file

@ -1,12 +0,0 @@
declare namespace Core {
export interface Notification {
id: number;
created_at: number;
title: string;
message: string;
notification_type: NotificationType;
progress?: number;
}
export type NotificationEvents = 'init' | 'modified' | 'added' | 'removed' | 'cleared';
}

View file

@ -1,35 +0,0 @@
declare namespace Core {
export type ClusterWithManifest<T extends keyof ClientType = keyof ClientType> = {
cluster: Cluster<T>,
manifest: Manifest
}
export interface Manifest {
id: string,
manifest: MinecraftManifest
}
export interface MinecraftManifest {
id: string,
javaVersion: {
majorVersion: number;
},
}
export interface Cluster<T extends keyof ClientType = keyof ClientType> {
id: string,
createdAt: number,
name: string,
cover: string | null,
group: string | null,
client: ClientType[T],
}
export interface ClientType {
Vanilla: VanillaProps,
}
export interface VanillaProps {
type: "Vanilla",
}
}

View file

@ -1,14 +1,14 @@
import type { ParentProps } from 'solid-js';
import { OverlayScrollbarsComponent } from 'overlayscrollbars-solid';
import environment from '../utils/environment';
import WindowFrame from './components/WindowFrame';
import Navbar from './components/Navbar';
import ErrorBoundary from './components/ErrorBoundary';
import AnimatedRoutes from './components/AnimatedRoutes';
import NotificationOverlay from './components/overlay/notifications/NotificationOverlay';
import { bridge } from '~index';
function App(props: ParentProps) {
if (!environment.isDev()) {
if (!bridge.commands.isDev()) {
document.addEventListener('contextmenu', (event) => {
event.preventDefault();
});

View file

@ -1,12 +1,11 @@
import { createResource } from 'solid-js';
import type { JSX } from 'solid-js';
import * as auth from '../../../bridge/auth';
import steveSrc from '../../../assets/images/steve.png';
type PlayerHeadProps = JSX.IntrinsicElements['img'] & { uuid: string };
async function fetchHeadSrc(uuid: string) {
return await auth.getUuidHeadSrc(uuid);
return `https://crafatar.com/avatars/${uuid}?size=32`;
}
function PlayerHead(props: PlayerHeadProps) {

View file

@ -1,28 +1,29 @@
import { type JSX, Match, Show, Switch, createEffect, createSignal } from 'solid-js';
import { AlertTriangleIcon, CheckCircleIcon, FolderCheckIcon, FolderDownloadIcon, FolderXIcon, InfoCircleIcon, RefreshCcw02Icon } from '@untitled-theme/icons-solid';
import TimeAgo from '../../TimeAgo';
import { NotificationType } from '../../../../bridge/notifications';
import { PausableTimer } from '~utils/PausableTimer';
type NotificationComponentProps = Core.Notification & {
overlay: boolean;
};
type NotificationType = '';
function IconFromNotificationType(type: NotificationType): (props: JSX.HTMLAttributes<HTMLDivElement>) => JSX.Element {
switch (type) {
case NotificationType.Alert:
return AlertTriangleIcon as any;
case NotificationType.Download:
return FolderDownloadIcon as any;
case NotificationType.DownloadSuccess:
return FolderCheckIcon as any;
case NotificationType.DownloadError:
return FolderXIcon as any;
case NotificationType.Refresh:
return RefreshCcw02Icon as any;
case NotificationType.Success:
return CheckCircleIcon as any;
case NotificationType.Info:
// case NotificationType.Alert:
// return AlertTriangleIcon as any;
// case NotificationType.Download:
// return FolderDownloadIcon as any;
// case NotificationType.DownloadSuccess:
// return FolderCheckIcon as any;
// case NotificationType.DownloadError:
// return FolderXIcon as any;
// case NotificationType.Refresh:
// return RefreshCcw02Icon as any;
// case NotificationType.Success:
// return CheckCircleIcon as any;
// case NotificationType.Info:
default:
return InfoCircleIcon as any;
}
@ -30,19 +31,19 @@ function IconFromNotificationType(type: NotificationType): (props: JSX.HTMLAttri
function ColorFromNotificationType(type: NotificationType): string {
switch (type) {
case NotificationType.Info:
case NotificationType.Refresh:
return 'text-brand';
// case NotificationType.Info:
// case NotificationType.Refresh:
// return 'text-brand';
case NotificationType.Alert:
case NotificationType.DownloadError:
return 'text-danger';
// case NotificationType.Alert:
// case NotificationType.DownloadError:
// return 'text-danger';
case NotificationType.DownloadSuccess:
case NotificationType.Success:
return 'text-success';
// case NotificationType.DownloadSuccess:
// case NotificationType.Success:
// return 'text-success';
case NotificationType.Download:
// case NotificationType.Download:
default:
return 'text-fg-primary';
}

View file

@ -3,7 +3,6 @@ import { Settings01Icon, Trash01Icon } from '@untitled-theme/icons-solid';
import { OverlayScrollbarsComponent } from 'overlayscrollbars-solid';
import Popup from '../Popup';
import Button from '../../base/Button';
import * as manager from '../../../../bridge/notifications';
import NotificationComponent from './NotificationComponent';
import useNotifications from '~ui/hooks/useNotifications';
@ -20,39 +19,19 @@ function NotificationPopup(props: Popup.PopupProps) {
}
}
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),
});
// if (progress.progress) {
setTimeout(() => {
manager.updateNotification(id, {
progress: 0.5,
});
}, 3500);
setTimeout(() => {
manager.updateNotification(id, {
progress: 0.75,
});
}, 8500);
setTimeout(() => {
manager.updateNotification(id, {
progress: 1,
});
}, 13500);
// }
}
});
});
// 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),
// });
// }
// });
// });
return (
<Popup {...props}>
@ -82,7 +61,7 @@ function NotificationPopup(props: Popup.PopupProps) {
</Switch>
<div class="flex flex-row justify-between items-end">
<Button onClick={() => manager.clearNotifications()} buttonStyle="ghost" iconLeft={<Trash01Icon />}>
<Button onClick={() => {}} buttonStyle="ghost" iconLeft={<Trash01Icon />}>
Clear Notifications
</Button>

View file

@ -1,6 +1,9 @@
import type { Resource } from 'solid-js';
import { createResource } from 'solid-js';
import { getCluster } from '../../bridge/game';
function getCluster(uuid: string): Core.ClusterWithManifest {
throw new Error('get cluster');
}
function useCluster(uuid: string | undefined | null): Resource<Core.ClusterWithManifest> | null {
if (typeof uuid !== 'string' || uuid.length === 0)

View file

@ -1,5 +1,4 @@
import { type Signal, createSignal, onMount } from 'solid-js';
import * as manager from '~bridge/notifications';
type Notifications = Core.Notification[];
type OnUpdateFn = (event: Core.NotificationEvents) => unknown;
@ -8,7 +7,7 @@ function useNotifications(onUpdate?: OnUpdateFn): Signal<Notifications> {
const [notifications, setNotifications] = createSignal<Notifications>([]);
async function update(event: Core.NotificationEvents) {
const notis = await manager.getNotifications();
const notis: Notifications = []; // await manager.getNotifications();
setNotifications(notis);
if (onUpdate)
@ -18,10 +17,10 @@ function useNotifications(onUpdate?: OnUpdateFn): Signal<Notifications> {
onMount(() => {
update('init');
manager.on('added', () => update('added'));
manager.on('removed', () => update('removed'));
manager.on('cleared', () => update('cleared'));
manager.on('modified', () => update('modified'));
// manager.on('added', () => update('added'));
// manager.on('removed', () => update('removed'));
// manager.on('cleared', () => update('cleared'));
// manager.on('modified', () => update('modified'));
});
return [notifications, setNotifications];

View file

@ -7,11 +7,10 @@ import {
SearchMdIcon,
} from '@untitled-theme/icons-solid';
import type { Accessor, Setter } from 'solid-js';
import { For, Show, createResource, createSignal } from 'solid-js';
import { For, createSignal } from 'solid-js';
import { useNavigate } from '@solidjs/router';
import image from '../../assets/images/header.png';
import ContextMenu from '../components/overlay/ContextMenu';
import { createCluster, getClustersGrouped, launchCluster } from '../../bridge/game';
import Button from '../components/base/Button';
import Tag from '../components/base/Tag';
import TextField from '../components/base/TextField';
@ -75,16 +74,6 @@ function ClusterCardContextMenu(props: ClusterCardContextMenuProps) {
navigate(`/clusters/?id=${props.id}`);
}
async function launch() {
const exit_code = await launchCluster(props.id, {
on_launch: pid => console.log('Launched with PID:', pid),
on_stdout: line => console.log('STDOUT:', line),
on_stderr: line => console.log('STDERR:', line),
});
console.log('Exit with code: ', exit_code);
}
return (
<ContextMenu
pos={props.pos}
@ -102,7 +91,7 @@ function ClusterCardContextMenu(props: ClusterCardContextMenuProps) {
<ContextMenu.Row
icon={<PlayIcon />}
text="Launch"
onClick={() => launch()}
onClick={() => {}}
/>
</ContextMenu>
);
@ -192,20 +181,20 @@ function ClusterGroup(props: ClusterGroupProps) {
}
function HomePage() {
const [clusters, { refetch }] = createResource(getClustersGrouped);
// const [clusters, { refetch }] = createResource(getClustersGrouped);
async function newInstance() {
await createCluster({
name: '1.0 cluster',
version: 'a1.0.11',
client: {
type: 'Vanilla',
},
cover: null,
group: null,
});
// await createCluster({
// name: '1.0 cluster',
// version: 'a1.0.11',
// client: {
// type: 'Vanilla',
// },
// cover: null,
// group: null,
// });
await refetch();
// await refetch();
}
return (
@ -232,13 +221,13 @@ function HomePage() {
</div>
</div>
<Show when={clusters() !== undefined}>
{/* <Show when={clusters() !== undefined}>
<For each={Array.from(clusters()!.entries())}>
{([title, clusters]) => (
<ClusterGroup title={title} clusters={clusters} />
)}
</For>
</Show>
</Show> */}
</div>
);
}

View file

@ -1,29 +1,30 @@
import { useSearchParams } from '@solidjs/router';
import { For, Show, createEffect, createResource, createSignal } from 'solid-js';
import { OverlayScrollbarsComponent } from 'overlayscrollbars-solid';
import { getClusterLog, getClusterLogs } from '../../../../bridge/game';
function ClusterLogs() {
const [params] = useSearchParams();
const [logs] = createResource(params.id, getClusterLogs);
const [logs] = createResource(params.id, async (uuid: string) => {
return [] as string[];
});
const [activeLogFile, setActiveLogFile] = createSignal<string | null>(null);
const [log, setLog] = createSignal<string | null>(null);
createEffect(() => {
const log = logs()?.[0];
// createEffect(() => {
// const log = logs()?.[0];
// Set default log to the first file
if (log && activeLogFile() === null)
setActiveLogFile(log);
// // Set default log to the first file
// if (log && activeLogFile() === null)
// setActiveLogFile(log);
// Fetch log content
if (activeLogFile() !== null) {
getClusterLog(params.id!, activeLogFile()!).then((log) => {
setLog(log);
});
}
});
// // Fetch log content
// if (activeLogFile() !== null) {
// getClusterLog(params.id!, activeLogFile()!).then((log) => {
// setLog(log);
// });
// }
// });
return (
<div class="flex flex-col flex-1">

View file

@ -5,7 +5,6 @@ import useCluster from '../../../hooks/useCluster';
import ClusterCover from '../../../components/game/ClusterCover';
import LoaderIcon from '../../../components/game/LoaderIcon';
import Button from '../../../components/base/Button';
import { launchCluster } from '../../../../bridge/game';
import ScrollableContainer from '~ui/components/ScrollableContainer';
import Sidebar from '~ui/components/Sidebar';
@ -63,7 +62,7 @@ function Banner({ cluster, manifest }: Core.ClusterWithManifest) {
iconLeft={<PlayIcon />}
children="Launch"
class="!w-auto"
onClick={() => launchCluster(cluster.id)}
onClick={() => {}}
/>
</div>
</div>

View file

@ -22,6 +22,7 @@ export default petal({
'*.rs',
'pnpm-lock.yaml',
'./src-tauri/prisma/migrations/migration_lock.toml',
'./desktop/src/bindings.ts',
],
}).append([
{