mirror of
https://github.com/modrinth/code.git
synced 2026-08-28 10:34:53 +00:00
refactor: app event bus (#6985)
* refactor: app event bus * fix: use messagepack + cleanup * fix: use postcard * fix: lint * fix: import gate * fix: prettierignore + lint * fix: lint * fix: rev
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
import { invoke } from '@tauri-apps/api/core'
|
||||
import { listen } from '@tauri-apps/api/event'
|
||||
|
||||
export async function init_ads_window(overrideShown = false) {
|
||||
return await invoke('plugin:ads|init_ads_window', {
|
||||
@@ -47,10 +46,6 @@ export async function open_ads_consent_preferences() {
|
||||
return await invoke('plugin:ads|open_ads_consent_preferences')
|
||||
}
|
||||
|
||||
export async function ads_consent_listener(callback) {
|
||||
return await listen('ads-consent-required', (event) => callback(event.payload))
|
||||
}
|
||||
|
||||
export async function record_ads_click() {
|
||||
return await invoke('plugin:ads|record_ads_click')
|
||||
}
|
||||
|
||||
@@ -1,137 +0,0 @@
|
||||
/*
|
||||
Event listeners for interacting with the Rust api
|
||||
These are all async functions that return a promise that resolves to the payload object (whatever Rust is trying to deliver)
|
||||
*/
|
||||
|
||||
/*
|
||||
callback is a function that takes a single argument, which is the payload object (whatever Rust is trying to deliver)
|
||||
|
||||
You can call these to await any kind of emitted signal from Rust, and then do something with the payload object
|
||||
An example place to put this is at the start of main.js before the state is initialized- that way
|
||||
you can listen for any emitted signal from Rust and do something with it as the state is being initialized
|
||||
|
||||
Example:
|
||||
import { loading_listener } from '@/helpers/events'
|
||||
await loading_listener((event) => {
|
||||
// event.event is the event name (useful if you want to use a single callback fn for multiple event types)
|
||||
// event.payload is the payload object
|
||||
console.log(event)
|
||||
})
|
||||
|
||||
Putting that in a script will print any emitted signal from rust
|
||||
*/
|
||||
import { listen } from '@tauri-apps/api/event'
|
||||
|
||||
/// Payload for the 'loading' event
|
||||
/*
|
||||
LoadingPayload {
|
||||
event: {
|
||||
type: string, one of "StateInit", "PackDownload", etc
|
||||
(Optional fields depending on event type)
|
||||
pack_name: name of the pack
|
||||
pack_id, optional, the id of the modpack
|
||||
pack_version, optional, the version of the modpack
|
||||
instance_name: name of the instance
|
||||
instance_id: unique identification of the instance
|
||||
|
||||
}
|
||||
loader_uuid: unique identification of the loading bar
|
||||
fraction: number, (as a fraction of 1, how much we've loaded so far). If null, by convention, loading is finished
|
||||
message: message to display to the user
|
||||
}
|
||||
*/
|
||||
export async function loading_listener(callback) {
|
||||
return await listen('loading', (event) => callback(event.payload))
|
||||
}
|
||||
|
||||
/// Payload for the 'process' event
|
||||
/*
|
||||
ProcessPayload {
|
||||
uuid: unique identification of the process in the state (currently identified by PID, but that will change)
|
||||
pid: process ID
|
||||
event: event type ("Launched", "Finished")
|
||||
message: message to display to the user
|
||||
}
|
||||
*/
|
||||
export async function process_listener(callback) {
|
||||
return await listen('process', (event) => callback(event.payload))
|
||||
}
|
||||
|
||||
/// Payload for the 'instance' event
|
||||
/*
|
||||
InstancePayload {
|
||||
instance_id: unique identification of the instance
|
||||
event: event type ("Created", "Added", "Edited", "Removed")
|
||||
}
|
||||
*/
|
||||
export async function instance_listener(callback) {
|
||||
return await listen('instance', (event) => callback(event.payload))
|
||||
}
|
||||
|
||||
/// Payload for the 'instance_bulk_update_progress' event
|
||||
/*
|
||||
InstanceBulkUpdateProgress {
|
||||
instanceId: string
|
||||
stage: "resolving_versions" | "downloading" | "finishing"
|
||||
current: number
|
||||
total: number
|
||||
}
|
||||
*/
|
||||
export async function instance_bulk_update_progress_listener(callback) {
|
||||
return await listen('instance_bulk_update_progress', (event) => callback(event.payload))
|
||||
}
|
||||
|
||||
export async function install_job_listener(callback) {
|
||||
return await listen('install_job', (event) => callback(event.payload))
|
||||
}
|
||||
|
||||
/// Payload for the 'command' event
|
||||
/*
|
||||
CommandPayload {
|
||||
event: event type ("InstallMod", "InstallModpack", "InstallVersion"),
|
||||
id: string id of the mod/modpack/version to install
|
||||
}
|
||||
*/
|
||||
export async function command_listener(callback) {
|
||||
return await listen('command', (event) => {
|
||||
callback(event.payload)
|
||||
})
|
||||
}
|
||||
|
||||
/// Payload for the 'warning' event
|
||||
/*
|
||||
WarningPayload {
|
||||
message: message to display to the user
|
||||
}
|
||||
*/
|
||||
export async function warning_listener(callback) {
|
||||
return await listen('warning', (event) => callback(event.payload))
|
||||
}
|
||||
|
||||
export async function friend_listener(callback) {
|
||||
return await listen('friend', (event) => callback(event.payload))
|
||||
}
|
||||
|
||||
export async function notification_listener(callback) {
|
||||
return await listen('notification', (event) => callback(event.payload))
|
||||
}
|
||||
|
||||
/// Payload for the 'log' event
|
||||
/*
|
||||
LogPayload {
|
||||
instance_id: string,
|
||||
type: "log4j" | "legacy",
|
||||
// log4j fields (when type === "log4j"):
|
||||
timestamp_millis?: number,
|
||||
logger_name?: string,
|
||||
level?: string,
|
||||
thread_name?: string,
|
||||
message?: string,
|
||||
throwable?: string,
|
||||
// legacy fields (when type === "legacy"):
|
||||
message?: string,
|
||||
}
|
||||
*/
|
||||
export async function log_listener(callback) {
|
||||
return await listen('log', (event) => callback(event.payload))
|
||||
}
|
||||
@@ -1,8 +1,28 @@
|
||||
import { invoke } from '@tauri-apps/api/core'
|
||||
|
||||
import { install_job_listener } from './events'
|
||||
import type { InstallErrorView } from '@/generated/app-events/InstallErrorView'
|
||||
import type { InstallJavaStep } from '@/generated/app-events/InstallJavaStep'
|
||||
import type { InstallJobSnapshot } from '@/generated/app-events/InstallJobSnapshot'
|
||||
import type { InstallJobStatus } from '@/generated/app-events/InstallJobStatus'
|
||||
import type { InstallPhaseId } from '@/generated/app-events/InstallPhaseId'
|
||||
import type { InstallProgress } from '@/generated/app-events/InstallProgress'
|
||||
import type { InstallProgressSecondary } from '@/generated/app-events/InstallProgressSecondary'
|
||||
import type { SharedInstanceUnavailableReason } from '@/generated/app-events/SharedInstanceUnavailableReason'
|
||||
import type { AppEvents } from '@/providers/app-events'
|
||||
|
||||
import type { InstanceLink, InstanceLoader } from './types'
|
||||
|
||||
export type {
|
||||
InstallErrorView,
|
||||
InstallJavaStep,
|
||||
InstallJobSnapshot,
|
||||
InstallJobStatus,
|
||||
InstallPhaseId,
|
||||
InstallProgress,
|
||||
InstallProgressSecondary,
|
||||
SharedInstanceUnavailableReason,
|
||||
}
|
||||
|
||||
export interface PackLocationVersionId {
|
||||
type: 'fromVersionId'
|
||||
project_id: string
|
||||
@@ -104,8 +124,6 @@ export interface SharedInstanceUpdateDiff {
|
||||
export const SHARED_INSTANCE_UNAVAILABLE_ERROR_CODE = 'shared_instance_unavailable'
|
||||
export const SHARED_INSTANCES_API_ERROR_CODE = 'shared_instances_api_error'
|
||||
|
||||
export type SharedInstanceUnavailableReason = 'deleted' | 'access_revoked' | 'quarantined'
|
||||
|
||||
function isRecord(value: unknown): value is Record<string, unknown> {
|
||||
return typeof value === 'object' && value !== null
|
||||
}
|
||||
@@ -136,116 +154,6 @@ export function getErrorMessage(error: unknown): string {
|
||||
return 'Unknown error'
|
||||
}
|
||||
|
||||
export type InstallJobStatus =
|
||||
| 'queued'
|
||||
| 'running'
|
||||
| 'succeeded'
|
||||
| 'failed'
|
||||
| 'interrupted'
|
||||
| 'canceled'
|
||||
|
||||
export type InstallPhaseId =
|
||||
| 'preparing_instance'
|
||||
| 'resolving_pack'
|
||||
| 'downloading_pack_file'
|
||||
| 'reading_pack_manifest'
|
||||
| 'downloading_content'
|
||||
| 'extracting_overrides'
|
||||
| 'resolving_minecraft'
|
||||
| 'resolving_loader'
|
||||
| 'preparing_java'
|
||||
| 'downloading_minecraft'
|
||||
| 'running_loader_processors'
|
||||
| 'finalizing'
|
||||
| 'rolling_back'
|
||||
|
||||
export interface InstallProgress {
|
||||
current: number
|
||||
total: number
|
||||
secondary?: InstallProgressSecondary | null
|
||||
}
|
||||
|
||||
export interface InstallProgressSecondary {
|
||||
current: number
|
||||
total: number
|
||||
}
|
||||
|
||||
export type InstallJavaStep =
|
||||
| 'resolving'
|
||||
| 'fetching_metadata'
|
||||
| 'downloading'
|
||||
| 'extracting'
|
||||
| 'validating'
|
||||
|
||||
export interface InstallErrorView {
|
||||
code: string
|
||||
phase?: InstallPhaseId | null
|
||||
message: string
|
||||
reason?: SharedInstanceUnavailableReason | null
|
||||
api?: {
|
||||
error: string
|
||||
status?: number | null
|
||||
method?: string | null
|
||||
url?: string | null
|
||||
route?: string | null
|
||||
} | null
|
||||
context?: {
|
||||
operation: string
|
||||
source_path?: string | null
|
||||
target_path?: string | null
|
||||
file_path?: string | null
|
||||
entry_path?: string | null
|
||||
urls?: string[]
|
||||
expected_hash?: string | null
|
||||
expected_size?: number | null
|
||||
project_id?: string | null
|
||||
version_id?: string | null
|
||||
minecraft_version?: string | null
|
||||
loader?: string | null
|
||||
java_version?: number | null
|
||||
os?: string | null
|
||||
arch?: string | null
|
||||
} | null
|
||||
}
|
||||
|
||||
export interface InstallJobSnapshot {
|
||||
job_id: string
|
||||
instance_id?: string | null
|
||||
kind:
|
||||
| 'create_instance'
|
||||
| 'create_modpack_instance'
|
||||
| 'create_shared_instance'
|
||||
| 'update_shared_instance'
|
||||
| 'import_instance'
|
||||
| 'duplicate_instance'
|
||||
| 'install_existing_instance'
|
||||
| 'install_pack_to_existing_instance'
|
||||
status: InstallJobStatus
|
||||
target:
|
||||
| { type: 'new_instance'; instance_id?: string | null }
|
||||
| { type: 'existing_instance'; instance_id: string }
|
||||
phase: InstallPhaseId
|
||||
progress?: InstallProgress | null
|
||||
details:
|
||||
| { type: 'empty' }
|
||||
| { type: 'instance'; name: string }
|
||||
| { type: 'minecraft'; game_version: string; loader: InstanceLoader }
|
||||
| { type: 'java'; major_version: number; step: InstallJavaStep }
|
||||
| {
|
||||
type: 'modpack'
|
||||
project_id?: string | null
|
||||
version_id?: string | null
|
||||
title?: string | null
|
||||
}
|
||||
| { type: 'import'; launcher_type: string; instance_folder: string }
|
||||
display?: { title: string; icon?: string | null } | null
|
||||
error?: InstallErrorView | null
|
||||
rollback_error?: InstallErrorView | null
|
||||
created: string
|
||||
modified: string
|
||||
finished?: string | null
|
||||
}
|
||||
|
||||
export async function install_get_modpack_preview(location: CreatePackLocation) {
|
||||
return await invoke<InstallModpackPreview>('plugin:install|install_get_modpack_preview', {
|
||||
location,
|
||||
@@ -399,7 +307,7 @@ function settleInstallJob(job: InstallJobSnapshot) {
|
||||
throw new Error(`Install job ${job.job_id} ${job.status}`)
|
||||
}
|
||||
|
||||
export async function wait_for_install_job(jobId: string) {
|
||||
export async function wait_for_install_job(events: AppEvents, jobId: string) {
|
||||
const current = await install_job_get(jobId)
|
||||
if (isInstallJobFinished(current.status)) return settleInstallJob(current)
|
||||
|
||||
@@ -434,16 +342,7 @@ export async function wait_for_install_job(jobId: string) {
|
||||
reject(err)
|
||||
}
|
||||
|
||||
install_job_listener(resolveJob)
|
||||
.then((listener) => {
|
||||
if (finished) {
|
||||
listener()
|
||||
return
|
||||
}
|
||||
|
||||
unlisten = listener
|
||||
install_job_get(jobId).then(resolveJob).catch(rejectWait)
|
||||
})
|
||||
.catch(rejectWait)
|
||||
unlisten = events.on('install_job', resolveJob)
|
||||
install_job_get(jobId).then(resolveJob).catch(rejectWait)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
* So, for example, addDefaultInstance creates a blank instance object, where the Rust struct is serialized,
|
||||
* and deserialized into a usable JS object.
|
||||
*/
|
||||
import { invoke } from '@tauri-apps/api/core'
|
||||
import { type Channel, invoke } from '@tauri-apps/api/core'
|
||||
|
||||
import type { AppEvent } from '@/generated/app-events/AppEvent'
|
||||
|
||||
export interface LoadingBarType {
|
||||
type?: string
|
||||
@@ -24,24 +26,12 @@ export interface LoadingBar {
|
||||
bar_type?: LoadingBarType
|
||||
}
|
||||
|
||||
export type OpeningCommandEvent =
|
||||
| 'RunMRPack'
|
||||
| 'InstallServer'
|
||||
| 'InstallVersion'
|
||||
| 'InstallMod'
|
||||
| 'InstallModpack'
|
||||
| string
|
||||
|
||||
export interface OpeningCommand {
|
||||
event: OpeningCommandEvent
|
||||
id?: string
|
||||
path?: string
|
||||
}
|
||||
export type OpeningCommand = Extract<AppEvent, { type: 'command' }>['payload']
|
||||
|
||||
// Initialize the theseus API state
|
||||
// This should be called during the initializion/opening of the launcher
|
||||
export async function initialize_state() {
|
||||
return await invoke<void>('initialize_state')
|
||||
export async function initialize_state(events: Channel<ArrayBuffer>) {
|
||||
return await invoke<void>('initialize_state', { events })
|
||||
}
|
||||
|
||||
// Gets active progress bars
|
||||
|
||||
@@ -3,6 +3,7 @@ import { autoToHTML } from '@sfirew/minecraft-motd-parser'
|
||||
import { invoke } from '@tauri-apps/api/core'
|
||||
import dayjs from 'dayjs'
|
||||
|
||||
import type { InstancePayload } from '@/generated/app-events/InstancePayload'
|
||||
import { get_full_path } from '@/helpers/instance'
|
||||
import { openPath } from '@/helpers/utils'
|
||||
|
||||
@@ -532,18 +533,4 @@ export function hasWorldQuickPlaySupport(gameVersions: GameVersion[], currentVer
|
||||
return versionIndex !== -1 && targetIndex !== -1 && versionIndex <= targetIndex
|
||||
}
|
||||
|
||||
export type InstanceEvent = { instance_id: string } & (
|
||||
| {
|
||||
event: 'servers_updated'
|
||||
}
|
||||
| {
|
||||
event: 'world_updated'
|
||||
world: string
|
||||
}
|
||||
| {
|
||||
event: 'server_joined'
|
||||
host: string
|
||||
port: number
|
||||
timestamp: string
|
||||
}
|
||||
)
|
||||
export type InstanceEvent = InstancePayload
|
||||
|
||||
Reference in New Issue
Block a user