mirror of
https://github.com/cheeaun/phanpy.git
synced 2025-03-31 11:01:35 +02:00
Add follow requests section in Notifications
This commit is contained in:
parent
d37537c61e
commit
dcf7d3c750
6 changed files with 162 additions and 48 deletions
|
@ -1312,6 +1312,9 @@ body:has(.media-modal-container + .status-deck) .media-post-link {
|
||||||
.tag .icon {
|
.tag .icon {
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
.tag.collapsed {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* MENU POPUP */
|
/* MENU POPUP */
|
||||||
|
|
||||||
|
|
54
src/components/follow-request-buttons.jsx
Normal file
54
src/components/follow-request-buttons.jsx
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
import { useState } from 'preact/hooks';
|
||||||
|
|
||||||
|
import { api } from '../utils/api';
|
||||||
|
|
||||||
|
import Loader from './loader';
|
||||||
|
|
||||||
|
function FollowRequestButtons({ accountID, onChange }) {
|
||||||
|
const { masto } = api();
|
||||||
|
const [uiState, setUIState] = useState('default');
|
||||||
|
return (
|
||||||
|
<p class="follow-request-buttons">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
disabled={uiState === 'loading'}
|
||||||
|
onClick={() => {
|
||||||
|
setUIState('loading');
|
||||||
|
(async () => {
|
||||||
|
try {
|
||||||
|
await masto.v1.followRequests.authorize(accountID);
|
||||||
|
onChange();
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
setUIState('default');
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Accept
|
||||||
|
</button>{' '}
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
disabled={uiState === 'loading'}
|
||||||
|
class="light danger"
|
||||||
|
onClick={() => {
|
||||||
|
setUIState('loading');
|
||||||
|
(async () => {
|
||||||
|
try {
|
||||||
|
await masto.v1.followRequests.reject(accountID);
|
||||||
|
onChange();
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
setUIState('default');
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Reject
|
||||||
|
</button>
|
||||||
|
<Loader hidden={uiState !== 'loading'} />
|
||||||
|
</p>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default FollowRequestButtons;
|
|
@ -2,6 +2,7 @@ import states from '../utils/states';
|
||||||
import store from '../utils/store';
|
import store from '../utils/store';
|
||||||
|
|
||||||
import Avatar from './avatar';
|
import Avatar from './avatar';
|
||||||
|
import FollowRequestButtons from './follow-request-buttons';
|
||||||
import Icon from './icon';
|
import Icon from './icon';
|
||||||
import Link from './link';
|
import Link from './link';
|
||||||
import NameText from './name-text';
|
import NameText from './name-text';
|
||||||
|
@ -205,51 +206,4 @@ function Notification({ notification, instance }) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function FollowRequestButtons({ accountID, onChange }) {
|
|
||||||
const { masto } = api();
|
|
||||||
const [uiState, setUIState] = useState('default');
|
|
||||||
return (
|
|
||||||
<p>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
disabled={uiState === 'loading'}
|
|
||||||
onClick={() => {
|
|
||||||
setUIState('loading');
|
|
||||||
(async () => {
|
|
||||||
try {
|
|
||||||
await masto.v1.followRequests.authorize(accountID);
|
|
||||||
onChange();
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
setUIState('default');
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Accept
|
|
||||||
</button>{' '}
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
disabled={uiState === 'loading'}
|
|
||||||
class="light danger"
|
|
||||||
onClick={() => {
|
|
||||||
setUIState('loading');
|
|
||||||
(async () => {
|
|
||||||
try {
|
|
||||||
await masto.v1.followRequests.reject(accountID);
|
|
||||||
onChange();
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
setUIState('default');
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Reject
|
|
||||||
</button>
|
|
||||||
<Loader hidden={uiState !== 'loading'} />
|
|
||||||
</p>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Notification;
|
export default Notification;
|
||||||
|
|
|
@ -135,11 +135,20 @@ function NotificationsMenu({ anchorRef, state, onClose }) {
|
||||||
return allNotifications;
|
return allNotifications;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const [hasFollowRequests, setHasFollowRequests] = useState(false);
|
||||||
|
function fetchFollowRequests() {
|
||||||
|
return masto.v1.followRequests.list({
|
||||||
|
limit: 1,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function loadNotifications() {
|
function loadNotifications() {
|
||||||
setUIState('loading');
|
setUIState('loading');
|
||||||
(async () => {
|
(async () => {
|
||||||
try {
|
try {
|
||||||
await fetchNotifications();
|
await fetchNotifications();
|
||||||
|
const followRequests = await fetchFollowRequests();
|
||||||
|
setHasFollowRequests(!!followRequests?.length);
|
||||||
setUIState('default');
|
setUIState('default');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setUIState('error');
|
setUIState('error');
|
||||||
|
@ -204,7 +213,15 @@ function NotificationsMenu({ anchorRef, state, onClose }) {
|
||||||
<Icon icon="at" /> <span>Mentions</span>
|
<Icon icon="at" /> <span>Mentions</span>
|
||||||
</Link>
|
</Link>
|
||||||
<Link to="/notifications" class="button plain2">
|
<Link to="/notifications" class="button plain2">
|
||||||
<b>See all</b> <Icon icon="arrow-right" />
|
{hasFollowRequests ? (
|
||||||
|
<>
|
||||||
|
<span class="tag collapsed">New</span>{' '}
|
||||||
|
<span>Follow Requests</span>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<b>See all</b>
|
||||||
|
)}{' '}
|
||||||
|
<Icon icon="arrow-right" />
|
||||||
</Link>
|
</Link>
|
||||||
</footer>
|
</footer>
|
||||||
</ControlledMenu>
|
</ControlledMenu>
|
||||||
|
|
|
@ -152,3 +152,41 @@
|
||||||
color: var(--text-color);
|
color: var(--text-color);
|
||||||
background-color: var(--bg-color);
|
background-color: var(--bg-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.follow-requests {
|
||||||
|
padding-block-end: 16px;
|
||||||
|
}
|
||||||
|
.follow-requests ul {
|
||||||
|
list-style: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
max-height: 50vh;
|
||||||
|
max-height: 50dvh;
|
||||||
|
overflow: auto;
|
||||||
|
border-bottom: var(--hairline-width) solid var(--outline-color);
|
||||||
|
}
|
||||||
|
.follow-requests ul li {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 16px;
|
||||||
|
border-bottom: var(--hairline-width) solid var(--outline-color);
|
||||||
|
justify-content: space-between;
|
||||||
|
column-gap: 16px;
|
||||||
|
row-gap: 4px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
.follow-requests ul li:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
.follow-requests ul li .follow-request-buttons {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
|
gap: 4px;
|
||||||
|
justify-content: flex-end;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.follow-requests ul li .follow-request-buttons .loader-container {
|
||||||
|
order: -1;
|
||||||
|
}
|
||||||
|
|
|
@ -4,6 +4,8 @@ import { memo } from 'preact/compat';
|
||||||
import { useCallback, useEffect, useRef, useState } from 'preact/hooks';
|
import { useCallback, useEffect, useRef, useState } from 'preact/hooks';
|
||||||
import { useSnapshot } from 'valtio';
|
import { useSnapshot } from 'valtio';
|
||||||
|
|
||||||
|
import AccountBlock from '../components/account-block';
|
||||||
|
import FollowRequestButtons from '../components/follow-request-buttons';
|
||||||
import Icon from '../components/icon';
|
import Icon from '../components/icon';
|
||||||
import Link from '../components/link';
|
import Link from '../components/link';
|
||||||
import Loader from '../components/loader';
|
import Loader from '../components/loader';
|
||||||
|
@ -31,6 +33,7 @@ function Notifications() {
|
||||||
scrollableRef,
|
scrollableRef,
|
||||||
});
|
});
|
||||||
const hiddenUI = scrollDirection === 'end' && !nearReachStart;
|
const hiddenUI = scrollDirection === 'end' && !nearReachStart;
|
||||||
|
const [followRequests, setFollowRequests] = useState([]);
|
||||||
|
|
||||||
console.debug('RENDER Notifications');
|
console.debug('RENDER Notifications');
|
||||||
|
|
||||||
|
@ -67,12 +70,39 @@ function Notifications() {
|
||||||
return allNotifications;
|
return allNotifications;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function fetchFollowRequests() {
|
||||||
|
const followRequests = await masto.v1.followRequests.list({
|
||||||
|
limit: 80,
|
||||||
|
});
|
||||||
|
// Note: no pagination here yet because this better be on a separate page. Should be rare use-case???
|
||||||
|
return followRequests;
|
||||||
|
}
|
||||||
|
|
||||||
|
const loadFollowRequests = () => {
|
||||||
|
setUIState('loading');
|
||||||
|
(async () => {
|
||||||
|
try {
|
||||||
|
const requests = await fetchFollowRequests();
|
||||||
|
setFollowRequests(requests);
|
||||||
|
setUIState('default');
|
||||||
|
} catch (e) {
|
||||||
|
setUIState('error');
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
};
|
||||||
|
|
||||||
const loadNotifications = (firstLoad) => {
|
const loadNotifications = (firstLoad) => {
|
||||||
setUIState('loading');
|
setUIState('loading');
|
||||||
(async () => {
|
(async () => {
|
||||||
try {
|
try {
|
||||||
const { done } = await fetchNotifications(firstLoad);
|
const { done } = await fetchNotifications(firstLoad);
|
||||||
setShowMore(!done);
|
setShowMore(!done);
|
||||||
|
|
||||||
|
if (firstLoad) {
|
||||||
|
const requests = await fetchFollowRequests();
|
||||||
|
setFollowRequests(requests);
|
||||||
|
}
|
||||||
|
|
||||||
setUIState('default');
|
setUIState('default');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setUIState('error');
|
setUIState('error');
|
||||||
|
@ -184,6 +214,24 @@ function Notifications() {
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</header>
|
</header>
|
||||||
|
{followRequests.length > 0 && (
|
||||||
|
<div class="follow-requests">
|
||||||
|
<h2 class="timeline-header">Follow requests</h2>
|
||||||
|
<ul>
|
||||||
|
{followRequests.map((account) => (
|
||||||
|
<li>
|
||||||
|
<AccountBlock account={account} />
|
||||||
|
<FollowRequestButtons
|
||||||
|
accountID={account.id}
|
||||||
|
onChange={() => {
|
||||||
|
loadFollowRequests();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<div id="mentions-option">
|
<div id="mentions-option">
|
||||||
<label>
|
<label>
|
||||||
<input
|
<input
|
||||||
|
|
Loading…
Add table
Reference in a new issue