Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

List counts #293

Merged
merged 35 commits into from
Feb 4, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
1caa5b5
dev to main (v4.2.0) (#159)
thieflord06 Nov 28, 2024
85753ec
Merge remote-tracking branch 'origin/dev'
noahm Nov 28, 2024
2ba0338
Merge to main (#162)
thieflord06 Nov 28, 2024
59abea3
Merge dev to main (#170)
thieflord06 Nov 30, 2024
966d0a2
merge to main (4.4.0) (#183)
thieflord06 Dec 9, 2024
e2d400b
add change.org petition link (#190)
thieflord06 Dec 13, 2024
e3e7b75
Merge to main (#193)
thieflord06 Dec 15, 2024
01eb6d3
Merge to main (#194)
thieflord06 Dec 16, 2024
87748b0
Merge into main 4.6.0 (#199)
thieflord06 Dec 19, 2024
0970113
Merge to main 4.7.0 (#200)
thieflord06 Dec 19, 2024
2574fd4
merge to main (#207)
thieflord06 Dec 20, 2024
bd2db09
add new `useListSize` hook
noahm Dec 21, 2024
91642ef
merge to main 4.8.0 (#212)
thieflord06 Dec 22, 2024
a73a46e
Merge to main 4.9.0 (#219)
thieflord06 Dec 27, 2024
87b9164
virtualized list with react window & throttled the request to prevent…
tb38r Jan 6, 2025
28edc62
virtualized list with react window & throttled the request to prevent…
tb38r Jan 6, 2025
aeadb6f
fixed most issues,to address double windowing with react-window
tb38r Jan 9, 2025
66df957
outstanding issue, list items not rendering
tb38r Jan 10, 2025
9d3a6ae
outstanding issue, list items not rendering
tb38r Jan 10, 2025
b0c953e
outstanding issue, list items not fully rendering
tb38r Jan 10, 2025
f1291d7
list count displayed, hook throttled to prevent server overload
tb38r Jan 14, 2025
076aa61
list count displayed, hook throttled to prevent server overload
tb38r Jan 14, 2025
bca248f
list count displayed, hook throttled to prevent server overload
tb38r Jan 14, 2025
c237579
Merge to main (#245)
thieflord06 Jan 17, 2025
79dc93a
4.11.0: add count formatting // fix top 24 hour list display issue (#…
thieflord06 Jan 17, 2025
daad373
add spam indicator (#279)
thieflord06 Jan 21, 2025
a31c65b
swap lodash.throttle for p-queue
noahm Jan 25, 2025
b6d0b30
add a small loading indicator for list entry size
noahm Jan 25, 2025
e75a25f
format list entry total
noahm Jan 25, 2025
a1bd248
tune up request rate limiting
noahm Jan 26, 2025
48505f9
use existing "poor-man's virtualization" for lists
noahm Jan 26, 2025
1deef25
Without virtualisation (#266)
thieflord06 Jan 26, 2025
e3cca5f
Merge remote-tracking branch 'origin/main' into list-counts
noahm Feb 4, 2025
cc35eeb
Merge remote-tracking branch 'origin/dev' into list-counts
noahm Feb 4, 2025
ad62570
remove timeout and concurrency cap
noahm Feb 4, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 44 additions & 8 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,13 @@
"@mui/icons-material": "^5.14.16",
"@mui/material": "^5.14.20",
"@mui/styles": "^5.14.20",
"@tanstack/react-query": "^5.61.4",
"@tanstack/react-query": "^5.64.1",
"@yornaath/batshit": "^0.10.1",
"ag-grid-community": "^31.0.2",
"ag-grid-react": "^31.0.3",
"fuse.js": "^7.0.0",
"iso-web": "^1.0.6",
"p-queue": "npm:@nmann/p-queue@^8.1.1",
"punycode2": "^1.0.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
Expand Down
62 changes: 57 additions & 5 deletions src/api/lists.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
// @ts-check

import { unwrapShortHandle } from '.';
import { fetchClearskyApi } from './core';
import { fetchClearskyApi, unwrapClearskyURL } from './core';
import { useResolveHandleOrDid } from './resolve-handle-or-did';
import { useInfiniteQuery, useQuery } from '@tanstack/react-query';
import PQueue from 'p-queue';

const PAGE_SIZE = 100;

Expand All @@ -24,16 +25,33 @@ export function useList(handleOrDID) {
}

/**
* Look up the total number of lists to which a given handle/DID belongs
* @param {string | undefined} handleOrDID
*/
export function useListTotal(handleOrDID) {
export function useListCount(handleOrDID) {
const profileQuery = useResolveHandleOrDid(handleOrDID);
const shortHandle = profileQuery.data?.shortHandle;
return useQuery({
enabled: !!shortHandle,
queryKey: ['list-total', shortHandle],
// @ts-expect-error shortHandle won't really be undefined because the query will be disabled
queryFn: () => getListTotal(shortHandle),
queryFn: () => getListCount(shortHandle),
});
}

const TWELVE_HOURS = 1000 * 60 * 60 * 12;

/**
* Gets the size (length) of a given user list
* @param {string} listUrl
*/
export function useListSize(listUrl) {
return useQuery({
enabled: !!listUrl,
queryKey: ['list-size', listUrl],
queryFn: ({ signal }) => getListSize(listUrl, signal),
staleTime: TWELVE_HOURS,
gcTime: TWELVE_HOURS,
});
}

Expand Down Expand Up @@ -70,12 +88,46 @@ async function getList(shortHandle, currentPage = 1) {
}

/**
* Gets the total number of lists to which a given handle belongs
* @param {string} shortHandle
*/
async function getListTotal(shortHandle) {
async function getListCount(shortHandle) {
const handleURL = 'get-list/total/' + unwrapShortHandle(shortHandle);

/** @type {{ data: { count: number; pages: number } }} */
const re = await fetchClearskyApi('v1', handleURL);
return re.data;
}

/**
* Gets the size (length) of a given user list
* @param {string} listUrl
* @param {AbortSignal} signal
* @returns {Promise<{ count: number } | null>} null if response is a 400/404
*/
async function getListSize(listUrl, signal) {
const apiUrl = unwrapClearskyURL(
`/api/v1/anon/get-list/specific/total/${encodeURIComponent(listUrl)}`
);
signal.throwIfAborted;
const resp = await listSizeQueue.add(() => fetch(apiUrl, { signal }), {
signal,
throwOnTimeout: true,
});
if (resp.ok) {
/** @type {{ data: { count: number }, list_uri: string }} */
const respData = await resp.json();
return respData.data;
}
if (resp.status === 400 || resp.status === 404) {
return null;
}
throw new Error('getListSize error: ' + resp.statusText);
}

/**
* create a queue where at most 1 may be sent in any 250 millisecond interval
*/
const listSizeQueue = new PQueue({
intervalCap: 1,
interval: 200,
});
45 changes: 45 additions & 0 deletions src/common-components/progressive-render.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// @ts-check
import { useState, cloneElement } from 'react';
import { Visible } from './visible';

const INITIAL_SIZE = 20;
const GROW_BLOCK_SIZE = 29;

/**
* @template ItemType
* @param {{ items: Array<ItemType>, renderItem(item: ItemType): import('react').ReactElement }} props
* Given a list of items, only renders the first 20 by default,
* and then more whenever the last item is close to being on screen
*/
export function ProgressiveRender(props) {
const [listSize, setListSize] = useState(INITIAL_SIZE);
const showSize = Math.min(props.items.length, listSize);

return (
<>
{props.items.slice(0, showSize).map((item, index) => {
const entry = cloneElement(props.renderItem(item), { key: index });

return index < showSize - 1 ? (
entry
) : (
<Visible
key={index}
rootMargin="0px 0px 300px 0px"
onVisible={handleBottomVisible}
>
{entry}
</Visible>
);
})}
</>
);

function handleBottomVisible() {
const incrementListSize = listSize + GROW_BLOCK_SIZE;
setListSize(incrementListSize);
if (incrementListSize > props.items.length) {
// TODO: control fetch more from here?
}
}
}
36 changes: 5 additions & 31 deletions src/detail-panels/block-panel-generic/list-view.jsx
Original file line number Diff line number Diff line change
@@ -1,49 +1,23 @@
// @ts-check
import { FormatTimestamp } from '../../common-components/format-timestamp';
import { Visible } from '../../common-components/visible';
import { useState } from 'react';
import { ProgressiveRender } from '../../common-components/progressive-render';
import { AccountShortEntry } from '../../common-components/account-short-entry';
import { localise } from '../../localisation';

const INITIAL_SIZE = 20;
const GROW_BLOCK_SIZE = 29;

/**
* @param {{
* blocklist: (BlockedByRecord | { did: string; blocked_date: string })[];
* }} _
*/
export function ListView({ blocklist }) {
const [listSize, setListSize] = useState(INITIAL_SIZE);
const showSize = Math.min(blocklist.length, listSize);

return (
<ul className="block-list">
{blocklist.slice(0, showSize).map((block, index) => {
const entry = <ListViewEntry key={index} {...block} />;

return index < showSize - 1 ? (
entry
) : (
<Visible
key={index}
rootMargin="0px 0px 300px 0px"
onVisible={handleBottomVisible}
>
{entry}
</Visible>
);
})}
<ProgressiveRender
items={blocklist}
renderItem={(item) => <ListViewEntry {...item} />}
/>
</ul>
);

function handleBottomVisible() {
const incrementListSize = listSize + GROW_BLOCK_SIZE;
setListSize(incrementListSize);
if (incrementListSize > blocklist.length) {
// TODO: fetch more
}
}
}

/**
Expand Down
11 changes: 8 additions & 3 deletions src/detail-panels/lists/list-view.css
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
.lists-as-list-view {
margin: 0;
padding: 0
padding: 0;
}

.lists-as-list-view .lists-entry {
Expand Down Expand Up @@ -40,7 +40,7 @@
.lists-as-list-view .lists-entry .list-name {
grid-row: 1/ 2;
grid-column: 1 / 2;
padding-left: 1.7em;
padding-left: 0.5em;
font-weight: bold;
}

Expand All @@ -51,4 +51,9 @@
padding-left: 1.7em;
white-space: normal;
word-wrap: break-word;
}
}

.lists-as-list-view .lists-entry .list-count {
padding-left: 0.3em;
color: green;
}
Loading