slight post UI polish
This commit is contained in:
		
							parent
							
								
									c8f38bfd4a
								
							
						
					
					
						commit
						7715e747a2
					
				
					 8 changed files with 185 additions and 202 deletions
				
			
		|  | @ -145,7 +145,6 @@ | |||
|                     {`@${client.user.username}@${client.user.host}`} | ||||
|                 </span> | ||||
|             </div> | ||||
|             <!-- <button class="settings" aria-label={`Account: ${client.user.username}@${client.user.host}`} on:click={() => play_sound()}>🔧</button> --> | ||||
|         </div> | ||||
|     </div> | ||||
|     {/if} | ||||
|  | @ -153,7 +152,8 @@ | |||
|         campfire v{VERSION} | ||||
|         <br> | ||||
|         <ul> | ||||
|             <li><a href="https://git.arimelody.me/ari/spacesocial-client">source</a></li> | ||||
|             <li><a href="https://git.arimelody.me/blisstown/campfire">source</a></li> | ||||
|             <li><a href="https://github.com/blisstown/campfire/issues">issues</a></li> | ||||
|         </ul> | ||||
|     </span> | ||||
| </div> | ||||
|  |  | |||
							
								
								
									
										101
									
								
								src/lib/ui/post/ActionBar.svelte
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								src/lib/ui/post/ActionBar.svelte
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,101 @@ | |||
| <script> | ||||
|     import { Client } from '../../client/client.js'; | ||||
|     import * as api from '../../client/api.js'; | ||||
|     import { get } from 'svelte/store'; | ||||
| 
 | ||||
|     import ActionButton from './ActionButton.svelte'; | ||||
| 
 | ||||
|     import ReplyIcon from '../../../img/icons/reply.svg'; | ||||
|     import RepostIcon from '../../../img/icons/repost.svg'; | ||||
|     import FavouriteIcon from '../../../img/icons/like.svg'; | ||||
|     import FavouriteIconFill from '../../../img/icons/like_fill.svg'; | ||||
|     import ReactIcon from '../../../img/icons/react.svg'; | ||||
|     import QuoteIcon from '../../../img/icons/quote.svg'; | ||||
|     import MoreIcon from '../../../img/icons/more.svg'; | ||||
| 
 | ||||
|     export let post; | ||||
| 
 | ||||
|     async function toggleBoost() { | ||||
|         let client = get(Client.get()); | ||||
|         let data; | ||||
|         if (post.boosted) | ||||
|             data = await client.unboostPost(post.id); | ||||
|         else | ||||
|             data = await client.boostPost(post.id); | ||||
|         if (!data) { | ||||
|             console.error(`Failed to boost post ${post.id}`); | ||||
|             return; | ||||
|         } | ||||
|         post.boosted = data.reblog ? data.reblog.reblogged : data.boosted; | ||||
|         post.boost_count = data.reblog ? data.reblog.reblogs_count : data.reblogs_count; | ||||
|     } | ||||
| 
 | ||||
|     async function toggleFavourite() { | ||||
|         let client = get(Client.get()); | ||||
|         let data; | ||||
|         if (post.favourited) | ||||
|             data = await client.unfavouritePost(post.id); | ||||
|         else | ||||
|             data = await client.favouritePost(post.id); | ||||
|         if (!data) { | ||||
|             console.error(`Failed to favourite post ${post.id}`); | ||||
|             return; | ||||
|         } | ||||
|         post.favourited = data.favourited; | ||||
|         post.favourite_count = data.favourites_count; | ||||
|         if (data.reactions) post.reactions = api.parseReactions(data.reactions); | ||||
|     } | ||||
| 
 | ||||
|     async function toggleReaction(reaction) { | ||||
|         if (reaction.name.includes('@')) return; | ||||
|         let client = get(Client.get()); | ||||
| 
 | ||||
|         let data; | ||||
|         if (reaction.me) | ||||
|             data = await client.unreactPost(post.id, reaction.name); | ||||
|         else | ||||
|             data = await client.reactPost(post.id, reaction.name); | ||||
|         if (!data) { | ||||
|             console.error(`Failed to favourite post ${post.id}`); | ||||
|             return; | ||||
|         } | ||||
|         post.favourited = data.favourited; | ||||
|         post.favourite_count = data.favourites_count; | ||||
|         if (data.reactions) post.reactions = api.parseReactions(data.reactions); | ||||
|     } | ||||
| </script> | ||||
| 
 | ||||
| <div class="post-actions" aria-label="Post actions" on:click|stopPropagation on:keydown|stopPropagation> | ||||
|     <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"> | ||||
|         <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}> | ||||
|         <FavouriteIcon/> | ||||
|         <svelte:fragment slot="activeIcon"> | ||||
|             <FavouriteIconFill/> | ||||
|         </svelte:fragment> | ||||
|     </ActionButton> | ||||
|     <ActionButton type="quote" label="Quote" disabled> | ||||
|         <QuoteIcon/> | ||||
|     </ActionButton> | ||||
|     <ActionButton type="more" label="More" disabled> | ||||
|         <MoreIcon/> | ||||
|     </ActionButton> | ||||
| </div> | ||||
| 
 | ||||
| <style> | ||||
|     .post-actions { | ||||
|         width: fit-content; | ||||
|         height: 36px; | ||||
|         margin-top: 8px; | ||||
|         display: flex; | ||||
|         flex-direction: row; | ||||
|         gap: 4px; | ||||
|     } | ||||
| </style> | ||||
|  | @ -74,7 +74,8 @@ | |||
| 
 | ||||
|     button.disabled { | ||||
|         opacity: .5; | ||||
|         cursor: initial; | ||||
|         /* cursor: initial; */ | ||||
|         cursor: not-allowed; | ||||
|     } | ||||
| 
 | ||||
|     .icon { | ||||
|  |  | |||
|  | @ -25,23 +25,25 @@ | |||
|         {#if post.text} | ||||
|             <span class="post-text">{@html rich_text}</span> | ||||
|         {/if} | ||||
|         <div class="post-media-container" data-count={post.files.length}> | ||||
|             {#each post.files as file} | ||||
|                 <div class="post-media {file.type}" on:click|stopPropagation={null}> | ||||
|                     {#if file.type === "image"} | ||||
|                         <a href={file.url} target="_blank"> | ||||
|                             <img src={file.url} alt={file.description} title={file.description} height="200" loading="lazy" decoding="async"> | ||||
|                         </a> | ||||
|                     {:else if file.type === "video"} | ||||
|                         <video controls height="200"> | ||||
|                             <source src={file.url} alt={file.description} title={file.description} type={file.url.endsWith('.mp4') ? 'video/mp4' : 'video/webm'}> | ||||
|                             <p>{file.description}   <a href={file.url}>[link]</a></p> | ||||
|                             <!-- <media src={file.url} alt={file.description} loading="lazy" decoding="async"> --> | ||||
|                         </video> | ||||
|                     {/if} | ||||
|                 </div> | ||||
|             {/each} | ||||
|         </div> | ||||
|         {#if post.files && post.files.length > 0} | ||||
|             <div class="post-media-container" data-count={post.files.length}> | ||||
|                 {#each post.files as file} | ||||
|                     <div class="post-media {file.type}" on:click|stopPropagation={null}> | ||||
|                         {#if file.type === "image"} | ||||
|                             <a href={file.url} target="_blank"> | ||||
|                                 <img src={file.url} alt={file.description} title={file.description} height="200" loading="lazy" decoding="async"> | ||||
|                             </a> | ||||
|                         {:else if file.type === "video"} | ||||
|                             <video controls height="200"> | ||||
|                                 <source src={file.url} alt={file.description} title={file.description} type={file.url.endsWith('.mp4') ? 'video/mp4' : 'video/webm'}> | ||||
|                                 <p>{file.description}   <a href={file.url}>[link]</a></p> | ||||
|                                 <!-- <media src={file.url} alt={file.description} loading="lazy" decoding="async"> --> | ||||
|                             </video> | ||||
|                         {/if} | ||||
|                     </div> | ||||
|                 {/each} | ||||
|             </div> | ||||
|         {/if} | ||||
|         {#if post.boost && post.text} | ||||
|             <p class="post-warning"><strong>this is quoting a post! quotes are not supported yet.</strong></p> | ||||
|             <!-- TODO: quotes support --> | ||||
|  |  | |||
|  | @ -1,25 +1,15 @@ | |||
| <script> | ||||
|     import { parseOne as parseEmoji } from '../../emoji.js'; | ||||
|     import { play_sound } from '../../sound.js'; | ||||
|     import { onMount } from 'svelte'; | ||||
|     import { goto } from '$app/navigation'; | ||||
| 
 | ||||
|     import BoostContext from './BoostContext.svelte'; | ||||
|     import ReplyContext from './ReplyContext.svelte'; | ||||
|     import PostHeader from './PostHeader.svelte'; | ||||
|     import Body from './Body.svelte'; | ||||
|     import ReactionButton from './ReactionButton.svelte'; | ||||
|     import ActionButton from './ActionButton.svelte'; | ||||
|     import { parseOne as parseEmoji } from '../../emoji.js'; | ||||
|     import { play_sound } from '../../sound.js'; | ||||
|     import { onMount } from 'svelte'; | ||||
|     import { get } from 'svelte/store'; | ||||
|     import { Client } from '../../client/client.js'; | ||||
|     import * as api from '../../client/api.js'; | ||||
|     import { goto } from '$app/navigation'; | ||||
| 
 | ||||
|     import ReplyIcon from '../../../img/icons/reply.svg'; | ||||
|     import RepostIcon from '../../../img/icons/repost.svg'; | ||||
|     import FavouriteIcon from '../../../img/icons/like.svg'; | ||||
|     import FavouriteIconFill from '../../../img/icons/like_fill.svg'; | ||||
|     import ReactIcon from '../../../img/icons/react.svg'; | ||||
|     import QuoteIcon from '../../../img/icons/quote.svg'; | ||||
|     import MoreIcon from '../../../img/icons/more.svg'; | ||||
|     import ActionBar from './ActionBar.svelte'; | ||||
|     import ReactionBar from './ReactionBar.svelte'; | ||||
| 
 | ||||
|     export let post_data; | ||||
|     export let focused = false; | ||||
|  | @ -39,55 +29,6 @@ | |||
|         goto(`/post/${post.id}`); | ||||
|     } | ||||
| 
 | ||||
|     async function toggleBoost() { | ||||
|         let client = get(Client.get()); | ||||
|         let data; | ||||
|         if (post.boosted) | ||||
|             data = await client.unboostPost(post.id); | ||||
|         else | ||||
|             data = await client.boostPost(post.id); | ||||
|         if (!data) { | ||||
|             console.error(`Failed to boost post ${post.id}`); | ||||
|             return; | ||||
|         } | ||||
|         post.boosted = data.reblog ? data.reblog.reblogged : data.boosted; | ||||
|         post.boost_count = data.reblog ? data.reblog.reblogs_count : data.reblogs_count; | ||||
|     } | ||||
| 
 | ||||
|     async function toggleFavourite() { | ||||
|         let client = get(Client.get()); | ||||
|         let data; | ||||
|         if (post.favourited) | ||||
|             data = await client.unfavouritePost(post.id); | ||||
|         else | ||||
|             data = await client.favouritePost(post.id); | ||||
|         if (!data) { | ||||
|             console.error(`Failed to favourite post ${post.id}`); | ||||
|             return; | ||||
|         } | ||||
|         post.favourited = data.favourited; | ||||
|         post.favourite_count = data.favourites_count; | ||||
|         if (data.reactions) post.reactions = api.parseReactions(data.reactions); | ||||
|     } | ||||
| 
 | ||||
|     async function toggleReaction(reaction) { | ||||
|         if (reaction.name.includes('@')) return; | ||||
|         let client = get(Client.get()); | ||||
| 
 | ||||
|         let data; | ||||
|         if (reaction.me) | ||||
|             data = await client.unreactPost(post.id, reaction.name); | ||||
|         else | ||||
|             data = await client.reactPost(post.id, reaction.name); | ||||
|         if (!data) { | ||||
|             console.error(`Failed to favourite post ${post.id}`); | ||||
|             return; | ||||
|         } | ||||
|         post.favourited = data.favourited; | ||||
|         post.favourite_count = data.favourites_count; | ||||
|         if (data.reactions) post.reactions = api.parseReactions(data.reactions); | ||||
|     } | ||||
| 
 | ||||
|     let el; | ||||
|     onMount(() => { | ||||
|         if (focused) { | ||||
|  | @ -114,50 +55,8 @@ | |||
|         <PostHeader post={post} /> | ||||
|         <Body post={post} /> | ||||
|         <footer class="post-footer"> | ||||
|             <div class="post-reactions" aria-label="Reactions" on:click|stopPropagation on:keydown|stopPropagation> | ||||
|                 {#each post.reactions as reaction} | ||||
|                     <ReactionButton | ||||
|                             type="reaction" | ||||
|                             on:click={() => toggleReaction(reaction)} | ||||
|                             bind:active={reaction.me} | ||||
|                             bind:count={reaction.count} | ||||
|                             disabled={reaction.name.includes('@')} | ||||
|                             title={reaction.name} | ||||
|                             label=""> | ||||
|                         {#if reaction.url} | ||||
|                             <img src={reaction.url} class="emoji" height="20" title={reaction.name} alt={reaction.name}> | ||||
|                         {:else} | ||||
|                             {reaction.name} | ||||
|                         {/if} | ||||
|                     </ReactionButton> | ||||
|                 {/each} | ||||
|             </div> | ||||
|             <div class="post-actions" aria-label="Post actions" on:click|stopPropagation on:keydown|stopPropagation> | ||||
|                 <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"> | ||||
|                     <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}> | ||||
|                     <FavouriteIcon/> | ||||
|                     <svelte:fragment slot="activeIcon"> | ||||
|                         <FavouriteIconFill/> | ||||
|                     </svelte:fragment> | ||||
|                 </ActionButton> | ||||
|                 <ActionButton type="react" label="React" disabled> | ||||
|                     <ReactIcon/> | ||||
|                 </ActionButton> | ||||
|                 <ActionButton type="quote" label="Quote" disabled> | ||||
|                     <QuoteIcon/> | ||||
|                 </ActionButton> | ||||
|                 <ActionButton type="more" label="More" disabled> | ||||
|                     <MoreIcon/> | ||||
|                 </ActionButton> | ||||
|             </div> | ||||
|             <ReactionBar post={post} /> | ||||
|             <ActionBar post={post} /> | ||||
|         </footer> | ||||
|     </article> | ||||
| </div> | ||||
|  | @ -197,21 +96,6 @@ | |||
|         margin-top: -32px; | ||||
|     } | ||||
| 
 | ||||
|     :global(.post-reactions) { | ||||
|         width: fit-content; | ||||
|         display: flex; | ||||
|         flex-direction: row; | ||||
|         gap: 4px; | ||||
|     } | ||||
| 
 | ||||
|     :global(.post-actions) { | ||||
|         width: fit-content; | ||||
|         margin-top: 8px; | ||||
|         display: flex; | ||||
|         flex-direction: row; | ||||
|         gap: 2px; | ||||
|     } | ||||
| 
 | ||||
|     .post-container :global(.emoji) { | ||||
|         height: 20px; | ||||
|     } | ||||
|  |  | |||
							
								
								
									
										43
									
								
								src/lib/ui/post/ReactionBar.svelte
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								src/lib/ui/post/ReactionBar.svelte
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,43 @@ | |||
| <script> | ||||
|     import ReactionButton from './ReactionButton.svelte'; | ||||
|     import ReactIcon from '../../../img/icons/react.svg'; | ||||
| 
 | ||||
|     export let post; | ||||
| </script> | ||||
| 
 | ||||
| <div class="post-reactions" aria-label="Reactions" on:click|stopPropagation on:keydown|stopPropagation> | ||||
|     {#each post.reactions as reaction} | ||||
|         <ReactionButton | ||||
|                 type="reaction" | ||||
|                 on:click={() => toggleReaction(reaction)} | ||||
|                 bind:active={reaction.me} | ||||
|                 bind:count={reaction.count} | ||||
|                 disabled={reaction.name.includes('@')} | ||||
|                 title={reaction.name} | ||||
|                 label=""> | ||||
|                 {#if reaction.url} | ||||
|                     <img src={reaction.url} class="emoji" height="20" title={reaction.name} alt={reaction.name}> | ||||
|                 {:else} | ||||
|                     {reaction.name} | ||||
|                 {/if} | ||||
|         </ReactionButton> | ||||
|     {/each} | ||||
|     <ReactionButton | ||||
|             type="reaction" | ||||
|             title="react" | ||||
|             label="React" | ||||
|             disabled> | ||||
|     <ReactIcon/> | ||||
|     </ReactionButton> | ||||
| </div> | ||||
| 
 | ||||
| <style> | ||||
|     .post-reactions { | ||||
|         width: fit-content; | ||||
|         height: 32px; | ||||
|         margin-top: 8px; | ||||
|         display: flex; | ||||
|         flex-direction: row; | ||||
|         gap: 2px; | ||||
|     } | ||||
| </style> | ||||
|  | @ -67,7 +67,8 @@ | |||
|     } | ||||
| 
 | ||||
|     button.disabled { | ||||
|         cursor: initial; | ||||
|         /* cursor: initial; */ | ||||
|         cursor: not-allowed; | ||||
|     } | ||||
| 
 | ||||
|     .icon { | ||||
|  |  | |||
|  | @ -1,9 +1,4 @@ | |||
| <script> | ||||
|     import PostHeader from './PostHeader.svelte'; | ||||
|     import Body from './Body.svelte'; | ||||
|     import ReactionButton from './ReactionButton.svelte'; | ||||
|     import ActionButton from './ActionButton.svelte'; | ||||
|     import Post from './Post.svelte'; | ||||
|     import { parseText as parseEmojis, parseOne as parseEmoji } from '../../emoji.js'; | ||||
|     import { shorthand as short_time } from '../../time.js'; | ||||
|     import { get } from 'svelte/store'; | ||||
|  | @ -11,13 +6,11 @@ | |||
|     import * as api from '../../client/api.js'; | ||||
|     import { goto } from '$app/navigation'; | ||||
| 
 | ||||
|     import ReplyIcon from '../../../img/icons/reply.svg'; | ||||
|     import RepostIcon from '../../../img/icons/repost.svg'; | ||||
|     import FavouriteIcon from '../../../img/icons/like.svg'; | ||||
|     import FavouriteIconFill from '../../../img/icons/like_fill.svg'; | ||||
|     import ReactIcon from '../../../img/icons/react.svg'; | ||||
|     import QuoteIcon from '../../../img/icons/quote.svg'; | ||||
|     import MoreIcon from '../../../img/icons/more.svg'; | ||||
|     import PostHeader from './PostHeader.svelte'; | ||||
|     import Body from './Body.svelte'; | ||||
|     import Post from './Post.svelte'; | ||||
|     import ActionBar from './ActionBar.svelte'; | ||||
|     import ReactionBar from './ReactionBar.svelte'; | ||||
| 
 | ||||
|     export let post; | ||||
|     let time_string = post.created_at.toLocaleString(); | ||||
|  | @ -96,50 +89,8 @@ | |||
|         <Body post={post} /> | ||||
| 
 | ||||
|         <footer class="post-footer"> | ||||
|             <div class="post-reactions" aria-label="Reactions" on:click|stopPropagation on:keydown|stopPropagation> | ||||
|                 {#each post.reactions as reaction} | ||||
|                     <ReactionButton | ||||
|                             type="reaction" | ||||
|                             on:click={() => toggleReaction(reaction)} | ||||
|                             bind:active={reaction.me} | ||||
|                             bind:count={reaction.count} | ||||
|                             disabled={reaction.name.includes('@')} | ||||
|                             title={reaction.name} | ||||
|                             label=""> | ||||
|                         {#if reaction.url} | ||||
|                             <img src={reaction.url} class="emoji" height="20" title={reaction.name} alt={reaction.name}> | ||||
|                         {:else} | ||||
|                             {reaction.name} | ||||
|                         {/if} | ||||
|                     </ReactionButton> | ||||
|                 {/each} | ||||
|             </div> | ||||
|             <div class="post-actions" aria-label="Post actions" on:click|stopPropagation on:keydown|stopPropagation> | ||||
|                 <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"> | ||||
|                     <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}> | ||||
|                     <FavouriteIcon/> | ||||
|                     <svelte:fragment slot="activeIcon"> | ||||
|                         <FavouriteIconFill/> | ||||
|                     </svelte:fragment> | ||||
|                 </ActionButton> | ||||
|                 <ActionButton type="react" label="React" disabled> | ||||
|                     <ReactIcon/> | ||||
|                 </ActionButton> | ||||
|                 <ActionButton type="quote" label="Quote" disabled> | ||||
|                     <QuoteIcon/> | ||||
|                 </ActionButton> | ||||
|                 <ActionButton type="more" label="More" disabled> | ||||
|                     <MoreIcon/> | ||||
|                 </ActionButton> | ||||
|             </div> | ||||
|             <ReactionBar post={post} /> | ||||
|             <ActionBar post={post} /> | ||||
|         </footer> | ||||
|     </div> | ||||
| </article> | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue