this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Fix race conditions when accept/rejecting many follow requests

- No longer reload the whole list of follow requests and notifications for every accept/reject action
- Notifications list now exclude follow requests (experimental)

+52 -12
+30 -4
src/components/follow-request-buttons.jsx
··· 2 2 3 3 import { api } from '../utils/api'; 4 4 5 + import Icon from './icon'; 5 6 import Loader from './loader'; 6 7 7 8 function FollowRequestButtons({ accountID, onChange }) { 8 9 const { masto } = api(); 9 10 const [uiState, setUIState] = useState('default'); 11 + const [requestState, setRequestState] = useState(null); // accept, reject 12 + const [relationship, setRelationship] = useState(null); 13 + 14 + const hasRelationship = relationship !== null; 15 + 10 16 return ( 11 17 <p class="follow-request-buttons"> 12 18 <button ··· 14 20 disabled={uiState === 'loading'} 15 21 onClick={() => { 16 22 setUIState('loading'); 23 + setRequestState('accept'); 17 24 (async () => { 18 25 try { 19 - await masto.v1.followRequests.authorize(accountID); 26 + const rel = await masto.v1.followRequests.authorize(accountID); 27 + if (!rel?.followedBy) { 28 + throw new Error('Follow request not accepted'); 29 + } 30 + setRelationship(rel); 20 31 onChange(); 21 32 } catch (e) { 22 33 console.error(e); 23 - setUIState('default'); 24 34 } 35 + setUIState('default'); 25 36 })(); 26 37 }} 27 38 > ··· 33 44 class="light danger" 34 45 onClick={() => { 35 46 setUIState('loading'); 47 + setRequestState('reject'); 36 48 (async () => { 37 49 try { 38 - await masto.v1.followRequests.reject(accountID); 50 + const rel = await masto.v1.followRequests.reject(accountID); 51 + if (rel?.followedBy) { 52 + throw new Error('Follow request not rejected'); 53 + } 54 + setRelationship(rel); 39 55 onChange(); 40 56 } catch (e) { 41 57 console.error(e); ··· 46 62 > 47 63 Reject 48 64 </button> 49 - <Loader hidden={uiState !== 'loading'} /> 65 + <span class="follow-request-states"> 66 + {hasRelationship && requestState ? ( 67 + requestState === 'accept' ? ( 68 + <Icon icon="check-circle" alt="Accepted" class="follow-accepted" /> 69 + ) : ( 70 + <Icon icon="x-circle" alt="Rejected" class="follow-rejected" /> 71 + ) 72 + ) : ( 73 + <Loader hidden={uiState !== 'loading'} /> 74 + )} 75 + </span> 50 76 </p> 51 77 ); 52 78 }
+1
src/components/icon.jsx
··· 13 13 heart: () => import('@iconify-icons/mingcute/heart-line'), 14 14 bookmark: () => import('@iconify-icons/mingcute/bookmark-line'), 15 15 'check-circle': () => import('@iconify-icons/mingcute/check-circle-line'), 16 + 'x-circle': () => import('@iconify-icons/mingcute/close-circle-line'), 16 17 transfer: () => import('@iconify-icons/mingcute/transfer-4-line'), 17 18 rocket: () => import('@iconify-icons/mingcute/rocket-line'), 18 19 'arrow-left': () => import('@iconify-icons/mingcute/arrow-left-line'),
+1 -1
src/components/notification.jsx
··· 137 137 <FollowRequestButtons 138 138 accountID={account.id} 139 139 onChange={() => { 140 - reload(); 140 + // reload(); 141 141 }} 142 142 /> 143 143 )}
+13 -1
src/pages/notifications.css
··· 204 204 justify-content: flex-end; 205 205 align-items: center; 206 206 } 207 - .follow-requests ul li .follow-request-buttons .loader-container { 207 + .follow-request-buttons .follow-request-states { 208 + vertical-align: middle; 209 + } 210 + .follow-request-buttons .follow-request-states .icon { 211 + margin-inline: 8px; 212 + } 213 + .follow-request-buttons .follow-request-states .icon.follow-accepted { 214 + color: var(--green-color); 215 + } 216 + .follow-request-buttons .follow-request-states .icon.follow-rejected { 217 + color: var(--red-color); 218 + } 219 + .follow-requests ul li .follow-request-buttons .follow-request-states { 208 220 order: -1; 209 221 } 210 222
+7 -6
src/pages/notifications.jsx
··· 49 49 // Reset iterator 50 50 notificationsIterator.current = masto.v1.notifications.list({ 51 51 limit: LIMIT, 52 + excludeTypes: ['follow_request'], 52 53 }); 53 54 } 54 55 const allNotifications = await notificationsIterator.current.next(); ··· 295 296 <summary>{followRequests.length} follow requests</summary> 296 297 <ul> 297 298 {followRequests.map((account) => ( 298 - <li> 299 + <li key={account.id}> 299 300 <AccountBlock account={account} /> 300 301 <FollowRequestButtons 301 302 accountID={account.id} 302 303 onChange={() => { 303 - loadFollowRequests(); 304 - loadNotifications(true); 304 + // loadFollowRequests(); 305 + // loadNotifications(true); 305 306 }} 306 307 /> 307 308 </li> ··· 311 312 ) : ( 312 313 <ul> 313 314 {followRequests.map((account) => ( 314 - <li> 315 + <li key={account.id}> 315 316 <AccountBlock account={account} /> 316 317 <FollowRequestButtons 317 318 accountID={account.id} 318 319 onChange={() => { 319 - loadFollowRequests(); 320 - loadNotifications(true); 320 + // loadFollowRequests(); 321 + // loadNotifications(true); 321 322 }} 322 323 /> 323 324 </li>