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 InputGroupButton from './ui/input-group/input-group-button.svelte';
import InputGroupTextarea from './ui/input-group/input-group-textarea.svelte';
@@ -6,29 +6,76 @@
import ArrowUpIcon from '@lucide/svelte/icons/arrow-up';
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 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>
<InputGroup>
<InputGroupTextarea bind:value={mensaje} maxlength="255" placeholder="Alguna novedad?"
></InputGroupTextarea>
<!-- <hr class="w-full" /> -->
<InputGroupAddon align="block-end" class="bg-">
<div class="flex w-full justify-between">
<Kbd class="text-sm leading-none font-medium italic">
<p class:text-red-500={mensaje.length > 229}>
{mensaje.length}
</p>
/ 255
</Kbd>
<InputGroupButton
variant="default"
class="transform rounded-full transition-transform ease-in hover:scale-120"
size="xs"
>
<p>Publicar</p>
<ArrowUpIcon />
</InputGroupButton>
</div>
</InputGroupAddon>
</InputGroup>
<form onsubmit={(e: Event) => handlePost(e)}>
<InputGroup>
<InputGroupTextarea bind:value={mensaje} maxlength="280" placeholder="Alguna novedad?"
></InputGroupTextarea>
<InputGroupAddon align="block-end" class="bg-">
<div class="flex w-full justify-between">
<Kbd class="text-sm leading-none font-medium italic">
<p class:text-red-500={mensaje.length > 239}>
{mensaje.length}
</p>
/ 280
</Kbd>
<InputGroupButton
variant="default"
type="submit"
class="transform rounded-full transition-transform ease-in hover:scale-120"
size="xs"
>
<p>Publicar</p>
<ArrowUpIcon />
</InputGroupButton>
</div>
</InputGroupAddon>
</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 { sesionStore } from '@/stores/usuario';
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() {
const { subscribe } = apiBase;
let baseUrl: string = '';
@@ -20,9 +22,9 @@
baseUrl = value;
})();
const req = await fetch(`${baseUrl}/api/posts/timeline?pageSize=3`);
const req = await fetch(`${baseUrl}/timeline?pageSize=20`);
if (req.ok) {
posts = await req.json();
return await req.json();
}
}
</script>
@@ -35,36 +37,40 @@
{/if}
<hr />
{#if posts.length <= 0}
{#if $posts.length <= 0}
<Card>
<Content>
<p class=" text-center leading-7 not-first:mt-6">No hay Posts que mostrar</p>
</Content>
</Card>
{:else}
{#each posts as post}
{#each $posts as post}
<Card>
<Content>
<CardHeader>
<div class="flex flex-col space-y-2">
<div class="flex items-center justify-between">
<span class="text-sm font-medium">{post.authorId}</span>
<span class="text-xs text-muted-foreground"
>{post.createdAt.toLocaleDateString()}</span
>{post.createdAt.replace("T", " ").split(".")[0]}</span
>
</div>
<p class="text-sm">{post.content}</p>
{#if post.imageUrl}
<img src={post.imageUrl} alt="Post" class="mt-2 rounded-md" />
{/if}
<div class="flex items-center justify-between pt-2 text-xs text-muted-foreground">
<span>{post.likesCount} likes</span>
<span>{post.repliesCount} replies</span>
{#if post.isEdited}
<span>Editado</span>
{/if}
</div>
</div>
</CardHeader>
<Content>
<p class="text-sm">{post.content}</p>
{#if post.imageUrl}
<img src={post.imageUrl} alt="Post" class="mt-2 rounded-md" />
{/if}
</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.repliesCount} replies</span>
{#if post.isEdited}
<span>Editado</span>
{/if}
</div>
</CardFooter>
</Card>
{/each}
{/if}

21
src/types.d.ts vendored
View File

@@ -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;
@@ -34,13 +35,19 @@ export interface Sesion {
}
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?;
}