Compare commits

...

3 Commits

Author SHA1 Message Date
HorizonCode cf86dba5c4 chore: rework titlebar buttons 2026-02-21 23:31:43 +01:00
HorizonCode 540c653399 fix: reconnect presence after pipe was closed 2026-02-21 23:18:01 +01:00
HorizonCode c9b8d9f599 fix: respect reducedAnimations 2026-02-21 22:59:19 +01:00
6 changed files with 85 additions and 45 deletions
+1
View File
@@ -1011,6 +1011,7 @@ version = "3.1.2"
dependencies = [ dependencies = [
"base64 0.22.1", "base64 0.22.1",
"discord-rich-presence", "discord-rich-presence",
"futures-util",
"hardware-id", "hardware-id",
"md5", "md5",
"once_cell", "once_cell",
+2 -1
View File
@@ -32,15 +32,16 @@ reqwest = { version = "0.13.1", features = ["json", "stream", "query"] }
md5 = "0.8.0" md5 = "0.8.0"
tokio = { version = "1.48.0", features = ["full"] } tokio = { version = "1.48.0", features = ["full"] }
open = "5.3.3" open = "5.3.3"
windows-sys = "0.61.2"
discord-rich-presence = "1.0.0" discord-rich-presence = "1.0.0"
once_cell = "1.21.3" once_cell = "1.21.3"
futures-util = "0.3"
[target.'cfg(windows)'.dependencies] [target.'cfg(windows)'.dependencies]
winreg = "0.55.0" winreg = "0.55.0"
winapi = { version = "0.3", features = ["winuser", "wincrypt", "memoryapi", "winbase", "dpapi"] } winapi = { version = "0.3", features = ["winuser", "wincrypt", "memoryapi", "winbase", "dpapi"] }
base64 = "0.22.1" base64 = "0.22.1"
widestring = "1.2.1" widestring = "1.2.1"
windows-sys = "0.61.2"
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies] [target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies]
tauri-plugin-single-instance = "2.3.6" tauri-plugin-single-instance = "2.3.6"
+29 -11
View File
@@ -2,6 +2,7 @@ use discord_rich_presence::{
DiscordIpc, DiscordIpcClient, DiscordIpc, DiscordIpcClient,
activity::{Activity, Assets, Button, Timestamps}, activity::{Activity, Assets, Button, Timestamps},
}; };
use futures_util::future::BoxFuture;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use std::sync::Mutex as StdMutex; use std::sync::Mutex as StdMutex;
use std::time::{SystemTime, UNIX_EPOCH}; use std::time::{SystemTime, UNIX_EPOCH};
@@ -24,6 +25,7 @@ enum PresenceCommand {
Disconnect(oneshot::Sender<()>), Disconnect(oneshot::Sender<()>),
UpdateData(PresenceData), UpdateData(PresenceData),
IsConnected(oneshot::Sender<bool>), IsConnected(oneshot::Sender<bool>),
Tick,
} }
struct PresenceActor { struct PresenceActor {
@@ -58,7 +60,7 @@ impl PresenceActor {
} }
async fn run(&mut self) { async fn run(&mut self) {
let mut update_interval = interval(Duration::from_millis(2500)); let mut update_interval = interval(Duration::from_secs(5));
loop { loop {
tokio::select! { tokio::select! {
@@ -74,18 +76,22 @@ impl PresenceActor {
PresenceCommand::IsConnected(responder) => { PresenceCommand::IsConnected(responder) => {
let _ = responder.send(self.client.is_some()); let _ = responder.send(self.client.is_some());
} }
} PresenceCommand::Tick => {
}
_ = update_interval.tick() => {
if self.client.is_some() { if self.client.is_some() {
self.handle_update().await; self.handle_update().await;
} }
} }
} }
} }
_ = update_interval.tick() => {
let _ = PRESENCE_TX.send(PresenceCommand::Tick).await;
}
}
}
} }
async fn handle_connect(&mut self, responder: oneshot::Sender<bool>) { fn handle_connect(&mut self, responder: oneshot::Sender<bool>) -> BoxFuture<'_, ()> {
Box::pin(async move {
if self.client.is_some() { if self.client.is_some() {
let _ = responder.send(true); let _ = responder.send(true);
return; return;
@@ -101,11 +107,21 @@ impl PresenceActor {
let _ = responder.send(true); let _ = responder.send(true);
} }
Err(e) => { Err(e) => {
eprintln!("Failed to create Discord client: {:?}", e); eprintln!("Failed to connect to Discord: {:?}. Retrying in 5s...", e);
let tx = PRESENCE_TX.clone();
tokio::spawn(async move {
tokio::time::sleep(Duration::from_secs(5)).await;
let (resp_tx, _) = oneshot::channel();
let _ = tx.send(PresenceCommand::Connect(resp_tx)).await;
});
if !responder.is_closed() {
let _ = responder.send(false); let _ = responder.send(false);
} }
} }
} }
})
}
async fn handle_disconnect(&mut self, responder: oneshot::Sender<()>) { async fn handle_disconnect(&mut self, responder: oneshot::Sender<()>) {
if let Some(mut client) = self.client.take() { if let Some(mut client) = self.client.take() {
@@ -143,12 +159,14 @@ impl PresenceActor {
Button::new("Join EZPPFarm", "https://ez-pp.farm/discord"), Button::new("Join EZPPFarm", "https://ez-pp.farm/discord"),
]); ]);
if let Err(e) = client.set_activity(activity).map_err(|e| e.to_string()) { if let Err(e) = client.set_activity(activity) {
eprintln!("Failed to set activity, disconnecting: {:?}", e); eprintln!("Failed to set activity, disconnecting: {:?}", e);
if let Some(mut client) = self.client.take() { self.client = None;
let _ = client.clear_activity();
let _ = client.close(); let (tx, _) = oneshot::channel();
} self.handle_connect(tx).await;
} else {
println!("Actor: Presence updated successfully.");
} }
} }
} }
@@ -1,5 +1,7 @@
<script lang="ts"> <script lang="ts">
import Logo from '$assets/logo.png'; import Logo from '$assets/logo.png';
import { reduceAnimations } from '@/userSettings';
import { fade } from 'svelte/transition';
//TODO: maybe dynamic background images fetched from ezpp? //TODO: maybe dynamic background images fetched from ezpp?
@@ -37,15 +39,15 @@
<div <div
class="relative h-full w-full from-0% to-90% bg-gradient-to-b from-transparent to-black z-50" class="relative h-full w-full from-0% to-90% bg-gradient-to-b from-transparent to-black z-50"
></div> ></div>
{#each streaks as s} {#each streaks as s, index (index)}
<div <div
class="streak drop-shadow-lg" class="streak drop-shadow-lg transition-opacity duration-1000"
style=" style="
width:{s.width}px; width:{s.width}px;
height:{s.height}px; height:{s.height}px;
left:{s.x}%; left:{s.x}%;
top:{s.y}%; top:{s.y}%;
opacity:{s.opacity}; opacity:{$reduceAnimations ? 0 : s.opacity};
animation-duration:{s.duration}s; animation-duration:{s.duration}s;
animation-delay:-{s.delay}s; animation-delay:-{s.delay}s;
" "
+24 -21
View File
@@ -8,6 +8,7 @@
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import { launcherVersion } from '@/global'; import { launcherVersion } from '@/global';
import Badge from '../badge/badge.svelte'; import Badge from '../badge/badge.svelte';
import { reduceAnimations } from '@/userSettings';
onMount(() => { onMount(() => {
const appWindow = getCurrentWindow(); const appWindow = getCurrentWindow();
@@ -20,22 +21,39 @@
</script> </script>
<div data-tauri-drag-region class="titlebar z-[100]"> <div data-tauri-drag-region class="titlebar z-[100]">
<div class="titlebar-button rounded transition-colors duration-75" id="titlebar-minimize"> <div class="relative flex items-center justify-center p-1 group" id="titlebar-minimize">
<Minimize size={14} /> <div
class="absolute inset-0 bg-background rounded
scale-50 opacity-0
group-hover:opacity-100
group-hover:scale-100
{$reduceAnimations ? '' : 'transition-all duration-200 ease-out'}
origin-center"
></div>
<Minimize class="z-10 pointer-events-none" size={14} />
</div> </div>
<div class="titlebar-button close rounded transition-colors duration-75" id="titlebar-close"> <div class="relative flex items-center justify-center p-1 group" id="titlebar-close">
<Close size={14} /> <div
class="absolute inset-0 bg-red-500 rounded
scale-50 opacity-0
group-hover:opacity-100
group-hover:scale-100
{$reduceAnimations ? '' : 'transition-all duration-200 ease-out'}
origin-center"
></div>
<Close class="z-10 pointer-events-none" size={14} />
</div> </div>
</div> </div>
<style lang="scss"> <style lang="scss">
.titlebar { .titlebar {
height: 35px; height: 35px;
/* background: #040612; */
user-select: none; user-select: none;
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;
gap: 10px; gap: 5px;
align-items: center; align-items: center;
position: fixed; position: fixed;
top: 0; top: 0;
@@ -45,19 +63,4 @@
padding-right: 5px; padding-right: 5px;
pointer-events: auto; pointer-events: auto;
} }
.titlebar-button {
display: inline-flex;
justify-content: center;
align-items: center;
width: 25px;
height: 25px;
user-select: none;
-webkit-user-select: none;
}
.titlebar-button:hover {
&.close {
background: #c22e2e;
}
background: #2d3049;
}
</style> </style>
+23 -8
View File
@@ -948,8 +948,13 @@
{#if selectedView === 'home'} {#if selectedView === 'home'}
<div <div
class="flex flex-col-reverse h-full" class="flex flex-col-reverse h-full"
in:fly={{ duration: 400, delay: 400, y: 5, opacity: 0 }} in:fly={{
out:fly={{ duration: 400, y: -5, opacity: 0 }} duration: $reduceAnimations ? 0 : 400,
delay: $reduceAnimations ? 0 : 400,
y: 5,
opacity: 0,
}}
out:fly={{ duration: $reduceAnimations ? 0 : 400, y: -5, opacity: 0 }}
> >
<div class="relative z-10 px-8 py-4 flex items-center justify-between"> <div class="relative z-10 px-8 py-4 flex items-center justify-between">
<div class="flex items-center gap-4"> <div class="flex items-center gap-4">
@@ -1070,8 +1075,8 @@
{#if $currentUserInfo} {#if $currentUserInfo}
<div <div
class="m-1 w-72 h-48 bg-black/40 backdrop-blur-sm rounded-md border border-black/40 flex flex-col items-center p-3" class="m-1 w-72 h-48 bg-black/40 backdrop-blur-sm rounded-md border border-black/40 flex flex-col items-center p-3"
in:fly={{ duration: 400, y: 5, opacity: 0 }} in:fly={{ duration: $reduceAnimations ? 0 : 400, y: 5, opacity: 0 }}
out:fly={{ duration: 400, y: -5, opacity: 0 }} out:fly={{ duration: $reduceAnimations ? 0 : 400, y: -5, opacity: 0 }}
> >
<Select.Root <Select.Root
type="single" type="single"
@@ -1223,8 +1228,13 @@
{:else if selectedView === 'settings'} {:else if selectedView === 'settings'}
<div <div
class="h-[100vh] w-full flex flex-col items-center justify-center" class="h-[100vh] w-full flex flex-col items-center justify-center"
in:fly={{ duration: 400, delay: 400, y: 5, opacity: 0 }} in:fly={{
out:fly={{ duration: 400, y: -5, opacity: 0 }} duration: $reduceAnimations ? 0 : 400,
delay: $reduceAnimations ? 0 : 400,
y: 5,
opacity: 0,
}}
out:fly={{ duration: $reduceAnimations ? 0 : 400, y: -5, opacity: 0 }}
> >
<div class="p-8 w-full"> <div class="p-8 w-full">
<div class="bg-black/40 backdrop-blur-sm py-8 px-6 rounded-lg"> <div class="bg-black/40 backdrop-blur-sm py-8 px-6 rounded-lg">
@@ -1425,8 +1435,13 @@
{:else if selectedView === 'login'} {:else if selectedView === 'login'}
<div <div
class="h-[100vh] w-full flex flex-col items-center justify-center" class="h-[100vh] w-full flex flex-col items-center justify-center"
in:fly={{ duration: 400, delay: 400, y: 5, opacity: 0 }} in:fly={{
out:fly={{ duration: 400, y: -5, opacity: 0 }} duration: $reduceAnimations ? 0 : 400,
delay: $reduceAnimations ? 0 : 400,
y: 5,
opacity: 0,
}}
out:fly={{ duration: $reduceAnimations ? 0 : 400, y: -5, opacity: 0 }}
> >
<div class="p-8 px-52 w-full"> <div class="p-8 px-52 w-full">
<form onsubmit={performLogin} class="bg-black/40 backdrop-blur-sm rounded-lg p-8"> <form onsubmit={performLogin} class="bg-black/40 backdrop-blur-sm rounded-lg p-8">