diff --git a/src/lib/components/PostCard.svelte b/src/lib/components/PostCard.svelte
new file mode 100644
index 0000000..b9fca16
--- /dev/null
+++ b/src/lib/components/PostCard.svelte
@@ -0,0 +1,68 @@
+
+
+
+
+
+
+
{post.authorId}
+
+
+
+
+
+
+ Opciones Publicación
+
+ Editar
+ {}}>
+
+ Borrar
+
+
+
+
+
+
+
+
+ {post.content}
+ {#if post.imageUrl}
+
+ {/if}
+
+
+
+ {post.likesCount} likes
+ {post.repliesCount} replies
+ {post.createdAt.replace('T', ' ').split('.')[0]}
+ {#if post.isEdited}
+ Editado
+ {/if}
+
+
+
diff --git a/src/lib/components/crear-post.svelte b/src/lib/components/crear-post.svelte
new file mode 100644
index 0000000..a0df5c2
--- /dev/null
+++ b/src/lib/components/crear-post.svelte
@@ -0,0 +1,81 @@
+
+
+
diff --git a/src/lib/components/ui/avatar/avatar-fallback.svelte b/src/lib/components/ui/avatar/avatar-fallback.svelte
new file mode 100644
index 0000000..249d4a4
--- /dev/null
+++ b/src/lib/components/ui/avatar/avatar-fallback.svelte
@@ -0,0 +1,17 @@
+
+
+
diff --git a/src/lib/components/ui/avatar/avatar-image.svelte b/src/lib/components/ui/avatar/avatar-image.svelte
new file mode 100644
index 0000000..2bb9db4
--- /dev/null
+++ b/src/lib/components/ui/avatar/avatar-image.svelte
@@ -0,0 +1,17 @@
+
+
+
diff --git a/src/lib/components/ui/avatar/avatar.svelte b/src/lib/components/ui/avatar/avatar.svelte
new file mode 100644
index 0000000..e37214d
--- /dev/null
+++ b/src/lib/components/ui/avatar/avatar.svelte
@@ -0,0 +1,19 @@
+
+
+
diff --git a/src/lib/components/ui/avatar/index.ts b/src/lib/components/ui/avatar/index.ts
new file mode 100644
index 0000000..d06457b
--- /dev/null
+++ b/src/lib/components/ui/avatar/index.ts
@@ -0,0 +1,13 @@
+import Root from "./avatar.svelte";
+import Image from "./avatar-image.svelte";
+import Fallback from "./avatar-fallback.svelte";
+
+export {
+ Root,
+ Image,
+ Fallback,
+ //
+ Root as Avatar,
+ Image as AvatarImage,
+ Fallback as AvatarFallback,
+};
diff --git a/src/lib/components/ui/input-group/index.ts b/src/lib/components/ui/input-group/index.ts
new file mode 100644
index 0000000..fe1f55d
--- /dev/null
+++ b/src/lib/components/ui/input-group/index.ts
@@ -0,0 +1,22 @@
+import Root from "./input-group.svelte";
+import Addon from "./input-group-addon.svelte";
+import Button from "./input-group-button.svelte";
+import Input from "./input-group-input.svelte";
+import Text from "./input-group-text.svelte";
+import Textarea from "./input-group-textarea.svelte";
+
+export {
+ Root,
+ Addon,
+ Button,
+ Input,
+ Text,
+ Textarea,
+ //
+ Root as InputGroup,
+ Addon as InputGroupAddon,
+ Button as InputGroupButton,
+ Input as InputGroupInput,
+ Text as InputGroupText,
+ Textarea as InputGroupTextarea,
+};
diff --git a/src/lib/components/ui/input-group/input-group-addon.svelte b/src/lib/components/ui/input-group/input-group-addon.svelte
new file mode 100644
index 0000000..09f2b64
--- /dev/null
+++ b/src/lib/components/ui/input-group/input-group-addon.svelte
@@ -0,0 +1,55 @@
+
+
+
+
+ {
+ if ((e.target as HTMLElement).closest("button")) {
+ return;
+ }
+ e.currentTarget.parentElement?.querySelector("input")?.focus();
+ }}
+ {...restProps}
+>
+ {@render children?.()}
+
diff --git a/src/lib/components/ui/input-group/input-group-button.svelte b/src/lib/components/ui/input-group/input-group-button.svelte
new file mode 100644
index 0000000..d38a71e
--- /dev/null
+++ b/src/lib/components/ui/input-group/input-group-button.svelte
@@ -0,0 +1,49 @@
+
+
+
+
+
diff --git a/src/lib/components/ui/input-group/input-group-input.svelte b/src/lib/components/ui/input-group/input-group-input.svelte
new file mode 100644
index 0000000..ded2655
--- /dev/null
+++ b/src/lib/components/ui/input-group/input-group-input.svelte
@@ -0,0 +1,23 @@
+
+
+
diff --git a/src/lib/components/ui/input-group/input-group-text.svelte b/src/lib/components/ui/input-group/input-group-text.svelte
new file mode 100644
index 0000000..332f63d
--- /dev/null
+++ b/src/lib/components/ui/input-group/input-group-text.svelte
@@ -0,0 +1,22 @@
+
+
+
+ {@render children?.()}
+
diff --git a/src/lib/components/ui/input-group/input-group-textarea.svelte b/src/lib/components/ui/input-group/input-group-textarea.svelte
new file mode 100644
index 0000000..91850ff
--- /dev/null
+++ b/src/lib/components/ui/input-group/input-group-textarea.svelte
@@ -0,0 +1,23 @@
+
+
+
diff --git a/src/lib/components/ui/input-group/input-group.svelte b/src/lib/components/ui/input-group/input-group.svelte
new file mode 100644
index 0000000..a216aeb
--- /dev/null
+++ b/src/lib/components/ui/input-group/input-group.svelte
@@ -0,0 +1,38 @@
+
+
+textarea]:h-auto",
+
+ // Variants based on alignment.
+ "has-[>[data-align=inline-start]]:[&>input]:ps-2",
+ "has-[>[data-align=inline-end]]:[&>input]:pe-2",
+ "has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>[data-align=block-start]]:[&>input]:pb-3",
+ "has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-end]]:[&>input]:pt-3",
+
+ // Focus state.
+ "has-[[data-slot=input-group-control]:focus-visible]:border-ring has-[[data-slot=input-group-control]:focus-visible]:ring-ring/50 has-[[data-slot=input-group-control]:focus-visible]:ring-[3px]",
+
+ // Error state.
+ "has-[[data-slot][aria-invalid=true]]:ring-destructive/20 has-[[data-slot][aria-invalid=true]]:border-destructive dark:has-[[data-slot][aria-invalid=true]]:ring-destructive/40",
+
+ className
+ )}
+ {...props}
+>
+ {@render children?.()}
+
diff --git a/src/lib/components/ui/kbd/index.ts b/src/lib/components/ui/kbd/index.ts
new file mode 100644
index 0000000..6aa7f07
--- /dev/null
+++ b/src/lib/components/ui/kbd/index.ts
@@ -0,0 +1,10 @@
+import Root from "./kbd.svelte";
+import Group from "./kbd-group.svelte";
+
+export {
+ Root,
+ Group,
+ //
+ Root as Kbd,
+ Group as KbdGroup,
+};
diff --git a/src/lib/components/ui/kbd/kbd-group.svelte b/src/lib/components/ui/kbd/kbd-group.svelte
new file mode 100644
index 0000000..61447a5
--- /dev/null
+++ b/src/lib/components/ui/kbd/kbd-group.svelte
@@ -0,0 +1,20 @@
+
+
+
+ {@render children?.()}
+
diff --git a/src/lib/components/ui/kbd/kbd.svelte b/src/lib/components/ui/kbd/kbd.svelte
new file mode 100644
index 0000000..cdf6a08
--- /dev/null
+++ b/src/lib/components/ui/kbd/kbd.svelte
@@ -0,0 +1,25 @@
+
+
+
+ {@render children?.()}
+
diff --git a/src/lib/components/ui/spinner/index.ts b/src/lib/components/ui/spinner/index.ts
new file mode 100644
index 0000000..f8b1ced
--- /dev/null
+++ b/src/lib/components/ui/spinner/index.ts
@@ -0,0 +1 @@
+export { default as Spinner } from "./spinner.svelte";
diff --git a/src/lib/components/ui/spinner/spinner.svelte b/src/lib/components/ui/spinner/spinner.svelte
new file mode 100644
index 0000000..9b12131
--- /dev/null
+++ b/src/lib/components/ui/spinner/spinner.svelte
@@ -0,0 +1,14 @@
+
+
+
diff --git a/src/lib/components/ui/textarea/index.ts b/src/lib/components/ui/textarea/index.ts
new file mode 100644
index 0000000..ace797a
--- /dev/null
+++ b/src/lib/components/ui/textarea/index.ts
@@ -0,0 +1,7 @@
+import Root from "./textarea.svelte";
+
+export {
+ Root,
+ //
+ Root as Textarea,
+};
diff --git a/src/lib/components/ui/textarea/textarea.svelte b/src/lib/components/ui/textarea/textarea.svelte
new file mode 100644
index 0000000..7fcef1a
--- /dev/null
+++ b/src/lib/components/ui/textarea/textarea.svelte
@@ -0,0 +1,23 @@
+
+
+
diff --git a/src/lib/head/AvatarButton.svelte b/src/lib/head/AvatarButton.svelte
new file mode 100644
index 0000000..1d25635
--- /dev/null
+++ b/src/lib/head/AvatarButton.svelte
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+ {$sesionStore?.displayName[0]}
+
+
+
+
+ goto('/' + $sesionStore?.username)}
+ >Mi Perfil
+
+ await logout(menuOpen)}>Cerrar Sesion
+
+
+
diff --git a/src/lib/head/Header.svelte b/src/lib/head/Header.svelte
index 774035e..1791b52 100644
--- a/src/lib/head/Header.svelte
+++ b/src/lib/head/Header.svelte
@@ -7,42 +7,41 @@
import { sesionStore } from '@/stores/usuario';
import { onMount } from 'svelte';
import { apiBase } from '@/stores/url';
+ import { goto } from '$app/navigation';
+ import AvatarButton from './AvatarButton.svelte';
let menuOpen = $state(false);
const toggleMenu = () => (menuOpen = !menuOpen);
- let showCerrarSesion = $state(false);
+ let showCerrarSesion = $state(false);
- onMount(()=>{
- sesionStore.subscribe((value)=>{
- showCerrarSesion = !!value?.accessToken;
- })
+ onMount(() => {
+ sesionStore.subscribe((value) => {
+ showCerrarSesion = !!value?.accessToken;
+ });
+ });
- });
-
- async function cerrarSesion(){
- try{
- const req = await fetch($apiBase+"/api/auth/logout", {
- method: 'POST',
- headers: {
- "Content-Type": "application/json",
- "Authorization": `Bearer ${$sesionStore?.accessToken}`
-
- },
- credentials: "include"
- });
- if(req.ok){
-
- sesionStore.reset();
- menuOpen = false;
- }
- }catch{
- console.log("fallo el lougout")
- } finally{
- sesionStore.reset();
- }
-
- }
+ async function cerrarSesion() {
+ try {
+ const req = await fetch($apiBase + '/api/auth/logout', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ Authorization: `Bearer ${$sesionStore?.accessToken}`
+ },
+ credentials: 'include'
+ });
+ if (req.ok) {
+ sesionStore.reset();
+ menuOpen = false;
+ }
+ } catch {
+ console.log('fallo el lougout');
+ } finally {
+ sesionStore.reset();
+ goto('/');
+ }
+ }
@@ -51,31 +50,38 @@
Mini-X
-
diff --git a/src/lib/hooks/deletePost.ts b/src/lib/hooks/deletePost.ts
new file mode 100644
index 0000000..e69de29
diff --git a/src/lib/hooks/logout.ts b/src/lib/hooks/logout.ts
new file mode 100644
index 0000000..9fe4b4e
--- /dev/null
+++ b/src/lib/hooks/logout.ts
@@ -0,0 +1,25 @@
+import { goto } from '$app/navigation';
+import { apiBase } from '@/stores/url';
+import { sesionStore } from '@/stores/usuario';
+
+export async function logout(menuOpen: boolean) {
+ try {
+ const req = await fetch($apiBase + '/api/auth/logout', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ Authorization: `Bearer ${$sesionStore.accessToken}`
+ },
+ credentials: 'include'
+ });
+ if (req.ok) {
+ sesionStore.reset();
+ menuOpen = false;
+ }
+ } catch {
+ console.log('fallo el lougout');
+ } finally {
+ sesionStore.reset();
+ goto('/');
+ }
+}
diff --git a/src/lib/hooks/updatePost.ts b/src/lib/hooks/updatePost.ts
new file mode 100644
index 0000000..e69de29
diff --git a/src/lib/stores/posts.ts b/src/lib/stores/posts.ts
new file mode 100644
index 0000000..bf1611f
--- /dev/null
+++ b/src/lib/stores/posts.ts
@@ -0,0 +1,22 @@
+import { writable } from 'svelte/store';
+import type { Post } from '../../types';
+
+export const posts = writable([]);
+
+export const setPosts = (newPosts: Post[]) => {
+ posts.set(newPosts);
+};
+
+export const addPost = (post: Post) => {
+ posts.update((currentPosts) => [post, ...currentPosts]);
+};
+
+export const updatePost = (postId: string, updatedData: Partial) => {
+ posts.update((currentPosts) =>
+ currentPosts.map((post) => (post._id === postId ? { ...post, ...updatedData } : post))
+ );
+};
+
+export const removePost = (postId: string) => {
+ posts.update((currentPosts) => currentPosts.filter((post) => post._id !== postId));
+};
diff --git a/src/lib/stores/usuario.ts b/src/lib/stores/usuario.ts
index 649b18b..f419c8d 100644
--- a/src/lib/stores/usuario.ts
+++ b/src/lib/stores/usuario.ts
@@ -1,7 +1,18 @@
import { writable } from 'svelte/store';
+import { browser } from '$app/environment';
import type { Sesion } from '../../types';
+import { apiBase } from '@/stores/url';
-export const currentSesion = writable(null);
+const { subscribe } = apiBase;
+let baseUrl: string = '';
+
+subscribe((value) => {
+ baseUrl = value;
+})();
+
+const initialValue = browser ? JSON.parse(localStorage.getItem('sesion') || 'null') : null;
+
+export const currentSesion = writable(initialValue);
export const sesionStore = {
subscribe: currentSesion.subscribe,
@@ -9,3 +20,40 @@ export const sesionStore = {
update: currentSesion.update,
reset: () => currentSesion.set(null)
};
+
+if (browser) {
+ currentSesion.subscribe((value) => {
+ localStorage.setItem('sesion', JSON.stringify(value));
+ });
+}
+if (browser) {
+ const refreshAccessToken = async () => {
+ try {
+ const response = await fetch(baseUrl + '/api/auth/refresh', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ credentials: 'include'
+ });
+
+ if (response.ok) {
+ const data = await response.json();
+ currentSesion.update((sesion) => {
+ if (sesion) {
+ return { ...sesion, accessToken: data.accessToken };
+ }
+ return sesion;
+ });
+ } else {
+ console.error('Error refreshing token:', response.statusText);
+ currentSesion.set(null);
+ }
+ } catch (error) {
+ console.error('Error refreshing token:', error);
+ currentSesion.set(null);
+ }
+ };
+
+ setInterval(refreshAccessToken, 10 * 60 * 1000);
+}
diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte
index 03797f5..bcde02d 100644
--- a/src/routes/+page.svelte
+++ b/src/routes/+page.svelte
@@ -1,66 +1,52 @@
-
- {#if posts.length <= 0}
-
-
- No hay Posts que mostrar
-
-
- {:else}
- {#each posts as post}
+
+
+ {#if $sesionStore !== null}
+
+ {/if}
+
+
+ {#if $posts.length <= 0}
-
-
- {post.authorId}
- {post.createdAt.toLocaleDateString()}
-
-
{post.content}
- {#if post.imageUrl}
-

- {/if}
-
- {post.likesCount} likes
- {post.repliesCount} replies
- {#if post.isEdited}
- Editado
- {/if}
-
-
+ No hay Posts que mostrar
- {/each}
- {/if}
+ {:else}
+ {#each $posts as post}
+
+ {/each}
+ {/if}
+
diff --git a/src/routes/[perfil]/+page.svelte b/src/routes/[perfil]/+page.svelte
new file mode 100644
index 0000000..ff7ad90
--- /dev/null
+++ b/src/routes/[perfil]/+page.svelte
@@ -0,0 +1,92 @@
+
+
+
+
+
+
+
+
+
+ {params.perfil[0].toUpperCase()}
+
+
+
+
+ @{params.perfil}
+
+
+
+
+
+ {#if cargando}
+
+ {:else if mensajeError !== ''}
+
+
+
+
+
+ {mensajeError}
+
+
+
+
+ {/if}
+
+
diff --git a/src/types.d.ts b/src/types.d.ts
index bc001ff..6305a56 100644
--- a/src/types.d.ts
+++ b/src/types.d.ts
@@ -6,12 +6,13 @@ export interface Post {
parentPostId?: string;
likesCount: number;
repliesCount: number;
- createdAt: Date;
+ createdAt: string;
updatedAt?: Date;
isEdited: boolean;
visibility: string;
hashtags?: string[];
}
+
export interface User {
_id: string;
displayName: string;
@@ -27,20 +28,27 @@ export interface User {
}
export interface Sesion {
- accessToken:string?;
- message:string;
- url:string;
- displayname:string;
+ accessToken: string?;
+ message: string;
+ url: string;
+ displayName: string;
+ username: string;
}
export interface LoginDto {
- username: string?;
- password: string?;
+ username: string?;
+ password: string?;
}
export interface RegisterDto {
- username: string?;
- email: string?;
- password: string?;
- displayName: string?;
+ username: string?;
+ email: string?;
+ password: string?;
+ displayName: string?;
+}
+
+export interface CreatePostDto {
+ content: string;
+ imageUrl: string?;
+ parentPostId: string?;
}