diff --git a/.release-please-manifest.json b/.release-please-manifest.json index e338e65..2a400b5 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,4 +1,4 @@ { "app": "0.0.0", - ".": "1.7.4" + ".": "1.7.5" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 0ae7f88..db1c5f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## [1.7.5](https://github.com/hackclub/hackatime-desktop/compare/app-v1.7.4...app-v1.7.5) (2025-10-24) + + +### 🐛 Bugfixes + +* eliminate duplicate discord definitions ([472867d](https://github.com/hackclub/hackatime-desktop/commit/472867d58f306d70241b07b5f3135c34055ad555)) +* make option enabled by default ([d037bdb](https://github.com/hackclub/hackatime-desktop/commit/d037bdb529a4a078a5d10f7daa68698da9726e5f)) + ## [1.7.4](https://github.com/hackclub/hackatime-desktop/compare/app-v1.7.3...app-v1.7.4) (2025-10-24) diff --git a/package.json b/package.json index ecfd797..7b9693c 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "desktop", "private": true, - "version": "1.7.4", + "version": "1.7.5", "type": "module", "packageManager": "pnpm@10.18.0", "scripts": { diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 1c8dcfb..689960e 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -17,6 +17,19 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if", + "getrandom 0.3.3", + "once_cell", + "version_check", + "zerocopy", +] + [[package]] name = "aho-corasick" version = "1.1.3" @@ -95,6 +108,19 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "async-compression" +version = "0.4.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93c1f86859c1af3d514fa19e8323147ff10ea98684e6c7b307912509f50e67b2" +dependencies = [ + "compression-codecs", + "compression-core", + "futures-core", + "pin-project-lite", + "tokio", +] + [[package]] name = "async-executor" version = "1.13.3" @@ -533,6 +559,23 @@ dependencies = [ "memchr", ] +[[package]] +name = "compression-codecs" +version = "0.4.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "680dc087785c5230f8e8843e2e57ac7c1c90488b6a91b88caa265410568f441b" +dependencies = [ + "compression-core", + "flate2", + "memchr", +] + +[[package]] +name = "compression-core" +version = "0.4.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75984efb6ed102a0d42db99afb6c1948f0380d1d91808d5529916e6c08b49d8d" + [[package]] name = "concurrent-queue" version = "2.5.0" @@ -724,6 +767,19 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "cssparser" +version = "0.31.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b3df4f93e5fbbe73ec01ec8d3f68bba73107993a5b1e7519273c32db9b0d5be" +dependencies = [ + "cssparser-macros", + "dtoa-short", + "itoa", + "phf 0.11.3", + "smallvec", +] + [[package]] name = "cssparser-macros" version = "0.6.1" @@ -838,6 +894,7 @@ dependencies = [ "open", "rand 0.9.2", "reqwest", + "scraper", "serde", "serde_json", "sha2", @@ -1024,6 +1081,12 @@ version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" +[[package]] +name = "ego-tree" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12a0bb14ac04a9fcf170d0bbbef949b44cc492f4452bd20c095636956f653642" + [[package]] name = "either" version = "1.15.0" @@ -1500,6 +1563,15 @@ dependencies = [ "version_check", ] +[[package]] +name = "getopts" +version = "0.2.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe4fbac503b8d1f88e6676011885f34b7174f46e59956bba534ba83abded4df" +dependencies = [ + "unicode-width", +] + [[package]] name = "getrandom" version = "0.1.16" @@ -1800,6 +1872,20 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "html5ever" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bea68cab48b8459f17cf1c944c67ddc572d272d9f2b274140f223ecb1da4a3b7" +dependencies = [ + "log", + "mac", + "markup5ever 0.11.0", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "html5ever" version = "0.29.1" @@ -1808,7 +1894,7 @@ checksum = "3b7410cae13cbc75623c98ac4cbfd1f0bedddf3227afc24f370cf0f50a44a11c" dependencies = [ "log", "mac", - "markup5ever", + "markup5ever 0.14.1", "match_token", ] @@ -2258,10 +2344,10 @@ version = "0.8.8-speedreader" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02cb977175687f33fa4afa0c95c112b987ea1443e5a51c8f8ff27dc618270cc2" dependencies = [ - "cssparser", - "html5ever", + "cssparser 0.29.6", + "html5ever 0.29.1", "indexmap 2.11.4", - "selectors", + "selectors 0.24.0", ] [[package]] @@ -2380,6 +2466,20 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" +[[package]] +name = "markup5ever" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2629bb1404f3d34c2e921f21fd34ba00b206124c81f65c50b43b6aaefeb016" +dependencies = [ + "log", + "phf 0.10.1", + "phf_codegen 0.10.0", + "string_cache", + "string_cache_codegen", + "tendril", +] + [[package]] name = "markup5ever" version = "0.14.1" @@ -3179,6 +3279,16 @@ dependencies = [ "phf_shared 0.8.0", ] +[[package]] +name = "phf_codegen" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd" +dependencies = [ + "phf_generator 0.10.0", + "phf_shared 0.10.0", +] + [[package]] name = "phf_codegen" version = "0.11.3" @@ -3742,9 +3852,11 @@ version = "0.12.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d429f34c8092b2d42c7c93cec323bb4adeb7c67698f70839adec842ec10c7ceb" dependencies = [ + "async-compression", "base64 0.22.1", "bytes", "encoding_rs", + "futures-channel", "futures-core", "futures-util", "h2", @@ -3983,6 +4095,23 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "scraper" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c95a930e03325234c18c7071fd2b60118307e025d6fff3e12745ffbf63a3d29c" +dependencies = [ + "ahash", + "cssparser 0.31.2", + "ego-tree", + "getopts", + "html5ever 0.26.0", + "once_cell", + "selectors 0.25.0", + "smallvec", + "tendril", +] + [[package]] name = "security-framework" version = "2.11.1" @@ -4013,14 +4142,33 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c37578180969d00692904465fb7f6b3d50b9a2b952b87c23d0e2e5cb5013416" dependencies = [ "bitflags 1.3.2", - "cssparser", + "cssparser 0.29.6", "derive_more", "fxhash", "log", "phf 0.8.0", "phf_codegen 0.8.0", "precomputed-hash", - "servo_arc", + "servo_arc 0.2.0", + "smallvec", +] + +[[package]] +name = "selectors" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4eb30575f3638fc8f6815f448d50cb1a2e255b0897985c8c59f4d37b72a07b06" +dependencies = [ + "bitflags 2.9.4", + "cssparser 0.31.2", + "derive_more", + "fxhash", + "log", + "new_debug_unreachable", + "phf 0.10.1", + "phf_codegen 0.10.0", + "precomputed-hash", + "servo_arc 0.3.0", "smallvec", ] @@ -4204,6 +4352,15 @@ dependencies = [ "stable_deref_trait", ] +[[package]] +name = "servo_arc" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d036d71a959e00c77a63538b90a6c2390969f9772b096ea837205c6bd0491a44" +dependencies = [ + "stable_deref_trait", +] + [[package]] name = "sha1" version = "0.10.6" @@ -5084,7 +5241,7 @@ dependencies = [ "ctor", "dunce", "glob", - "html5ever", + "html5ever 0.29.1", "http", "infer", "json-patch", @@ -5622,6 +5779,12 @@ version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" +[[package]] +name = "unicode-width" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" + [[package]] name = "untrusted" version = "0.9.0" @@ -6572,7 +6735,7 @@ dependencies = [ "dunce", "gdkx11", "gtk", - "html5ever", + "html5ever 0.29.1", "http", "javascriptcore-rs", "jni", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 977f4bd..3ba0759 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -27,7 +27,8 @@ serde = { version = "1", features = ["derive"] } serde_json = "1" open = "5" urlencoding = "2" -reqwest = { version = "0.12", features = ["json"] } +reqwest = { version = "0.12", features = ["json", "gzip", "blocking", "rustls-tls"] } +scraper = "0.17" tokio = { version = "1", features = ["full"] } sqlx = { version = "0.8", features = ["runtime-tokio-rustls", "sqlite", "chrono", "uuid"] } chrono = { version = "0.4", features = ["serde"] } diff --git a/src-tauri/icons/128x128.png b/src-tauri/icons/128x128.png index eba97df..8e21309 100644 Binary files a/src-tauri/icons/128x128.png and b/src-tauri/icons/128x128.png differ diff --git a/src-tauri/icons/128x128@2x.png b/src-tauri/icons/128x128@2x.png index 55269fb..5e28b33 100644 Binary files a/src-tauri/icons/128x128@2x.png and b/src-tauri/icons/128x128@2x.png differ diff --git a/src-tauri/icons/32x32.png b/src-tauri/icons/32x32.png index a4088cb..7d8970b 100644 Binary files a/src-tauri/icons/32x32.png and b/src-tauri/icons/32x32.png differ diff --git a/src-tauri/icons/Square107x107Logo.png b/src-tauri/icons/Square107x107Logo.png index 405be80..025488e 100644 Binary files a/src-tauri/icons/Square107x107Logo.png and b/src-tauri/icons/Square107x107Logo.png differ diff --git a/src-tauri/icons/Square142x142Logo.png b/src-tauri/icons/Square142x142Logo.png index 6a99d31..2efaa5f 100644 Binary files a/src-tauri/icons/Square142x142Logo.png and b/src-tauri/icons/Square142x142Logo.png differ diff --git a/src-tauri/icons/Square150x150Logo.png b/src-tauri/icons/Square150x150Logo.png index d35d494..4bd3c14 100644 Binary files a/src-tauri/icons/Square150x150Logo.png and b/src-tauri/icons/Square150x150Logo.png differ diff --git a/src-tauri/icons/Square284x284Logo.png b/src-tauri/icons/Square284x284Logo.png index 4b0554d..e1daaab 100644 Binary files a/src-tauri/icons/Square284x284Logo.png and b/src-tauri/icons/Square284x284Logo.png differ diff --git a/src-tauri/icons/Square30x30Logo.png b/src-tauri/icons/Square30x30Logo.png index 5980730..2911db8 100644 Binary files a/src-tauri/icons/Square30x30Logo.png and b/src-tauri/icons/Square30x30Logo.png differ diff --git a/src-tauri/icons/Square310x310Logo.png b/src-tauri/icons/Square310x310Logo.png index feb005a..e6591db 100644 Binary files a/src-tauri/icons/Square310x310Logo.png and b/src-tauri/icons/Square310x310Logo.png differ diff --git a/src-tauri/icons/Square44x44Logo.png b/src-tauri/icons/Square44x44Logo.png index a30822b..57a06a3 100644 Binary files a/src-tauri/icons/Square44x44Logo.png and b/src-tauri/icons/Square44x44Logo.png differ diff --git a/src-tauri/icons/Square71x71Logo.png b/src-tauri/icons/Square71x71Logo.png index 30c4f85..c3d58b4 100644 Binary files a/src-tauri/icons/Square71x71Logo.png and b/src-tauri/icons/Square71x71Logo.png differ diff --git a/src-tauri/icons/StoreLogo.png b/src-tauri/icons/StoreLogo.png index d9de142..1379136 100644 Binary files a/src-tauri/icons/StoreLogo.png and b/src-tauri/icons/StoreLogo.png differ diff --git a/src-tauri/icons/icon.png b/src-tauri/icons/icon.png index e7ba4a2..fe4aa53 100644 Binary files a/src-tauri/icons/icon.png and b/src-tauri/icons/icon.png differ diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index f0beaec..9f8f29b 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -5,6 +5,8 @@ use once_cell::sync::Lazy; use std::sync::Mutex; use tauri_plugin_deep_link::DeepLinkExt; +use scraper::{Html, Selector}; +use serde::Serialize; mod auth; mod config; @@ -26,6 +28,94 @@ pub use config::ApiConfig; pub use discord_rpc::{DiscordRpcService}; pub use session::SessionState; + +#[derive(Serialize, Clone, Debug)] +pub struct LeaderboardEntry { + pub name: String, + pub avatar: String, + pub time: String, +} + + +#[tauri::command] +fn get_leaderboard_data() -> Result, String> { + let url = "https://hackatime.hackclub.com/leaderboards/entries?period_type=daily&scope=global&page=1"; + + let client = reqwest::blocking::Client::builder() + .user_agent("Mozilla/5.0") + .build() + .map_err(|e| e.to_string())?; + + let res = client.get(url).send().map_err(|e| e.to_string())?; + + let html = res.text().map_err(|e| e.to_string())?; + + let document = Html::parse_document(&html); + + let user_selector = Selector::parse("a.text-blue").unwrap(); + let user_info_selector = Selector::parse("div.user-info").unwrap(); + let img_selector = Selector::parse("img").unwrap(); + + let mut results = Vec::new(); + + for element in document.select(&user_selector) { + let username = element.text().collect::>().join("").trim().to_string(); + + if !element.value().attr("href").unwrap_or("").starts_with("/@") { + continue; + } + + // AVATAR + let mut avatar = "Yok".to_string(); + + if let Some(parent) = element.ancestors().find(|n| { + n.value() + .as_element() + .map(|e| e.name() == "div" && e.has_class("user-info", scraper::CaseSensitivity::CaseSensitive)) + .unwrap_or(false) + }) { + let parent = scraper::ElementRef::wrap(parent).unwrap(); + + if let Some(img) = parent.select(&img_selector).next() { + if let Some(src) = img.value().attr("src") { + avatar = src.to_string(); + } + } + } + + let mut time = "0s".to_string(); + + if let Some(flex_parent) = element.ancestors().find(|n| { + n.value() + .as_element() + .map(|e| e.name() == "div" && e.has_class("flex-1", scraper::CaseSensitivity::CaseSensitive)) + .unwrap_or(false) + }) { + let flex_parent = scraper::ElementRef::wrap(flex_parent).unwrap(); + + for sibling in flex_parent.next_siblings() { + if let Some(el) = scraper::ElementRef::wrap(sibling) { + let text = el.text().collect::>().join("").trim().to_string(); + + if text.contains("h") || text.contains("m") || text.contains("s") { + time = text; + break; + } + } + } + } + + results.push(LeaderboardEntry { + name: username, + avatar, + time, + }); + } + + Ok(results) +} + + #[tauri::command] fn greet(name: &str) -> String { format!("Hello, {}! You've been greeted from Rust!", name) @@ -41,6 +131,9 @@ fn get_current_os() -> String { std::env::consts::OS.to_string() } + + + #[derive(Clone, serde::Serialize)] struct LogEntry { ts: i64, @@ -115,6 +208,7 @@ pub fn run() { entity: None, }))) .invoke_handler(tauri::generate_handler![ + get_leaderboard_data, greet, get_app_version, get_current_os, diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 40151a7..41681d0 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "https://schema.tauri.app/config/2", "productName": "Hackatime Desktop", - "version": "1.7.4", + "version": "1.7.5", "identifier": "com.hackclub.hackatime", "build": { "beforeDevCommand": "pnpm dev", diff --git a/src/App.vue b/src/App.vue index 8dae1bc..2a01624 100644 --- a/src/App.vue +++ b/src/App.vue @@ -48,6 +48,12 @@ if (!(window as any).__hackatimeErrorHandlerSet) { }); } +interface LeaderboardEntry { + name: string; + avatar: string; + time: string; +} + interface AuthState { is_authenticated: boolean; access_token: string | null; @@ -86,6 +92,8 @@ const oauthUrl = ref(null); const nextPresenceFetchAllowedAt = ref(0); const lastPresenceFetchAt = ref(0); const currentOs = ref(null); +const leaderboard = ref([]); +const isLoading_board = ref(true); const currentPage = ref<'home' | 'projects' | 'statistics' | 'settings'>('home'); @@ -130,13 +138,35 @@ const weeklyChartData = computed(() => { })); }); +const fetchLeaderboard = async () => { + try { + isLoading.value = true; + + + const data = await invoke('get_leaderboard_data'); + + + console.log("TS: Rust'tan Gelen Ham Veri:", data); + + + leaderboard.value = data; + + } catch (error) { + console.error("Liderlik tablosu hatası:", error); + } finally { + isLoading_board.value = false; + } +}; onMounted(async () => { + fetchLeaderboard(); await loadAuthState(); await loadApiConfig(); await loadHackatimeInfo(); await loadCurrentOs(); + setInterval(fetchLeaderboard, 1000 * 60 * 5); + try { const appVersion = await invoke("get_app_version") as string; currentVersion.value = appVersion; @@ -758,9 +788,9 @@ async function handleInstallNow() { -
+
-
+

leaderboard @@ -770,10 +800,42 @@ async function handleInstallNow() { global

-
-

- Coming Soon... -

+
+ + + + + + + + + + + + + + + + + + +
#UserTime
+ {{ index === 0 ? '🥇' : index === 1 ? '🥈' : index === 2 ? '🥉' : `#${index + 1}` }} + +
+ + + {{ entry.name }} + +
+
+ + {{ entry.time }} + +
@@ -864,6 +926,14 @@ async function handleInstallNow() { height: 100vh; } +::-webkit-scrollbar { + width: 6px; +} +::-webkit-scrollbar-thumb { + background: rgba(255,255,255,0.2); + border-radius: 10px; +} + .pushable { border-radius: 12px; border: none; diff --git a/src/assets/bird-illustration.svg b/src/assets/bird-illustration.svg index 2738669..f71f070 100644 --- a/src/assets/bird-illustration.svg +++ b/src/assets/bird-illustration.svg @@ -1,3 +1,16 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:af237154ecbef3d1f7bb8922a56f56f958ab74c9e208b6e432d914f22ec05b00 -size 2916 + + + + + + + + + + + + + + + + diff --git a/src/assets/decorative-lines.svg b/src/assets/decorative-lines.svg index 349de65..30ed595 100644 --- a/src/assets/decorative-lines.svg +++ b/src/assets/decorative-lines.svg @@ -1,3 +1,14 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:3b003ac873f82c3754fd182e13389997fa0c3dace3c22871a3742b630ab40f0d -size 1154 + + + + + + + + + + + + + + diff --git a/src/assets/suits-icons.svg b/src/assets/suits-icons.svg index 56776c6..5d8caa7 100644 --- a/src/assets/suits-icons.svg +++ b/src/assets/suits-icons.svg @@ -1,3 +1,8 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:63784807e210f4d80316421f2bcbf3df267875ce186dd9c8578dd7ce2cec7a9a -size 1553 + + + + + + + + diff --git a/src/main.ts b/src/main.ts index bcac316..20567d8 100644 --- a/src/main.ts +++ b/src/main.ts @@ -6,7 +6,6 @@ import { usePostHog } from './composables/usePostHog' const app = createApp(App) - const { posthog } = usePostHog()