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')}/>
|
||||||
|
|
||||||
</div>
|
{#if $followRequests.length < 1}
|
||||||
|
<p class="request-zero">{lang.string('follow_requests.none')}</p>
|
||||||
|
{/if}
|
||||||
|
|
||||||
<style></style>
|
{#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>
|
||||||
|
.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