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) { | ||||
|     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)); | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -73,7 +73,10 @@ export async function createServer(host) { | |||
|  */ | ||||
| function saveServer(server) { | ||||
|     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)); | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -88,7 +88,7 @@ | |||
|         </div> | ||||
|     </header> | ||||
| 
 | ||||
|     {#if $logged_in} | ||||
|     {#if $account} | ||||
|     <div id="nav-items"> | ||||
|         <Button label="Timeline" | ||||
|                 on:click={() => handle_btn("timeline")} | ||||
|  | @ -180,6 +180,7 @@ | |||
|         </div> | ||||
|     </div> | ||||
|     {/if} | ||||
| 
 | ||||
|     <span class="version"> | ||||
|         campfire v{VERSION} | ||||
|         <br> | ||||
|  |  | |||
|  | @ -1,6 +1,5 @@ | |||
| <script> | ||||
|     import * as api from '$lib/api'; | ||||
|     import { get } from 'svelte/store'; | ||||
|     import { server } from '$lib/client/server'; | ||||
|     import { app } from '$lib/client/app'; | ||||
|     import { account } from '@cf/store/account'; | ||||
|  | @ -20,11 +19,13 @@ | |||
|     export let post; | ||||
| 
 | ||||
|     async function toggleBoost() { | ||||
|         if (!$app || !$app.token) return; | ||||
| 
 | ||||
|         let data; | ||||
|         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 | ||||
|             data = await api.boostPost(get(server).host, get(app).token, post.id); | ||||
|             data = await api.boostPost($server.host, $app.token, post.id); | ||||
|         if (!data) { | ||||
|             console.error(`Failed to boost post ${post.id}`); | ||||
|             return; | ||||
|  | @ -34,11 +35,13 @@ | |||
|     } | ||||
| 
 | ||||
|     async function toggleFavourite() { | ||||
|         if (!$app || !$app.token) return; | ||||
| 
 | ||||
|         let data; | ||||
|         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 | ||||
|             data = await api.favouritePost(get(server).host, get(app).token, post.id); | ||||
|             data = await api.favouritePost($server.host, $app.token, post.id); | ||||
|         if (!data) { | ||||
|             console.error(`Failed to favourite post ${post.id}`); | ||||
|             return; | ||||
|  | @ -69,13 +72,13 @@ | |||
|     <ActionButton type="reply" label="Reply" bind:count={post.reply_count} sound="post" disabled> | ||||
|         <ReplyIcon/> | ||||
|     </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/> | ||||
|         <svelte:fragment slot="activeIcon"> | ||||
|             <RepostIcon/> | ||||
|         </svelte:fragment> | ||||
|     </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/> | ||||
|         <svelte:fragment slot="activeIcon"> | ||||
|             <FavouriteIconFill/> | ||||
|  |  | |||
|  | @ -1,6 +1,7 @@ | |||
| <script> | ||||
|     import { onMount } from 'svelte'; | ||||
|     import { goto } from '$app/navigation'; | ||||
|     import { server } from '$lib/client/server'; | ||||
| 
 | ||||
|     import BoostContext from './BoostContext.svelte'; | ||||
|     import ReplyContext from './ReplyContext.svelte'; | ||||
|  | @ -31,7 +32,7 @@ | |||
|                 event.ctrlKey)) return; | ||||
|             if (event.key && event.key !== "Enter") return; | ||||
|         } | ||||
|         goto(`/post/${post.id}`); | ||||
|         goto(`/${$server.host}/${post.account.mention}/${post.id}`); | ||||
|     } | ||||
| 
 | ||||
|     let el; | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ | |||
|     import * as api from '$lib/api.js'; | ||||
|     import { server, capabilities } from '$lib/client/server.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 ReactionButton from './ReactionButton.svelte'; | ||||
|  | @ -11,6 +11,8 @@ | |||
|     export let post; | ||||
| 
 | ||||
|     async function toggleReaction(reaction) { | ||||
|         if (!$app || !$app.token) return; | ||||
| 
 | ||||
|         if ( | ||||
|             reaction.name.includes('@') && | ||||
|             !$server.capabilities.includes(capabilities.FOREIGN_REACTIONS) | ||||
|  | @ -18,9 +20,9 @@ | |||
| 
 | ||||
|         let data; | ||||
|         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 | ||||
|             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) { | ||||
|             console.error(`Failed to favourite post ${post.id}`); | ||||
|             return; | ||||
|  | @ -39,7 +41,7 @@ | |||
|                 on:click={() => toggleReaction(reaction)} | ||||
|                 bind:active={reaction.me} | ||||
|                 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} | ||||
|                 label=""> | ||||
|                 {#if reaction.url} | ||||
|  |  | |||
|  | @ -6,7 +6,6 @@ | |||
|     import { account, logged_in } from '$lib/stores/account.js'; | ||||
|     import { parseAccount } from '$lib/account.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 Modal from '@cf/ui/Modal.svelte'; | ||||
|  | @ -16,25 +15,25 @@ | |||
|     let show_composer = false; | ||||
| 
 | ||||
|     async function init() { | ||||
|         if (!get(app) || !get(app).token) { | ||||
|         if (!$app || !$app.token) { | ||||
|             account.set(false); | ||||
|             logged_in.set(false); | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         // 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; | ||||
| 
 | ||||
|         account.set(parseAccount(data)); | ||||
|         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 | ||||
|         const notif_data = await api.getNotifications( | ||||
|             get(server).host, | ||||
|             get(app).token, | ||||
|             get(last_read_notif_id) | ||||
|             $server.host, | ||||
|             $app.token, | ||||
|             $last_read_notif_id | ||||
|         ); | ||||
| 
 | ||||
|         if (!notif_data) return; | ||||
|  |  | |||
|  | @ -1,15 +1,15 @@ | |||
| <script> | ||||
|     import { page } from '$app/stores'; | ||||
|     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 LoginForm from '$lib/ui/LoginForm.svelte'; | ||||
|     import Button from '$lib/ui/Button.svelte'; | ||||
|     import Post from '$lib/ui/post/Post.svelte'; | ||||
| 
 | ||||
|     logged_in.subscribe(logged_in => { | ||||
|         if (logged_in) getTimeline(); | ||||
|     account.subscribe(account => { | ||||
|         if (account) getTimeline(); | ||||
|     }); | ||||
| 
 | ||||
|     document.addEventListener("scroll", () => { | ||||
|  | @ -20,7 +20,7 @@ | |||
|     }); | ||||
| </script> | ||||
| 
 | ||||
| {#if $logged_in} | ||||
| {#if $account} | ||||
|     <header> | ||||
|         <h1>Home</h1> | ||||
|         <nav> | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| export async function load({ params }) { | ||||
|     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 => { | ||||
|             if (!token) { | ||||
|                 error(400, { message: "Invalid auth code provided" }); | ||||
|                 return; | ||||
|             } | ||||
| 
 | ||||
|             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