ci / api / api (push) Successful in 43s
ci / contract / contract (сгенерированный код совпадает с openapi.yaml) (push) Successful in 1m19s
ci / desktop / rust (desktop proxy) (push) Failing after 3m34s
ci / ts / ts (packages + web) (push) Failing after 2m49s
release / release (версия и заметки) (push) Successful in 5s
ci / db / db (push) Successful in 40s
ci / mobile / mobile (Metro-бандл Android) (push) Successful in 1m12s
ci / docker / docker (образы собираются — их сервер собирает сам при обновлении) (push) Successful in 1m28s
release / android (APK в релиз) (push) Failing after 2m0s
EOF Co-authored-by: Cursor <cursoragent@cursor.com>
95 lines
3.0 KiB
TypeScript
95 lines
3.0 KiB
TypeScript
import type { AuthUser, RefreshStore } from '@pipu/core';
|
|
import { createContext, createElement, useContext, type ReactNode } from 'react';
|
|
|
|
type Tauri = {
|
|
core: { invoke: (cmd: string, args?: Record<string, unknown>) => Promise<unknown> };
|
|
event: { listen: (event: string, handler: (e: { payload: string }) => void) => Promise<() => void> };
|
|
};
|
|
|
|
function api(): Tauri | undefined {
|
|
return (window as Window & { __TAURI__?: Tauri }).__TAURI__;
|
|
}
|
|
|
|
export function isDesktop(): boolean {
|
|
return api() !== undefined;
|
|
}
|
|
|
|
function invoke<T>(cmd: string, args?: Record<string, unknown>): Promise<T> {
|
|
const tauri = api();
|
|
if (!tauri) throw new Error('Tauri API unavailable');
|
|
return tauri.core.invoke(cmd, args) as Promise<T>;
|
|
}
|
|
|
|
export type DesktopHandle = {
|
|
proxyBase: string;
|
|
server: string | null;
|
|
setServer: (url: string) => Promise<string>;
|
|
clearServer: () => Promise<void>;
|
|
refreshStore: RefreshStore;
|
|
listenMedia: (handler: (action: string) => void) => Promise<() => void>;
|
|
setMedia: (info: { title: string; artist: string; album: string; coverUrl?: string | null; playing: boolean }) => void;
|
|
};
|
|
|
|
export const DesktopContext = createContext<DesktopHandle | null>(null);
|
|
|
|
export function DesktopProvider({ value, children }: { value: DesktopHandle | null; children: ReactNode }) {
|
|
return createElement(DesktopContext.Provider, { value }, children);
|
|
}
|
|
|
|
export function useDesktop(): DesktopHandle | null {
|
|
return useContext(DesktopContext);
|
|
}
|
|
|
|
let active: DesktopHandle | null = null;
|
|
|
|
export function desktopHandle(): DesktopHandle | null {
|
|
return active;
|
|
}
|
|
|
|
export async function createDesktop(): Promise<DesktopHandle | null> {
|
|
if (!isDesktop()) return null;
|
|
const boot = await invoke<{ proxyBase: string; server: string | null }>('bootstrap');
|
|
const handle: DesktopHandle = {
|
|
proxyBase: boot.proxyBase,
|
|
server: boot.server,
|
|
setServer: async (url) => {
|
|
const server = await invoke<string>('set_server', { url });
|
|
handle.server = server;
|
|
return server;
|
|
},
|
|
clearServer: async () => {
|
|
await invoke('clear_server');
|
|
handle.server = null;
|
|
},
|
|
refreshStore: {
|
|
get: () => invoke<string | null>('token_get'),
|
|
set: (token) => invoke('token_set', { token }),
|
|
clear: async () => {
|
|
await invoke('token_clear');
|
|
await invoke('user_clear');
|
|
},
|
|
getUser: async () => {
|
|
const json = await invoke<string | null>('user_get');
|
|
return json ? (JSON.parse(json) as AuthUser) : null;
|
|
},
|
|
setUser: (user) => invoke('user_set', { json: JSON.stringify(user) }),
|
|
},
|
|
listenMedia: async (handler) => {
|
|
const tauri = api();
|
|
if (!tauri) return () => undefined;
|
|
return tauri.event.listen('media', (e) => handler(e.payload));
|
|
},
|
|
setMedia: (info) => {
|
|
void invoke('media_set', {
|
|
title: info.title,
|
|
artist: info.artist,
|
|
album: info.album,
|
|
coverUrl: info.coverUrl ?? null,
|
|
playing: info.playing,
|
|
});
|
|
},
|
|
};
|
|
active = handle;
|
|
return handle;
|
|
}
|