-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
11 changed files
with
1,182 additions
and
54 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
61 changes: 61 additions & 0 deletions
61
packages/svelte/src/lib/components/gun/account/AccountAvatar.svelte
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
<script lang="ts"> | ||
import { useAvatar } from '$lib/gun/avatar'; | ||
import { useUser } from '$lib/gun/user'; | ||
import { onMount } from 'svelte'; | ||
export let pub: string = ''; | ||
export let size: number = 42; | ||
export let border: number = 2; | ||
const { user } = useUser(); | ||
let isOwnAvatar = false; | ||
let fileInput: HTMLInputElement; | ||
$: ({ avatar, blink, uploadAvatar, uploadStatus } = useAvatar(pub, size)); | ||
onMount(() => { | ||
isOwnAvatar = $user.pub === pub; | ||
console.log("isOwnAvatar:", isOwnAvatar, "$user.pub:", $user.pub, "pub:", pub); | ||
}); | ||
async function handleFileChange(event: Event) { | ||
const file = (event.target as HTMLInputElement).files?.[0]; | ||
if (file) await uploadAvatar(file); | ||
} | ||
</script> | ||
|
||
<div class="flex flex-col items-center justify-center relative my-10 w-fit mx-auto"> | ||
{#if pub} | ||
<img | ||
class="border rounded-full overflow-hidden transition duration-500 ease-out" | ||
style="border-color: {$blink ? 'accent' : 'transparent'}; border-width: {border}px;" | ||
width={size} | ||
height={size} | ||
src={$avatar} | ||
alt="Avatar" | ||
/> | ||
{#if isOwnAvatar} | ||
<button | ||
class="absolute bottom-0 right-0 bg-accent text-white rounded-full p-1 hover:bg-accent/80" | ||
on:click={() => fileInput.click()} | ||
> | ||
<div class="i-la-camera" style="font-size: {size / 3}px;"></div> | ||
</button> | ||
<input | ||
bind:this={fileInput} | ||
type="file" | ||
accept="image/*" | ||
on:change={handleFileChange} | ||
class="hidden" | ||
/> | ||
{#if $uploadStatus} | ||
<div class="mt-2 text-sm text-gray-600">{$uploadStatus}</div> | ||
{/if} | ||
{/if} | ||
{:else} | ||
<div class="pb-2 px-1" style="font-size: {size}px;"> | ||
<div class="i-la-user"></div> | ||
</div> | ||
{/if} | ||
<slot></slot> | ||
</div> |
66 changes: 66 additions & 0 deletions
66
packages/svelte/src/lib/components/gun/account/AccountProfile.svelte
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
<script lang="ts"> | ||
import { useAccount } from "$lib/gun/account"; | ||
import { onMount } from "svelte"; | ||
import urlRegex from "url-regex"; | ||
export let pub: string | undefined = "URlViuNviAEET30fV4QgPDmGB6DMk4ZxGr-MDBVaPM4.5KaWNBz8Nvq5_yUcmJvQc6E2miOnu-z4uL7TLXbpsaw"; | ||
const { account } = useAccount(pub); | ||
function isLink(text: string): boolean { | ||
return urlRegex({ exact: true }).test(text); | ||
} | ||
let lastPulse = 0; | ||
$: if ($account && $account.pulse !== lastPulse) { | ||
lastPulse = $account.pulse; | ||
console.log("Pulse updated:", $account.pulse); | ||
} | ||
onMount(() => { | ||
console.log("AccountProfile mounted", $account); | ||
}); | ||
</script> | ||
|
||
{#if $account} | ||
<div class="flex flex-col break-all"> | ||
<h2 class="text-xl font-bold mb-2">Account Information</h2> | ||
{#each ['pub', 'Color', 'pulse', 'Blink', 'lastSeen'] as field} | ||
<div class="p-2 flex items-center"> | ||
<div class="mr-2 font-bold" style="flex: 1 1 60px">{field}</div> | ||
<div class="flex items-center ml-1" style="flex: 1 1 180px"> | ||
{#if field === 'Color'} | ||
<div class="p-0" style="background-color: {$account.color}; width: 20px; height: 20px; border-radius: 50%;"></div> | ||
<span class="ml-2">{$account.color}</span> | ||
{:else if field === 'Blink'} | ||
<div class="p-0">{$account.blink ? 'Yes' : 'No'}</div> | ||
{:else} | ||
<div class="p-0 break-all">{$account[field.toLowerCase().replace(' ', '')]}</div> | ||
{/if} | ||
</div> | ||
</div> | ||
{/each} | ||
|
||
<h2 class="text-xl font-bold mt-4 mb-2">Profile</h2> | ||
{#if $account.profile} | ||
{#each Object.entries($account.profile) as [field, content]} | ||
<div class="p-2 flex items-center"> | ||
<div class="mr-2 font-bold" style="flex: 1 1 60px">{field}</div> | ||
<div class="flex items-center ml-1" style="flex: 1 1 180px"> | ||
{#if !isLink(content as string)} | ||
<div class="p-0">{content}</div> | ||
{:else} | ||
<a class="font-bold underline" href={content as string} target="_blank"> | ||
{content} | ||
</a> | ||
{/if} | ||
</div> | ||
</div> | ||
{/each} | ||
{:else} | ||
<p>Nessun dato del profilo disponibile.</p> | ||
{/if} | ||
</div> | ||
{:else} | ||
<p>Caricamento account...</p> | ||
{/if} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,191 @@ | ||
import { writable, derived, get } from "svelte/store"; | ||
import ms from "ms"; | ||
import { useGun } from "./gun"; | ||
import { useUser } from "./user"; | ||
import SEA from "gun/sea"; | ||
import { useColor } from "./colors"; | ||
|
||
const TIMEOUT = 10000; | ||
const colorDeep = useColor("deep"); | ||
|
||
interface Profile { | ||
name?: string; | ||
first_name?: string; | ||
last_name?: string; | ||
birth_day?: string; | ||
} | ||
|
||
interface Account { | ||
pub?: string; | ||
color?: string; | ||
pulse?: number; | ||
blink?: boolean; | ||
profile?: Profile; | ||
petname?: string; | ||
db?: any; | ||
lastSeen?: string | number; | ||
} | ||
|
||
export function useAccount(pubKey: string) { | ||
console.log("useAccount called with:", pubKey); | ||
const gun = useGun(); | ||
const { user } = useUser(); | ||
console.log("user store:", user); | ||
const pub = writable(pubKey); | ||
|
||
const accountStore = writable<Account>({ | ||
pub: pubKey, | ||
color: colorDeep.hex(pubKey), | ||
profile: { name: "" }, | ||
pulse: 0, | ||
blink: false, | ||
db: gun.user(pubKey), | ||
lastSeen: "offline", | ||
}); | ||
|
||
const pulseStore = writable(0); | ||
|
||
const calculateLastSeen = (pulse: number) => { | ||
if (!pulse) return "offline"; | ||
const timeDiff = Date.now() - pulse; | ||
if (timeDiff <= TIMEOUT) return "online"; | ||
return ms(timeDiff); | ||
}; | ||
|
||
|
||
|
||
// Aggiorna lastSeen ogni secondo | ||
const lastSeenInterval = setInterval(() => { | ||
accountStore.update(acc => ({ | ||
...acc, | ||
lastSeen: calculateLastSeen(get(pulseStore)), | ||
})); | ||
}, 1000); | ||
|
||
const account = derived([pub, user, accountStore, pulseStore], ([$pub, $user, $account, $pulse]) => { | ||
if (!$pub || !$user) return $account; | ||
|
||
gun | ||
.user($pub) | ||
.get("pulse") | ||
.on(d => { | ||
pulseStore.set(d); | ||
accountStore.update(acc => ({ ...acc, blink: !acc.blink, pulse: d })); | ||
}); | ||
|
||
if ($user.is) { | ||
gun | ||
.user() | ||
.get("petnames") | ||
.get($pub) | ||
.on(async d => { | ||
const decrypted = await $user.decrypt(d); | ||
accountStore.update(acc => ({ ...acc, petname: decrypted })); | ||
}); | ||
} | ||
|
||
gun | ||
.user($pub) | ||
.get("profile") | ||
.map() | ||
.on((data, key) => { | ||
accountStore.update(acc => ({ | ||
...acc, | ||
profile: { ...acc.profile, [key]: data }, | ||
})); | ||
}); | ||
|
||
return $account; | ||
}); | ||
|
||
return { | ||
account, | ||
setPetname, | ||
destroy: () => clearInterval(lastSeenInterval), // Funzione per pulire l'intervallo | ||
}; | ||
} | ||
|
||
export async function setPetname(pub: string, name: string): Promise<void> { | ||
const { user } = useUser(); | ||
if (!get(user).is) return; | ||
const gun = useGun(); | ||
const enc = await get(user).encrypt(name); | ||
gun.user().get("petnames").get(pub).put(enc); | ||
} | ||
|
||
export function useAvatar(pub: string, size: number) { | ||
const gun = useGun(); | ||
const { user } = useUser(); | ||
const avatar = writable(""); | ||
const blink = writable(false); | ||
const uploadStatus = writable(""); | ||
|
||
const updateAvatar = () => { | ||
console.log("Updating avatar for pub:", pub); | ||
gun | ||
.user(pub) | ||
.get("avatar") | ||
.once(data => { | ||
console.log("Avatar data:", data); | ||
if (data) { | ||
avatar.set(data); | ||
} else { | ||
avatar.set(`https://avatars.dicebear.com/api/identicon/${pub}.svg?size=${size}`); | ||
} | ||
}); | ||
}; | ||
|
||
updateAvatar(); | ||
|
||
gun | ||
.user(pub) | ||
.get("pulse") | ||
.on(() => { | ||
blink.update(b => !b); | ||
}); | ||
|
||
const uploadAvatar = async (file: File) => { | ||
console.log("Starting avatar upload"); | ||
uploadStatus.set("Iniziando il caricamento..."); | ||
|
||
const currentUser = get(user); | ||
if (!currentUser || !currentUser.is || currentUser.pub !== pub) { | ||
console.error("Utente non autenticato o non autorizzato"); | ||
uploadStatus.set("Errore: Utente non autenticato o non autorizzato"); | ||
return; | ||
} | ||
|
||
try { | ||
const reader = new FileReader(); | ||
reader.onload = async e => { | ||
const base64 = e.target?.result as string; | ||
uploadStatus.set("Salvando avatar..."); | ||
|
||
gun | ||
.user() | ||
.get("avatar") | ||
.put(base64, ack => { | ||
if (ack.err) { | ||
console.error("Errore nel salvare l'avatar:", ack.err); | ||
uploadStatus.set("Errore nel salvare l'avatar"); | ||
} else { | ||
console.log("Avatar salvato con successo"); | ||
uploadStatus.set("Avatar caricato con successo"); | ||
updateAvatar(); | ||
} | ||
}); | ||
}; | ||
reader.readAsDataURL(file); | ||
} catch (error) { | ||
console.error("Errore durante il caricamento dell'avatar:", error); | ||
uploadStatus.set("Errore durante il caricamento. Riprova."); | ||
} | ||
}; | ||
|
||
return { | ||
avatar: derived(avatar, $a => $a), | ||
blink: derived(blink, $b => $b), | ||
uploadAvatar, | ||
uploadStatus: derived(uploadStatus, $s => $s), | ||
}; | ||
} |
Oops, something went wrong.