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"
|
||||
},
|
||||
|
||||
"follow_requests": {
|
||||
"none": "no follow requests to action right now!"
|
||||
},
|
||||
|
||||
"timeline": {
|
||||
"home": "Home",
|
||||
"local": "Local",
|
||||
|
|
|
@ -202,6 +202,40 @@ export async function getFollowRequests(host, token, since_id, max_id, limit) {
|
|||
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}
|
||||
* @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 { app } from './client/app.js';
|
||||
import { get } from 'svelte/store';
|
||||
import { parseAccount } from './account.js';
|
||||
|
||||
// Cache for all requests
|
||||
export let followRequests = writable();
|
||||
|
@ -20,5 +21,8 @@ export async function fetchFollowRequests(force) {
|
|||
get(app).token
|
||||
);
|
||||
|
||||
// parse accounts
|
||||
newReqs = newReqs.map((r) => parseAccount(r));
|
||||
|
||||
followRequests.set(newReqs);
|
||||
}
|
|
@ -1,10 +1,144 @@
|
|||
<script>
|
||||
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>
|
||||
|
||||
<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 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