mirror of
https://github.com/System-End/hackatime-desktop.git
synced 2026-04-19 19:45:09 +00:00
fix: solve windows deeplink error
This commit is contained in:
parent
fa6acaf41f
commit
ad3e27c859
6 changed files with 87 additions and 11 deletions
16
src-tauri/Cargo.lock
generated
16
src-tauri/Cargo.lock
generated
|
|
@ -836,6 +836,7 @@ dependencies = [
|
|||
"tauri-plugin-deep-link",
|
||||
"tauri-plugin-opener",
|
||||
"tauri-plugin-process",
|
||||
"tauri-plugin-single-instance",
|
||||
"tauri-plugin-updater",
|
||||
"tokio",
|
||||
"urlencoding",
|
||||
|
|
@ -4915,6 +4916,21 @@ dependencies = [
|
|||
"tauri-plugin",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-single-instance"
|
||||
version = "2.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fb9cac815bf11c4a80fb498666bcdad66d65b89e3ae24669e47806febb76389c"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tauri",
|
||||
"thiserror 2.0.17",
|
||||
"tracing",
|
||||
"windows-sys 0.60.2",
|
||||
"zbus",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-updater"
|
||||
version = "2.9.0"
|
||||
|
|
|
|||
|
|
@ -10,6 +10,20 @@
|
|||
],
|
||||
"permissions": [
|
||||
"core:default",
|
||||
{
|
||||
"identifier": "opener:allow-open-url",
|
||||
"allow": [
|
||||
{
|
||||
"url": "https://*"
|
||||
},
|
||||
{
|
||||
"url": "http://*"
|
||||
},
|
||||
{
|
||||
"url": "hackatime://*"
|
||||
}
|
||||
]
|
||||
},
|
||||
"opener:default",
|
||||
"deep-link:default",
|
||||
"updater:default",
|
||||
|
|
|
|||
|
|
@ -82,8 +82,8 @@ pub async fn get_auth_state(
|
|||
pub async fn authenticate_with_rails(
|
||||
api_config: crate::config::ApiConfig,
|
||||
pkce_state: State<'_, Arc<tauri::async_runtime::Mutex<Option<PkceState>>>>,
|
||||
_app_handle: tauri::AppHandle,
|
||||
) -> Result<(), String> {
|
||||
app_handle: tauri::AppHandle,
|
||||
) -> Result<String, String> {
|
||||
|
||||
let callback_url = "hackatime://auth/callback";
|
||||
|
||||
|
|
@ -106,12 +106,16 @@ pub async fn authenticate_with_rails(
|
|||
urlencoding::encode(&code_challenge)
|
||||
);
|
||||
|
||||
if let Err(e) = open::that(&auth_url) {
|
||||
return Err(format!("Failed to open authentication URL: {}", e));
|
||||
// Use Tauri's opener plugin for better cross-platform support
|
||||
use tauri_plugin_opener::OpenerExt;
|
||||
if let Err(e) = app_handle.opener().open_url(&auth_url, None::<&str>) {
|
||||
push_log("error", "backend", format!("Failed to open authentication URL: {}", e));
|
||||
// Return the URL so the frontend can show a fallback button
|
||||
return Ok(auth_url);
|
||||
}
|
||||
|
||||
push_log("info", "backend", "OAuth authentication URL opened in browser. Waiting for callback...".to_string());
|
||||
Ok(())
|
||||
Ok(auth_url)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
|
|
|
|||
|
|
@ -61,7 +61,6 @@
|
|||
]
|
||||
}
|
||||
},
|
||||
"single-instance": {},
|
||||
"updater": {
|
||||
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDdERjg4QTFCNTJFMDk0MUQKUldRZGxPQlNHNHI0ZlRkMDN0MGI1MnllY1dUVStZalV3dVdhcTFuREx5SGtBc0txQ2xnTWs3WU4K",
|
||||
"endpoints": [
|
||||
|
|
|
|||
27
src/App.vue
27
src/App.vue
|
|
@ -58,6 +58,7 @@ const sessionStats = ref<any>(null);
|
|||
const presenceData = ref<any>(null);
|
||||
const presenceRefreshInterval = ref<number | null>(null);
|
||||
const presenceFetchInProgress = ref(false);
|
||||
const oauthUrl = ref<string | null>(null);
|
||||
const nextPresenceFetchAllowedAt = ref<number>(0);
|
||||
const lastPresenceFetchAt = ref<number>(0);
|
||||
|
||||
|
|
@ -343,10 +344,11 @@ function stopPresenceRefresh() {
|
|||
|
||||
async function authenticate() {
|
||||
isLoading.value = true;
|
||||
oauthUrl.value = null;
|
||||
try {
|
||||
await invoke("authenticate_with_rails", { apiConfig: apiConfig.value });
|
||||
|
||||
alert(`OAuth authentication opened in browser!\n\nInstructions:\n1. Complete the OAuth flow in your browser\n2. The app will automatically handle the callback\n3. If the callback doesn't work, you can manually paste the authorization code from the URL\n\nFor manual entry:\n- Copy the 'code' parameter from the callback URL\n- Use the "Direct OAuth" field below to paste it`);
|
||||
const url = await invoke("authenticate_with_rails", { apiConfig: apiConfig.value });
|
||||
oauthUrl.value = url as string;
|
||||
console.log("OAuth URL:", url);
|
||||
} catch (error) {
|
||||
console.error("Authentication failed:", error);
|
||||
alert("Authentication failed: " + (error instanceof Error ? error.message : String(error)));
|
||||
|
|
@ -355,6 +357,23 @@ async function authenticate() {
|
|||
}
|
||||
}
|
||||
|
||||
async function openOAuthUrlManually() {
|
||||
if (oauthUrl.value) {
|
||||
try {
|
||||
const { openUrl } = await import("@tauri-apps/plugin-opener");
|
||||
await openUrl(oauthUrl.value);
|
||||
} catch (error) {
|
||||
console.error("Failed to open URL manually:", error);
|
||||
try {
|
||||
await navigator.clipboard.writeText(oauthUrl.value);
|
||||
alert("Failed to open link. URL copied to clipboard!");
|
||||
} catch (clipError) {
|
||||
alert(`Failed to open link. Please visit: ${oauthUrl.value}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function logout() {
|
||||
try {
|
||||
stopPresenceRefresh();
|
||||
|
|
@ -580,9 +599,11 @@ async function checkForUpdatesAndInstall() {
|
|||
:weeklyChartData="weeklyChartData"
|
||||
:isLoading="isLoading"
|
||||
:isDevMode="isDevMode"
|
||||
:oauthUrl="oauthUrl"
|
||||
v-model:directOAuthToken="directOAuthToken"
|
||||
@authenticate="authenticate"
|
||||
@handleDirectOAuthAuth="handleDirectOAuthAuth"
|
||||
@openOAuthUrlManually="openOAuthUrlManually"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
<div v-if="!authState.is_authenticated" class="flex items-center justify-center min-h-96">
|
||||
<div class="text-center max-w-md">
|
||||
<h3 class="text-2xl mb-4 text-text-primary">Welcome to Hackatime</h3>
|
||||
<p class="text-text-secondary mb-8 leading-relaxed">Connect to your KubeTime account to start tracking your coding time.</p>
|
||||
<p class="text-text-secondary mb-8 leading-relaxed">Connect to your Hackatime account to start tracking your coding time.</p>
|
||||
|
||||
<!-- Production authentication (deep link) -->
|
||||
<template v-if="!isDevMode">
|
||||
|
|
@ -12,9 +12,17 @@
|
|||
:disabled="isLoading"
|
||||
class="bg-accent-primary text-white border-0 px-8 py-4 rounded-xl text-base font-medium cursor-pointer transition-all duration-200 my-4 w-full hover:bg-accent-secondary hover:shadow-card-hover disabled:bg-text-muted disabled:cursor-not-allowed disabled:transform-none"
|
||||
>
|
||||
{{ isLoading ? 'Opening Login...' : 'Login with KubeTime' }}
|
||||
{{ isLoading ? 'Opening Login...' : 'Login with Hackatime' }}
|
||||
</button>
|
||||
<p class="text-text-secondary text-sm mt-2">This will open your browser for OAuth authentication.</p>
|
||||
|
||||
<button
|
||||
v-if="oauthUrl && !isLoading"
|
||||
@click="openOAuthUrlManually"
|
||||
class="bg-bg-secondary text-text-primary border border-border-secondary px-6 py-3 rounded-xl text-sm font-medium cursor-pointer transition-all duration-200 mt-4 w-full hover:bg-bg-tertiary hover:border-accent-primary"
|
||||
>
|
||||
Link didn't open? Click here
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<!-- Development authentication options -->
|
||||
|
|
@ -28,6 +36,14 @@
|
|||
{{ isLoading ? 'Opening Login...' : 'Open Browser Login' }}
|
||||
</button>
|
||||
<p class="text-text-secondary text-sm mt-2">This will open your browser for OAuth authentication.</p>
|
||||
|
||||
<button
|
||||
v-if="oauthUrl && !isLoading"
|
||||
@click="openOAuthUrlManually"
|
||||
class="bg-bg-secondary text-text-primary border border-border-secondary px-6 py-3 rounded-xl text-sm font-medium cursor-pointer transition-all duration-200 mt-4 w-full hover:bg-bg-tertiary hover:border-accent-primary"
|
||||
>
|
||||
Link didn't open? Click here
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="mt-8 pt-8 border-t border-border-primary text-left">
|
||||
|
|
@ -190,11 +206,13 @@ defineProps<{
|
|||
isLoading: boolean;
|
||||
isDevMode: boolean;
|
||||
directOAuthToken: string;
|
||||
oauthUrl: string | null;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
authenticate: [];
|
||||
handleDirectOAuthAuth: [];
|
||||
openOAuthUrlManually: [];
|
||||
'update:directOAuthToken': [value: string];
|
||||
}>();
|
||||
|
||||
|
|
@ -230,6 +248,10 @@ async function handleDirectOAuthAuth() {
|
|||
emit('handleDirectOAuthAuth');
|
||||
}
|
||||
|
||||
async function openOAuthUrlManually() {
|
||||
emit('openOAuthUrlManually');
|
||||
}
|
||||
|
||||
function getWeeklyCardStyle(percentage: number): string {
|
||||
const positiveColor = { r: 52, g: 148, b: 230 };
|
||||
const negativeColor = { r: 236, g: 110, b: 173 };
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue