diff --git a/src/lib/components/PostCard.svelte b/src/lib/components/PostCard.svelte index b9fca16..5ac4cdc 100644 --- a/src/lib/components/PostCard.svelte +++ b/src/lib/components/PostCard.svelte @@ -15,35 +15,90 @@ import DropdownMenuLabel from './ui/dropdown-menu/dropdown-menu-label.svelte'; import DropdownMenuSeparator from './ui/dropdown-menu/dropdown-menu-separator.svelte'; import DropdownMenuItem from './ui/dropdown-menu/dropdown-menu-item.svelte'; + import Avatar from './ui/avatar/avatar.svelte'; + import AvatarImage from './ui/avatar/avatar-image.svelte'; + import AvatarFallback from './ui/avatar/avatar-fallback.svelte'; + import { deletePost } from '@/hooks/deletePost'; + import Spinner from './ui/spinner/spinner.svelte'; + import { removePost, updatePostStore } from '@/stores/posts'; + import { Dialog } from './ui/dialog'; + import DialogContent from './ui/dialog/dialog-content.svelte'; + import DialogHeader from './ui/dialog/dialog-header.svelte'; + import DialogTitle from './ui/dialog/dialog-title.svelte'; + import DialogDescription from './ui/dialog/dialog-description.svelte'; + import { updatePost } from '@/hooks/updatePost'; + import { sesionStore } from '@/stores/usuario'; interface postProp { post: Post; + postAModificar: Post | null; } - let { post }: postProp = $props(); + let { post, postAModificar = $bindable() }: postProp = $props(); + + let cargandoBorrar = $state(false); + let mensajeError = $state(''); + let cargandoEditar = $state(false); + + async function handleBorrar() { + await deletePost( + post, + () => { + removePost(post.id); + }, + cargandoBorrar, + mensajeError + ); + } + + async function handleEditar() { + postAModificar = post; + }
- {post.authorId} - - - - - - - Opciones Publicación - - Editar - {}}> - -

Borrar

-
-
-
-
+
+ + + {post.authorDisplayName[0].toUpperCase()} + +
+ {post.authorDisplayName} + @{post.authorName} +
+
+ {#if post.authorName === $sesionStore?.username} + + + + + + + Opciones Publicación + + handleEditar()}> + Editar + + { + handleBorrar(); + }} + > + {#if cargandoBorrar} + + {:else} + + {/if} +

Borrar

+
+
+
+
+ {/if}
@@ -66,3 +121,15 @@
+{#if mensajeError} + + + + Hubo un fallo + + {mensajeError} + + + + +{/if} diff --git a/src/lib/components/crear-post.svelte b/src/lib/components/crear-post.svelte index a0df5c2..bcbcbfc 100644 --- a/src/lib/components/crear-post.svelte +++ b/src/lib/components/crear-post.svelte @@ -30,7 +30,7 @@ //credentials: 'include', headers: { 'Content-Type': 'application/json', - "Authorization": `Bearer ${$sesionStore?.accessToken}` + Authorization: `Bearer ${$sesionStore?.accessToken}` }, body: JSON.stringify(data) @@ -51,11 +51,21 @@ cargando = false; } } + + function handleKeydown(e: KeyboardEvent) { + if (e.ctrlKey && e.key === 'Enter') { + handlePost(e); + } + }
handlePost(e)}> - diff --git a/src/lib/components/ui/dialog/dialog-close.svelte b/src/lib/components/ui/dialog/dialog-close.svelte new file mode 100644 index 0000000..840b2f6 --- /dev/null +++ b/src/lib/components/ui/dialog/dialog-close.svelte @@ -0,0 +1,7 @@ + + + diff --git a/src/lib/components/ui/dialog/dialog-content.svelte b/src/lib/components/ui/dialog/dialog-content.svelte new file mode 100644 index 0000000..9d7c7f5 --- /dev/null +++ b/src/lib/components/ui/dialog/dialog-content.svelte @@ -0,0 +1,43 @@ + + + + + + {@render children?.()} + {#if showCloseButton} + + + Close + + {/if} + + diff --git a/src/lib/components/ui/dialog/dialog-description.svelte b/src/lib/components/ui/dialog/dialog-description.svelte new file mode 100644 index 0000000..3845023 --- /dev/null +++ b/src/lib/components/ui/dialog/dialog-description.svelte @@ -0,0 +1,17 @@ + + + diff --git a/src/lib/components/ui/dialog/dialog-footer.svelte b/src/lib/components/ui/dialog/dialog-footer.svelte new file mode 100644 index 0000000..e7ff446 --- /dev/null +++ b/src/lib/components/ui/dialog/dialog-footer.svelte @@ -0,0 +1,20 @@ + + +
+ {@render children?.()} +
diff --git a/src/lib/components/ui/dialog/dialog-header.svelte b/src/lib/components/ui/dialog/dialog-header.svelte new file mode 100644 index 0000000..4e5c447 --- /dev/null +++ b/src/lib/components/ui/dialog/dialog-header.svelte @@ -0,0 +1,20 @@ + + +
+ {@render children?.()} +
diff --git a/src/lib/components/ui/dialog/dialog-overlay.svelte b/src/lib/components/ui/dialog/dialog-overlay.svelte new file mode 100644 index 0000000..f81ad83 --- /dev/null +++ b/src/lib/components/ui/dialog/dialog-overlay.svelte @@ -0,0 +1,20 @@ + + + diff --git a/src/lib/components/ui/dialog/dialog-title.svelte b/src/lib/components/ui/dialog/dialog-title.svelte new file mode 100644 index 0000000..067e55e --- /dev/null +++ b/src/lib/components/ui/dialog/dialog-title.svelte @@ -0,0 +1,17 @@ + + + diff --git a/src/lib/components/ui/dialog/dialog-trigger.svelte b/src/lib/components/ui/dialog/dialog-trigger.svelte new file mode 100644 index 0000000..9d1e801 --- /dev/null +++ b/src/lib/components/ui/dialog/dialog-trigger.svelte @@ -0,0 +1,7 @@ + + + diff --git a/src/lib/components/ui/dialog/index.ts b/src/lib/components/ui/dialog/index.ts new file mode 100644 index 0000000..dce1d9d --- /dev/null +++ b/src/lib/components/ui/dialog/index.ts @@ -0,0 +1,37 @@ +import { Dialog as DialogPrimitive } from "bits-ui"; + +import Title from "./dialog-title.svelte"; +import Footer from "./dialog-footer.svelte"; +import Header from "./dialog-header.svelte"; +import Overlay from "./dialog-overlay.svelte"; +import Content from "./dialog-content.svelte"; +import Description from "./dialog-description.svelte"; +import Trigger from "./dialog-trigger.svelte"; +import Close from "./dialog-close.svelte"; + +const Root = DialogPrimitive.Root; +const Portal = DialogPrimitive.Portal; + +export { + Root, + Title, + Portal, + Footer, + Header, + Trigger, + Overlay, + Content, + Description, + Close, + // + Root as Dialog, + Title as DialogTitle, + Portal as DialogPortal, + Footer as DialogFooter, + Header as DialogHeader, + Trigger as DialogTrigger, + Overlay as DialogOverlay, + Content as DialogContent, + Description as DialogDescription, + Close as DialogClose, +}; diff --git a/src/lib/hooks/deletePost.ts b/src/lib/hooks/deletePost.ts index e69de29..adb1c0c 100644 --- a/src/lib/hooks/deletePost.ts +++ b/src/lib/hooks/deletePost.ts @@ -0,0 +1,33 @@ +import { apiBase } from '@/stores/url'; +import type { Post } from '../../types'; +import { sesionStore } from '@/stores/usuario'; +import { get } from 'svelte/store'; + +export async function deletePost( + post: Post, + callbackfn: Function, + cargando: boolean, + message: string = '' +) { + try { + cargando = true; + + const req = await fetch(get(apiBase) + `/api/posts/${post.id}`, { + method: 'DELETE', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${get(sesionStore)?.accessToken}` + } + }); + if (req.status === 204) { + callbackfn(); + return; + } + const msg = await req.json(); + message = msg.message; + } catch { + message = 'No se pudo alcanzar el servidor'; + } finally { + cargando = false; + } +} diff --git a/src/lib/hooks/updatePost.ts b/src/lib/hooks/updatePost.ts index e69de29..aaa0ca7 100644 --- a/src/lib/hooks/updatePost.ts +++ b/src/lib/hooks/updatePost.ts @@ -0,0 +1,29 @@ +import { apiBase } from '@/stores/url'; +import type { Post, PostResponseDto } from '../../types'; +import { get } from 'svelte/store'; +import { sesionStore } from '@/stores/usuario'; + +export async function updatePost(post: Post, callbackfn: Function, message: string) { + try { + const data = { + content: post.content, + imageUrl: post.imageUrl + }; + const req = await fetch(get(apiBase) + `/api/posts/${post.id}`, { + method: 'PUT', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${get(sesionStore)?.accessToken}` + }, + body: JSON.stringify(data) + }); + if (req.ok) { + const newpost: PostResponseDto = await req.json(); + callbackfn(newpost); + return; + } + message = 'Fallo al intentar modificar la publicación'; + } catch { + message = 'No se pudo alcanzar el servidor'; + } +} diff --git a/src/lib/stores/posts.ts b/src/lib/stores/posts.ts index bf1611f..185c5a5 100644 --- a/src/lib/stores/posts.ts +++ b/src/lib/stores/posts.ts @@ -11,12 +11,18 @@ export const addPost = (post: Post) => { posts.update((currentPosts) => [post, ...currentPosts]); }; -export const updatePost = (postId: string, updatedData: Partial) => { +export const updatePostStore = (postId: string, updatedData: Partial) => { posts.update((currentPosts) => - currentPosts.map((post) => (post._id === postId ? { ...post, ...updatedData } : post)) + currentPosts.map((post) => (post.id === postId ? { ...post, ...updatedData } : post)) ); }; export const removePost = (postId: string) => { - posts.update((currentPosts) => currentPosts.filter((post) => post._id !== postId)); + posts.update((currentPosts) => { + const a = currentPosts.filter((post) => post.id !== postId); + console.log(a); + return a; + }); + + console.log(postId); }; diff --git a/src/lib/stores/url.ts b/src/lib/stores/url.ts index ccd5124..7a0bd63 100644 --- a/src/lib/stores/url.ts +++ b/src/lib/stores/url.ts @@ -1,6 +1,6 @@ import { dev } from '$app/environment'; -import { writable } from 'svelte/store'; +import { readable } from 'svelte/store'; -export const apiBase = writable( +export const apiBase = readable( dev ? 'http://localhost:5000' : 'https://minix-back-dsuk.onrender.com' ); diff --git a/src/lib/stores/usuario.ts b/src/lib/stores/usuario.ts index f419c8d..4a7c5ac 100644 --- a/src/lib/stores/usuario.ts +++ b/src/lib/stores/usuario.ts @@ -1,4 +1,4 @@ -import { writable } from 'svelte/store'; +import { get, writable } from 'svelte/store'; import { browser } from '$app/environment'; import type { Sesion } from '../../types'; import { apiBase } from '@/stores/url'; @@ -29,7 +29,7 @@ if (browser) { if (browser) { const refreshAccessToken = async () => { try { - const response = await fetch(baseUrl + '/api/auth/refresh', { + const response = await fetch(get(apiBase) + '/api/auth/refresh', { method: 'POST', headers: { 'Content-Type': 'application/json' diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index bcde02d..115bc0b 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -29,7 +29,7 @@
-
+
{#if $sesionStore !== null} diff --git a/src/routes/[perfil]/+page.svelte b/src/routes/[perfil]/+page.svelte index ff7ad90..69cb778 100644 --- a/src/routes/[perfil]/+page.svelte +++ b/src/routes/[perfil]/+page.svelte @@ -9,12 +9,19 @@ import type { Post } from '../../types.js'; import Spinner from '@/components/ui/spinner/spinner.svelte'; import { fade, slide } from 'svelte/transition'; + import PostCard from '@/components/PostCard.svelte'; + import { posts, setPosts, updatePostStore } from '@/stores/posts.js'; + import InputGroup from '@/components/ui/input-group/input-group.svelte'; + import InputGroupTextarea from '@/components/ui/input-group/input-group-textarea.svelte'; + import InputGroupAddon from '@/components/ui/input-group/input-group-addon.svelte'; + import { updatePost } from '@/hooks/updatePost.js'; + import ModalEditar from './modalEditar.svelte'; let { params } = $props(); - let posts: Post[] = $state([]); let cargando = $state(true); let mensajeError = $state(''); + let postAModificar: Post | null = $state(null); const { subscribe } = apiBase; let baseUrl: string = ''; @@ -33,7 +40,7 @@ method: 'GET' }); if (req.ok) { - posts = await req.json(); + setPosts(await req.json()); return; } mensajeError = 'Fallo al obtener los datos'; @@ -43,10 +50,23 @@ cargando = false; } } + + async function handleEditar(e: SubmitEvent) { + e.preventDefault(); + // post.content = 'test'; + if (postAModificar == null) return; + await updatePost( + postAModificar, + (postnuevo: Post) => updatePostStore(postAModificar!.id, postnuevo), + + mensajeError + ); + postAModificar = null; + }
-
+
@@ -87,6 +107,19 @@
+ {:else} +
+ {#each $posts as post} +
+ +
+ {/each} +
{/if}
+{#if postAModificar} +
+ +
+{/if} diff --git a/src/routes/[perfil]/modalEditar.svelte b/src/routes/[perfil]/modalEditar.svelte new file mode 100644 index 0000000..68ae6fc --- /dev/null +++ b/src/routes/[perfil]/modalEditar.svelte @@ -0,0 +1,71 @@ + + + (post = null)}> + + + Editar Publicacion + + + { + callbackfn(e); + }} + > + + + + +
+ +

239}> + {post!.content.length} +

+ / 280 +
+ +

Modificar

+ +
+
+
+
+ +
+
+
diff --git a/src/types.d.ts b/src/types.d.ts index 6305a56..16d437a 100644 --- a/src/types.d.ts +++ b/src/types.d.ts @@ -1,6 +1,10 @@ export interface Post { _id: string; + id: string; authorId: string; + authorDisplayName: string; + authorImageUrl: string; + authorName: string; content: string; imageUrl?: string; parentPostId?: string; @@ -52,3 +56,21 @@ export interface CreatePostDto { imageUrl: string?; parentPostId: string?; } + +export interface PostResponseDto { + id: string; + authorId: string; + authorImageUrl: string?; + authorDisplayName: string; + authorName: string; + content: string; + imageUrl: string?; + parentPostId: string?; + likesCount: number; + repliesCount: number; + createdAt: string; + updatedAt: string?; + isEdited: boolean; + visibility: string; + hashtags: string[]?; +}