diff --git a/.prettierrc b/.prettierrc index 8103a0b..8855237 100644 --- a/.prettierrc +++ b/.prettierrc @@ -3,7 +3,10 @@ "singleQuote": true, "trailingComma": "none", "printWidth": 100, - "plugins": ["prettier-plugin-svelte", "prettier-plugin-tailwindcss"], + "plugins": [ + "prettier-plugin-svelte", + "prettier-plugin-tailwindcss" + ], "overrides": [ { "files": "*.svelte", diff --git a/bun.lock b/bun.lock index aff54d0..4622ea0 100644 --- a/bun.lock +++ b/bun.lock @@ -1,5 +1,6 @@ { "lockfileVersion": 1, + "configVersion": 0, "workspaces": { "": { "name": "mini-x-front", @@ -15,9 +16,9 @@ "@tailwindcss/vite": "^4.1.14", "bits-ui": "^2.11.0", "clsx": "^2.1.1", - "prettier": "^3.6.2", + "prettier": "^3.7.4", "prettier-plugin-svelte": "^3.4.0", - "prettier-plugin-tailwindcss": "^0.7.1", + "prettier-plugin-tailwindcss": "^0.7.2", "svelte": "^5.41.0", "svelte-check": "^4.3.3", "tailwind-merge": "^3.3.1", @@ -368,11 +369,11 @@ "postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="], - "prettier": ["prettier@3.6.2", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ=="], + "prettier": ["prettier@3.7.4", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA=="], "prettier-plugin-svelte": ["prettier-plugin-svelte@3.4.0", "", { "peerDependencies": { "prettier": "^3.0.0", "svelte": "^3.2.0 || ^4.0.0-next.0 || ^5.0.0-next.0" } }, "sha512-pn1ra/0mPObzqoIQn/vUTR3ZZI6UuZ0sHqMK5x2jMLGrs53h0sXhkVuDcrlssHwIMk7FYrMjHBPoUSyyEEDlBQ=="], - "prettier-plugin-tailwindcss": ["prettier-plugin-tailwindcss@0.7.1", "", { "peerDependencies": { "@ianvs/prettier-plugin-sort-imports": "*", "@prettier/plugin-hermes": "*", "@prettier/plugin-oxc": "*", "@prettier/plugin-pug": "*", "@shopify/prettier-plugin-liquid": "*", "@trivago/prettier-plugin-sort-imports": "*", "@zackad/prettier-plugin-twig": "*", "prettier": "^3.0", "prettier-plugin-astro": "*", "prettier-plugin-css-order": "*", "prettier-plugin-jsdoc": "*", "prettier-plugin-marko": "*", "prettier-plugin-multiline-arrays": "*", "prettier-plugin-organize-attributes": "*", "prettier-plugin-organize-imports": "*", "prettier-plugin-sort-imports": "*", "prettier-plugin-svelte": "*" }, "optionalPeers": ["@ianvs/prettier-plugin-sort-imports", "@prettier/plugin-hermes", "@prettier/plugin-oxc", "@prettier/plugin-pug", "@shopify/prettier-plugin-liquid", "@trivago/prettier-plugin-sort-imports", "@zackad/prettier-plugin-twig", "prettier-plugin-astro", "prettier-plugin-css-order", "prettier-plugin-jsdoc", "prettier-plugin-marko", "prettier-plugin-multiline-arrays", "prettier-plugin-organize-attributes", "prettier-plugin-organize-imports", "prettier-plugin-sort-imports", "prettier-plugin-svelte"] }, "sha512-Bzv1LZcuiR1Sk02iJTS1QzlFNp/o5l2p3xkopwOrbPmtMeh3fK9rVW5M3neBQzHq+kGKj/4LGQMTNcTH4NGPtQ=="], + "prettier-plugin-tailwindcss": ["prettier-plugin-tailwindcss@0.7.2", "", { "peerDependencies": { "@ianvs/prettier-plugin-sort-imports": "*", "@prettier/plugin-hermes": "*", "@prettier/plugin-oxc": "*", "@prettier/plugin-pug": "*", "@shopify/prettier-plugin-liquid": "*", "@trivago/prettier-plugin-sort-imports": "*", "@zackad/prettier-plugin-twig": "*", "prettier": "^3.0", "prettier-plugin-astro": "*", "prettier-plugin-css-order": "*", "prettier-plugin-jsdoc": "*", "prettier-plugin-marko": "*", "prettier-plugin-multiline-arrays": "*", "prettier-plugin-organize-attributes": "*", "prettier-plugin-organize-imports": "*", "prettier-plugin-sort-imports": "*", "prettier-plugin-svelte": "*" }, "optionalPeers": ["@ianvs/prettier-plugin-sort-imports", "@prettier/plugin-hermes", "@prettier/plugin-oxc", "@prettier/plugin-pug", "@shopify/prettier-plugin-liquid", "@trivago/prettier-plugin-sort-imports", "@zackad/prettier-plugin-twig", "prettier-plugin-astro", "prettier-plugin-css-order", "prettier-plugin-jsdoc", "prettier-plugin-marko", "prettier-plugin-multiline-arrays", "prettier-plugin-organize-attributes", "prettier-plugin-organize-imports", "prettier-plugin-sort-imports", "prettier-plugin-svelte"] }, "sha512-LkphyK3Fw+q2HdMOoiEHWf93fNtYJwfamoKPl7UwtjFQdei/iIBoX11G6j706FzN3ymX9mPVi97qIY8328vdnA=="], "readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="], diff --git a/package.json b/package.json index 631d920..23ebb7e 100644 --- a/package.json +++ b/package.json @@ -22,9 +22,9 @@ "@tailwindcss/vite": "^4.1.14", "bits-ui": "^2.11.0", "clsx": "^2.1.1", - "prettier": "^3.6.2", + "prettier": "^3.7.4", "prettier-plugin-svelte": "^3.4.0", - "prettier-plugin-tailwindcss": "^0.7.1", + "prettier-plugin-tailwindcss": "^0.7.2", "svelte": "^5.41.0", "svelte-check": "^4.3.3", "tailwind-merge": "^3.3.1", diff --git a/src/lib/components/CardPerfil.svelte b/src/lib/components/CardPerfil.svelte index 2a49fcb..6517782 100644 --- a/src/lib/components/CardPerfil.svelte +++ b/src/lib/components/CardPerfil.svelte @@ -11,12 +11,16 @@ import { updateUsuario } from '@/hooks/updateUsuario'; import { sesionStore } from '@/stores/usuario'; import { obtenerUsuarioPorUsername } from '@/hooks/obtenerUsuario'; + import CardHeader from './ui/card/card-header.svelte'; + import CardTitle from './ui/card/card-title.svelte'; + import Badge from './ui/badge/badge.svelte'; let { data = $bindable() } = $props(); let cargando = $state(false); let hoverimg = $state(false); let image: File | null = $state(null); + let usu = $state({ displayName: data.displayName, bio: data.bio }); async function cambiarFotoDePerfil() { const input = document.createElement('input'); @@ -44,8 +48,8 @@ } - - + + {#if cargando}
@@ -54,38 +58,43 @@
{:else if $sesionStore?.isAdmin || $sesionStore?.username == data.username} - + + + + {data.displayName?.[0]?.toUpperCase() || ''} + + +
+ +
+ + +

- {data.displayName} + {usu.displayName}

@{data.username}

-

- {data.bio} -

+ {#if usu.bio} +

+ {@html usu.bio.replaceAll('\n', '
')} +

+ {/if} {:else}

- {data.displayName} + {usu.displayName} +

@{data.username}

-

- {data.bio} -

+ {#if usu.bio} +

+ {@html usu.bio.replaceAll('\n', '
')} +

+ {/if} {/if} +
+ + + + Seguidos: + {data.seguidos.response.length} + + + {#if data.seguidos.response.length === 0} +

No hay Seguidos

+ {:else} + {#each data.seguidos.response as seguidos (seguidos.id)} +

+ {seguidos.username} +

+ {/each} + {/if} +
+
+
+ + + + Seguidores: + {data.seguidores.response.length} + + + {#if data.seguidores.response.length === 0} +

No hay Seguidores

+ {:else} + {#each data.seguidores.response as seguidores (seguidores.id)} +

+ {seguidores.username} +

+ {/each} + {/if} +
+
+
+
diff --git a/src/lib/components/DialogModificarUsuario.svelte b/src/lib/components/DialogModificarUsuario.svelte new file mode 100644 index 0000000..1d6403f --- /dev/null +++ b/src/lib/components/DialogModificarUsuario.svelte @@ -0,0 +1,98 @@ + + + + +
+ +
+
+
+ + + +

Modificar Usuario

+
+
+ + + Nombre + + + + bio + + + + Email + + + + + + +
+
+
diff --git a/src/lib/components/PostCard.svelte b/src/lib/components/PostCard.svelte index bd9ab80..5a48e3c 100644 --- a/src/lib/components/PostCard.svelte +++ b/src/lib/components/PostCard.svelte @@ -47,7 +47,7 @@ let contenido = $derived(() => { let t = post.content.replaceAll('\n', '
'); t = t.replace( - /#\w*/gm, + /#\p{L}*/u, (match) => `${match}` ); diff --git a/src/lib/hooks/obtenerCantidadDeUsosdeHtag.ts b/src/lib/hooks/obtenerCantidadDeUsosdeHtag.ts new file mode 100644 index 0000000..1289737 --- /dev/null +++ b/src/lib/hooks/obtenerCantidadDeUsosdeHtag.ts @@ -0,0 +1,22 @@ +import { apiBase } from '@/stores/url'; +import { sesionStore } from '@/stores/usuario'; +import { get } from 'svelte/store'; + +export async function obtenerCantidadDeUsosdeHtag(htag: string) { + if (!htag) return null; + try { + const req = await fetch(`${get(apiBase)}/api/posts/hashtag/${htag}`, { + method: 'GET', + headers: { + Authorization: `Bearer ${get(sesionStore)?.accessToken}` + } + }); + if (req.ok) { + let data = await req.json(); + return data; + } + return null; + } catch { + return null; + } +} diff --git a/src/lib/hooks/obtenerSeguidoresPorUsuario.ts b/src/lib/hooks/obtenerSeguidoresPorUsuario.ts index 0a57e4c..3c84b05 100644 --- a/src/lib/hooks/obtenerSeguidoresPorUsuario.ts +++ b/src/lib/hooks/obtenerSeguidoresPorUsuario.ts @@ -1,25 +1,30 @@ -import { sesionStore } from "@/stores/usuario"; -import type { UserResponseDto } from "../../types"; -import { get } from "svelte/store"; -import { apiBase } from "@/stores/url"; +import { sesionStore } from '@/stores/usuario'; +import type { UsersResponseDto } from '../../types'; +import { get } from 'svelte/store'; +import { apiBase } from '@/stores/url'; -export async function obtenerSeguidoresPorUsuario(id: string, limit:number = 20): Promise { - try { - const response = await fetch(`${get(apiBase)}/api/users/${id}/followers?limit=${limit}`, { - method: 'GET', - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${get(sesionStore)?.accessToken}` - } - }); +export async function obtenerSeguidoresPorUsuario( + id: string, + limit: number = 20, + fetch2: Function +): Promise { + try { + const fetchFunc = fetch2 || fetch; + const response = await fetchFunc(`${get(apiBase)}/api/users/${id}/followers?limit=${limit}`, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${get(sesionStore)?.accessToken}` + } + }); - if (!response.ok) { - return null; - } + if (!response.ok) { + return null; + } - const followers: UserResponseDto[] = await response.json(); - return followers; - } catch (error) { - return null; - } + const followers: UsersResponseDto = await response.json(); + return followers; + } catch (error) { + return null; + } } diff --git a/src/lib/hooks/obtenerSeguidosPorUsuario.ts b/src/lib/hooks/obtenerSeguidosPorUsuario.ts index a32aebe..1db413b 100644 --- a/src/lib/hooks/obtenerSeguidosPorUsuario.ts +++ b/src/lib/hooks/obtenerSeguidosPorUsuario.ts @@ -1,25 +1,31 @@ -import { sesionStore } from "@/stores/usuario"; -import type { UserResponseDto } from "../../types"; -import { apiBase } from "@/stores/url"; -import { get } from "svelte/store"; +import { sesionStore } from '@/stores/usuario'; +import type { UsersResponseDto } from '../../types'; +import { apiBase } from '@/stores/url'; +import { get } from 'svelte/store'; -export async function obtenerSeguidosPorUsuario(id: string, limit:number = 20): Promise { - try { - const response = await fetch(`${get(apiBase)}/api/users/${id}/following?limit=${limit}`, { - method: 'GET', - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${get(sesionStore)?.accessToken}` - } - }); +export async function obtenerSeguidosPorUsuario( + id: string, + limit: number = 20, + fetch2?: Function +): Promise { + try { + const fetchFunc = fetch2 || fetch; - if (!response.ok) { - return null; - } + const response = await fetchFunc(`${get(apiBase)}/api/users/${id}/following?limit=${limit}`, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${get(sesionStore)?.accessToken}` + } + }); - const users: UserResponseDto[] = await response.json(); - return users; - } catch (error) { - return null; - } + if (!response.ok) { + return null; + } + + const users: UsersResponseDto = await response.json(); + return users; + } catch (error) { + return null; + } } diff --git a/src/lib/hooks/obtenerUsuario.ts b/src/lib/hooks/obtenerUsuario.ts index 40481bf..c519c59 100644 --- a/src/lib/hooks/obtenerUsuario.ts +++ b/src/lib/hooks/obtenerUsuario.ts @@ -3,9 +3,14 @@ import type { UserResponseDto } from '../../types'; import { get } from 'svelte/store'; import { sesionStore } from '@/stores/usuario'; -export async function obtenerUsuarioPorUsername(username: string): Promise { +export async function obtenerUsuarioPorUsername( + username: string, + fetch2?: Function +): Promise { try { - const response = await fetch(`${get(apiBase)}/api/users/username/${username}`, { + const fetchFunction = fetch2 || fetch; + + const response = await fetchFunction(`${get(apiBase)}/api/users/username/${username}`, { method: 'GET', headers: { 'Content-Type': 'application/json', diff --git a/src/lib/hooks/updateUsuario.ts b/src/lib/hooks/updateUsuario.ts index aa6a820..8ced334 100644 --- a/src/lib/hooks/updateUsuario.ts +++ b/src/lib/hooks/updateUsuario.ts @@ -1,48 +1,50 @@ -import { apiBase } from "@/stores/url" -import { sesionStore } from "@/stores/usuario" -import { get } from "svelte/store" +import { apiBase } from '@/stores/url'; +import { sesionStore } from '@/stores/usuario'; +import { get } from 'svelte/store'; export interface AdminUpdateUsuario { - id:string, - displayName: string | null, - bio: string | null, - profileImage:boolean, - image:File, - profileImageUrl:string|null + id: string; + displayName: string | null; + bio: string | null; + profileImage: boolean; + image: File; + profileImageUrl: string | null; } export async function updateUsuario(usuario: Partial) { + const formData = new FormData(); + if (usuario.displayName) formData.append('displayName', usuario.displayName); + if (usuario.bio) formData.append('bio', usuario.bio); + if (usuario.profileImage) { + if (usuario.profileImageUrl) formData.append('profileImageUrl', 'null'); + } else { + if (usuario.profileImageUrl) { + formData.append('profileImageUrl', usuario.profileImageUrl); + } else { + if (usuario.image) formData.append('profileImage', usuario.image); + } + } - const formData = new FormData(); - if (usuario.displayName) formData.append('displayName', usuario.displayName); - if (usuario.bio) formData.append('bio', usuario.bio); - if (usuario.image) formData.append('profileImage', usuario.image); - if (usuario.profileImage){ - if (usuario.profileImageUrl) formData.append('profileImageUrl', 'null'); - }else{ - if (usuario.profileImageUrl) formData.append('profileImageUrl', usuario.profileImageUrl); - } + try { + const req = await fetch(get(apiBase) + '/api/users/' + usuario.id, { + method: 'PUT', + headers: { + Authorization: `Bearer ${get(sesionStore)?.accessToken}` + }, + body: formData + }); + if (req.status === 204) { + let ret = { + // bio: usuario.bio, + displayName: usuario.displayName + // oldImageUrl: usuario.oldImageUrl, + }; + return ret; + } + const dataa = await req.json(); - try { - const req = await fetch(get(apiBase) + "/api/users/"+usuario.id, { - method: "PUT", - headers: { - Authorization: `Bearer ${get(sesionStore)?.accessToken}` - }, - body: formData, - }); - if (req.status === 204) { - let ret = { - // bio: usuario.bio, - displayName: usuario.displayName, - // oldImageUrl: usuario.oldImageUrl, - } - return ret; - } - const dataa = await req.json(); - - return dataa.message; - } catch { - return "No se pudo alcanzar el servidor" - } + return dataa.message; + } catch { + return 'No se pudo alcanzar el servidor'; + } } diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index cfb4bff..50ac318 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -1,7 +1,6 @@ + + + + + + + +
diff --git a/src/routes/[perfil]/+page.svelte b/src/routes/[perfil]/+page.svelte index 644b9ff..6578d40 100644 --- a/src/routes/[perfil]/+page.svelte +++ b/src/routes/[perfil]/+page.svelte @@ -1,14 +1,7 @@ +
-
+ {#key data} - -
- + {/key}

@@ -182,3 +126,15 @@

+ +{#if $sesionStore?.isAdmin || $sesionStore?.username == params.perfil} + +{/if} + + + + + + + + diff --git a/src/routes/[perfil]/+page.ts b/src/routes/[perfil]/+page.ts index f6695b5..f678027 100644 --- a/src/routes/[perfil]/+page.ts +++ b/src/routes/[perfil]/+page.ts @@ -4,12 +4,15 @@ import { error } from '@sveltejs/kit'; import { obtenerSeguidosPorUsuario } from '@/hooks/obtenerSeguidosPorUsuario.js'; import { obtenerSeguidoresPorUsuario } from '@/hooks/obtenerSeguidoresPorUsuario.js'; -export async function load({ params }) { - const usuario: UserResponseDto | null = await obtenerUsuarioPorUsername(params.perfil); - if(!usuario) error(404, 'No se encontro el usuario, ' + params.perfil); +export async function load({ params, depends, fetch }) { + depends('perfil:general'); + const usuario: UserResponseDto | null = await obtenerUsuarioPorUsername(params.perfil, fetch); + if (!usuario) error(404, 'No se encontro el usuario, ' + params.perfil); - const seguidos = await obtenerSeguidosPorUsuario(usuario.id, 3); - const seguidores = await obtenerSeguidoresPorUsuario(usuario.id, 3); + const [seguidos, seguidores] = await Promise.all([ + obtenerSeguidosPorUsuario(usuario.id, 3, fetch), + obtenerSeguidoresPorUsuario(usuario.id, 3, fetch) + ]); - return { ...usuario, seguidos, seguidores }; + return { ...usuario, seguidos, seguidores }; } diff --git a/src/routes/htag/[htag]/+page.svelte b/src/routes/htag/[htag]/+page.svelte new file mode 100644 index 0000000..bc7cacc --- /dev/null +++ b/src/routes/htag/[htag]/+page.svelte @@ -0,0 +1,89 @@ + + +
+
+
+ + + +

+ #{data.htag} +

+ +
+

+ El hashtag se ha utilizado {data.posts.count} ve{#if data.posts.count > 1}ces{:else}z{/if} +

+
+
+
+
+ +
+ {#each postsfiltro as post} + + {/each} +
+
+
+{#if postAModificar} +
+ +
+{/if} + + + + + + + + diff --git a/src/routes/htag/[htag]/+page.ts b/src/routes/htag/[htag]/+page.ts new file mode 100644 index 0000000..a4c51d3 --- /dev/null +++ b/src/routes/htag/[htag]/+page.ts @@ -0,0 +1,10 @@ +import { obtenerCantidadDeUsosdeHtag } from '@/hooks/obtenerCantidadDeUsosdeHtag.js'; +import { error } from '@sveltejs/kit'; + +export async function load({ params }) { + let { htag } = params; + + const posts = await obtenerCantidadDeUsosdeHtag(htag); + // if (cantidad == null || posts.lenght == 0) return error(404, 'no existe el #(hashtag)'); + return { htag, posts }; +} diff --git a/src/routes/login/+page.js b/src/routes/login/+page.js index 044e0b7..590ae24 100644 --- a/src/routes/login/+page.js +++ b/src/routes/login/+page.js @@ -1,5 +1,5 @@ export function load({ url }) { - return { - message: url.searchParams.get('msg') - }; + return { + message: url.searchParams.get('msg') + }; } diff --git a/src/routes/login/+page.svelte b/src/routes/login/+page.svelte index a402bd9..276fa50 100644 --- a/src/routes/login/+page.svelte +++ b/src/routes/login/+page.svelte @@ -1,60 +1,62 @@ + $effect(() => { + resetAlert(); + if (data.message) { + history.replaceState(history.state, '', '/login'); + setTimeout(() => { + message = ''; + }, 7000); + } + }); + async function resetAlert() { + if (showAlert == true) { + await new Promise((res) => setTimeout(res, 2000)); + showAlert = false; + } + } +
- {#if message} -
- - - Info - - Ingrese las credenciales de la cuenta recien creada - - -
- - {/if} - - {#if showAlert} -
- - - No se pudo iniciar sesion - - Revise su usuario o contraseƱa - - -
- {/if} + {#if message} +
+ + + Info + Ingrese las credenciales de la cuenta recien creada + +
+ {/if} + + {#if showAlert} +
+ + + No se pudo iniciar sesion + Revise su usuario o contraseƱa + +
+ {/if}
+ + + + + + + + diff --git a/src/routes/register/+page.svelte b/src/routes/register/+page.svelte index 471d138..11d6656 100644 --- a/src/routes/register/+page.svelte +++ b/src/routes/register/+page.svelte @@ -1,38 +1,42 @@
-
- - {#if showAlert} -
- - - No se pudo crear la cuenta - - Intente nuevamente. - - -
- {/if} +
+ + {#if showAlert} +
+ + + No se pudo crear la cuenta + Intente nuevamente. + +
+ {/if}
+ + + + + + + + diff --git a/src/routes/search/[user]/+page.svelte b/src/routes/search/[user]/+page.svelte index 13e3e4f..23634fd 100644 --- a/src/routes/search/[user]/+page.svelte +++ b/src/routes/search/[user]/+page.svelte @@ -1,6 +1,6 @@ @@ -9,3 +9,11 @@ {/each}
+ + + + + + + + diff --git a/src/types.d.ts b/src/types.d.ts index 9ce1978..e38ce95 100644 --- a/src/types.d.ts +++ b/src/types.d.ts @@ -84,18 +84,34 @@ export interface UserResponseDto { displayName: string; email: string; bio: string; - imageUrl: string?; + imageUrl: string?; profileImageUrl: string; followersCount: number; followingCount: number; createdAt: string; postsCount: number; } +export interface UsersResponseDto { + response: { + id: string; + username: string; + displayName: string; + email: string; + bio: string; + imageUrl?: string; + profileImageUrl: string; + followersCount: number; + followingCount: number; + createdAt: string; + postsCount: number; + }[]; + totalCount: number; +} export interface UpdateUserRequest { - username: string?; - displayName: string?; - bio: string?; - email: string?; - profileImage: File?; + username: string?; + displayName: string?; + bio: string?; + email: string?; + profileImage: File?; } diff --git a/static/x.png b/static/x.png new file mode 100644 index 0000000..62fc830 Binary files /dev/null and b/static/x.png differ