Fix signout and redesign auth step
All checks were successful
Build Docker image / build (push) Successful in 3m14s
Build Docker image / deploy (push) Successful in 4s
Build Docker image / verify (push) Successful in 1m8s

This commit is contained in:
Roman Krček
2025-07-30 16:54:17 +02:00
parent 923300e49b
commit 1e96668e48
2 changed files with 144 additions and 81 deletions

View File

@@ -1,80 +1,123 @@
<script lang="ts">
import { currentStep } from '$lib/stores.js';
import { isSignedIn, handleSignIn, handleSignOut, isGoogleApiReady } from '$lib/google';
function proceed() {
currentStep.set(2);
}
import { currentStep } from '$lib/stores.js';
import { isSignedIn, handleSignIn, handleSignOut, isGoogleApiReady } from '$lib/google';
import Navigator from './subcomponents/Navigator.svelte';
</script>
<div class="p-6">
<div class="max-w-md mx-auto text-center">
<div class="mb-6">
<div class="mx-auto mb-4 w-16 h-16 bg-blue-100 rounded-full flex items-center justify-center">
<svg class="w-8 h-8 text-blue-600" fill="currentColor" viewBox="0 0 24 24">
<path d="M12.017 11.215c-3.573-2.775-9.317-.362-9.317 4.686C2.7 21.833 6.943 24 12.017 24c5.076 0 9.319-2.167 9.319-8.099 0-5.048-5.744-7.461-9.319-4.686z"/>
<path d="M20.791 5.016c-1.395-1.395-3.61-1.428-5.024-.033l-1.984 1.984v-.002L12.017 8.73 10.25 6.965l-1.984-1.984c-1.414-1.395-3.629-1.362-5.024.033L1.498 6.758c-1.438 1.438-1.438 3.77 0 5.208l1.744 1.744c1.395 1.395 3.61 1.428 5.024.033l1.984-1.984v.002L12.017 9.996l1.767 1.765 1.984 1.984c1.414 1.395 3.629 1.362 5.024-.033l1.744-1.744c1.438-1.438 1.438-3.77 0-5.208L20.791 5.016z"/>
</svg>
</div>
<h2 class="text-xl font-semibold text-gray-900 mb-2">
Connect to Google
</h2>
<p class="text-sm text-gray-700 leading-relaxed mb-6">
Sign in with your Google account to access your Google Sheets and Google Drive for photo downloads.
</p>
<div class="text-xs text-gray-500 mb-6 space-y-1">
<p>Required permissions:</p>
<p>• View your Google Spreadsheets</p>
<p>• View and manage the files in your Google Drive</p>
</div>
</div>
<div class="mb-6">
<h2 class="mb-2 text-xl font-semibold text-gray-900">Connect to Google</h2>
<p class="text-sm text-gray-700">
Sign in with your Google account to access your Google Sheets and Google Drive for photo
downloads.
</p>
</div>
{#if $isSignedIn}
<!-- Authenticated state -->
<div class="bg-green-50 border border-green-300 rounded-lg p-4 mb-4">
<div class="flex items-center justify-center mb-2">
<svg class="w-5 h-5 text-green-600 mr-2" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"/>
</svg>
<span class="text-sm font-medium text-green-800">Authenticated</span>
</div>
<p class="text-sm text-green-800 mb-3">
You are signed in.
</p>
<div class="flex space-x-3 justify-center">
<button
onclick={proceed}
class="bg-blue-600 text-white px-4 py-2 rounded-lg text-sm font-medium hover:bg-blue-700"
>
Continue →
</button>
<button
onclick={handleSignOut}
class="text-red-600 hover:text-red-700 px-4 py-2 text-sm font-medium"
>
Sign Out
</button>
</div>
</div>
{:else}
<!-- Unauthenticated state -->
<button
onclick={handleSignIn}
disabled={!$isGoogleApiReady}
class="w-full bg-blue-600 text-white px-4 py-3 rounded-lg font-semibold hover:bg-blue-700 transition-colors disabled:bg-gray-400 disabled:cursor-not-allowed"
>
{#if $isGoogleApiReady}
Sign In with Google
{:else}
Loading Google API...
{/if}
</button>
{/if}
</div>
<div class="grid gap-8 md:grid-cols-2">
<!-- Left Column: Information -->
<div class="space-y-6 text-gray-700">
<div>
<h4 class="font-semibold text-gray-900">Google Sheets Integration</h4>
<p class="text-sm">
Seamlessly import your data without the hassle of manual copy-pasting.
</p>
</div>
<div>
<h4 class="font-semibold text-gray-900">Google Drive Access</h4>
<p class="text-sm">
Automatically download photos for your cards directly from your Google Drive.
</p>
</div>
<div>
<h4 class="font-semibold text-gray-900">Privacy & Security</h4>
<p class="text-sm">
Your data is processed entirely in your browser. Nothing is ever uploaded to or stored on
our servers. We only request read-only access. All of the data is then removed from your browser
when the work is finished.
</p>
</div>
</div>
<!-- Right Column: Action -->
<div
class="flex flex-col items-center justify-center rounded-lg border border-gray-200 bg-gray-50 p-8"
>
{#if $isSignedIn}
<!-- Authenticated state -->
<div class="text-center">
<div class="flex items-center justify-center gap-2">
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-6 w-6 text-green-500"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
stroke-width="2"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg>
<h3 class="text-lg font-medium text-gray-900">Successfully Connected</h3>
</div>
<p class="mb-6 mt-2 text-sm text-gray-600">You are signed in and ready to proceed.</p>
<button
onclick={handleSignOut}
class="rounded-lg border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-100"
>
Sign Out
</button>
</div>
{:else}
<!-- Unauthenticated state -->
<div class="w-full text-center">
<h3 class="text-lg font-medium text-gray-900">Ready to Connect?</h3>
<p class="mb-6 text-sm text-gray-600">
Click the button below to sign in with your Google account.
</p>
<button
onclick={handleSignIn}
disabled={!$isGoogleApiReady}
class="flex w-full items-center justify-center rounded-lg bg-blue-600 px-4 py-3 font-semibold text-white transition-colors hover:bg-blue-700 disabled:cursor-not-allowed disabled:bg-gray-400"
>
{#if $isGoogleApiReady}
Sign In with Google
{:else}
<svg
class="mr-2 h-5 w-5 animate-spin text-white"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
>
<circle
class="opacity-25"
cx="12"
cy="12"
r="10"
stroke="currentColor"
stroke-width="4"
></circle>
<path
class="opacity-75"
fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
></path>
</svg>
Loading Google API...
{/if}
</button>
</div>
{/if}
</div>
</div> <div class="mt-8">
<Navigator
canProceed={$isSignedIn}
{currentStep}
textBack="Back to Splash"
textForwardDisabled="Sign in to continue"
textForwardEnabled="Continue to Sheet Search"
/>
</div>
</div>

View File

@@ -81,13 +81,33 @@ export function handleSignIn() {
}
export function handleSignOut() {
const token = gapi.client.getToken();
if (token !== null) {
google.accounts.oauth2.revoke(token.access_token, () => {
gapi.client.setToken(null);
isSignedIn.set(false);
});
}
const savedToken = localStorage.getItem(TOKEN_KEY);
if (savedToken) {
try {
const tokenData = JSON.parse(savedToken);
if (tokenData.access_token) {
google.accounts.oauth2.revoke(tokenData.access_token, () => {
console.log('User token revoked.');
});
}
} catch (e) {
console.error('Error parsing token from localStorage', e);
}
}
// Disables automatic sign-in on the next page load.
google.accounts.id.disableAutoSelect();
// Clear gapi client token
gapi.client.setToken(null);
// Clear token from localStorage
localStorage.removeItem(TOKEN_KEY);
// Update signed in state
isSignedIn.set(false);
console.log('User signed out.');
}
export async function searchSheets(query: string) {