You've already forked AstralRinth
forked from didirus/AstralRinth
Offline mode (#403)
* offline mode * fixes, mixpanels, etc * changes * prettier * rev * actions
This commit is contained in:
@@ -233,6 +233,22 @@ pub async fn emit_warning(message: &str) -> crate::Result<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// emit_offline(bool)
|
||||||
|
// This is used to emit an event to the frontend that the app is offline after a refresh (or online)
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
pub async fn emit_offline(offline: bool) -> crate::Result<()> {
|
||||||
|
#[cfg(feature = "tauri")]
|
||||||
|
{
|
||||||
|
let event_state = crate::EventState::get().await?;
|
||||||
|
event_state
|
||||||
|
.app
|
||||||
|
.emit_all("offline", offline)
|
||||||
|
.map_err(EventError::from)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
// emit_command(CommandPayload::Something { something })
|
// emit_command(CommandPayload::Something { something })
|
||||||
// ie: installing a pack, opening an .mrpack, etc
|
// ie: installing a pack, opening an .mrpack, etc
|
||||||
// Generally used for url deep links and file opens that we we want to handle in the frontend
|
// Generally used for url deep links and file opens that we we want to handle in the frontend
|
||||||
|
|||||||
@@ -194,6 +194,11 @@ pub struct LoadingPayload {
|
|||||||
pub message: String,
|
pub message: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Clone)]
|
||||||
|
pub struct OfflinePayload {
|
||||||
|
pub offline: bool,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Clone)]
|
#[derive(Serialize, Clone)]
|
||||||
pub struct WarningPayload {
|
pub struct WarningPayload {
|
||||||
pub message: String,
|
pub message: String,
|
||||||
|
|||||||
@@ -542,7 +542,7 @@ pub async fn launch_minecraft(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
if !*state.offline.read().await {
|
||||||
// Add game played to discord rich presence
|
// Add game played to discord rich presence
|
||||||
let _ = state
|
let _ = state
|
||||||
.discord_rpc
|
.discord_rpc
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ impl Metadata {
|
|||||||
#[theseus_macros::debug_pin]
|
#[theseus_macros::debug_pin]
|
||||||
pub async fn init(
|
pub async fn init(
|
||||||
dirs: &DirectoryInfo,
|
dirs: &DirectoryInfo,
|
||||||
|
fetch_online: bool,
|
||||||
io_semaphore: &IoSemaphore,
|
io_semaphore: &IoSemaphore,
|
||||||
) -> crate::Result<Self> {
|
) -> crate::Result<Self> {
|
||||||
let mut metadata = None;
|
let mut metadata = None;
|
||||||
@@ -67,7 +68,7 @@ impl Metadata {
|
|||||||
read_json::<Metadata>(&metadata_path, io_semaphore).await
|
read_json::<Metadata>(&metadata_path, io_semaphore).await
|
||||||
{
|
{
|
||||||
metadata = Some(metadata_json);
|
metadata = Some(metadata_json);
|
||||||
} else {
|
} else if fetch_online {
|
||||||
let res = async {
|
let res = async {
|
||||||
let metadata_fetch = Self::fetch().await?;
|
let metadata_fetch = Self::fetch().await?;
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,17 @@
|
|||||||
//! Theseus state management system
|
//! Theseus state management system
|
||||||
use crate::event::emit::{emit_loading, init_loading_unsafe};
|
use crate::event::emit::{emit_loading, emit_offline, init_loading_unsafe};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use crate::event::LoadingBarType;
|
use crate::event::LoadingBarType;
|
||||||
use crate::loading_join;
|
use crate::loading_join;
|
||||||
|
|
||||||
use crate::state::users::Users;
|
use crate::state::users::Users;
|
||||||
use crate::util::fetch::{FetchSemaphore, IoSemaphore};
|
use crate::util::fetch::{self, FetchSemaphore, IoSemaphore};
|
||||||
use notify::RecommendedWatcher;
|
use notify::RecommendedWatcher;
|
||||||
use notify_debouncer_mini::{new_debouncer, DebounceEventResult, Debouncer};
|
use notify_debouncer_mini::{new_debouncer, DebounceEventResult, Debouncer};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
use tokio::join;
|
||||||
use tokio::sync::{OnceCell, RwLock, Semaphore};
|
use tokio::sync::{OnceCell, RwLock, Semaphore};
|
||||||
|
|
||||||
use futures::{channel::mpsc::channel, SinkExt, StreamExt};
|
use futures::{channel::mpsc::channel, SinkExt, StreamExt};
|
||||||
@@ -55,6 +56,9 @@ pub use self::discord::*;
|
|||||||
// RwLock on state only has concurrent reads, except for config dir change which takes control of the State
|
// RwLock on state only has concurrent reads, except for config dir change which takes control of the State
|
||||||
static LAUNCHER_STATE: OnceCell<RwLock<State>> = OnceCell::const_new();
|
static LAUNCHER_STATE: OnceCell<RwLock<State>> = OnceCell::const_new();
|
||||||
pub struct State {
|
pub struct State {
|
||||||
|
/// Whether or not the launcher is currently operating in 'offline mode'
|
||||||
|
pub offline: RwLock<bool>,
|
||||||
|
|
||||||
/// Information on the location of files used in the launcher
|
/// Information on the location of files used in the launcher
|
||||||
pub directories: DirectoryInfo,
|
pub directories: DirectoryInfo,
|
||||||
|
|
||||||
@@ -145,10 +149,17 @@ impl State {
|
|||||||
)));
|
)));
|
||||||
emit_loading(&loading_bar, 10.0, None).await?;
|
emit_loading(&loading_bar, 10.0, None).await?;
|
||||||
|
|
||||||
let metadata_fut = Metadata::init(&directories, &io_semaphore);
|
let is_offline = !fetch::check_internet(&fetch_semaphore, 3).await;
|
||||||
|
|
||||||
|
let metadata_fut =
|
||||||
|
Metadata::init(&directories, !is_offline, &io_semaphore);
|
||||||
let profiles_fut = Profiles::init(&directories, &mut file_watcher);
|
let profiles_fut = Profiles::init(&directories, &mut file_watcher);
|
||||||
let tags_fut =
|
let tags_fut = Tags::init(
|
||||||
Tags::init(&directories, &io_semaphore, &fetch_semaphore);
|
&directories,
|
||||||
|
!is_offline,
|
||||||
|
&io_semaphore,
|
||||||
|
&fetch_semaphore,
|
||||||
|
);
|
||||||
let users_fut = Users::init(&directories, &io_semaphore);
|
let users_fut = Users::init(&directories, &io_semaphore);
|
||||||
// Launcher data
|
// Launcher data
|
||||||
let (metadata, profiles, tags, users) = loading_join! {
|
let (metadata, profiles, tags, users) = loading_join! {
|
||||||
@@ -165,9 +176,13 @@ impl State {
|
|||||||
|
|
||||||
let discord_rpc = DiscordGuard::init().await?;
|
let discord_rpc = DiscordGuard::init().await?;
|
||||||
|
|
||||||
|
// Starts a loop of checking if we are online, and updating
|
||||||
|
Self::offine_check_loop();
|
||||||
|
|
||||||
emit_loading(&loading_bar, 10.0, None).await?;
|
emit_loading(&loading_bar, 10.0, None).await?;
|
||||||
|
|
||||||
Ok::<RwLock<Self>, crate::Error>(RwLock::new(Self {
|
Ok::<RwLock<Self>, crate::Error>(RwLock::new(Self {
|
||||||
|
offline: RwLock::new(is_offline),
|
||||||
directories,
|
directories,
|
||||||
fetch_semaphore,
|
fetch_semaphore,
|
||||||
fetch_semaphore_max: RwLock::new(
|
fetch_semaphore_max: RwLock::new(
|
||||||
@@ -190,13 +205,36 @@ impl State {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Updates state with data from the web
|
/// Starts a loop of checking if we are online, and updating
|
||||||
|
pub fn offine_check_loop() {
|
||||||
|
tokio::task::spawn(async {
|
||||||
|
loop {
|
||||||
|
let state = Self::get().await;
|
||||||
|
if let Ok(state) = state {
|
||||||
|
let _ = state.refresh_offline().await;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait 5 seconds
|
||||||
|
tokio::time::sleep(Duration::from_secs(5)).await;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Updates state with data from the web, if we are online
|
||||||
pub fn update() {
|
pub fn update() {
|
||||||
tokio::task::spawn(Metadata::update());
|
tokio::task::spawn(async {
|
||||||
tokio::task::spawn(Tags::update());
|
if let Ok(state) = crate::State::get().await {
|
||||||
tokio::task::spawn(Profiles::update_projects());
|
if !*state.offline.read().await {
|
||||||
tokio::task::spawn(Profiles::update_modrinth_versions());
|
let res1 = Profiles::update_modrinth_versions();
|
||||||
tokio::task::spawn(Settings::update_java());
|
let res2 = Tags::update();
|
||||||
|
let res3 = Metadata::update();
|
||||||
|
let res4 = Profiles::update_projects();
|
||||||
|
let res5 = Settings::update_java();
|
||||||
|
|
||||||
|
let _ = join!(res1, res2, res3, res4, res5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument]
|
#[tracing::instrument]
|
||||||
@@ -264,6 +302,21 @@ impl State {
|
|||||||
*total_permits = settings.max_concurrent_downloads as u32;
|
*total_permits = settings.max_concurrent_downloads as u32;
|
||||||
*io_semaphore = Semaphore::new(settings.max_concurrent_downloads);
|
*io_semaphore = Semaphore::new(settings.max_concurrent_downloads);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Refreshes whether or not the launcher should be offline, by whether or not there is an internet connection
|
||||||
|
pub async fn refresh_offline(&self) -> crate::Result<()> {
|
||||||
|
let is_online = fetch::check_internet(&self.fetch_semaphore, 3).await;
|
||||||
|
|
||||||
|
let mut offline = self.offline.write().await;
|
||||||
|
|
||||||
|
if *offline != is_online {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
emit_offline(!is_online).await?;
|
||||||
|
*offline = !is_online;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn init_watcher() -> crate::Result<Debouncer<RecommendedWatcher>> {
|
pub async fn init_watcher() -> crate::Result<Debouncer<RecommendedWatcher>> {
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ impl Tags {
|
|||||||
#[theseus_macros::debug_pin]
|
#[theseus_macros::debug_pin]
|
||||||
pub async fn init(
|
pub async fn init(
|
||||||
dirs: &DirectoryInfo,
|
dirs: &DirectoryInfo,
|
||||||
|
fetch_online: bool,
|
||||||
io_semaphore: &IoSemaphore,
|
io_semaphore: &IoSemaphore,
|
||||||
fetch_semaphore: &FetchSemaphore,
|
fetch_semaphore: &FetchSemaphore,
|
||||||
) -> crate::Result<Self> {
|
) -> crate::Result<Self> {
|
||||||
@@ -33,7 +34,7 @@ impl Tags {
|
|||||||
if let Ok(tags_json) = read_json::<Self>(&tags_path, io_semaphore).await
|
if let Ok(tags_json) = read_json::<Self>(&tags_path, io_semaphore).await
|
||||||
{
|
{
|
||||||
tags = Some(tags_json);
|
tags = Some(tags_json);
|
||||||
} else {
|
} else if fetch_online {
|
||||||
match Self::fetch(fetch_semaphore).await {
|
match Self::fetch(fetch_semaphore).await {
|
||||||
Ok(tags_fetch) => tags = Some(tags_fetch),
|
Ok(tags_fetch) => tags = Some(tags_fetch),
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use reqwest::Method;
|
|||||||
use serde::de::DeserializeOwned;
|
use serde::de::DeserializeOwned;
|
||||||
use std::ffi::OsStr;
|
use std::ffi::OsStr;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::time;
|
use std::time::{self, Duration};
|
||||||
use tokio::sync::{RwLock, Semaphore};
|
use tokio::sync::{RwLock, Semaphore};
|
||||||
use tokio::{fs::File, io::AsyncWriteExt};
|
use tokio::{fs::File, io::AsyncWriteExt};
|
||||||
|
|
||||||
@@ -182,6 +182,16 @@ pub async fn fetch_mirrors(
|
|||||||
unreachable!()
|
unreachable!()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Using labrinth API, checks if an internet response can be found, with a timeout in seconds
|
||||||
|
#[tracing::instrument(skip(semaphore))]
|
||||||
|
#[theseus_macros::debug_pin]
|
||||||
|
pub async fn check_internet(semaphore: &FetchSemaphore, timeout: u64) -> bool {
|
||||||
|
let result = fetch("https://api.modrinth.com", None, semaphore);
|
||||||
|
let result =
|
||||||
|
tokio::time::timeout(Duration::from_secs(timeout), result).await;
|
||||||
|
matches!(result, Ok(Ok(_)))
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn read_json<T>(
|
pub async fn read_json<T>(
|
||||||
path: &Path,
|
path: &Path,
|
||||||
semaphore: &IoSemaphore,
|
semaphore: &IoSemaphore,
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
"floating-vue": "^2.0.0-beta.20",
|
"floating-vue": "^2.0.0-beta.20",
|
||||||
"mixpanel-browser": "^2.47.0",
|
"mixpanel-browser": "^2.47.0",
|
||||||
"ofetch": "^1.0.1",
|
"ofetch": "^1.0.1",
|
||||||
"omorphia": "^0.4.33",
|
"omorphia": "^0.4.34",
|
||||||
"pinia": "^2.1.3",
|
"pinia": "^2.1.3",
|
||||||
"qrcode.vue": "^3.4.0",
|
"qrcode.vue": "^3.4.0",
|
||||||
"tauri-plugin-window-state-api": "github:tauri-apps/tauri-plugin-window-state#v1",
|
"tauri-plugin-window-state-api": "github:tauri-apps/tauri-plugin-window-state#v1",
|
||||||
|
|||||||
14
theseus_gui/pnpm-lock.yaml
generated
14
theseus_gui/pnpm-lock.yaml
generated
@@ -17,8 +17,8 @@ dependencies:
|
|||||||
specifier: ^1.0.1
|
specifier: ^1.0.1
|
||||||
version: 1.0.1
|
version: 1.0.1
|
||||||
omorphia:
|
omorphia:
|
||||||
specifier: ^0.4.33
|
specifier: ^0.4.34
|
||||||
version: 0.4.33
|
version: 0.4.34
|
||||||
pinia:
|
pinia:
|
||||||
specifier: ^2.1.3
|
specifier: ^2.1.3
|
||||||
version: 2.1.3(vue@3.3.4)
|
version: 2.1.3(vue@3.3.4)
|
||||||
@@ -27,7 +27,7 @@ dependencies:
|
|||||||
version: 3.4.0(vue@3.3.4)
|
version: 3.4.0(vue@3.3.4)
|
||||||
tauri-plugin-window-state-api:
|
tauri-plugin-window-state-api:
|
||||||
specifier: github:tauri-apps/tauri-plugin-window-state#v1
|
specifier: github:tauri-apps/tauri-plugin-window-state#v1
|
||||||
version: github.com/tauri-apps/tauri-plugin-window-state/347c792535d2623fc21f66590d06f4c8dadd85ba
|
version: github.com/tauri-apps/tauri-plugin-window-state/5ea9eb0d4a9affd17269f92c0085935046be3f4a
|
||||||
vite-svg-loader:
|
vite-svg-loader:
|
||||||
specifier: ^4.0.0
|
specifier: ^4.0.0
|
||||||
version: 4.0.0
|
version: 4.0.0
|
||||||
@@ -1344,8 +1344,8 @@ packages:
|
|||||||
ufo: 1.1.2
|
ufo: 1.1.2
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/omorphia@0.4.33:
|
/omorphia@0.4.34:
|
||||||
resolution: {integrity: sha512-Wo0t16zRL8ZLJSKVAYv6pdYhL4YXYjDYs18shO7V5cfxjcynvd5j0sui6uBR8ghVMWFEJH994AEC/urLwcHiBA==}
|
resolution: {integrity: sha512-6uAH1kgzbYYmJDM41Vy4/MhzT9kRj+s1t8IknHKeOQqmVft+wPtv/pbA7pqTMfCzBOarLKKO5s4sNlz8TeMmaQ==}
|
||||||
dependencies:
|
dependencies:
|
||||||
dayjs: 1.11.7
|
dayjs: 1.11.7
|
||||||
floating-vue: 2.0.0-beta.20(vue@3.3.4)
|
floating-vue: 2.0.0-beta.20(vue@3.3.4)
|
||||||
@@ -1808,8 +1808,8 @@ packages:
|
|||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
github.com/tauri-apps/tauri-plugin-window-state/347c792535d2623fc21f66590d06f4c8dadd85ba:
|
github.com/tauri-apps/tauri-plugin-window-state/5ea9eb0d4a9affd17269f92c0085935046be3f4a:
|
||||||
resolution: {tarball: https://codeload.github.com/tauri-apps/tauri-plugin-window-state/tar.gz/347c792535d2623fc21f66590d06f4c8dadd85ba}
|
resolution: {tarball: https://codeload.github.com/tauri-apps/tauri-plugin-window-state/tar.gz/5ea9eb0d4a9affd17269f92c0085935046be3f4a}
|
||||||
name: tauri-plugin-window-state-api
|
name: tauri-plugin-window-state-api
|
||||||
version: 0.0.0
|
version: 0.0.0
|
||||||
dependencies:
|
dependencies:
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ pub fn init<R: tauri::Runtime>() -> tauri::plugin::TauriPlugin<R> {
|
|||||||
safety_check_safe_loading_bars,
|
safety_check_safe_loading_bars,
|
||||||
get_opening_command,
|
get_opening_command,
|
||||||
await_sync,
|
await_sync,
|
||||||
|
is_offline,
|
||||||
|
refresh_offline
|
||||||
])
|
])
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
@@ -125,3 +127,20 @@ pub async fn await_sync() -> Result<()> {
|
|||||||
tracing::debug!("State synced");
|
tracing::debug!("State synced");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Check if theseus is currently in offline mode, without a refresh attempt
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn is_offline() -> Result<bool> {
|
||||||
|
let state = State::get().await?;
|
||||||
|
let offline = *state.offline.read().await;
|
||||||
|
Ok(offline)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Refreshes whether or not theseus is in offline mode, and returns the new value
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn refresh_offline() -> Result<bool> {
|
||||||
|
let state = State::get().await?;
|
||||||
|
state.refresh_offline().await?;
|
||||||
|
let offline = *state.offline.read().await;
|
||||||
|
Ok(offline)
|
||||||
|
}
|
||||||
|
|||||||
@@ -20,12 +20,17 @@ import RunningAppBar from '@/components/ui/RunningAppBar.vue'
|
|||||||
import SplashScreen from '@/components/ui/SplashScreen.vue'
|
import SplashScreen from '@/components/ui/SplashScreen.vue'
|
||||||
import ModrinthLoadingIndicator from '@/components/modrinth-loading-indicator'
|
import ModrinthLoadingIndicator from '@/components/modrinth-loading-indicator'
|
||||||
import { useNotifications } from '@/store/notifications.js'
|
import { useNotifications } from '@/store/notifications.js'
|
||||||
import { command_listener, warning_listener } from '@/helpers/events.js'
|
import { offline_listener, command_listener, warning_listener } from '@/helpers/events.js'
|
||||||
import { MinimizeIcon, MaximizeIcon } from '@/assets/icons'
|
import { MinimizeIcon, MaximizeIcon } from '@/assets/icons'
|
||||||
import { type } from '@tauri-apps/api/os'
|
import { type } from '@tauri-apps/api/os'
|
||||||
import { appWindow } from '@tauri-apps/api/window'
|
import { appWindow } from '@tauri-apps/api/window'
|
||||||
import { isDev } from '@/helpers/utils.js'
|
import { isDev, isOffline } from '@/helpers/utils.js'
|
||||||
import mixpanel from 'mixpanel-browser'
|
import {
|
||||||
|
mixpanel_track,
|
||||||
|
mixpanel_init,
|
||||||
|
mixpanel_opt_out_tracking,
|
||||||
|
mixpanel_is_loaded,
|
||||||
|
} from '@/helpers/mixpanel'
|
||||||
import { saveWindowState, StateFlags } from 'tauri-plugin-window-state-api'
|
import { saveWindowState, StateFlags } from 'tauri-plugin-window-state-api'
|
||||||
import { getVersion } from '@tauri-apps/api/app'
|
import { getVersion } from '@tauri-apps/api/app'
|
||||||
import { window as TauriWindow } from '@tauri-apps/api'
|
import { window as TauriWindow } from '@tauri-apps/api'
|
||||||
@@ -33,13 +38,14 @@ import { TauriEvent } from '@tauri-apps/api/event'
|
|||||||
import { await_sync, check_safe_loading_bars_complete } from './helpers/state'
|
import { await_sync, check_safe_loading_bars_complete } from './helpers/state'
|
||||||
import { confirm } from '@tauri-apps/api/dialog'
|
import { confirm } from '@tauri-apps/api/dialog'
|
||||||
import URLConfirmModal from '@/components/ui/URLConfirmModal.vue'
|
import URLConfirmModal from '@/components/ui/URLConfirmModal.vue'
|
||||||
// import OnboardingScreen from '@/components/ui/tutorial/OnboardingScreen.vue'
|
|
||||||
import StickyTitleBar from '@/components/ui/tutorial/StickyTitleBar.vue'
|
import StickyTitleBar from '@/components/ui/tutorial/StickyTitleBar.vue'
|
||||||
import OnboardingScreen from '@/components/ui/tutorial/OnboardingScreen.vue'
|
import OnboardingScreen from '@/components/ui/tutorial/OnboardingScreen.vue'
|
||||||
|
|
||||||
const themeStore = useTheming()
|
const themeStore = useTheming()
|
||||||
const urlModal = ref(null)
|
const urlModal = ref(null)
|
||||||
const isLoading = ref(true)
|
const isLoading = ref(true)
|
||||||
|
const offline = ref(false)
|
||||||
|
|
||||||
const videoPlaying = ref(true)
|
const videoPlaying = ref(true)
|
||||||
const showOnboarding = ref(false)
|
const showOnboarding = ref(false)
|
||||||
|
|
||||||
@@ -58,11 +64,11 @@ defineExpose({
|
|||||||
themeStore.collapsedNavigation = collapsed_navigation
|
themeStore.collapsedNavigation = collapsed_navigation
|
||||||
themeStore.advancedRendering = advanced_rendering
|
themeStore.advancedRendering = advanced_rendering
|
||||||
|
|
||||||
mixpanel.init('014c7d6a336d0efaefe3aca91063748d', { debug: dev, persistence: 'localStorage' })
|
mixpanel_init('014c7d6a336d0efaefe3aca91063748d', { debug: dev, persistence: 'localStorage' })
|
||||||
if (opt_out_analytics) {
|
if (opt_out_analytics) {
|
||||||
mixpanel.opt_out_tracking()
|
mixpanel_opt_out_tracking()
|
||||||
}
|
}
|
||||||
mixpanel.track('Launched', { version, dev, onboarded_new })
|
mixpanel_track('Launched', { version, dev, onboarded_new })
|
||||||
|
|
||||||
if (!dev) document.addEventListener('contextmenu', (event) => event.preventDefault())
|
if (!dev) document.addEventListener('contextmenu', (event) => event.preventDefault())
|
||||||
|
|
||||||
@@ -72,6 +78,11 @@ defineExpose({
|
|||||||
document.getElementsByTagName('html')[0].classList.add('windows')
|
document.getElementsByTagName('html')[0].classList.add('windows')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
offline.value = await isOffline()
|
||||||
|
await offline_listener((b) => {
|
||||||
|
offline.value = b
|
||||||
|
})
|
||||||
|
|
||||||
await warning_listener((e) =>
|
await warning_listener((e) =>
|
||||||
notificationsWrapper.value.addNotification({
|
notificationsWrapper.value.addNotification({
|
||||||
title: 'Warning',
|
title: 'Warning',
|
||||||
@@ -121,8 +132,8 @@ TauriWindow.getCurrent().listen(TauriEvent.WINDOW_CLOSE_REQUESTED, async () => {
|
|||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
router.afterEach((to, from, failure) => {
|
router.afterEach((to, from, failure) => {
|
||||||
if (mixpanel.__loaded) {
|
if (mixpanel_is_loaded()) {
|
||||||
mixpanel.track('PageView', { path: to.path, fromPath: from.path, failed: failure })
|
mixpanel_track('PageView', { path: to.path, fromPath: from.path, failed: failure })
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
@@ -214,6 +225,7 @@ command_listener((e) => {
|
|||||||
<Button
|
<Button
|
||||||
class="sleek-primary collapsed-button"
|
class="sleek-primary collapsed-button"
|
||||||
icon-only
|
icon-only
|
||||||
|
:disabled="offline"
|
||||||
@click="() => $refs.installationModal.show()"
|
@click="() => $refs.installationModal.show()"
|
||||||
>
|
>
|
||||||
<PlusIcon />
|
<PlusIcon />
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ import { showProfileInFolder } from '@/helpers/utils.js'
|
|||||||
import { useFetch } from '@/helpers/fetch.js'
|
import { useFetch } from '@/helpers/fetch.js'
|
||||||
import { install as pack_install } from '@/helpers/pack.js'
|
import { install as pack_install } from '@/helpers/pack.js'
|
||||||
import { useTheming } from '@/store/state.js'
|
import { useTheming } from '@/store/state.js'
|
||||||
import mixpanel from 'mixpanel-browser'
|
import { mixpanel_track } from '@/helpers/mixpanel'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
@@ -125,7 +125,7 @@ const handleOptionsClick = async (args) => {
|
|||||||
switch (args.option) {
|
switch (args.option) {
|
||||||
case 'play':
|
case 'play':
|
||||||
await run(args.item.path).catch(handleError)
|
await run(args.item.path).catch(handleError)
|
||||||
mixpanel.track('InstanceStart', {
|
mixpanel_track('InstanceStart', {
|
||||||
loader: args.item.metadata.loader,
|
loader: args.item.metadata.loader,
|
||||||
game_version: args.item.metadata.game_version,
|
game_version: args.item.metadata.game_version,
|
||||||
})
|
})
|
||||||
@@ -134,7 +134,7 @@ const handleOptionsClick = async (args) => {
|
|||||||
for (const u of await get_uuids_by_profile_path(args.item.path).catch(handleError)) {
|
for (const u of await get_uuids_by_profile_path(args.item.path).catch(handleError)) {
|
||||||
await kill_by_uuid(u).catch(handleError)
|
await kill_by_uuid(u).catch(handleError)
|
||||||
}
|
}
|
||||||
mixpanel.track('InstanceStop', {
|
mixpanel_track('InstanceStop', {
|
||||||
loader: args.item.metadata.loader,
|
loader: args.item.metadata.loader,
|
||||||
game_version: args.item.metadata.game_version,
|
game_version: args.item.metadata.game_version,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ import {
|
|||||||
import { get, set } from '@/helpers/settings'
|
import { get, set } from '@/helpers/settings'
|
||||||
import { WebviewWindow } from '@tauri-apps/api/window'
|
import { WebviewWindow } from '@tauri-apps/api/window'
|
||||||
import { handleError } from '@/store/state.js'
|
import { handleError } from '@/store/state.js'
|
||||||
import mixpanel from 'mixpanel-browser'
|
import { mixpanel_track } from '@/helpers/mixpanel'
|
||||||
|
|
||||||
defineProps({
|
defineProps({
|
||||||
mode: {
|
mode: {
|
||||||
@@ -127,7 +127,7 @@ async function login() {
|
|||||||
await setAccount(loggedIn)
|
await setAccount(loggedIn)
|
||||||
await refreshValues()
|
await refreshValues()
|
||||||
await window.close()
|
await window.close()
|
||||||
mixpanel.track('AccountLogIn')
|
mixpanel_track('AccountLogIn')
|
||||||
}
|
}
|
||||||
|
|
||||||
const logout = async (id) => {
|
const logout = async (id) => {
|
||||||
@@ -139,7 +139,7 @@ const logout = async (id) => {
|
|||||||
} else {
|
} else {
|
||||||
emit('change')
|
emit('change')
|
||||||
}
|
}
|
||||||
mixpanel.track('AccountLogOut')
|
mixpanel_track('AccountLogOut')
|
||||||
}
|
}
|
||||||
|
|
||||||
let showCard = ref(false)
|
let showCard = ref(false)
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ import { Button, Modal, XIcon, DownloadIcon, DropdownSelect, formatCategory } fr
|
|||||||
import { add_project_from_version as installMod } from '@/helpers/profile'
|
import { add_project_from_version as installMod } from '@/helpers/profile'
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { handleError, useTheming } from '@/store/state.js'
|
import { handleError, useTheming } from '@/store/state.js'
|
||||||
import mixpanel from 'mixpanel-browser'
|
import { mixpanel_track } from '@/helpers/mixpanel'
|
||||||
|
|
||||||
const themeStore = useTheming()
|
const themeStore = useTheming()
|
||||||
|
|
||||||
@@ -94,7 +94,7 @@ defineExpose({
|
|||||||
incompatibleModal.value.show()
|
incompatibleModal.value.show()
|
||||||
markInstalled = extMarkInstalled
|
markInstalled = extMarkInstalled
|
||||||
|
|
||||||
mixpanel.track('ProjectInstallStart', { source: 'ProjectIncompatibilityWarningModal' })
|
mixpanel_track('ProjectInstallStart', { source: 'ProjectIncompatibilityWarningModal' })
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -105,7 +105,7 @@ const install = async () => {
|
|||||||
markInstalled()
|
markInstalled()
|
||||||
incompatibleModal.value.hide()
|
incompatibleModal.value.hide()
|
||||||
|
|
||||||
mixpanel.track('ProjectInstall', {
|
mixpanel_track('ProjectInstall', {
|
||||||
loader: instance.value.metadata.loader,
|
loader: instance.value.metadata.loader,
|
||||||
game_version: instance.value.metadata.game_version,
|
game_version: instance.value.metadata.game_version,
|
||||||
id: project.value,
|
id: project.value,
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
import { Button, Modal, XIcon, DownloadIcon } from 'omorphia'
|
import { Button, Modal, XIcon, DownloadIcon } from 'omorphia'
|
||||||
import { install as pack_install } from '@/helpers/pack'
|
import { install as pack_install } from '@/helpers/pack'
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import mixpanel from 'mixpanel-browser'
|
import { mixpanel_track } from '@/helpers/mixpanel'
|
||||||
import { useTheming } from '@/store/theme.js'
|
import { useTheming } from '@/store/theme.js'
|
||||||
import { handleError } from '@/store/state.js'
|
import { handleError } from '@/store/state.js'
|
||||||
|
|
||||||
@@ -24,7 +24,7 @@ defineExpose({
|
|||||||
installing.value = false
|
installing.value = false
|
||||||
confirmModal.value.show()
|
confirmModal.value.show()
|
||||||
|
|
||||||
mixpanel.track('PackInstallStart')
|
mixpanel_track('PackInstallStart')
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -38,7 +38,7 @@ async function install() {
|
|||||||
title.value,
|
title.value,
|
||||||
icon.value ? icon.value : null
|
icon.value ? icon.value : null
|
||||||
).catch(handleError)
|
).catch(handleError)
|
||||||
mixpanel.track('PackInstall', {
|
mixpanel_track('PackInstall', {
|
||||||
id: projectId.value,
|
id: projectId.value,
|
||||||
version_id: version.value,
|
version_id: version.value,
|
||||||
title: title.value,
|
title: title.value,
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import { useFetch } from '@/helpers/fetch.js'
|
|||||||
import { handleError } from '@/store/state.js'
|
import { handleError } from '@/store/state.js'
|
||||||
import { showProfileInFolder } from '@/helpers/utils.js'
|
import { showProfileInFolder } from '@/helpers/utils.js'
|
||||||
import ModInstallModal from '@/components/ui/ModInstallModal.vue'
|
import ModInstallModal from '@/components/ui/ModInstallModal.vue'
|
||||||
import mixpanel from 'mixpanel-browser'
|
import { mixpanel_track } from '@/helpers/mixpanel'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
instance: {
|
instance: {
|
||||||
@@ -93,7 +93,7 @@ const install = async (e) => {
|
|||||||
).catch(handleError)
|
).catch(handleError)
|
||||||
modLoading.value = false
|
modLoading.value = false
|
||||||
|
|
||||||
mixpanel.track('PackInstall', {
|
mixpanel_track('PackInstall', {
|
||||||
id: props.instance.project_id,
|
id: props.instance.project_id,
|
||||||
version_id: versions[0].id,
|
version_id: versions[0].id,
|
||||||
title: props.instance.title,
|
title: props.instance.title,
|
||||||
@@ -125,7 +125,7 @@ const play = async (e, context) => {
|
|||||||
modLoading.value = false
|
modLoading.value = false
|
||||||
playing.value = true
|
playing.value = true
|
||||||
|
|
||||||
mixpanel.track('InstancePlay', {
|
mixpanel_track('InstancePlay', {
|
||||||
loader: props.instance.metadata.loader,
|
loader: props.instance.metadata.loader,
|
||||||
game_version: props.instance.metadata.game_version,
|
game_version: props.instance.metadata.game_version,
|
||||||
source: context,
|
source: context,
|
||||||
@@ -145,7 +145,7 @@ const stop = async (e, context) => {
|
|||||||
uuids.forEach(async (u) => await kill_by_uuid(u).catch(handleError))
|
uuids.forEach(async (u) => await kill_by_uuid(u).catch(handleError))
|
||||||
} else await kill_by_uuid(uuid.value).catch(handleError) // If we still have the uuid, just kill it
|
} else await kill_by_uuid(uuid.value).catch(handleError) // If we still have the uuid, just kill it
|
||||||
|
|
||||||
mixpanel.track('InstanceStop', {
|
mixpanel_track('InstanceStop', {
|
||||||
loader: props.instance.metadata.loader,
|
loader: props.instance.metadata.loader,
|
||||||
game_version: props.instance.metadata.game_version,
|
game_version: props.instance.metadata.game_version,
|
||||||
source: context,
|
source: context,
|
||||||
|
|||||||
@@ -215,7 +215,7 @@ import {
|
|||||||
} from '@/helpers/metadata'
|
} from '@/helpers/metadata'
|
||||||
import { handleError } from '@/store/notifications.js'
|
import { handleError } from '@/store/notifications.js'
|
||||||
import Multiselect from 'vue-multiselect'
|
import Multiselect from 'vue-multiselect'
|
||||||
import mixpanel from 'mixpanel-browser'
|
import { mixpanel_track } from '@/helpers/mixpanel'
|
||||||
import { useTheming } from '@/store/state.js'
|
import { useTheming } from '@/store/state.js'
|
||||||
import { listen } from '@tauri-apps/api/event'
|
import { listen } from '@tauri-apps/api/event'
|
||||||
import { install_from_file } from '@/helpers/pack.js'
|
import { install_from_file } from '@/helpers/pack.js'
|
||||||
@@ -249,7 +249,7 @@ defineExpose({
|
|||||||
display_icon.value = null
|
display_icon.value = null
|
||||||
modal.value.show()
|
modal.value.show()
|
||||||
|
|
||||||
mixpanel.track('InstanceCreateStart', { source: 'CreationModal' })
|
mixpanel_track('InstanceCreateStart', { source: 'CreationModal' })
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -314,7 +314,7 @@ const create_instance = async () => {
|
|||||||
icon.value
|
icon.value
|
||||||
).catch(handleError)
|
).catch(handleError)
|
||||||
|
|
||||||
mixpanel.track('InstanceCreate', {
|
mixpanel_track('InstanceCreate', {
|
||||||
profile_name: profile_name.value,
|
profile_name: profile_name.value,
|
||||||
game_version: game_version.value,
|
game_version: game_version.value,
|
||||||
loader: loader.value,
|
loader: loader.value,
|
||||||
@@ -370,7 +370,7 @@ const openFile = async () => {
|
|||||||
modal.value.hide()
|
modal.value.hide()
|
||||||
await install_from_file(newProject).catch(handleError)
|
await install_from_file(newProject).catch(handleError)
|
||||||
|
|
||||||
mixpanel.track('InstanceCreate', {
|
mixpanel_track('InstanceCreate', {
|
||||||
source: 'CreationModalFileOpen',
|
source: 'CreationModalFileOpen',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -379,7 +379,7 @@ listen('tauri://file-drop', async (event) => {
|
|||||||
modal.value.hide()
|
modal.value.hide()
|
||||||
if (event.payload && event.payload.length > 0 && event.payload[0].endsWith('.mrpack')) {
|
if (event.payload && event.payload.length > 0 && event.payload[0].endsWith('.mrpack')) {
|
||||||
await install_from_file(event.payload[0]).catch(handleError)
|
await install_from_file(event.payload[0]).catch(handleError)
|
||||||
mixpanel.track('InstanceCreate', {
|
mixpanel_track('InstanceCreate', {
|
||||||
source: 'CreationModalFileDrop',
|
source: 'CreationModalFileDrop',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ import {
|
|||||||
get_all_jre,
|
get_all_jre,
|
||||||
} from '@/helpers/jre.js'
|
} from '@/helpers/jre.js'
|
||||||
import { handleError } from '@/store/notifications.js'
|
import { handleError } from '@/store/notifications.js'
|
||||||
import mixpanel from 'mixpanel-browser'
|
import { mixpanel_track } from '@/helpers/mixpanel'
|
||||||
import { useTheming } from '@/store/theme.js'
|
import { useTheming } from '@/store/theme.js'
|
||||||
|
|
||||||
const themeStore = useTheming()
|
const themeStore = useTheming()
|
||||||
@@ -79,7 +79,7 @@ const emit = defineEmits(['submit'])
|
|||||||
function setJavaInstall(javaInstall) {
|
function setJavaInstall(javaInstall) {
|
||||||
emit('submit', javaInstall)
|
emit('submit', javaInstall)
|
||||||
detectJavaModal.value.hide()
|
detectJavaModal.value.hide()
|
||||||
mixpanel.track('JavaAutoDetect', {
|
mixpanel_track('JavaAutoDetect', {
|
||||||
path: javaInstall.path,
|
path: javaInstall.path,
|
||||||
version: javaInstall.version,
|
version: javaInstall.version,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ import { find_jre_17_jres, get_jre } from '@/helpers/jre.js'
|
|||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { open } from '@tauri-apps/api/dialog'
|
import { open } from '@tauri-apps/api/dialog'
|
||||||
import JavaDetectionModal from '@/components/ui/JavaDetectionModal.vue'
|
import JavaDetectionModal from '@/components/ui/JavaDetectionModal.vue'
|
||||||
import mixpanel from 'mixpanel-browser'
|
import { mixpanel_track } from '@/helpers/mixpanel'
|
||||||
import { handleError } from '@/store/state.js'
|
import { handleError } from '@/store/state.js'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@@ -88,7 +88,7 @@ async function testJava() {
|
|||||||
testingJava.value = false
|
testingJava.value = false
|
||||||
testingJavaSuccess.value = !!result
|
testingJavaSuccess.value = !!result
|
||||||
|
|
||||||
mixpanel.track('JavaTest', {
|
mixpanel_track('JavaTest', {
|
||||||
path: props.modelValue ? props.modelValue.path : '',
|
path: props.modelValue ? props.modelValue.path : '',
|
||||||
success: !!result,
|
success: !!result,
|
||||||
})
|
})
|
||||||
@@ -110,7 +110,7 @@ async function handleJavaFileInput() {
|
|||||||
architecture: 'x86',
|
architecture: 'x86',
|
||||||
}
|
}
|
||||||
|
|
||||||
mixpanel.track('JavaManualSelect', {
|
mixpanel_track('JavaManualSelect', {
|
||||||
path: filePath,
|
path: filePath,
|
||||||
version: props.version,
|
version: props.version,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import { open } from '@tauri-apps/api/dialog'
|
|||||||
import { create } from '@/helpers/profile'
|
import { create } from '@/helpers/profile'
|
||||||
import { installVersionDependencies } from '@/helpers/utils'
|
import { installVersionDependencies } from '@/helpers/utils'
|
||||||
import { handleError } from '@/store/notifications.js'
|
import { handleError } from '@/store/notifications.js'
|
||||||
import mixpanel from 'mixpanel-browser'
|
import { mixpanel_track } from '@/helpers/mixpanel'
|
||||||
import { useTheming } from '@/store/theme.js'
|
import { useTheming } from '@/store/theme.js'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { tauri } from '@tauri-apps/api'
|
import { tauri } from '@tauri-apps/api'
|
||||||
@@ -57,7 +57,7 @@ defineExpose({
|
|||||||
|
|
||||||
profiles.value = await getData()
|
profiles.value = await getData()
|
||||||
|
|
||||||
mixpanel.track('ProjectInstallStart', { source: 'ProjectInstallModal' })
|
mixpanel_track('ProjectInstallStart', { source: 'ProjectInstallModal' })
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -88,7 +88,7 @@ async function install(instance) {
|
|||||||
instance.installedMod = true
|
instance.installedMod = true
|
||||||
instance.installing = false
|
instance.installing = false
|
||||||
|
|
||||||
mixpanel.track('ProjectInstall', {
|
mixpanel_track('ProjectInstall', {
|
||||||
loader: instance.metadata.loader,
|
loader: instance.metadata.loader,
|
||||||
game_version: instance.metadata.game_version,
|
game_version: instance.metadata.game_version,
|
||||||
id: project.value,
|
id: project.value,
|
||||||
@@ -137,7 +137,7 @@ const toggleCreation = () => {
|
|||||||
|
|
||||||
if (!alreadySentCreation.value) {
|
if (!alreadySentCreation.value) {
|
||||||
alreadySentCreation.value = false
|
alreadySentCreation.value = false
|
||||||
mixpanel.track('InstanceCreateStart', { source: 'ProjectInstallModal' })
|
mixpanel_track('InstanceCreateStart', { source: 'ProjectInstallModal' })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -186,7 +186,7 @@ const createInstance = async () => {
|
|||||||
const instance = await get(id, true)
|
const instance = await get(id, true)
|
||||||
await installVersionDependencies(instance, versions.value)
|
await installVersionDependencies(instance, versions.value)
|
||||||
|
|
||||||
mixpanel.track('InstanceCreate', {
|
mixpanel_track('InstanceCreate', {
|
||||||
profile_name: name.value,
|
profile_name: name.value,
|
||||||
game_version: versions.value[0].game_versions[0],
|
game_version: versions.value[0].game_versions[0],
|
||||||
loader: loader,
|
loader: loader,
|
||||||
@@ -195,7 +195,7 @@ const createInstance = async () => {
|
|||||||
source: 'ProjectInstallModal',
|
source: 'ProjectInstallModal',
|
||||||
})
|
})
|
||||||
|
|
||||||
mixpanel.track('ProjectInstall', {
|
mixpanel_track('ProjectInstall', {
|
||||||
loader: loader,
|
loader: loader,
|
||||||
game_version: versions.value[0].game_versions[0],
|
game_version: versions.value[0].game_versions[0],
|
||||||
id: project.value,
|
id: project.value,
|
||||||
|
|||||||
@@ -9,6 +9,12 @@
|
|||||||
>
|
>
|
||||||
<DownloadIcon />
|
<DownloadIcon />
|
||||||
</Button>
|
</Button>
|
||||||
|
<div v-if="offline" class="status">
|
||||||
|
<span class="circle stopped" />
|
||||||
|
<div class="running-text clickable" @click="refreshInternet()">
|
||||||
|
<span> Offline </span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div v-if="selectedProfile" class="status">
|
<div v-if="selectedProfile" class="status">
|
||||||
<span class="circle running" />
|
<span class="circle running" />
|
||||||
<div ref="profileButton" class="running-text">
|
<div ref="profileButton" class="running-text">
|
||||||
@@ -107,12 +113,13 @@ import {
|
|||||||
kill_by_uuid as killProfile,
|
kill_by_uuid as killProfile,
|
||||||
get_uuids_by_profile_path as getProfileProcesses,
|
get_uuids_by_profile_path as getProfileProcesses,
|
||||||
} from '@/helpers/process'
|
} from '@/helpers/process'
|
||||||
import { loading_listener, process_listener } from '@/helpers/events'
|
import { loading_listener, process_listener, offline_listener } from '@/helpers/events'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { progress_bars_list } from '@/helpers/state.js'
|
import { progress_bars_list } from '@/helpers/state.js'
|
||||||
|
import { refreshOffline, isOffline } from '@/helpers/utils.js'
|
||||||
import ProgressBar from '@/components/ui/ProgressBar.vue'
|
import ProgressBar from '@/components/ui/ProgressBar.vue'
|
||||||
import { handleError } from '@/store/notifications.js'
|
import { handleError } from '@/store/notifications.js'
|
||||||
import mixpanel from 'mixpanel-browser'
|
import { mixpanel_track } from '@/helpers/mixpanel'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const card = ref(null)
|
const card = ref(null)
|
||||||
@@ -126,10 +133,20 @@ const showProfiles = ref(false)
|
|||||||
const currentProcesses = ref(await getRunningProfiles().catch(handleError))
|
const currentProcesses = ref(await getRunningProfiles().catch(handleError))
|
||||||
const selectedProfile = ref(currentProcesses.value[0])
|
const selectedProfile = ref(currentProcesses.value[0])
|
||||||
|
|
||||||
|
const offline = ref(await isOffline().catch(handleError))
|
||||||
|
const refreshInternet = async () => {
|
||||||
|
offline.value = await refreshOffline().catch(handleError)
|
||||||
|
}
|
||||||
|
|
||||||
const unlistenProcess = await process_listener(async () => {
|
const unlistenProcess = await process_listener(async () => {
|
||||||
await refresh()
|
await refresh()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const unlistenRefresh = await offline_listener(async (b) => {
|
||||||
|
offline.value = b
|
||||||
|
await refresh()
|
||||||
|
})
|
||||||
|
|
||||||
const refresh = async () => {
|
const refresh = async () => {
|
||||||
currentProcesses.value = await getRunningProfiles().catch(handleError)
|
currentProcesses.value = await getRunningProfiles().catch(handleError)
|
||||||
if (!currentProcesses.value.includes(selectedProfile.value)) {
|
if (!currentProcesses.value.includes(selectedProfile.value)) {
|
||||||
@@ -142,7 +159,7 @@ const stop = async (path) => {
|
|||||||
const processes = await getProfileProcesses(path ?? selectedProfile.value.path)
|
const processes = await getProfileProcesses(path ?? selectedProfile.value.path)
|
||||||
await killProfile(processes[0])
|
await killProfile(processes[0])
|
||||||
|
|
||||||
mixpanel.track('InstanceStop', {
|
mixpanel_track('InstanceStop', {
|
||||||
loader: currentProcesses.value[0].metadata.loader,
|
loader: currentProcesses.value[0].metadata.loader,
|
||||||
game_version: currentProcesses.value[0].metadata.game_version,
|
game_version: currentProcesses.value[0].metadata.game_version,
|
||||||
source: 'AppBar',
|
source: 'AppBar',
|
||||||
@@ -240,6 +257,7 @@ onBeforeUnmount(() => {
|
|||||||
window.removeEventListener('click', handleClickOutsideProfile)
|
window.removeEventListener('click', handleClickOutsideProfile)
|
||||||
unlistenProcess()
|
unlistenProcess()
|
||||||
unlistenLoading()
|
unlistenLoading()
|
||||||
|
unlistenRefresh()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ import { install as packInstall } from '@/helpers/pack.js'
|
|||||||
import { installVersionDependencies } from '@/helpers/utils.js'
|
import { installVersionDependencies } from '@/helpers/utils.js'
|
||||||
import { useFetch } from '@/helpers/fetch.js'
|
import { useFetch } from '@/helpers/fetch.js'
|
||||||
import { handleError } from '@/store/notifications.js'
|
import { handleError } from '@/store/notifications.js'
|
||||||
import mixpanel from 'mixpanel-browser'
|
import { mixpanel_track } from '@/helpers/mixpanel'
|
||||||
dayjs.extend(relativeTime)
|
dayjs.extend(relativeTime)
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@@ -165,7 +165,7 @@ async function install() {
|
|||||||
props.project.icon_url
|
props.project.icon_url
|
||||||
).catch(handleError)
|
).catch(handleError)
|
||||||
|
|
||||||
mixpanel.track('PackInstall', {
|
mixpanel_track('PackInstall', {
|
||||||
id: props.project.project_id,
|
id: props.project.project_id,
|
||||||
version_id: queuedVersionData.id,
|
version_id: queuedVersionData.id,
|
||||||
title: props.project.title,
|
title: props.project.title,
|
||||||
@@ -196,7 +196,7 @@ async function install() {
|
|||||||
await installMod(props.instance.path, queuedVersionData.id).catch(handleError)
|
await installMod(props.instance.path, queuedVersionData.id).catch(handleError)
|
||||||
await installVersionDependencies(props.instance, queuedVersionData)
|
await installVersionDependencies(props.instance, queuedVersionData)
|
||||||
|
|
||||||
mixpanel.track('ProjectInstall', {
|
mixpanel_track('ProjectInstall', {
|
||||||
loader: props.instance.metadata.loader,
|
loader: props.instance.metadata.loader,
|
||||||
game_version: props.instance.metadata.game_version,
|
game_version: props.instance.metadata.game_version,
|
||||||
id: props.project.project_id,
|
id: props.project.project_id,
|
||||||
|
|||||||
@@ -92,3 +92,15 @@ export async function command_listener(callback) {
|
|||||||
export async function warning_listener(callback) {
|
export async function warning_listener(callback) {
|
||||||
return await listen('warning', (event) => callback(event.payload))
|
return await listen('warning', (event) => callback(event.payload))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Payload for the 'offline' event
|
||||||
|
/*
|
||||||
|
OfflinePayload {
|
||||||
|
offline: bool, true or false
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
export async function offline_listener(callback) {
|
||||||
|
return await listen('offline', (event) => {
|
||||||
|
return callback(event.payload)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
57
theseus_gui/src/helpers/mixpanel.js
Normal file
57
theseus_gui/src/helpers/mixpanel.js
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
import mixpanel from 'mixpanel-browser'
|
||||||
|
|
||||||
|
// mixpanel_track
|
||||||
|
function trackWrapper(originalTrack) {
|
||||||
|
return function (event_name, properties = {}) {
|
||||||
|
try {
|
||||||
|
originalTrack(event_name, properties)
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export const mixpanel_track = trackWrapper(mixpanel.track.bind(mixpanel))
|
||||||
|
|
||||||
|
// mixpanel_opt_out_tracking()
|
||||||
|
function optOutTrackingWrapper(originalOptOutTracking) {
|
||||||
|
return function () {
|
||||||
|
try {
|
||||||
|
originalOptOutTracking()
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export const mixpanel_opt_out_tracking = optOutTrackingWrapper(
|
||||||
|
mixpanel.opt_out_tracking.bind(mixpanel)
|
||||||
|
)
|
||||||
|
|
||||||
|
// mixpanel_opt_in_tracking()
|
||||||
|
function optInTrackingWrapper(originalOptInTracking) {
|
||||||
|
return function () {
|
||||||
|
try {
|
||||||
|
originalOptInTracking()
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export const mixpanel_opt_in_tracking = optInTrackingWrapper(
|
||||||
|
mixpanel.opt_in_tracking.bind(mixpanel)
|
||||||
|
)
|
||||||
|
|
||||||
|
// mixpanel_init
|
||||||
|
function initWrapper(originalInit) {
|
||||||
|
return function (token, config = {}) {
|
||||||
|
try {
|
||||||
|
originalInit(token, config)
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export const mixpanel_init = initWrapper(mixpanel.init.bind(mixpanel))
|
||||||
|
|
||||||
|
export const mixpanel_is_loaded = () => {
|
||||||
|
return mixpanel.__loaded
|
||||||
|
}
|
||||||
@@ -77,3 +77,12 @@ export const openLink = (url) => {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const refreshOffline = async () => {
|
||||||
|
return await invoke('plugin:utils|refresh_offline', {})
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns true/false
|
||||||
|
export const isOffline = async () => {
|
||||||
|
return await invoke('plugin:utils|is_offline', {})
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { computed, nextTick, ref, readonly, shallowRef, watch } from 'vue'
|
import { computed, nextTick, ref, readonly, shallowRef, watch, onUnmounted } from 'vue'
|
||||||
import {
|
import {
|
||||||
Pagination,
|
Pagination,
|
||||||
Checkbox,
|
Checkbox,
|
||||||
@@ -31,10 +31,17 @@ import IncompatibilityWarningModal from '@/components/ui/IncompatibilityWarningM
|
|||||||
import { useFetch } from '@/helpers/fetch.js'
|
import { useFetch } from '@/helpers/fetch.js'
|
||||||
import { check_installed, get as getInstance } from '@/helpers/profile.js'
|
import { check_installed, get as getInstance } from '@/helpers/profile.js'
|
||||||
import { convertFileSrc } from '@tauri-apps/api/tauri'
|
import { convertFileSrc } from '@tauri-apps/api/tauri'
|
||||||
|
import { isOffline } from '@/helpers/utils'
|
||||||
|
import { offline_listener } from '@/helpers/events'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
|
|
||||||
|
const offline = ref(await isOffline())
|
||||||
|
const unlistenOffline = await offline_listener((b) => {
|
||||||
|
offline.value = b
|
||||||
|
})
|
||||||
|
|
||||||
const confirmModal = ref(null)
|
const confirmModal = ref(null)
|
||||||
const modInstallModal = ref(null)
|
const modInstallModal = ref(null)
|
||||||
const incompatibilityWarningModal = ref(null)
|
const incompatibilityWarningModal = ref(null)
|
||||||
@@ -498,10 +505,12 @@ const showLoaders = computed(
|
|||||||
instanceContext.value === null) ||
|
instanceContext.value === null) ||
|
||||||
ignoreInstanceLoaders.value
|
ignoreInstanceLoaders.value
|
||||||
)
|
)
|
||||||
|
|
||||||
|
onUnmounted(() => unlistenOffline())
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div ref="searchWrapper" class="search-container">
|
<div v-if="!offline" ref="searchWrapper" class="search-container">
|
||||||
<aside class="filter-panel">
|
<aside class="filter-panel">
|
||||||
<Card v-if="instanceContext" class="small-instance">
|
<Card v-if="instanceContext" class="small-instance">
|
||||||
<router-link :to="`/instance/${encodeURIComponent(instanceContext.path)}`" class="instance">
|
<router-link :to="`/instance/${encodeURIComponent(instanceContext.path)}`" class="instance">
|
||||||
@@ -695,7 +704,7 @@ const showLoaders = computed(
|
|||||||
class="pagination-before"
|
class="pagination-before"
|
||||||
@switch-page="onSearchChange"
|
@switch-page="onSearchChange"
|
||||||
/>
|
/>
|
||||||
<SplashScreen v-if="loading" />
|
<SplashScreen v-if="loading || offline" />
|
||||||
<section v-else class="project-list display-mode--list instance-results" role="list">
|
<section v-else class="project-list display-mode--list instance-results" role="list">
|
||||||
<SearchCard
|
<SearchCard
|
||||||
v-for="result in results.hits"
|
v-for="result in results.hits"
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref, onUnmounted, shallowRef } from 'vue'
|
import { ref, onUnmounted, shallowRef, computed } from 'vue'
|
||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
import RowDisplay from '@/components/RowDisplay.vue'
|
import RowDisplay from '@/components/RowDisplay.vue'
|
||||||
import { list } from '@/helpers/profile.js'
|
import { list } from '@/helpers/profile.js'
|
||||||
import { profile_listener } from '@/helpers/events'
|
import { offline_listener, profile_listener } from '@/helpers/events'
|
||||||
import { useBreadcrumbs } from '@/store/breadcrumbs'
|
import { useBreadcrumbs } from '@/store/breadcrumbs'
|
||||||
import { useFetch } from '@/helpers/fetch.js'
|
import { useFetch } from '@/helpers/fetch.js'
|
||||||
import { handleError } from '@/store/notifications.js'
|
import { handleError } from '@/store/notifications.js'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
|
import { isOffline } from '@/helpers/utils'
|
||||||
|
|
||||||
const featuredModpacks = ref({})
|
const featuredModpacks = ref({})
|
||||||
const featuredMods = ref({})
|
const featuredMods = ref({})
|
||||||
@@ -20,6 +21,8 @@ breadcrumbs.setRootContext({ name: 'Home', link: route.path })
|
|||||||
|
|
||||||
const recentInstances = shallowRef([])
|
const recentInstances = shallowRef([])
|
||||||
|
|
||||||
|
const offline = ref(await isOffline())
|
||||||
|
|
||||||
const getInstances = async () => {
|
const getInstances = async () => {
|
||||||
const profiles = await list(true).catch(handleError)
|
const profiles = await list(true).catch(handleError)
|
||||||
recentInstances.value = Object.values(profiles).sort((a, b) => {
|
recentInstances.value = Object.values(profiles).sort((a, b) => {
|
||||||
@@ -40,32 +43,55 @@ const getFeaturedModpacks = async () => {
|
|||||||
`https://api.modrinth.com/v2/search?facets=[["project_type:modpack"]]&limit=10&index=follows&filters=${filter.value}`,
|
`https://api.modrinth.com/v2/search?facets=[["project_type:modpack"]]&limit=10&index=follows&filters=${filter.value}`,
|
||||||
'featured modpacks'
|
'featured modpacks'
|
||||||
)
|
)
|
||||||
featuredModpacks.value = response.hits
|
if (response) featuredModpacks.value = response.hits
|
||||||
}
|
}
|
||||||
const getFeaturedMods = async () => {
|
const getFeaturedMods = async () => {
|
||||||
const response = await useFetch(
|
const response = await useFetch(
|
||||||
'https://api.modrinth.com/v2/search?facets=[["project_type:mod"]]&limit=10&index=follows',
|
'https://api.modrinth.com/v2/search?facets=[["project_type:mod"]]&limit=10&index=follows',
|
||||||
'featured mods'
|
'featured mods'
|
||||||
)
|
)
|
||||||
featuredMods.value = response.hits
|
if (response) featuredMods.value = response.hits
|
||||||
}
|
}
|
||||||
|
|
||||||
await getInstances()
|
await getInstances()
|
||||||
await Promise.all([getFeaturedModpacks(), getFeaturedMods()])
|
|
||||||
|
|
||||||
const unlisten = await profile_listener(async (e) => {
|
if (!offline.value) {
|
||||||
|
await Promise.all([getFeaturedModpacks(), getFeaturedMods()])
|
||||||
|
}
|
||||||
|
|
||||||
|
const unlistenProfile = await profile_listener(async (e) => {
|
||||||
await getInstances()
|
await getInstances()
|
||||||
if (e.event === 'created' || e.event === 'removed') {
|
if (e.event === 'created' || e.event === 'removed') {
|
||||||
await Promise.all([getFeaturedModpacks(), getFeaturedMods()])
|
await Promise.all([getFeaturedModpacks(), getFeaturedMods()])
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
onUnmounted(() => unlisten())
|
const unlistenOffline = await offline_listener(async (b) => {
|
||||||
|
offline.value = b
|
||||||
|
if (!b) {
|
||||||
|
await Promise.all([getFeaturedModpacks(), getFeaturedMods()])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// computed sums of recentInstances, featuredModpacks, featuredMods, treating them as arrays if they are not
|
||||||
|
const total = computed(() => {
|
||||||
|
return (
|
||||||
|
(recentInstances.value?.length ?? 0) +
|
||||||
|
(featuredModpacks.value?.length ?? 0) +
|
||||||
|
(featuredMods.value?.length ?? 0)
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
unlistenProfile()
|
||||||
|
unlistenOffline()
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="page-container">
|
<div class="page-container">
|
||||||
<RowDisplay
|
<RowDisplay
|
||||||
|
v-if="total > 0"
|
||||||
:instances="[
|
:instances="[
|
||||||
{
|
{
|
||||||
label: 'Jump back in',
|
label: 'Jump back in',
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { onUnmounted, shallowRef } from 'vue'
|
import { onUnmounted, ref, shallowRef } from 'vue'
|
||||||
import GridDisplay from '@/components/GridDisplay.vue'
|
import GridDisplay from '@/components/GridDisplay.vue'
|
||||||
import { list } from '@/helpers/profile.js'
|
import { list } from '@/helpers/profile.js'
|
||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
import { useBreadcrumbs } from '@/store/breadcrumbs'
|
import { useBreadcrumbs } from '@/store/breadcrumbs'
|
||||||
import { profile_listener } from '@/helpers/events.js'
|
import { offline_listener, profile_listener } from '@/helpers/events.js'
|
||||||
import { handleError } from '@/store/notifications.js'
|
import { handleError } from '@/store/notifications.js'
|
||||||
import { Button, PlusIcon } from 'omorphia'
|
import { Button, PlusIcon } from 'omorphia'
|
||||||
import InstanceCreationModal from '@/components/ui/InstanceCreationModal.vue'
|
import InstanceCreationModal from '@/components/ui/InstanceCreationModal.vue'
|
||||||
import { NewInstanceImage } from '@/assets/icons'
|
import { NewInstanceImage } from '@/assets/icons'
|
||||||
|
import { isOffline } from '@/helpers/utils'
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const breadcrumbs = useBreadcrumbs()
|
const breadcrumbs = useBreadcrumbs()
|
||||||
@@ -18,11 +19,19 @@ breadcrumbs.setRootContext({ name: 'Library', link: route.path })
|
|||||||
const profiles = await list(true).catch(handleError)
|
const profiles = await list(true).catch(handleError)
|
||||||
const instances = shallowRef(Object.values(profiles))
|
const instances = shallowRef(Object.values(profiles))
|
||||||
|
|
||||||
const unlisten = await profile_listener(async () => {
|
const offline = ref(await isOffline())
|
||||||
|
const unlistenOffline = await offline_listener((b) => {
|
||||||
|
offline.value = b
|
||||||
|
})
|
||||||
|
|
||||||
|
const unlistenProfile = await profile_listener(async () => {
|
||||||
const profiles = await list(true).catch(handleError)
|
const profiles = await list(true).catch(handleError)
|
||||||
instances.value = Object.values(profiles)
|
instances.value = Object.values(profiles)
|
||||||
})
|
})
|
||||||
onUnmounted(() => unlisten())
|
onUnmounted(() => {
|
||||||
|
unlistenProfile()
|
||||||
|
unlistenOffline()
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -37,7 +46,7 @@ onUnmounted(() => unlisten())
|
|||||||
<NewInstanceImage />
|
<NewInstanceImage />
|
||||||
</div>
|
</div>
|
||||||
<h3>No instances found</h3>
|
<h3>No instances found</h3>
|
||||||
<Button color="primary" @click="$refs.installationModal.show()">
|
<Button color="primary" :disabled="offline" @click="$refs.installationModal.show()">
|
||||||
<PlusIcon />
|
<PlusIcon />
|
||||||
Create new instance
|
Create new instance
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { handleError, useTheming } from '@/store/state'
|
|||||||
import { get, set } from '@/helpers/settings'
|
import { get, set } from '@/helpers/settings'
|
||||||
import { get_max_memory } from '@/helpers/jre'
|
import { get_max_memory } from '@/helpers/jre'
|
||||||
import JavaSelector from '@/components/ui/JavaSelector.vue'
|
import JavaSelector from '@/components/ui/JavaSelector.vue'
|
||||||
import mixpanel from 'mixpanel-browser'
|
import { mixpanel_opt_out_tracking, mixpanel_opt_in_tracking } from '@/helpers/mixpanel'
|
||||||
|
|
||||||
const pageOptions = ['Home', 'Library']
|
const pageOptions = ['Home', 'Library']
|
||||||
|
|
||||||
@@ -30,9 +30,9 @@ watch(
|
|||||||
const setSettings = JSON.parse(JSON.stringify(newSettings))
|
const setSettings = JSON.parse(JSON.stringify(newSettings))
|
||||||
|
|
||||||
if (setSettings.opt_out_analytics) {
|
if (setSettings.opt_out_analytics) {
|
||||||
mixpanel.opt_out_tracking()
|
mixpanel_opt_out_tracking()
|
||||||
} else {
|
} else {
|
||||||
mixpanel.opt_in_tracking()
|
mixpanel_opt_in_tracking()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (setSettings.java_globals.JAVA_8?.path === '') {
|
if (setSettings.java_globals.JAVA_8?.path === '') {
|
||||||
|
|||||||
@@ -86,7 +86,12 @@
|
|||||||
<RouterView v-slot="{ Component }">
|
<RouterView v-slot="{ Component }">
|
||||||
<template v-if="Component">
|
<template v-if="Component">
|
||||||
<Suspense @pending="loadingBar.startLoading()" @resolve="loadingBar.stopLoading()">
|
<Suspense @pending="loadingBar.startLoading()" @resolve="loadingBar.stopLoading()">
|
||||||
<component :is="Component" :instance="instance" :options="options"></component>
|
<component
|
||||||
|
:is="Component"
|
||||||
|
:instance="instance"
|
||||||
|
:options="options"
|
||||||
|
:offline="offline"
|
||||||
|
></component>
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</template>
|
</template>
|
||||||
</RouterView>
|
</RouterView>
|
||||||
@@ -144,13 +149,13 @@ import {
|
|||||||
get_uuids_by_profile_path,
|
get_uuids_by_profile_path,
|
||||||
kill_by_uuid,
|
kill_by_uuid,
|
||||||
} from '@/helpers/process'
|
} from '@/helpers/process'
|
||||||
import { process_listener, profile_listener } from '@/helpers/events'
|
import { offline_listener, process_listener, profile_listener } from '@/helpers/events'
|
||||||
import { useRoute, useRouter } from 'vue-router'
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
import { ref, onUnmounted } from 'vue'
|
import { ref, onUnmounted } from 'vue'
|
||||||
import { handleError, useBreadcrumbs, useLoading } from '@/store/state'
|
import { handleError, useBreadcrumbs, useLoading } from '@/store/state'
|
||||||
import { showProfileInFolder } from '@/helpers/utils.js'
|
import { isOffline, showProfileInFolder } from '@/helpers/utils.js'
|
||||||
import ContextMenu from '@/components/ui/ContextMenu.vue'
|
import ContextMenu from '@/components/ui/ContextMenu.vue'
|
||||||
import mixpanel from 'mixpanel-browser'
|
import { mixpanel_track } from '@/helpers/mixpanel'
|
||||||
import { PackageIcon } from '@/assets/icons/index.js'
|
import { PackageIcon } from '@/assets/icons/index.js'
|
||||||
import ExportModal from '@/components/ui/ExportModal.vue'
|
import ExportModal from '@/components/ui/ExportModal.vue'
|
||||||
import { convertFileSrc } from '@tauri-apps/api/tauri'
|
import { convertFileSrc } from '@tauri-apps/api/tauri'
|
||||||
@@ -170,6 +175,8 @@ breadcrumbs.setContext({
|
|||||||
query: route.query,
|
query: route.query,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const offline = ref(await isOffline())
|
||||||
|
|
||||||
const loadingBar = useLoading()
|
const loadingBar = useLoading()
|
||||||
|
|
||||||
const uuid = ref(null)
|
const uuid = ref(null)
|
||||||
@@ -183,7 +190,7 @@ const startInstance = async (context) => {
|
|||||||
loading.value = false
|
loading.value = false
|
||||||
playing.value = true
|
playing.value = true
|
||||||
|
|
||||||
mixpanel.track('InstanceStart', {
|
mixpanel_track('InstanceStart', {
|
||||||
loader: instance.value.metadata.loader,
|
loader: instance.value.metadata.loader,
|
||||||
game_version: instance.value.metadata.game_version,
|
game_version: instance.value.metadata.game_version,
|
||||||
source: context,
|
source: context,
|
||||||
@@ -211,7 +218,7 @@ const stopInstance = async (context) => {
|
|||||||
uuids.forEach(async (u) => await kill_by_uuid(u).catch(handleError))
|
uuids.forEach(async (u) => await kill_by_uuid(u).catch(handleError))
|
||||||
} else await kill_by_uuid(uuid.value).catch(handleError)
|
} else await kill_by_uuid(uuid.value).catch(handleError)
|
||||||
|
|
||||||
mixpanel.track('InstanceStop', {
|
mixpanel_track('InstanceStop', {
|
||||||
loader: instance.value.metadata.loader,
|
loader: instance.value.metadata.loader,
|
||||||
game_version: instance.value.metadata.game_version,
|
game_version: instance.value.metadata.game_version,
|
||||||
source: context,
|
source: context,
|
||||||
@@ -292,9 +299,14 @@ const unlistenProcesses = await process_listener((e) => {
|
|||||||
if (e.event === 'finished' && uuid.value === e.uuid) playing.value = false
|
if (e.event === 'finished' && uuid.value === e.uuid) playing.value = false
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const unlistenOffline = await offline_listener((b) => {
|
||||||
|
offline.value = b
|
||||||
|
})
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
unlistenProcesses()
|
unlistenProcesses()
|
||||||
unlistenProfiles()
|
unlistenProfiles()
|
||||||
|
unlistenOffline()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
<CheckIcon v-else />
|
<CheckIcon v-else />
|
||||||
{{ copied ? 'Copied' : 'Copy' }}
|
{{ copied ? 'Copied' : 'Copy' }}
|
||||||
</Button>
|
</Button>
|
||||||
<Button color="primary" @click="share">
|
<Button color="primary" :disabled="offline" @click="share">
|
||||||
<ShareIcon />
|
<ShareIcon />
|
||||||
Share
|
Share
|
||||||
</Button>
|
</Button>
|
||||||
@@ -78,6 +78,10 @@ const props = defineProps({
|
|||||||
type: Object,
|
type: Object,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
|
offline: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const logs = ref([])
|
const logs = ref([])
|
||||||
|
|||||||
@@ -93,6 +93,7 @@
|
|||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
class="transparent update"
|
class="transparent update"
|
||||||
|
:disabled="offline"
|
||||||
@click="updateAll()"
|
@click="updateAll()"
|
||||||
@mouseover="selectedOption = 'Update'"
|
@mouseover="selectedOption = 'Update'"
|
||||||
>
|
>
|
||||||
@@ -139,7 +140,7 @@
|
|||||||
</Button>
|
</Button>
|
||||||
</section>
|
</section>
|
||||||
<section v-if="selectedOption === 'Update'" class="options">
|
<section v-if="selectedOption === 'Update'" class="options">
|
||||||
<Button class="transparent" @click="updateAll()">
|
<Button class="transparent" :disabled="offline" @click="updateAll()">
|
||||||
<UpdatedIcon />
|
<UpdatedIcon />
|
||||||
Update all
|
Update all
|
||||||
</Button>
|
</Button>
|
||||||
@@ -181,6 +182,7 @@
|
|||||||
<router-link
|
<router-link
|
||||||
v-if="mod.slug"
|
v-if="mod.slug"
|
||||||
:to="{ path: `/project/${mod.slug}/`, query: { i: props.instance.path } }"
|
:to="{ path: `/project/${mod.slug}/`, query: { i: props.instance.path } }"
|
||||||
|
:disabled="offline"
|
||||||
class="mod-content"
|
class="mod-content"
|
||||||
>
|
>
|
||||||
<Avatar :src="mod.icon" />
|
<Avatar :src="mod.icon" />
|
||||||
@@ -208,7 +210,7 @@
|
|||||||
<Button
|
<Button
|
||||||
v-else
|
v-else
|
||||||
v-tooltip="'Update project'"
|
v-tooltip="'Update project'"
|
||||||
:disabled="!mod.outdated"
|
:disabled="!mod.outdated || offline"
|
||||||
icon-only
|
icon-only
|
||||||
@click="updateProject(mod)"
|
@click="updateProject(mod)"
|
||||||
>
|
>
|
||||||
@@ -345,7 +347,7 @@ import {
|
|||||||
update_project,
|
update_project,
|
||||||
} from '@/helpers/profile.js'
|
} from '@/helpers/profile.js'
|
||||||
import { handleError } from '@/store/notifications.js'
|
import { handleError } from '@/store/notifications.js'
|
||||||
import mixpanel from 'mixpanel-browser'
|
import { mixpanel_track } from '@/helpers/mixpanel'
|
||||||
import { open } from '@tauri-apps/api/dialog'
|
import { open } from '@tauri-apps/api/dialog'
|
||||||
import { listen } from '@tauri-apps/api/event'
|
import { listen } from '@tauri-apps/api/event'
|
||||||
import { convertFileSrc } from '@tauri-apps/api/tauri'
|
import { convertFileSrc } from '@tauri-apps/api/tauri'
|
||||||
@@ -368,6 +370,12 @@ const props = defineProps({
|
|||||||
return {}
|
return {}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
offline: {
|
||||||
|
type: Boolean,
|
||||||
|
default() {
|
||||||
|
return false
|
||||||
|
},
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const projects = ref([])
|
const projects = ref([])
|
||||||
@@ -376,8 +384,9 @@ const showingOptions = ref(false)
|
|||||||
|
|
||||||
const initProjects = (initInstance) => {
|
const initProjects = (initInstance) => {
|
||||||
projects.value = []
|
projects.value = []
|
||||||
|
if (!initInstance || !initInstance.projects) return
|
||||||
for (const [path, project] of Object.entries(initInstance.projects)) {
|
for (const [path, project] of Object.entries(initInstance.projects)) {
|
||||||
if (project.metadata.type === 'modrinth') {
|
if (project.metadata.type === 'modrinth' && !props.offline) {
|
||||||
let owner = project.metadata.members.find((x) => x.role === 'Owner')
|
let owner = project.metadata.members.find((x) => x.role === 'Owner')
|
||||||
projects.value.push({
|
projects.value.push({
|
||||||
path,
|
path,
|
||||||
@@ -442,6 +451,13 @@ watch(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.offline,
|
||||||
|
() => {
|
||||||
|
if (props.instance) initProjects(props.instance)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
const searchFilter = ref('')
|
const searchFilter = ref('')
|
||||||
const selectAll = ref(false)
|
const selectAll = ref(false)
|
||||||
const selectedProjectType = ref('All')
|
const selectedProjectType = ref('All')
|
||||||
@@ -576,7 +592,7 @@ const updateAll = async () => {
|
|||||||
projects.value[project].updating = false
|
projects.value[project].updating = false
|
||||||
}
|
}
|
||||||
|
|
||||||
mixpanel.track('InstanceUpdateAll', {
|
mixpanel_track('InstanceUpdateAll', {
|
||||||
loader: props.instance.metadata.loader,
|
loader: props.instance.metadata.loader,
|
||||||
game_version: props.instance.metadata.game_version,
|
game_version: props.instance.metadata.game_version,
|
||||||
count: setProjects.length,
|
count: setProjects.length,
|
||||||
@@ -601,7 +617,7 @@ const updateProject = async (mod) => {
|
|||||||
mod.version = mod.updateVersion.version_number
|
mod.version = mod.updateVersion.version_number
|
||||||
mod.updateVersion = null
|
mod.updateVersion = null
|
||||||
|
|
||||||
mixpanel.track('InstanceProjectUpdate', {
|
mixpanel_track('InstanceProjectUpdate', {
|
||||||
loader: props.instance.metadata.loader,
|
loader: props.instance.metadata.loader,
|
||||||
game_version: props.instance.metadata.game_version,
|
game_version: props.instance.metadata.game_version,
|
||||||
id: mod.id,
|
id: mod.id,
|
||||||
@@ -628,7 +644,7 @@ const toggleDisableMod = async (mod) => {
|
|||||||
.then((newPath) => {
|
.then((newPath) => {
|
||||||
mod.path = newPath
|
mod.path = newPath
|
||||||
mod.disabled = !mod.disabled
|
mod.disabled = !mod.disabled
|
||||||
mixpanel.track('InstanceProjectDisable', {
|
mixpanel_track('InstanceProjectDisable', {
|
||||||
loader: props.instance.metadata.loader,
|
loader: props.instance.metadata.loader,
|
||||||
game_version: props.instance.metadata.game_version,
|
game_version: props.instance.metadata.game_version,
|
||||||
id: mod.id,
|
id: mod.id,
|
||||||
@@ -649,7 +665,7 @@ const removeMod = async (mod) => {
|
|||||||
await remove_project(props.instance.path, mod.path).catch(handleError)
|
await remove_project(props.instance.path, mod.path).catch(handleError)
|
||||||
projects.value = projects.value.filter((x) => mod.path !== x.path)
|
projects.value = projects.value.filter((x) => mod.path !== x.path)
|
||||||
|
|
||||||
mixpanel.track('InstanceProjectRemove', {
|
mixpanel_track('InstanceProjectRemove', {
|
||||||
loader: props.instance.metadata.loader,
|
loader: props.instance.metadata.loader,
|
||||||
game_version: props.instance.metadata.game_version,
|
game_version: props.instance.metadata.game_version,
|
||||||
id: mod.id,
|
id: mod.id,
|
||||||
@@ -778,7 +794,7 @@ listen('tauri://file-drop', async (event) => {
|
|||||||
}
|
}
|
||||||
initProjects(await get(props.instance.path).catch(handleError))
|
initProjects(await get(props.instance.path).catch(handleError))
|
||||||
}
|
}
|
||||||
mixpanel.track('InstanceCreate', {
|
mixpanel_track('InstanceCreate', {
|
||||||
source: 'FileDrop',
|
source: 'FileDrop',
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -90,7 +90,12 @@
|
|||||||
Allows you to change the mod loader, loader version, or game version of the instance.
|
Allows you to change the mod loader, loader version, or game version of the instance.
|
||||||
</span>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
<button id="edit-versions" class="btn" @click="$refs.changeVersionsModal.show()">
|
<button
|
||||||
|
id="edit-versions"
|
||||||
|
class="btn"
|
||||||
|
@click="$refs.changeVersionsModal.show()"
|
||||||
|
:disabled="offline"
|
||||||
|
>
|
||||||
<EditIcon />
|
<EditIcon />
|
||||||
Edit versions
|
Edit versions
|
||||||
</button>
|
</button>
|
||||||
@@ -291,7 +296,7 @@
|
|||||||
<button
|
<button
|
||||||
id="repair-profile"
|
id="repair-profile"
|
||||||
class="btn btn-highlight"
|
class="btn btn-highlight"
|
||||||
:disabled="repairing"
|
:disabled="repairing || offline"
|
||||||
@click="repairProfile"
|
@click="repairProfile"
|
||||||
>
|
>
|
||||||
<HammerIcon /> Repair
|
<HammerIcon /> Repair
|
||||||
@@ -308,7 +313,7 @@
|
|||||||
<button
|
<button
|
||||||
id="repair-profile"
|
id="repair-profile"
|
||||||
class="btn btn-highlight"
|
class="btn btn-highlight"
|
||||||
:disabled="repairing"
|
:disabled="repairing || offline"
|
||||||
@click="repairModpack"
|
@click="repairModpack"
|
||||||
>
|
>
|
||||||
<DownloadIcon /> Reinstall
|
<DownloadIcon /> Reinstall
|
||||||
@@ -373,7 +378,7 @@ import { open } from '@tauri-apps/api/dialog'
|
|||||||
import { get_fabric_versions, get_forge_versions, get_quilt_versions } from '@/helpers/metadata.js'
|
import { get_fabric_versions, get_forge_versions, get_quilt_versions } from '@/helpers/metadata.js'
|
||||||
import { get_game_versions, get_loaders } from '@/helpers/tags.js'
|
import { get_game_versions, get_loaders } from '@/helpers/tags.js'
|
||||||
import { handleError } from '@/store/notifications.js'
|
import { handleError } from '@/store/notifications.js'
|
||||||
import mixpanel from 'mixpanel-browser'
|
import { mixpanel_track } from '@/helpers/mixpanel'
|
||||||
import { useTheming } from '@/store/theme.js'
|
import { useTheming } from '@/store/theme.js'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
@@ -383,6 +388,10 @@ const props = defineProps({
|
|||||||
type: Object,
|
type: Object,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
|
offline: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const themeStore = useTheming()
|
const themeStore = useTheming()
|
||||||
@@ -403,7 +412,7 @@ const availableGroups = ref([
|
|||||||
async function resetIcon() {
|
async function resetIcon() {
|
||||||
icon.value = null
|
icon.value = null
|
||||||
await edit_icon(props.instance.path, null).catch(handleError)
|
await edit_icon(props.instance.path, null).catch(handleError)
|
||||||
mixpanel.track('InstanceRemoveIcon')
|
mixpanel_track('InstanceRemoveIcon')
|
||||||
}
|
}
|
||||||
|
|
||||||
async function setIcon() {
|
async function setIcon() {
|
||||||
@@ -422,7 +431,7 @@ async function setIcon() {
|
|||||||
icon.value = value
|
icon.value = value
|
||||||
await edit_icon(props.instance.path, icon.value).catch(handleError)
|
await edit_icon(props.instance.path, icon.value).catch(handleError)
|
||||||
|
|
||||||
mixpanel.track('InstanceSetIcon')
|
mixpanel_track('InstanceSetIcon')
|
||||||
}
|
}
|
||||||
|
|
||||||
const globalSettings = await get().catch(handleError)
|
const globalSettings = await get().catch(handleError)
|
||||||
@@ -536,7 +545,7 @@ async function repairProfile() {
|
|||||||
await install(props.instance.path).catch(handleError)
|
await install(props.instance.path).catch(handleError)
|
||||||
repairing.value = false
|
repairing.value = false
|
||||||
|
|
||||||
mixpanel.track('InstanceRepair', {
|
mixpanel_track('InstanceRepair', {
|
||||||
loader: props.instance.metadata.loader,
|
loader: props.instance.metadata.loader,
|
||||||
game_version: props.instance.metadata.game_version,
|
game_version: props.instance.metadata.game_version,
|
||||||
})
|
})
|
||||||
@@ -547,7 +556,7 @@ async function repairModpack() {
|
|||||||
await update_repair_modrinth(props.instance.path).catch(handleError)
|
await update_repair_modrinth(props.instance.path).catch(handleError)
|
||||||
repairing.value = false
|
repairing.value = false
|
||||||
|
|
||||||
mixpanel.track('InstanceRepair', {
|
mixpanel_track('InstanceRepair', {
|
||||||
loader: props.instance.metadata.loader,
|
loader: props.instance.metadata.loader,
|
||||||
game_version: props.instance.metadata.game_version,
|
game_version: props.instance.metadata.game_version,
|
||||||
})
|
})
|
||||||
@@ -559,7 +568,7 @@ async function removeProfile() {
|
|||||||
await remove(props.instance.path).catch(handleError)
|
await remove(props.instance.path).catch(handleError)
|
||||||
removing.value = false
|
removing.value = false
|
||||||
|
|
||||||
mixpanel.track('InstanceRemove', {
|
mixpanel_track('InstanceRemove', {
|
||||||
loader: props.instance.metadata.loader,
|
loader: props.instance.metadata.loader,
|
||||||
game_version: props.instance.metadata.game_version,
|
game_version: props.instance.metadata.game_version,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ import {
|
|||||||
Button,
|
Button,
|
||||||
} from 'omorphia'
|
} from 'omorphia'
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import mixpanel from 'mixpanel-browser'
|
import { mixpanel_track } from '@/helpers/mixpanel'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
project: {
|
project: {
|
||||||
@@ -112,7 +112,7 @@ const nextImage = () => {
|
|||||||
expandedGalleryIndex.value = 0
|
expandedGalleryIndex.value = 0
|
||||||
}
|
}
|
||||||
expandedGalleryItem.value = props.project.gallery[expandedGalleryIndex.value]
|
expandedGalleryItem.value = props.project.gallery[expandedGalleryIndex.value]
|
||||||
mixpanel.track('GalleryImageNext', {
|
mixpanel_track('GalleryImageNext', {
|
||||||
project_id: props.project.id,
|
project_id: props.project.id,
|
||||||
url: expandedGalleryItem.value.url,
|
url: expandedGalleryItem.value.url,
|
||||||
})
|
})
|
||||||
@@ -124,7 +124,7 @@ const previousImage = () => {
|
|||||||
expandedGalleryIndex.value = props.project.gallery.length - 1
|
expandedGalleryIndex.value = props.project.gallery.length - 1
|
||||||
}
|
}
|
||||||
expandedGalleryItem.value = props.project.gallery[expandedGalleryIndex.value]
|
expandedGalleryItem.value = props.project.gallery[expandedGalleryIndex.value]
|
||||||
mixpanel.track('GalleryImagePrevious', {
|
mixpanel_track('GalleryImagePrevious', {
|
||||||
project_id: props.project.id,
|
project_id: props.project.id,
|
||||||
url: expandedGalleryItem.value,
|
url: expandedGalleryItem.value,
|
||||||
})
|
})
|
||||||
@@ -135,7 +135,7 @@ const expandImage = (item, index) => {
|
|||||||
expandedGalleryIndex.value = index
|
expandedGalleryIndex.value = index
|
||||||
zoomedIn.value = false
|
zoomedIn.value = false
|
||||||
|
|
||||||
mixpanel.track('GalleryImageExpand', {
|
mixpanel_track('GalleryImageExpand', {
|
||||||
project_id: props.project.id,
|
project_id: props.project.id,
|
||||||
url: item.url,
|
url: item.url,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -267,7 +267,7 @@ import dayjs from 'dayjs'
|
|||||||
import relativeTime from 'dayjs/plugin/relativeTime'
|
import relativeTime from 'dayjs/plugin/relativeTime'
|
||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
import { ref, shallowRef, watch } from 'vue'
|
import { ref, shallowRef, watch } from 'vue'
|
||||||
import { installVersionDependencies } from '@/helpers/utils'
|
import { installVersionDependencies, isOffline } from '@/helpers/utils'
|
||||||
import InstallConfirmModal from '@/components/ui/InstallConfirmModal.vue'
|
import InstallConfirmModal from '@/components/ui/InstallConfirmModal.vue'
|
||||||
import ModInstallModal from '@/components/ui/ModInstallModal.vue'
|
import ModInstallModal from '@/components/ui/ModInstallModal.vue'
|
||||||
import { useBreadcrumbs } from '@/store/breadcrumbs'
|
import { useBreadcrumbs } from '@/store/breadcrumbs'
|
||||||
@@ -276,7 +276,7 @@ import { useFetch } from '@/helpers/fetch.js'
|
|||||||
import { handleError } from '@/store/notifications.js'
|
import { handleError } from '@/store/notifications.js'
|
||||||
import { convertFileSrc } from '@tauri-apps/api/tauri'
|
import { convertFileSrc } from '@tauri-apps/api/tauri'
|
||||||
import ContextMenu from '@/components/ui/ContextMenu.vue'
|
import ContextMenu from '@/components/ui/ContextMenu.vue'
|
||||||
import mixpanel from 'mixpanel-browser'
|
import { mixpanel_track } from '@/helpers/mixpanel'
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const breadcrumbs = useBreadcrumbs()
|
const breadcrumbs = useBreadcrumbs()
|
||||||
@@ -297,6 +297,8 @@ const instance = ref(null)
|
|||||||
const installed = ref(false)
|
const installed = ref(false)
|
||||||
const installedVersion = ref(null)
|
const installedVersion = ref(null)
|
||||||
|
|
||||||
|
const offline = ref(await isOffline())
|
||||||
|
|
||||||
async function fetchProjectData() {
|
async function fetchProjectData() {
|
||||||
;[
|
;[
|
||||||
data.value,
|
data.value,
|
||||||
@@ -325,7 +327,7 @@ async function fetchProjectData() {
|
|||||||
: null
|
: null
|
||||||
}
|
}
|
||||||
|
|
||||||
await fetchProjectData()
|
if (!offline.value) await fetchProjectData()
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => route.params.id,
|
() => route.params.id,
|
||||||
@@ -392,7 +394,7 @@ async function install(version) {
|
|||||||
data.value.icon_url
|
data.value.icon_url
|
||||||
).catch(handleError)
|
).catch(handleError)
|
||||||
|
|
||||||
mixpanel.track('PackInstall', {
|
mixpanel_track('PackInstall', {
|
||||||
id: data.value.id,
|
id: data.value.id,
|
||||||
version_id: queuedVersionData.id,
|
version_id: queuedVersionData.id,
|
||||||
title: data.value.title,
|
title: data.value.title,
|
||||||
@@ -434,7 +436,7 @@ async function install(version) {
|
|||||||
await installMod(instance.value.path, selectedVersion.id).catch(handleError)
|
await installMod(instance.value.path, selectedVersion.id).catch(handleError)
|
||||||
await installVersionDependencies(instance.value, queuedVersionData)
|
await installVersionDependencies(instance.value, queuedVersionData)
|
||||||
installedVersion.value = selectedVersion.id
|
installedVersion.value = selectedVersion.id
|
||||||
mixpanel.track('ProjectInstall', {
|
mixpanel_track('ProjectInstall', {
|
||||||
loader: instance.value.metadata.loader,
|
loader: instance.value.metadata.loader,
|
||||||
game_version: instance.value.metadata.game_version,
|
game_version: instance.value.metadata.game_version,
|
||||||
id: data.value.id,
|
id: data.value.id,
|
||||||
@@ -458,7 +460,7 @@ async function install(version) {
|
|||||||
await installMod(instance.value.path, queuedVersionData.id).catch(handleError)
|
await installMod(instance.value.path, queuedVersionData.id).catch(handleError)
|
||||||
await installVersionDependencies(instance.value, queuedVersionData)
|
await installVersionDependencies(instance.value, queuedVersionData)
|
||||||
installedVersion.value = queuedVersionData.id
|
installedVersion.value = queuedVersionData.id
|
||||||
mixpanel.track('ProjectInstall', {
|
mixpanel_track('ProjectInstall', {
|
||||||
loader: instance.value.metadata.loader,
|
loader: instance.value.metadata.loader,
|
||||||
game_version: instance.value.metadata.game_version,
|
game_version: instance.value.metadata.game_version,
|
||||||
id: data.value.id,
|
id: data.value.id,
|
||||||
|
|||||||
@@ -23,3 +23,7 @@ export const handleError = (err) => {
|
|||||||
})
|
})
|
||||||
console.error(err)
|
console.error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const handleMixpanelError = (err) => {
|
||||||
|
console.error(err)
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user