arreglado path /timeline y añadida logica crearpost al fetch

This commit is contained in:
2025-11-23 19:49:07 -03:00
parent 3fbacce3fe
commit 5159ebffd4
4 changed files with 134 additions and 52 deletions

View File

@@ -1,4 +1,4 @@
<script> <script lang="ts">
import InputGroupAddon from './ui/input-group/input-group-addon.svelte'; import InputGroupAddon from './ui/input-group/input-group-addon.svelte';
import InputGroupButton from './ui/input-group/input-group-button.svelte'; import InputGroupButton from './ui/input-group/input-group-button.svelte';
import InputGroupTextarea from './ui/input-group/input-group-textarea.svelte'; import InputGroupTextarea from './ui/input-group/input-group-textarea.svelte';
@@ -6,23 +6,69 @@
import ArrowUpIcon from '@lucide/svelte/icons/arrow-up'; import ArrowUpIcon from '@lucide/svelte/icons/arrow-up';
import Kbd from './ui/kbd/kbd.svelte'; import Kbd from './ui/kbd/kbd.svelte';
import { apiBase } from '@/stores/url';
import { sesionStore } from '@/stores/usuario';
import type { CreatePostDto } from '../../types';
import { addPost } from '@/stores/posts';
let mensaje = $state(''); let mensaje = $state('');
let cargando = $state(false);
let mostrarError = $state('');
async function handlePost(e: Event) {
e.preventDefault();
try {
const data: CreatePostDto = {
content: mensaje,
imageUrl: null,
parentPostId: null
};
const req = fetch($apiBase + '/api/posts', {
method: 'POST',
//credentials: 'include',
headers: {
'Content-Type': 'application/json',
"Authorization": `Bearer ${$sesionStore?.accessToken}`
},
body: JSON.stringify(data)
});
cargando = true;
const res = await req;
if (res.ok) {
mensaje = '';
const post = await res.json();
addPost(post);
return;
}
mostrarError = 'No se pudo crear el post.';
} catch {
mostrarError = 'Fallo al alcanzar el servidor';
} finally {
cargando = false;
}
}
</script> </script>
<InputGroup> <form onsubmit={(e: Event) => handlePost(e)}>
<InputGroupTextarea bind:value={mensaje} maxlength="255" placeholder="Alguna novedad?" <InputGroup>
<InputGroupTextarea bind:value={mensaje} maxlength="280" placeholder="Alguna novedad?"
></InputGroupTextarea> ></InputGroupTextarea>
<!-- <hr class="w-full" /> -->
<InputGroupAddon align="block-end" class="bg-"> <InputGroupAddon align="block-end" class="bg-">
<div class="flex w-full justify-between"> <div class="flex w-full justify-between">
<Kbd class="text-sm leading-none font-medium italic"> <Kbd class="text-sm leading-none font-medium italic">
<p class:text-red-500={mensaje.length > 229}> <p class:text-red-500={mensaje.length > 239}>
{mensaje.length} {mensaje.length}
</p> </p>
/ 255 / 280
</Kbd> </Kbd>
<InputGroupButton <InputGroupButton
variant="default" variant="default"
type="submit"
class="transform rounded-full transition-transform ease-in hover:scale-120" class="transform rounded-full transition-transform ease-in hover:scale-120"
size="xs" size="xs"
> >
@@ -31,4 +77,5 @@
</InputGroupButton> </InputGroupButton>
</div> </div>
</InputGroupAddon> </InputGroupAddon>
</InputGroup> </InputGroup>
</form>

22
src/lib/stores/posts.ts Normal file
View File

@@ -0,0 +1,22 @@
import { writable } from 'svelte/store';
import type { Post } from '../../types';
export const posts = writable<Post[]>([]);
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<Post>) => {
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));
};

View File

@@ -5,13 +5,15 @@
import { apiBase } from '@/stores/url'; import { apiBase } from '@/stores/url';
import { sesionStore } from '@/stores/usuario'; import { sesionStore } from '@/stores/usuario';
import CrearPost from '@/components/crear-post.svelte'; import CrearPost from '@/components/crear-post.svelte';
import CardHeader from '@/components/ui/card/card-header.svelte';
import CardFooter from '@/components/ui/card/card-footer.svelte';
import { posts, setPosts } from '@/stores/posts';
$effect(async () => {
setPosts(await getPosts());
$effect(() => {
getPosts();
}); });
let posts: Post[] = $state([]);
async function getPosts() { async function getPosts() {
const { subscribe } = apiBase; const { subscribe } = apiBase;
let baseUrl: string = ''; let baseUrl: string = '';
@@ -20,9 +22,9 @@
baseUrl = value; baseUrl = value;
})(); })();
const req = await fetch(`${baseUrl}/api/posts/timeline?pageSize=3`); const req = await fetch(`${baseUrl}/timeline?pageSize=20`);
if (req.ok) { if (req.ok) {
posts = await req.json(); return await req.json();
} }
} }
</script> </script>
@@ -35,36 +37,40 @@
{/if} {/if}
<hr /> <hr />
{#if posts.length <= 0} {#if $posts.length <= 0}
<Card> <Card>
<Content> <Content>
<p class=" text-center leading-7 not-first:mt-6">No hay Posts que mostrar</p> <p class=" text-center leading-7 not-first:mt-6">No hay Posts que mostrar</p>
</Content> </Content>
</Card> </Card>
{:else} {:else}
{#each posts as post} {#each $posts as post}
<Card> <Card>
<Content> <CardHeader>
<div class="flex flex-col space-y-2"> <div class="flex flex-col space-y-2">
<div class="flex items-center justify-between"> <div class="flex items-center justify-between">
<span class="text-sm font-medium">{post.authorId}</span> <span class="text-sm font-medium">{post.authorId}</span>
<span class="text-xs text-muted-foreground" <span class="text-xs text-muted-foreground"
>{post.createdAt.toLocaleDateString()}</span >{post.createdAt.replace("T", " ").split(".")[0]}</span
> >
</div> </div>
</div>
</CardHeader>
<Content>
<p class="text-sm">{post.content}</p> <p class="text-sm">{post.content}</p>
{#if post.imageUrl} {#if post.imageUrl}
<img src={post.imageUrl} alt="Post" class="mt-2 rounded-md" /> <img src={post.imageUrl} alt="Post" class="mt-2 rounded-md" />
{/if} {/if}
<div class="flex items-center justify-between pt-2 text-xs text-muted-foreground"> </Content>
<CardFooter>
<div class="flex items-center justify-between pt-2 gap-2 text-xs text-muted-foreground">
<span>{post.likesCount} likes</span> <span>{post.likesCount} likes</span>
<span>{post.repliesCount} replies</span> <span>{post.repliesCount} replies</span>
{#if post.isEdited} {#if post.isEdited}
<span>Editado</span> <span>Editado</span>
{/if} {/if}
</div> </div>
</div> </CardFooter>
</Content>
</Card> </Card>
{/each} {/each}
{/if} {/if}

9
src/types.d.ts vendored
View File

@@ -6,12 +6,13 @@ export interface Post {
parentPostId?: string; parentPostId?: string;
likesCount: number; likesCount: number;
repliesCount: number; repliesCount: number;
createdAt: Date; createdAt: string;
updatedAt?: Date; updatedAt?: Date;
isEdited: boolean; isEdited: boolean;
visibility: string; visibility: string;
hashtags?: string[]; hashtags?: string[];
} }
export interface User { export interface User {
_id: string; _id: string;
displayName: string; displayName: string;
@@ -44,3 +45,9 @@ export interface RegisterDto {
password: string?; password: string?;
displayName: string?; displayName: string?;
} }
export interface CreatePostDto {
content: string;
imageUrl: string?;
parentPostId: string?;
}