feat: follow requests
This commit is contained in:
		
							parent
							
								
									563541d0e6
								
							
						
					
					
						commit
						e3586f4eec
					
				
					 4 changed files with 179 additions and 3 deletions
				
			
		|  | @ -38,6 +38,10 @@ | ||||||
|         "back": "Back" |         "back": "Back" | ||||||
|     }, |     }, | ||||||
| 
 | 
 | ||||||
|  |     "follow_requests": { | ||||||
|  |         "none": "no follow requests to action right now!" | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|     "timeline": { |     "timeline": { | ||||||
|         "home": "Home", |         "home": "Home", | ||||||
|         "local": "Local", |         "local": "Local", | ||||||
|  |  | ||||||
|  | @ -202,6 +202,40 @@ export async function getFollowRequests(host, token, since_id, max_id, limit) { | ||||||
|     return data; |     return data; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /** | ||||||
|  |  * POST /api/v1/follow_requests/:account_id/authorize | ||||||
|  |  * @param {string} host - The domain of the target server. | ||||||
|  |  * @param {string} token - The application token. | ||||||
|  |  * @param {string} account_id - The account ID of the follow request to accept | ||||||
|  |  */ | ||||||
|  | export async function acceptFollowRequest(host, token, account_id) { | ||||||
|  |     let url = `https://${host}/api/v1/follow_requests/${account_id}/authorize`; | ||||||
|  | 
 | ||||||
|  |     const data = await fetch(url, { | ||||||
|  |         method: 'POST', | ||||||
|  |         headers: { "Authorization": "Bearer " + token } | ||||||
|  |     }).then(res => res.json()); | ||||||
|  | 
 | ||||||
|  |     return data; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * POST /api/v1/follow_requests/:account_id/reject | ||||||
|  |  * @param {string} host - The domain of the target server. | ||||||
|  |  * @param {string} token - The application token. | ||||||
|  |  * @param {string} account_id - The account ID of the follow request to reject | ||||||
|  |  */ | ||||||
|  | export async function rejectFollowRequest(host, token, account_id) { | ||||||
|  |     let url = `https://${host}/api/v1/follow_requests/${account_id}/reject`; | ||||||
|  | 
 | ||||||
|  |     const data = await fetch(url, { | ||||||
|  |         method: 'POST', | ||||||
|  |         headers: { "Authorization": "Bearer " + token } | ||||||
|  |     }).then(res => res.json()); | ||||||
|  | 
 | ||||||
|  |     return data; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /** | /** | ||||||
|  * GET /api/v1/timelines/{timeline} |  * GET /api/v1/timelines/{timeline} | ||||||
|  * @param {string} host - The domain of the target server. |  * @param {string} host - The domain of the target server. | ||||||
|  |  | ||||||
|  | @ -3,6 +3,7 @@ import { writable } from "svelte/store"; | ||||||
| import * as api from "./api.js"; | import * as api from "./api.js"; | ||||||
| import { app } from './client/app.js'; | import { app } from './client/app.js'; | ||||||
| import { get } from 'svelte/store'; | import { get } from 'svelte/store'; | ||||||
|  | import { parseAccount } from './account.js'; | ||||||
| 
 | 
 | ||||||
| // Cache for all requests
 | // Cache for all requests
 | ||||||
| export let followRequests = writable(); | export let followRequests = writable(); | ||||||
|  | @ -20,5 +21,8 @@ export async function fetchFollowRequests(force) { | ||||||
|         get(app).token |         get(app).token | ||||||
|     ); |     ); | ||||||
| 
 | 
 | ||||||
|  |     // parse accounts
 | ||||||
|  |     newReqs = newReqs.map((r) => parseAccount(r)); | ||||||
|  | 
 | ||||||
|     followRequests.set(newReqs); |     followRequests.set(newReqs); | ||||||
| } | } | ||||||
|  | @ -1,10 +1,144 @@ | ||||||
| <script> | <script> | ||||||
|     import { followRequests } from '$lib/followRequests.js'; |     import { followRequests } from '$lib/followRequests.js'; | ||||||
|  |     import PageHeader from '../../lib/ui/core/PageHeader.svelte'; | ||||||
|  |     import Lang from '$lib/lang'; | ||||||
|  |     import {server} from '$lib/client/server'; | ||||||
|  |     import {app} from '$lib/client/app'; | ||||||
|  |     import Button from '../../lib/ui/Button.svelte'; | ||||||
|  |     import * as api from '$lib/api' | ||||||
| 
 | 
 | ||||||
|  |     import TickIcon from '../../img/icons/tick.svg' | ||||||
|  |     import CrossIcon from '../../img/icons/cross.svg' | ||||||
|  |     import { get } from 'svelte/store'; | ||||||
|  | 
 | ||||||
|  |     const lang = Lang('en_GB'); | ||||||
|  | 
 | ||||||
|  |     async function actionRequest(account_id, approved) { | ||||||
|  |         // remove item from array first - this updates the ui and | ||||||
|  |         // makes the interaction more seamless | ||||||
|  |         $followRequests.splice( | ||||||
|  |             $followRequests.indexOf( | ||||||
|  |                 $followRequests.find(r => r.id) | ||||||
|  |             ), | ||||||
|  |             1 | ||||||
|  |         ); | ||||||
|  | 
 | ||||||
|  |         // hack: force the state to update now that we just spliced the array | ||||||
|  |         $followRequests = $followRequests | ||||||
|  | 
 | ||||||
|  |         if(approved) { | ||||||
|  |             await api.acceptFollowRequest( | ||||||
|  |                 get(server).host, | ||||||
|  |                 get(app).token, | ||||||
|  |                 account_id | ||||||
|  |             ) | ||||||
|  |         } else { | ||||||
|  |             await api.rejectFollowRequest( | ||||||
|  |                 get(server).host, | ||||||
|  |                 get(app).token, | ||||||
|  |                 account_id | ||||||
|  |             ) | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // aliases | ||||||
|  |     const acceptRequest = (id) => actionRequest(id, true); | ||||||
|  |     const denyRequest = (id) => actionRequest(id, false); | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
| <div> | <PageHeader title={lang.string('navigation.follow_requests')}/> | ||||||
| 
 | 
 | ||||||
|  | {#if $followRequests.length < 1} | ||||||
|  | <p class="request-zero">{lang.string('follow_requests.none')}</p> | ||||||
|  | {/if} | ||||||
|  | 
 | ||||||
|  | {#each $followRequests as req} | ||||||
|  |     <div class="request"> | ||||||
|  |         <a href="/{$server.host}/@{req.fqn}" class="request-avatar-container" on:mouseup|stopPropagation> | ||||||
|  |             <img src={req.avatar_url} alt="" width="48" height="48" class="post-avatar" loading="lazy" decoding="async"> | ||||||
|  |         </a> | ||||||
|  |         <div class="info"> | ||||||
|  |             <div class="request-user-info"> | ||||||
|  |                 <a href="/{$server.host}/@{req.fqn}" class="name">{@html req.rich_name}</a> | ||||||
|  |                 <span class="username">{req.mention}</span> | ||||||
|             </div> |             </div> | ||||||
|  |         </div> | ||||||
|  |         <div class="request-options"> | ||||||
|  |             <Button filled title="Yes" on:click={() => acceptRequest(req.id)}> | ||||||
|  |                 <TickIcon width="24px"/> | ||||||
|  |             </Button> | ||||||
|  |             <Button title="No" on:click={() => denyRequest(req.id)}> | ||||||
|  |                 <CrossIcon width="24px"/> | ||||||
|  |             </Button> | ||||||
|  |         </div> | ||||||
|  |     </div> | ||||||
|  | {/each} | ||||||
| 
 | 
 | ||||||
| <style></style> | <style> | ||||||
|  |     .request { | ||||||
|  |         width: 100%; | ||||||
|  |         display: flex; | ||||||
|  |         flex-direction: row; | ||||||
|  | 
 | ||||||
|  |         background: var(--bg-900); | ||||||
|  |         padding: .5rem; | ||||||
|  |         border-radius: 8px; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .request a, | ||||||
|  |     .request a:visited { | ||||||
|  |         color: inherit; | ||||||
|  |         text-decoration: none; | ||||||
|  |     } | ||||||
|  |     .request a:hover { | ||||||
|  |         text-decoration: underline; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .request-avatar-container { | ||||||
|  |         margin-right: 12px; | ||||||
|  |         display: flex; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .post-avatar { | ||||||
|  |         border-radius: 8px; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .info { | ||||||
|  |         display: flex; | ||||||
|  |         flex-grow: 1; | ||||||
|  |         flex-direction: row; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .request-user-info { | ||||||
|  |         margin-top: -2px; | ||||||
|  |         display: flex; | ||||||
|  |         flex-direction: column; | ||||||
|  |         justify-content: center; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .request-user-info a { | ||||||
|  |         display: block; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .request-user-info .username { | ||||||
|  |         opacity: .8; | ||||||
|  |         font-size: .9em; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .request-options { | ||||||
|  |         display: flex; | ||||||
|  |         gap: 8px; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .request-options :global(button) { | ||||||
|  |         width: fit-content; | ||||||
|  |         height: 100%; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .request-zero { | ||||||
|  |         opacity: 0.8; | ||||||
|  |         font-size: 0.95rem; | ||||||
|  |         text-align: center; | ||||||
|  |     } | ||||||
|  | </style> | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue