rewrite URLs to represent instance (#2)
This commit is contained in:
		
							parent
							
								
									41143cdddf
								
							
						
					
					
						commit
						a3fdd0007c
					
				
					 15 changed files with 199 additions and 168 deletions
				
			
		|  | @ -18,7 +18,10 @@ app.subscribe(app => { | ||||||
|  */ |  */ | ||||||
| function saveApp(app) { | function saveApp(app) { | ||||||
|     if (!browser) return; |     if (!browser) return; | ||||||
|     if (!app) localStorage.removeItem(app_name + "_app"); |     if (!app) { | ||||||
|  |         localStorage.removeItem(app_name + "_app"); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|     localStorage.setItem(app_name + "_app", JSON.stringify(app)); |     localStorage.setItem(app_name + "_app", JSON.stringify(app)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -73,7 +73,10 @@ export async function createServer(host) { | ||||||
|  */ |  */ | ||||||
| function saveServer(server) { | function saveServer(server) { | ||||||
|     if (!browser) return; |     if (!browser) return; | ||||||
|     if (!server) localStorage.removeItem(app_name + "_server"); |     if (!server) { | ||||||
|  |         localStorage.removeItem(app_name + "_server"); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|     localStorage.setItem(app_name + "_server", JSON.stringify(server)); |     localStorage.setItem(app_name + "_server", JSON.stringify(server)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -88,7 +88,7 @@ | ||||||
|         </div> |         </div> | ||||||
|     </header> |     </header> | ||||||
| 
 | 
 | ||||||
|     {#if $logged_in} |     {#if $account} | ||||||
|     <div id="nav-items"> |     <div id="nav-items"> | ||||||
|         <Button label="Timeline" |         <Button label="Timeline" | ||||||
|                 on:click={() => handle_btn("timeline")} |                 on:click={() => handle_btn("timeline")} | ||||||
|  | @ -180,6 +180,7 @@ | ||||||
|         </div> |         </div> | ||||||
|     </div> |     </div> | ||||||
|     {/if} |     {/if} | ||||||
|  | 
 | ||||||
|     <span class="version"> |     <span class="version"> | ||||||
|         campfire v{VERSION} |         campfire v{VERSION} | ||||||
|         <br> |         <br> | ||||||
|  |  | ||||||
|  | @ -1,6 +1,5 @@ | ||||||
| <script> | <script> | ||||||
|     import * as api from '$lib/api'; |     import * as api from '$lib/api'; | ||||||
|     import { get } from 'svelte/store'; |  | ||||||
|     import { server } from '$lib/client/server'; |     import { server } from '$lib/client/server'; | ||||||
|     import { app } from '$lib/client/app'; |     import { app } from '$lib/client/app'; | ||||||
|     import { account } from '@cf/store/account'; |     import { account } from '@cf/store/account'; | ||||||
|  | @ -20,11 +19,13 @@ | ||||||
|     export let post; |     export let post; | ||||||
| 
 | 
 | ||||||
|     async function toggleBoost() { |     async function toggleBoost() { | ||||||
|  |         if (!$app || !$app.token) return; | ||||||
|  | 
 | ||||||
|         let data; |         let data; | ||||||
|         if (post.boosted) |         if (post.boosted) | ||||||
|             data = await api.unboostPost(get(server).host, get(app).token, post.id); |             data = await api.unboostPost($server.host, $app.token, post.id); | ||||||
|         else |         else | ||||||
|             data = await api.boostPost(get(server).host, get(app).token, post.id); |             data = await api.boostPost($server.host, $app.token, post.id); | ||||||
|         if (!data) { |         if (!data) { | ||||||
|             console.error(`Failed to boost post ${post.id}`); |             console.error(`Failed to boost post ${post.id}`); | ||||||
|             return; |             return; | ||||||
|  | @ -34,11 +35,13 @@ | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     async function toggleFavourite() { |     async function toggleFavourite() { | ||||||
|  |         if (!$app || !$app.token) return; | ||||||
|  | 
 | ||||||
|         let data; |         let data; | ||||||
|         if (post.favourited) |         if (post.favourited) | ||||||
|             data = await api.unfavouritePost(get(server).host, get(app).token, post.id); |             data = await api.unfavouritePost($server.host, $app.token, post.id); | ||||||
|         else |         else | ||||||
|             data = await api.favouritePost(get(server).host, get(app).token, post.id); |             data = await api.favouritePost($server.host, $app.token, post.id); | ||||||
|         if (!data) { |         if (!data) { | ||||||
|             console.error(`Failed to favourite post ${post.id}`); |             console.error(`Failed to favourite post ${post.id}`); | ||||||
|             return; |             return; | ||||||
|  | @ -69,13 +72,13 @@ | ||||||
|     <ActionButton type="reply" label="Reply" bind:count={post.reply_count} sound="post" disabled> |     <ActionButton type="reply" label="Reply" bind:count={post.reply_count} sound="post" disabled> | ||||||
|         <ReplyIcon/> |         <ReplyIcon/> | ||||||
|     </ActionButton> |     </ActionButton> | ||||||
|     <ActionButton type="boost" label="Boost" on:click={toggleBoost} bind:active={post.boosted} bind:count={post.boost_count} sound="boost"> |     <ActionButton type="boost" label="Boost" on:click={toggleBoost} bind:active={post.boosted} bind:count={post.boost_count} sound="boost" disabled={!$account}> | ||||||
|         <RepostIcon/> |         <RepostIcon/> | ||||||
|         <svelte:fragment slot="activeIcon"> |         <svelte:fragment slot="activeIcon"> | ||||||
|             <RepostIcon/> |             <RepostIcon/> | ||||||
|         </svelte:fragment> |         </svelte:fragment> | ||||||
|     </ActionButton> |     </ActionButton> | ||||||
|     <ActionButton type="favourite" label="Favourite" on:click={toggleFavourite} bind:active={post.favourited} bind:count={post.favourite_count}> |     <ActionButton type="favourite" label="Favourite" on:click={toggleFavourite} bind:active={post.favourited} bind:count={post.favourite_count} disabled={!$account}> | ||||||
|         <FavouriteIcon/> |         <FavouriteIcon/> | ||||||
|         <svelte:fragment slot="activeIcon"> |         <svelte:fragment slot="activeIcon"> | ||||||
|             <FavouriteIconFill/> |             <FavouriteIconFill/> | ||||||
|  |  | ||||||
|  | @ -1,6 +1,7 @@ | ||||||
| <script> | <script> | ||||||
|     import { onMount } from 'svelte'; |     import { onMount } from 'svelte'; | ||||||
|     import { goto } from '$app/navigation'; |     import { goto } from '$app/navigation'; | ||||||
|  |     import { server } from '$lib/client/server'; | ||||||
| 
 | 
 | ||||||
|     import BoostContext from './BoostContext.svelte'; |     import BoostContext from './BoostContext.svelte'; | ||||||
|     import ReplyContext from './ReplyContext.svelte'; |     import ReplyContext from './ReplyContext.svelte'; | ||||||
|  | @ -31,7 +32,7 @@ | ||||||
|                 event.ctrlKey)) return; |                 event.ctrlKey)) return; | ||||||
|             if (event.key && event.key !== "Enter") return; |             if (event.key && event.key !== "Enter") return; | ||||||
|         } |         } | ||||||
|         goto(`/post/${post.id}`); |         goto(`/${$server.host}/${post.account.mention}/${post.id}`); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     let el; |     let el; | ||||||
|  |  | ||||||
|  | @ -2,7 +2,7 @@ | ||||||
|     import * as api from '$lib/api.js'; |     import * as api from '$lib/api.js'; | ||||||
|     import { server, capabilities } from '$lib/client/server.js'; |     import { server, capabilities } from '$lib/client/server.js'; | ||||||
|     import { app } from '$lib/client/app.js'; |     import { app } from '$lib/client/app.js'; | ||||||
|     import { get } from 'svelte/store'; |     import { account } from '@cf/store/account'; | ||||||
|     import { parseReactions } from '$lib/post.js'; |     import { parseReactions } from '$lib/post.js'; | ||||||
| 
 | 
 | ||||||
|     import ReactionButton from './ReactionButton.svelte'; |     import ReactionButton from './ReactionButton.svelte'; | ||||||
|  | @ -11,6 +11,8 @@ | ||||||
|     export let post; |     export let post; | ||||||
| 
 | 
 | ||||||
|     async function toggleReaction(reaction) { |     async function toggleReaction(reaction) { | ||||||
|  |         if (!$app || !$app.token) return; | ||||||
|  | 
 | ||||||
|         if ( |         if ( | ||||||
|             reaction.name.includes('@') && |             reaction.name.includes('@') && | ||||||
|             !$server.capabilities.includes(capabilities.FOREIGN_REACTIONS) |             !$server.capabilities.includes(capabilities.FOREIGN_REACTIONS) | ||||||
|  | @ -18,9 +20,9 @@ | ||||||
| 
 | 
 | ||||||
|         let data; |         let data; | ||||||
|         if (reaction.me) |         if (reaction.me) | ||||||
|             data = await api.unreactPost(get(server).host, get(app).token, post.id, reaction.name); |             data = await api.unreactPost($server.host, $app.token, post.id, reaction.name); | ||||||
|         else |         else | ||||||
|             data = await api.reactPost(get(server).host, get(app).token, post.id, reaction.name); |             data = await api.reactPost($server.host, $app.token, post.id, reaction.name); | ||||||
|         if (!data) { |         if (!data) { | ||||||
|             console.error(`Failed to favourite post ${post.id}`); |             console.error(`Failed to favourite post ${post.id}`); | ||||||
|             return; |             return; | ||||||
|  | @ -39,7 +41,7 @@ | ||||||
|                 on:click={() => toggleReaction(reaction)} |                 on:click={() => toggleReaction(reaction)} | ||||||
|                 bind:active={reaction.me} |                 bind:active={reaction.me} | ||||||
|                 bind:count={reaction.count} |                 bind:count={reaction.count} | ||||||
|                 disabled={reaction.name.includes('@') && !$server.capabilities.includes(capabilities.FOREIGN_REACTIONS)} |                 disabled={!$account || (reaction.name.includes('@') && !$server.capabilities.includes(capabilities.FOREIGN_REACTIONS))} | ||||||
|                 title={reaction.name} |                 title={reaction.name} | ||||||
|                 label=""> |                 label=""> | ||||||
|                 {#if reaction.url} |                 {#if reaction.url} | ||||||
|  |  | ||||||
|  | @ -6,7 +6,6 @@ | ||||||
|     import { account, logged_in } from '$lib/stores/account.js'; |     import { account, logged_in } from '$lib/stores/account.js'; | ||||||
|     import { parseAccount } from '$lib/account.js'; |     import { parseAccount } from '$lib/account.js'; | ||||||
|     import { unread_notif_count, last_read_notif_id } from '$lib/notifications.js'; |     import { unread_notif_count, last_read_notif_id } from '$lib/notifications.js'; | ||||||
|     import { get } from 'svelte/store'; |  | ||||||
| 
 | 
 | ||||||
|     import Navigation from '$lib/ui/Navigation.svelte'; |     import Navigation from '$lib/ui/Navigation.svelte'; | ||||||
|     import Modal from '@cf/ui/Modal.svelte'; |     import Modal from '@cf/ui/Modal.svelte'; | ||||||
|  | @ -16,25 +15,25 @@ | ||||||
|     let show_composer = false; |     let show_composer = false; | ||||||
| 
 | 
 | ||||||
|     async function init() { |     async function init() { | ||||||
|         if (!get(app) || !get(app).token) { |         if (!$app || !$app.token) { | ||||||
|             account.set(false); |             account.set(false); | ||||||
|             logged_in.set(false); |             logged_in.set(false); | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         // logged in- attempt to retrieve using token |         // logged in- attempt to retrieve using token | ||||||
|         const data = await api.verifyCredentials(get(server).host, get(app).token); |         const data = await api.verifyCredentials($server.host, $app.token); | ||||||
|         if (!data) return; |         if (!data) return; | ||||||
| 
 | 
 | ||||||
|         account.set(parseAccount(data)); |         account.set(parseAccount(data)); | ||||||
|         logged_in.set(true); |         logged_in.set(true); | ||||||
|         console.log(`Logged in as @${get(account).username}@${get(account).host}`); |         console.log(`Logged in as @${$account.username}@${$account.host}`); | ||||||
| 
 | 
 | ||||||
|         // spin up async task to fetch notifications |         // spin up async task to fetch notifications | ||||||
|         const notif_data = await api.getNotifications( |         const notif_data = await api.getNotifications( | ||||||
|             get(server).host, |             $server.host, | ||||||
|             get(app).token, |             $app.token, | ||||||
|             get(last_read_notif_id) |             $last_read_notif_id | ||||||
|         ); |         ); | ||||||
| 
 | 
 | ||||||
|         if (!notif_data) return; |         if (!notif_data) return; | ||||||
|  |  | ||||||
|  | @ -1,15 +1,15 @@ | ||||||
| <script> | <script> | ||||||
|     import { page } from '$app/stores'; |     import { page } from '$app/stores'; | ||||||
|     import { get } from 'svelte/store'; |     import { get } from 'svelte/store'; | ||||||
|     import { logged_in } from '$lib/stores/account.js'; |     import { account } from '$lib/stores/account.js'; | ||||||
|     import { timeline, getTimeline } from '$lib/timeline.js'; |     import { timeline, getTimeline } from '$lib/timeline.js'; | ||||||
| 
 | 
 | ||||||
|     import LoginForm from '$lib/ui/LoginForm.svelte'; |     import LoginForm from '$lib/ui/LoginForm.svelte'; | ||||||
|     import Button from '$lib/ui/Button.svelte'; |     import Button from '$lib/ui/Button.svelte'; | ||||||
|     import Post from '$lib/ui/post/Post.svelte'; |     import Post from '$lib/ui/post/Post.svelte'; | ||||||
| 
 | 
 | ||||||
|     logged_in.subscribe(logged_in => { |     account.subscribe(account => { | ||||||
|         if (logged_in) getTimeline(); |         if (account) getTimeline(); | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     document.addEventListener("scroll", () => { |     document.addEventListener("scroll", () => { | ||||||
|  | @ -20,7 +20,7 @@ | ||||||
|     }); |     }); | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
| {#if $logged_in} | {#if $account} | ||||||
|     <header> |     <header> | ||||||
|         <h1>Home</h1> |         <h1>Home</h1> | ||||||
|         <nav> |         <nav> | ||||||
|  |  | ||||||
|  | @ -1,5 +1,5 @@ | ||||||
| export async function load({ params }) { | export async function load({ params }) { | ||||||
|     return { |     return { | ||||||
|         post_id: params.id |         server_domain: params.server | ||||||
|     }; |     }; | ||||||
| } | } | ||||||
							
								
								
									
										8
									
								
								src/routes/[server]/[account]/+page.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								src/routes/[server]/[account]/+page.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,8 @@ | ||||||
|  | import { error } from '@sveltejs/kit'; | ||||||
|  | 
 | ||||||
|  | export async function load({ params }) { | ||||||
|  |     return error(404, 'Not Found'); | ||||||
|  |     // return {
 | ||||||
|  |     //     account_name: params.account
 | ||||||
|  |     // };
 | ||||||
|  | } | ||||||
							
								
								
									
										7
									
								
								src/routes/[server]/[account]/[post]/+page.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								src/routes/[server]/[account]/[post]/+page.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,7 @@ | ||||||
|  | export async function load({ params }) { | ||||||
|  |     return { | ||||||
|  |         server_host: params.server, | ||||||
|  |         account_handle: params.account, | ||||||
|  |         post_id: params.post | ||||||
|  |     }; | ||||||
|  | } | ||||||
							
								
								
									
										143
									
								
								src/routes/[server]/[account]/[post]/+page.svelte
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										143
									
								
								src/routes/[server]/[account]/[post]/+page.svelte
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,143 @@ | ||||||
|  | <script> | ||||||
|  |     import * as api from '$lib/api.js'; | ||||||
|  |     import { server, createServer } from '$lib/client/server.js'; | ||||||
|  |     import { app } from '$lib/client/app.js'; | ||||||
|  |     import { parsePost } from '$lib/post.js'; | ||||||
|  |     import { goto, afterNavigate } from '$app/navigation'; | ||||||
|  |     import { base as previous_page } from '$app/paths' | ||||||
|  | 
 | ||||||
|  |     import Post from '$lib/ui/post/Post.svelte'; | ||||||
|  |     import Button from '$lib/ui/Button.svelte'; | ||||||
|  | 
 | ||||||
|  |     export let data; | ||||||
|  | 
 | ||||||
|  |     let post; | ||||||
|  |     let error = false; | ||||||
|  | 
 | ||||||
|  |     if (($server && $server.host === data.server_host) && $app) { | ||||||
|  |         post = fetchPost(data.post_id, $app.token); | ||||||
|  |     } else { | ||||||
|  |         post = createServer(data.server_host).then(new_server => { | ||||||
|  |             server.set(new_server); | ||||||
|  |             if (!$server) { | ||||||
|  |                 error = `Failed to connect to <code>${data.server_host}</code>.`; | ||||||
|  |                 console.error(`Failed to connect to ${data.server_host}.`); | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  |             return post = fetchPost(data.post_id, null); | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     afterNavigate(({from}) => { | ||||||
|  |         previous_page = from?.url.pathname || previous_page | ||||||
|  |     }) | ||||||
|  | 
 | ||||||
|  |     async function fetchPost(post_id, token) { | ||||||
|  |         const post_data = await api.getPost($server.host, token, post_id); | ||||||
|  |         if (!post_data || post_data.error) { | ||||||
|  |             error = `Failed to retrieve post <code>${post_id}</code>.`; | ||||||
|  |             console.error(`Failed to retrieve post ${post_id}.`); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  |         let post = await parsePost(post_data, 0); | ||||||
|  | 
 | ||||||
|  |         const post_context = await api.getPostContext($server.host, token, post_id); | ||||||
|  | 
 | ||||||
|  |         if (!post_context || !post_context.ancestors || !post_context.descendants) | ||||||
|  |             return post; | ||||||
|  | 
 | ||||||
|  |         // handle ancestors (above post) | ||||||
|  |         let thread_top = post; | ||||||
|  |         while (post_context.ancestors.length > 0) { | ||||||
|  |             thread_top.reply = await parsePost(post_context.ancestors.pop(), 0); | ||||||
|  |             thread_top = thread_top.reply; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // handle descendants (below post) | ||||||
|  |         post.replies = []; | ||||||
|  |         for (let i in post_context.descendants) { | ||||||
|  |             post.replies.push(parsePost(post_context.descendants[i], 0)); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return post; | ||||||
|  |     } | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | {#await post} | ||||||
|  |     <div class="loading throb"> | ||||||
|  |         <span>loading post...</span> | ||||||
|  |     </div> | ||||||
|  | {:then post} | ||||||
|  |     {#if error} | ||||||
|  |         <p>{@html error}</p> | ||||||
|  |     {:else} | ||||||
|  |         <header> | ||||||
|  |             {#if previous_page} | ||||||
|  |                 <nav> | ||||||
|  |                     <Button centered on:click={() => {goto(previous_page)}}>Back</Button> | ||||||
|  |                 </nav> | ||||||
|  |             {/if} | ||||||
|  |             <img src={post.account.avatar_url} type={post.account.avatar_type || "image/png"} alt="" width="40" height="40" class="header-avatar" loading="lazy" decoding="async"> | ||||||
|  |             <h1> | ||||||
|  |                 Post by {@html post.account.rich_name} | ||||||
|  |             </h1> | ||||||
|  |         </header> | ||||||
|  |          | ||||||
|  |         <div id="feed" role="feed"> | ||||||
|  |             <Post post_data={post} focused /> | ||||||
|  |             <br> | ||||||
|  |             {#each post.replies as reply} | ||||||
|  |                 {#await reply then reply} | ||||||
|  |                     <Post post_data={reply} /> | ||||||
|  |                 {/await} | ||||||
|  |             {/each} | ||||||
|  |         </div> | ||||||
|  |     {/if} | ||||||
|  | {/await} | ||||||
|  | 
 | ||||||
|  | <style> | ||||||
|  |     header { | ||||||
|  |         width: 100%; | ||||||
|  |         height: 64px; | ||||||
|  |         margin: 16px 0 8px 0; | ||||||
|  |         display: flex; | ||||||
|  |         flex-direction: row; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     header .header-avatar { | ||||||
|  |         width: 40px; | ||||||
|  |         height: 40px; | ||||||
|  |         margin: auto 0; | ||||||
|  |         border-radius: 4px; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     header h1 { | ||||||
|  |         margin: auto auto auto 8px; | ||||||
|  |         font-size: 1.5em; | ||||||
|  |         text-overflow: ellipsis; | ||||||
|  |         overflow: hidden; | ||||||
|  |         white-space: nowrap; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     header nav { | ||||||
|  |         margin-right: 8px; | ||||||
|  |         display: flex; | ||||||
|  |         flex-direction: row; | ||||||
|  |         align-items: center; | ||||||
|  |         gap: 8px; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     #feed { | ||||||
|  |         margin-bottom: 20vh; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .loading { | ||||||
|  |         width: 100%; | ||||||
|  |         height: 80vh; | ||||||
|  |         display: flex; | ||||||
|  |         justify-content: center; | ||||||
|  |         align-items: center; | ||||||
|  |         font-size: 2em; | ||||||
|  |         font-weight: bold; | ||||||
|  |     } | ||||||
|  | </style> | ||||||
|  | @ -19,7 +19,6 @@ | ||||||
|         api.getToken(get(server).host, get(app).id, get(app).secret, auth_code).then(token => { |         api.getToken(get(server).host, get(app).id, get(app).secret, auth_code).then(token => { | ||||||
|             if (!token) { |             if (!token) { | ||||||
|                 error(400, { message: "Invalid auth code provided" }); |                 error(400, { message: "Invalid auth code provided" }); | ||||||
|                 return; |  | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             app.update(app => { |             app.update(app => { | ||||||
|  |  | ||||||
|  | @ -1,5 +0,0 @@ | ||||||
| import { error } from '@sveltejs/kit'; |  | ||||||
| 
 |  | ||||||
| export function load(event) { |  | ||||||
|     error(404, 'Not Found'); |  | ||||||
| } |  | ||||||
|  | @ -1,133 +0,0 @@ | ||||||
| <script> |  | ||||||
|     import * as api from '$lib/api.js'; |  | ||||||
|     import { logged_in } from '$lib/stores/account.js'; |  | ||||||
|     import { server } from '$lib/client/server.js'; |  | ||||||
|     import { app } from '$lib/client/app.js'; |  | ||||||
|     import { parsePost } from '$lib/post.js'; |  | ||||||
|     import { get } from 'svelte/store'; |  | ||||||
|     import { goto, afterNavigate } from '$app/navigation'; |  | ||||||
|     import { base } from '$app/paths' |  | ||||||
| 
 |  | ||||||
|     import Post from '$lib/ui/post/Post.svelte'; |  | ||||||
|     import Button from '$lib/ui/Button.svelte'; |  | ||||||
| 
 |  | ||||||
|     export let data; |  | ||||||
|     let error = false; |  | ||||||
| 
 |  | ||||||
|     if (!get(logged_in)) goto("/"); |  | ||||||
| 
 |  | ||||||
|     let previous_page = base; |  | ||||||
| 
 |  | ||||||
|     afterNavigate(({from}) => { |  | ||||||
|         previous_page = from?.url.pathname || previous_page |  | ||||||
|     }) |  | ||||||
| 
 |  | ||||||
|     $: post = (async resolve => { |  | ||||||
|         const post_data = await api.getPost(get(server).host, get(app).token, data.post_id); |  | ||||||
|         if (!post_data) { |  | ||||||
|             error = `Failed to retrieve post <code>${data.post_id}</code>.`; |  | ||||||
|             console.error(`Failed to retrieve post ${data.post_id}.`); |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|         let post = await parsePost(post_data, 0); |  | ||||||
| 
 |  | ||||||
|         const post_context = await api.getPostContext(get(server).host, get(app).token, data.post_id); |  | ||||||
| 
 |  | ||||||
|         if (!post_context || !post_context.ancestors || !post_context.descendants) |  | ||||||
|             return post; |  | ||||||
| 
 |  | ||||||
|         // handle ancestors (above post) |  | ||||||
|         let thread_top = post; |  | ||||||
|         while (post_context.ancestors.length > 0) { |  | ||||||
|             thread_top.reply = await parsePost(post_context.ancestors.pop(), 0); |  | ||||||
|             thread_top = thread_top.reply; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // handle descendants (below post) |  | ||||||
|         post.replies = []; |  | ||||||
|         for (let i in post_context.descendants) { |  | ||||||
|             post.replies.push(parsePost(post_context.descendants[i], 0)); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return post; |  | ||||||
|     })(); |  | ||||||
| </script> |  | ||||||
| 
 |  | ||||||
| {#if !error} |  | ||||||
| <header> |  | ||||||
|     {#await post then post} |  | ||||||
|         <nav> |  | ||||||
|             <Button centered on:click={() => {goto(previous_page)}}>Back</Button> |  | ||||||
|         </nav> |  | ||||||
|         <img src={post.account.avatar_url} type={post.account.avatar_type} alt="" width="40" height="40" class="header-avatar" loading="lazy" decoding="async"> |  | ||||||
|         <h1> |  | ||||||
|             Post by {@html post.account.rich_name} |  | ||||||
|         </h1> |  | ||||||
|     {/await} |  | ||||||
| </header> |  | ||||||
| 
 |  | ||||||
| <div id="feed" role="feed"> |  | ||||||
|     {#await post} |  | ||||||
|         <div class="loading throb"> |  | ||||||
|             <span>loading post...</span> |  | ||||||
|         </div> |  | ||||||
|     {:then post} |  | ||||||
|         <Post post_data={post} focused /> |  | ||||||
|         <br> |  | ||||||
|         {#each post.replies as reply} |  | ||||||
|             {#await reply then reply} |  | ||||||
|                 <Post post_data={reply} /> |  | ||||||
|             {/await} |  | ||||||
|         {/each} |  | ||||||
|     {/await} |  | ||||||
| </div> |  | ||||||
| {:else} |  | ||||||
|     <p>{@html error}</p> |  | ||||||
| {/if} |  | ||||||
| 
 |  | ||||||
| <style> |  | ||||||
|     header { |  | ||||||
|         width: 100%; |  | ||||||
|         height: 64px; |  | ||||||
|         margin: 16px 0 8px 0; |  | ||||||
|         display: flex; |  | ||||||
|         flex-direction: row; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     header .header-avatar { |  | ||||||
|         width: 40px; |  | ||||||
|         height: 40px; |  | ||||||
|         margin: auto 0; |  | ||||||
|         border-radius: 4px; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     header h1 { |  | ||||||
|         margin: auto auto auto 8px; |  | ||||||
|         font-size: 1.5em; |  | ||||||
|         text-overflow: ellipsis; |  | ||||||
|         overflow: hidden; |  | ||||||
|         white-space: nowrap; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     header nav { |  | ||||||
|         margin-right: 8px; |  | ||||||
|         display: flex; |  | ||||||
|         flex-direction: row; |  | ||||||
|         align-items: center; |  | ||||||
|         gap: 8px; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     #feed { |  | ||||||
|         margin-bottom: 20vh; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     .loading { |  | ||||||
|         width: 100%; |  | ||||||
|         height: 80vh; |  | ||||||
|         display: flex; |  | ||||||
|         justify-content: center; |  | ||||||
|         align-items: center; |  | ||||||
|         font-size: 2em; |  | ||||||
|         font-weight: bold; |  | ||||||
|     } |  | ||||||
| </style> |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue