react-query#useInfiniteQuery TypeScript Examples
The following examples show how to use
react-query#useInfiniteQuery.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: graphql.ts From dope-monorepo with GNU General Public License v3.0 | 6 votes |
useInfiniteAllHustlersQuery = <
TData = AllHustlersQuery,
TError = unknown
>(
variables?: AllHustlersQueryVariables,
options?: UseInfiniteQueryOptions<AllHustlersQuery, TError, TData>
) =>{
const query = useFetchData<AllHustlersQuery, AllHustlersQueryVariables>(AllHustlersDocument)
return useInfiniteQuery<AllHustlersQuery, TError, TData>(
variables === undefined ? ['AllHustlers.infinite'] : ['AllHustlers.infinite', variables],
(metaData) => query({...variables, ...(metaData.pageParam ?? {})}),
options
)}
Example #2
Source File: graphql.ts From dope-monorepo with GNU General Public License v3.0 | 6 votes |
useInfiniteSearchDopeQuery = <
TData = SearchDopeQuery,
TError = unknown
>(
variables: SearchDopeQueryVariables,
options?: UseInfiniteQueryOptions<SearchDopeQuery, TError, TData>
) =>{
const query = useFetchData<SearchDopeQuery, SearchDopeQueryVariables>(SearchDopeDocument)
return useInfiniteQuery<SearchDopeQuery, TError, TData>(
['SearchDope.infinite', variables],
(metaData) => query({...variables, ...(metaData.pageParam ?? {})}),
options
)}
Example #3
Source File: graphql.ts From dope-monorepo with GNU General Public License v3.0 | 6 votes |
useInfiniteRenderDopeQuery = <
TData = RenderDopeQuery,
TError = unknown
>(
variables?: RenderDopeQueryVariables,
options?: UseInfiniteQueryOptions<RenderDopeQuery, TError, TData>
) =>{
const query = useFetchData<RenderDopeQuery, RenderDopeQueryVariables>(RenderDopeDocument)
return useInfiniteQuery<RenderDopeQuery, TError, TData>(
variables === undefined ? ['RenderDope.infinite'] : ['RenderDope.infinite', variables],
(metaData) => query({...variables, ...(metaData.pageParam ?? {})}),
options
)}
Example #4
Source File: graphql.ts From dope-monorepo with GNU General Public License v3.0 | 6 votes |
useInfiniteProfileGearQuery = <
TData = ProfileGearQuery,
TError = unknown
>(
variables?: ProfileGearQueryVariables,
options?: UseInfiniteQueryOptions<ProfileGearQuery, TError, TData>
) =>{
const query = useFetchData<ProfileGearQuery, ProfileGearQueryVariables>(ProfileGearDocument)
return useInfiniteQuery<ProfileGearQuery, TError, TData>(
variables === undefined ? ['ProfileGear.infinite'] : ['ProfileGear.infinite', variables],
(metaData) => query({...variables, ...(metaData.pageParam ?? {})}),
options
)}
Example #5
Source File: graphql.ts From dope-monorepo with GNU General Public License v3.0 | 6 votes |
useInfiniteProfileHustlersQuery = <
TData = ProfileHustlersQuery,
TError = unknown
>(
variables?: ProfileHustlersQueryVariables,
options?: UseInfiniteQueryOptions<ProfileHustlersQuery, TError, TData>
) =>{
const query = useFetchData<ProfileHustlersQuery, ProfileHustlersQueryVariables>(ProfileHustlersDocument)
return useInfiniteQuery<ProfileHustlersQuery, TError, TData>(
variables === undefined ? ['ProfileHustlers.infinite'] : ['ProfileHustlers.infinite', variables],
(metaData) => query({...variables, ...(metaData.pageParam ?? {})}),
options
)}
Example #6
Source File: graphql.ts From dope-monorepo with GNU General Public License v3.0 | 6 votes |
useInfiniteProfileDopesQuery = <
TData = ProfileDopesQuery,
TError = unknown
>(
variables?: ProfileDopesQueryVariables,
options?: UseInfiniteQueryOptions<ProfileDopesQuery, TError, TData>
) =>{
const query = useFetchData<ProfileDopesQuery, ProfileDopesQueryVariables>(ProfileDopesDocument)
return useInfiniteQuery<ProfileDopesQuery, TError, TData>(
variables === undefined ? ['ProfileDopes.infinite'] : ['ProfileDopes.infinite', variables],
(metaData) => query({...variables, ...(metaData.pageParam ?? {})}),
options
)}
Example #7
Source File: graphql.ts From dope-monorepo with GNU General Public License v3.0 | 6 votes |
useInfiniteItemQuery = <
TData = ItemQuery,
TError = unknown
>(
variables?: ItemQueryVariables,
options?: UseInfiniteQueryOptions<ItemQuery, TError, TData>
) =>{
const query = useFetchData<ItemQuery, ItemQueryVariables>(ItemDocument)
return useInfiniteQuery<ItemQuery, TError, TData>(
variables === undefined ? ['Item.infinite'] : ['Item.infinite', variables],
(metaData) => query({...variables, ...(metaData.pageParam ?? {})}),
options
)}
Example #8
Source File: graphql.ts From dope-monorepo with GNU General Public License v3.0 | 6 votes |
useInfiniteHustlersWalletQuery = <
TData = HustlersWalletQuery,
TError = unknown
>(
variables?: HustlersWalletQueryVariables,
options?: UseInfiniteQueryOptions<HustlersWalletQuery, TError, TData>
) =>{
const query = useFetchData<HustlersWalletQuery, HustlersWalletQueryVariables>(HustlersWalletDocument)
return useInfiniteQuery<HustlersWalletQuery, TError, TData>(
variables === undefined ? ['HustlersWallet.infinite'] : ['HustlersWallet.infinite', variables],
(metaData) => query({...variables, ...(metaData.pageParam ?? {})}),
options
)}
Example #9
Source File: graphql.ts From dope-monorepo with GNU General Public License v3.0 | 6 votes |
useInfiniteHustlerQuery = <
TData = HustlerQuery,
TError = unknown
>(
variables?: HustlerQueryVariables,
options?: UseInfiniteQueryOptions<HustlerQuery, TError, TData>
) =>{
const query = useFetchData<HustlerQuery, HustlerQueryVariables>(HustlerDocument)
return useInfiniteQuery<HustlerQuery, TError, TData>(
variables === undefined ? ['Hustler.infinite'] : ['Hustler.infinite', variables],
(metaData) => query({...variables, ...(metaData.pageParam ?? {})}),
options
)}
Example #10
Source File: graphql.ts From dope-monorepo with GNU General Public License v3.0 | 6 votes |
useInfiniteDopesQuery = <
TData = DopesQuery,
TError = unknown
>(
variables?: DopesQueryVariables,
options?: UseInfiniteQueryOptions<DopesQuery, TError, TData>
) =>{
const query = useFetchData<DopesQuery, DopesQueryVariables>(DopesDocument)
return useInfiniteQuery<DopesQuery, TError, TData>(
variables === undefined ? ['Dopes.infinite'] : ['Dopes.infinite', variables],
(metaData) => query({...variables, ...(metaData.pageParam ?? {})}),
options
)}
Example #11
Source File: graphql.ts From dope-monorepo with GNU General Public License v3.0 | 6 votes |
useInfiniteDopeListingQuery = <
TData = DopeListingQuery,
TError = unknown
>(
variables?: DopeListingQueryVariables,
options?: UseInfiniteQueryOptions<DopeListingQuery, TError, TData>
) =>{
const query = useFetchData<DopeListingQuery, DopeListingQueryVariables>(DopeListingDocument)
return useInfiniteQuery<DopeListingQuery, TError, TData>(
variables === undefined ? ['DopeListing.infinite'] : ['DopeListing.infinite', variables],
(metaData) => query({...variables, ...(metaData.pageParam ?? {})}),
options
)}
Example #12
Source File: graphql.ts From dope-monorepo with GNU General Public License v3.0 | 6 votes |
useInfiniteAllItemsQuery = <
TData = AllItemsQuery,
TError = unknown
>(
variables?: AllItemsQueryVariables,
options?: UseInfiniteQueryOptions<AllItemsQuery, TError, TData>
) =>{
const query = useFetchData<AllItemsQuery, AllItemsQueryVariables>(AllItemsDocument)
return useInfiniteQuery<AllItemsQuery, TError, TData>(
variables === undefined ? ['AllItems.infinite'] : ['AllItems.infinite', variables],
(metaData) => query({...variables, ...(metaData.pageParam ?? {})}),
options
)}
Example #13
Source File: graphql.ts From dope-monorepo with GNU General Public License v3.0 | 6 votes |
useInfiniteDrugsQuery = <
TData = DrugsQuery,
TError = unknown
>(
variables?: DrugsQueryVariables,
options?: UseInfiniteQueryOptions<DrugsQuery, TError, TData>
) =>{
const query = useFetchData<DrugsQuery, DrugsQueryVariables>(DrugsDocument)
return useInfiniteQuery<DrugsQuery, TError, TData>(
variables === undefined ? ['Drugs.infinite'] : ['Drugs.infinite', variables],
(metaData) => query({...variables, ...(metaData.pageParam ?? {})}),
options
)}
Example #14
Source File: graphql.ts From dope-monorepo with GNU General Public License v3.0 | 6 votes |
useInfiniteDrugQuery = <
TData = DrugQuery,
TError = unknown
>(
variables?: DrugQueryVariables,
options?: UseInfiniteQueryOptions<DrugQuery, TError, TData>
) =>{
const query = useFetchData<DrugQuery, DrugQueryVariables>(DrugDocument)
return useInfiniteQuery<DrugQuery, TError, TData>(
variables === undefined ? ['Drug.infinite'] : ['Drug.infinite', variables],
(metaData) => query({...variables, ...(metaData.pageParam ?? {})}),
options
)}
Example #15
Source File: graphql.ts From dope-monorepo with GNU General Public License v3.0 | 6 votes |
useInfiniteWalletQuery = <
TData = WalletQuery,
TError = unknown
>(
variables?: WalletQueryVariables,
options?: UseInfiniteQueryOptions<WalletQuery, TError, TData>
) =>{
const query = useFetchData<WalletQuery, WalletQueryVariables>(WalletDocument)
return useInfiniteQuery<WalletQuery, TError, TData>(
variables === undefined ? ['Wallet.infinite'] : ['Wallet.infinite', variables],
(metaData) => query({...variables, ...(metaData.pageParam ?? {})}),
options
)}
Example #16
Source File: useApiInfinite.ts From kinopub.webos with MIT License | 6 votes |
function useApiInfinite<T extends Method>(
method: T,
params: Parameters<ApiClient[T]> = [] as Parameters<ApiClient[T]>,
options?: UseInfiniteQueryOptions<Methods[T]>,
) {
const client = useMemo(() => new ApiClient(), []);
const query = useInfiniteQuery<Methods[T], string, Methods[T]>(
[method, ...params],
({ pageParam }) => {
// @ts-expect-error
return client[method](...params, pageParam) as Methods[T];
},
{
// @ts-expect-error
getNextPageParam: (lastPage: PaginationResponse) => {
return lastPage?.pagination?.current + 1 || 1;
},
...options,
},
);
return query;
}
Example #17
Source File: useInfiniteReadingHistory.ts From apps with GNU Affero General Public License v3.0 | 5 votes |
function useInfiniteReadingHistory({
key,
query,
variables,
}: UseInifiniteReadingHistoryProps): UseInfiniteReadingHistory {
const { isLoading, isFetchingNextPage, hasNextPage, data, fetchNextPage } =
useInfiniteQuery<ReadHistoryData>(
key,
({ pageParam }) =>
request(`${apiUrl}/graphql`, query, {
...variables,
after: pageParam,
}),
{
getNextPageParam: (lastPage) =>
lastPage?.readHistory?.pageInfo.hasNextPage &&
lastPage?.readHistory?.pageInfo.endCursor,
},
);
const canFetchMore =
!isLoading && !isFetchingNextPage && hasNextPage && data.pages.length > 0;
const infiniteScrollRef = useFeedInfiniteScroll({
fetchPage: fetchNextPage,
canFetchMore,
});
const hasData = data?.pages?.some(
(page) => page.readHistory.edges.length > 0,
);
return useMemo(
() => ({
hasData,
isLoading,
data,
isInitialLoading: !hasData && isLoading,
infiniteScrollRef,
}),
[hasData, isLoading, data, infiniteScrollRef],
);
}
Example #18
Source File: CommentsSection.tsx From apps with GNU Affero General Public License v3.0 | 5 votes |
export default function CommentsSection({
userId,
tokenRefreshed,
isSameUser,
numComments,
}: CommentsSectionProps): ReactElement {
const comments = useInfiniteQuery<UserCommentsData>(
['user_comments', userId],
({ pageParam }) =>
request(`${apiUrl}/graphql`, USER_COMMENTS_QUERY, {
userId,
first: 3,
after: pageParam,
}),
{
enabled: !!userId && tokenRefreshed,
getNextPageParam: (lastPage) =>
lastPage.page.pageInfo.hasNextPage && lastPage.page.pageInfo.endCursor,
},
);
return (
<ActivitySection
title={`${isSameUser ? 'Your ' : ''}Comments`}
query={comments}
count={numComments}
emptyScreen={
<EmptyMessage data-testid="emptyComments">
{isSameUser ? `You didn't comment yet.` : 'No comments yet.'}
</EmptyMessage>
}
elementToNode={(comment) => (
<CommentContainer key={comment.id}>
<div className="flex flex-col tablet:flex-row tablet:justify-center items-center py-2 w-12 tablet:w-20 font-bold rounded-xl bg-theme-bg-secondary typo-callout">
<UpvoteIcon className="tablet:mr-1 mb-1 tablet:mb-0 text-2xl icon" />
{largeNumberFormat(comment.numUpvotes)}
</div>
<Link href={comment.permalink} passHref prefetch={false}>
<a className={commentInfoClass} aria-label={comment.content}>
<CommentContent>{comment.content}</CommentContent>
<CommentTime dateTime={comment.createdAt}>
{format(new Date(comment.createdAt), 'MMM d, y')}
</CommentTime>
</a>
</Link>
</CommentContainer>
)}
/>
);
}
Example #19
Source File: UpvotedPopupModal.tsx From apps with GNU Affero General Public License v3.0 | 5 votes |
export function UpvotedPopupModal({
listPlaceholderProps,
onRequestClose,
requestQuery: { queryKey, query, params, options = {} },
children,
...modalProps
}: UpvotedPopupModalProps): ReactElement {
const { requestMethod } = useRequestProtocol();
const queryResult = useInfiniteQuery<UpvotesData>(
queryKey,
({ pageParam }) =>
requestMethod(
`${apiUrl}/graphql`,
query,
{ ...params, after: pageParam },
{ requestKey: JSON.stringify(queryKey) },
),
{
...options,
getNextPageParam: (lastPage) =>
lastPage?.upvotes?.pageInfo?.hasNextPage &&
lastPage?.upvotes?.pageInfo?.endCursor,
},
);
const [page] = queryResult?.data?.pages || [];
const container = useRef<HTMLElement>();
const [modalRef, setModalRef] = useState<HTMLElement>();
return (
<ResponsiveModal
{...modalProps}
contentRef={(e) => setModalRef(e)}
onRequestClose={onRequestClose}
padding={false}
style={{
content: {
maxHeight: '40rem',
overflow: 'initial',
},
}}
>
<header className="flex items-center py-4 px-6 w-full border-b border-theme-divider-tertiary">
<h3 className="font-bold typo-title3">Upvoted by</h3>
<ModalCloseButton onClick={onRequestClose} />
</header>
<section
className="overflow-auto relative w-full h-full shrink max-h-full"
data-testid={`List of ${queryKey[0]} with ID ${queryKey[1]}`}
ref={container}
>
{page && page.upvotes.edges.length > 0 ? (
<UpvoterList
queryResult={queryResult}
scrollingContainer={container.current}
appendTooltipTo={modalRef}
/>
) : (
<UpvoterListPlaceholder {...listPlaceholderProps} />
)}
</section>
</ResponsiveModal>
);
}
Example #20
Source File: index.tsx From next-crud with MIT License | 4 votes |
Users = () => {
const { push } = useRouter()
const { data, fetchNextPage, isFetching, hasNextPage, refetch } =
useInfiniteQuery<TPaginationResult<User>>(
'users',
async ({ pageParam = 1 }) => {
const data: TPaginationResult<User> = await fetch(
`/api/users?page=${pageParam}`
).then((res) => res.json())
return data
},
{
getNextPageParam: (lastPage) => {
const pagination = lastPage.pagination as TPaginationDataPageBased
return pagination.page === pagination.pageCount
? undefined
: pagination.page + 1
},
}
)
const allData = useMemo(() => {
return data?.pages.flatMap((page) => page.data)
}, [data])
const onEditUser = (id: User['id']) => {
push(`/users/${id}`)
}
const onDeleteUser = async (id: User['id']) => {
await fetch(`/api/users/${id}`, {
method: 'DELETE',
})
refetch()
}
return (
<Layout title="Users" backRoute="/">
<VStack spacing={6} width="100%">
<Heading>Users</Heading>
<Flex direction="row" justify="flex-end" width="100%">
<Button
colorScheme="green"
leftIcon={<AddIcon color="white" />}
onClick={() => push('/users/create')}
>
Create user
</Button>
</Flex>
<VStack
boxShadow="0px 2px 8px #ccc"
p={4}
borderRadius={6}
width="100%"
align="flex-start"
>
{!data && (
<Stack width="100%">
<Skeleton height="20px" />
<Skeleton height="20px" />
<Skeleton height="20px" />
</Stack>
)}
{allData?.map((user) => (
<UserListItem
key={user.id}
{...user}
onEdit={onEditUser}
onDelete={onDeleteUser}
/>
))}
</VStack>
<Button
colorScheme="blue"
onClick={() => fetchNextPage()}
disabled={isFetching || !hasNextPage}
>
Load more
</Button>
</VStack>
</Layout>
)
}
Example #21
Source File: DashboardsPage.tsx From kubenav with MIT License | 4 votes |
DashboardsPage: React.FunctionComponent = () => {
const context = useContext<IContext>(AppContext);
const cluster = context.currentCluster();
const [searchText, setSearchText] = useState<string>('');
const fetchItems = async (cursor) =>
await kubernetesRequest(
'GET',
`${
context.settings.prometheusDashboardsNamespace
? `/api/v1/namespaces/${context.settings.prometheusDashboardsNamespace}/configmaps`
: `/api/v1/configmaps`
}?labelSelector=kubenav.io/prometheus-dashboard=true&limit=50${
cursor.pageParam ? `&continue=${cursor.pageParam}` : ''
}`,
'',
context.settings,
await context.kubernetesAuthWrapper(''),
);
const { isError, isFetching, isFetchingNextPage, hasNextPage, data, error, fetchNextPage, refetch } =
useInfiniteQuery(
`PrometheusDashboardsPage_${cluster ? cluster.id : ''}_${cluster ? cluster.namespace : ''}`,
fetchItems,
{
refetchInterval: context.settings.queryRefetchInterval,
getNextPageParam: (lastGroup) =>
lastGroup.metadata && lastGroup.metadata.continue ? lastGroup.metadata.continue : false,
},
);
const doRefresh = async (event: CustomEvent<RefresherEventDetail>) => {
event.detail.complete();
refetch();
};
const loadMore = async (event: CustomEvent<void>) => {
await fetchNextPage();
(event.target as HTMLIonInfiniteScrollElement).complete();
};
return (
<IonPage>
<IonHeader>
<IonToolbar>
<IonButtons slot="start">
<IonMenuButton />
</IonButtons>
<IonTitle>Prometheus</IonTitle>
<IonButtons slot="primary">
<Details refresh={refetch} />
</IonButtons>
</IonToolbar>
</IonHeader>
<IonContent>
{isFetching ? <IonProgressBar slot="fixed" type="indeterminate" color="primary" /> : null}
<IonRefresher slot="fixed" onIonRefresh={doRefresh} />
{!isError && cluster ? (
<React.Fragment>
<IonSearchbar
inputmode="search"
value={searchText}
onIonChange={(e) => setSearchText(e.detail.value ? e.detail.value : '')}
/>
{data && data.pages ? (
<IonList>
{data.pages.map((group, i) => (
<React.Fragment key={i}>
{group && group.items
? group.items
.filter((item) => {
const regex = new RegExp(searchText, 'gi');
return item.metadata && item.metadata.name && item.metadata.name.match(regex);
})
.map((item: V1ConfigMap, j) => {
return (
<IonItem
key={`${i}_${j}`}
routerLink={`/plugins/prometheus/${item.metadata ? item.metadata.namespace : ''}/${
item.metadata ? item.metadata.name : ''
}`}
routerDirection="forward"
>
<IonLabel>
<h2>{item.data && item.data['title'] ? item.data['title'] : ''}</h2>
<p>{item.data && item.data['description'] ? item.data['description'] : ''}</p>
</IonLabel>
</IonItem>
);
})
: null}
</React.Fragment>
))}
<IonInfiniteScroll
threshold="25%"
disabled={!hasNextPage || (isFetchingNextPage as boolean)}
onIonInfinite={loadMore}
>
{(!isFetchingNextPage as boolean) ? (
<IonButton size="small" expand="block" fill="clear" onClick={() => fetchNextPage()}>
Load more
</IonButton>
) : null}
<IonInfiniteScrollContent
loadingText={`Loading more Prometheus Dashboards...`}
></IonInfiniteScrollContent>
</IonInfiniteScroll>
</IonList>
) : null}
</React.Fragment>
) : isFetching ? null : (
<LoadingErrorCard
cluster={context.cluster}
clusters={context.clusters}
error={error as Error}
icon="/assets/icons/kubernetes/prometheus.png"
text="Could not get Prometheus Dashboards"
/>
)}
</IonContent>
</IonPage>
);
}
Example #22
Source File: ListPage.tsx From kubenav with MIT License | 4 votes |
ListPage: React.FunctionComponent<IListPageProps> = ({ match }: IListPageProps) => {
const context = useContext<IContext>(AppContext);
const cluster = context.currentCluster();
// Determine one which page we are currently (which items for a resource do we want to show) by the section and type
// parameter. Get the component 'ResourceItem' we want to render.
const page = resources[match.params.section].pages[match.params.type];
const Component = page.listItemComponent;
// namespace and showNamespace is used to group all items by namespace and to only show the namespace once via the
// IonItemDivider component.
let namespace = '';
let showNamespace = false;
// searchText is used to search and filter the list of items.
const [searchText, setSearchText] = useState<string>('');
const fetchItems = async (cursor) =>
await kubernetesRequest(
'GET',
`${page.listURL(cluster ? cluster.namespace : '')}?limit=50${
cursor.pageParam ? `&continue=${cursor.pageParam}` : ''
}`,
'',
context.settings,
await context.kubernetesAuthWrapper(''),
);
const { isError, isFetching, isFetchingNextPage, hasNextPage, data, error, fetchNextPage, refetch } =
useInfiniteQuery(
// NOTE: Array keys (https://react-query.tanstack.com/docs/guides/queries#array-keys) do not work with
// useInfiniteQuery, therefore we are creating a string only query key with the values, which normaly are used as
// query key.
// ['ListPage', cluster ? cluster.id : '', cluster ? cluster.namespace : '', match.params.section, match.params.type],
`ListPage_${cluster ? cluster.id : ''}_${cluster ? cluster.namespace : ''}_${match.params.section}_${
match.params.type
}`,
fetchItems,
{
refetchInterval: context.settings.queryRefetchInterval,
getNextPageParam: (lastGroup) =>
lastGroup.metadata && lastGroup.metadata.continue ? lastGroup.metadata.continue : false,
},
);
// The doRefresh method is used for a manual reload of the items for the corresponding resource. The
// event.detail.complete() call is required to finish the animation of the IonRefresher component.
const doRefresh = async (event: CustomEvent<RefresherEventDetail>) => {
event.detail.complete();
refetch();
};
// allGroups is used to fetch additional items from the Kubernetes API. When the fetchNextPage funtion is finished we
// have to call the complete() method on the infinite scroll instance.
const loadMore = async (event: CustomEvent<void>) => {
await fetchNextPage();
(event.target as HTMLIonInfiniteScrollElement).complete();
};
return (
<IonPage>
<IonHeader>
<IonToolbar>
<IonButtons slot="start">
<IonMenuButton />
</IonButtons>
<IonTitle>{page.pluralText}</IonTitle>
<IonButtons slot="primary">
{isNamespaced(match.params.type) ? <Namespaces /> : null}
<Details
refresh={refetch}
bookmark={{
title: page.pluralText,
url: match.url,
namespace: isNamespaced(match.params.type) ? (cluster ? cluster.namespace : '') : '',
}}
type={match.params.type}
page={page}
/>
</IonButtons>
</IonToolbar>
</IonHeader>
<IonContent>
{isFetching ? <IonProgressBar slot="fixed" type="indeterminate" color="primary" /> : null}
<IonRefresher slot="fixed" onIonRefresh={doRefresh} />
{!isError && cluster ? (
<React.Fragment>
<IonSearchbar
inputmode="search"
value={searchText}
onIonChange={(e) => setSearchText(e.detail.value ? e.detail.value : '')}
/>
<IonList>
{data && data.pages
? data.pages.map((group, i) => (
<React.Fragment key={i}>
{group && group.items
? group.items
.filter((item) => {
if (searchText.startsWith('$')) {
const parts = searchText.split(':');
if (
parts.length !== 2 ||
(parts.length === 2 && parts[1] === '') ||
getProperty(item, parts[0].substring(2)) === undefined
) {
return false;
}
const regex = new RegExp(parts[1].trim(), 'gi');
return getProperty(item, parts[0].substring(2).trim()).toString().match(regex);
} else {
const regex = new RegExp(searchText, 'gi');
if (match.params.type === 'events') {
return (
item.metadata &&
item.metadata.name &&
(item.metadata.name.match(regex) ||
item.message.match(regex) ||
item.reason.match(regex))
);
} else {
return item.metadata && item.metadata.name && item.metadata.name.match(regex);
}
}
})
.map((item, j) => {
if (
isNamespaced(match.params.type) &&
item.metadata &&
item.metadata.namespace &&
item.metadata.namespace !== namespace
) {
namespace = item.metadata.namespace;
showNamespace = true;
} else {
showNamespace = false;
}
return (
<IonItemGroup key={j}>
{showNamespace ? (
<IonItemDivider>
<IonLabel>{namespace}</IonLabel>
</IonItemDivider>
) : null}
<ItemOptions
item={item}
url={page.detailsURL(
item.metadata ? item.metadata.namespace : '',
item.metadata ? item.metadata.name : '',
)}
>
<Component item={item} section={match.params.section} type={match.params.type} />
</ItemOptions>
</IonItemGroup>
);
})
: null}
</React.Fragment>
))
: null}
<IonInfiniteScroll
threshold="25%"
disabled={!hasNextPage || (isFetchingNextPage as boolean)}
onIonInfinite={loadMore}
>
{(!isFetchingNextPage as boolean) ? (
<IonButton size="small" expand="block" fill="clear" onClick={() => fetchNextPage()}>
Load more
</IonButton>
) : null}
<IonInfiniteScrollContent loadingText={`Loading more ${page.pluralText}...`}></IonInfiniteScrollContent>
</IonInfiniteScroll>
</IonList>
</React.Fragment>
) : isFetching ? null : (
<LoadingErrorCard
cluster={context.cluster}
clusters={context.clusters}
error={error as Error}
icon={page.icon}
text={`Could not get ${page.pluralText}`}
/>
)}
</IonContent>
</IonPage>
);
}
Example #23
Source File: CustomResourcesListPage.tsx From kubenav with MIT License | 4 votes |
CustomResourcesListPage: React.FunctionComponent<ICustomResourcesListPageProps> = ({
location,
match,
}: ICustomResourcesListPageProps) => {
const context = useContext<IContext>(AppContext);
const cluster = context.currentCluster();
// scope can be "Cluster" or "Namespaced", for a cluster scoped CRD we have to set the namespace to "" to retrive all
// CRs.
const scope = new URLSearchParams(location.search).get('scope');
// namespace and showNamespace is used to group all items by namespace and to only show the namespace once via the
// IonItemDivider component.
let namespace = '';
let showNamespace = false;
// searchText is used to search and filter the list of items.
const [searchText, setSearchText] = useState<string>('');
const fetchItems = async (cursor) =>
await kubernetesRequest(
'GET',
`${getURL(
scope === 'Cluster' ? '' : cluster ? cluster.namespace : '',
match.params.group,
match.params.version,
match.params.name,
)}?limit=50${cursor.pageParam ? `&continue=${cursor.pageParam}` : ''}`,
'',
context.settings,
await context.kubernetesAuthWrapper(''),
);
const { isError, isFetching, isFetchingNextPage, hasNextPage, data, error, fetchNextPage, refetch } =
useInfiniteQuery(
// NOTE: Array keys (https://react-query.tanstack.com/docs/guides/queries#array-keys) do not work with
// useInfiniteQuery, therefore we are creating a string only query key with the values, which normaly are used as
// query key.
// ['CustomResourcesListPage', cluster ? cluster.id : '', cluster ? cluster.namespace : '', match.params.group, match.params.version, match.params.name],
`CustomResourcesListPage_${cluster ? cluster.id : ''}_${cluster ? cluster.namespace : ''}_${match.params.group}_${
match.params.version
}_${match.params.name}`,
fetchItems,
{
refetchInterval: context.settings.queryRefetchInterval,
getNextPageParam: (lastGroup) =>
lastGroup.metadata && lastGroup.metadata.continue ? lastGroup.metadata.continue : false,
},
);
// The doRefresh method is used for a manual reload of the items for the corresponding resource. The
// event.detail.complete() call is required to finish the animation of the IonRefresher component.
const doRefresh = async (event: CustomEvent<RefresherEventDetail>) => {
event.detail.complete();
refetch();
};
// allGroups is used to fetch additional items from the Kubernetes API. When the fetchNextPage funtion is finished we
// have to call the complete() method on the infinite scroll instance.
const loadMore = async (event: CustomEvent<void>) => {
await fetchNextPage();
(event.target as HTMLIonInfiniteScrollElement).complete();
};
return (
<IonPage>
<IonHeader>
<IonToolbar>
<IonButtons slot="start">
<IonBackButton defaultHref={`/resources/cluster/customresourcedefinitions`} />
</IonButtons>
<IonTitle>{match.params.name}</IonTitle>
<IonButtons slot="primary">
<Namespaces />
<Details
refresh={refetch}
bookmark={{
title: match.params.name,
url: match.url,
namespace: cluster ? cluster.namespace : '',
}}
type="customresourcedefinitions"
/>
</IonButtons>
</IonToolbar>
</IonHeader>
<IonContent>
{isFetching ? <IonProgressBar slot="fixed" type="indeterminate" color="primary" /> : null}
<IonRefresher slot="fixed" onIonRefresh={doRefresh} />
{!isError && cluster ? (
<React.Fragment>
<IonSearchbar
inputmode="search"
value={searchText}
onIonChange={(e) => setSearchText(e.detail.value ? e.detail.value : '')}
/>
<IonList>
{data && data.pages
? data.pages.map((group, i) => (
<React.Fragment key={i}>
{group && group.items
? group.items
.filter((item) => {
const regex = new RegExp(searchText, 'gi');
return item.metadata && item.metadata.name && item.metadata.name.match(regex);
})
.map((item, j) => {
if (item.metadata && item.metadata.namespace && item.metadata.namespace !== namespace) {
namespace = item.metadata.namespace;
showNamespace = true;
} else {
showNamespace = false;
}
return (
<IonItemGroup key={j}>
{showNamespace ? (
<IonItemDivider>
<IonLabel>{namespace}</IonLabel>
</IonItemDivider>
) : null}
<ItemOptions
item={item}
url={`${getURL(
item.metadata ? item.metadata.namespace : '',
match.params.group,
match.params.version,
match.params.name,
)}/${item.metadata ? item.metadata.name : ''}`}
>
<CustomResourceItem
group={match.params.group}
version={match.params.version}
name={match.params.name}
item={item}
/>
</ItemOptions>
</IonItemGroup>
);
})
: null}
</React.Fragment>
))
: null}
<IonInfiniteScroll
threshold="25%"
disabled={!hasNextPage || (isFetchingNextPage as boolean)}
onIonInfinite={loadMore}
>
<IonInfiniteScrollContent
loadingText={`Loading more ${match.params.name}...`}
></IonInfiniteScrollContent>
</IonInfiniteScroll>
</IonList>
</React.Fragment>
) : isFetching ? null : (
<LoadingErrorCard
cluster={context.cluster}
clusters={context.clusters}
error={error as Error}
icon="/assets/icons/kubernetes/crd.png"
text={`Could not get Custom Resources "${match.params.name}"`}
/>
)}
</IonContent>
</IonPage>
);
}
Example #24
Source File: KeywordManagement.tsx From apps with GNU Affero General Public License v3.0 | 4 votes |
export default function KeywordManagement({
keyword,
subtitle,
onOperationCompleted,
}: KeywordManagerProps): ReactElement {
const { windowLoaded } = useContext(ProgressiveEnhancementContext);
const [currentAction, setCurrentAction] = useState<string | null>(null);
const nextKeyword = async () => {
if (onOperationCompleted) {
await onOperationCompleted();
}
setCurrentAction(null);
};
const { mutateAsync: allowKeyword } = useMutation(
() =>
request(`${apiUrl}/graphql`, ALLOW_KEYWORD_MUTATION, {
keyword: keyword.value,
}),
{
onSuccess: () => nextKeyword(),
},
);
const { mutateAsync: denyKeyword } = useMutation(
() =>
request(`${apiUrl}/graphql`, DENY_KEYWORD_MUTATION, {
keyword: keyword.value,
}),
{
onSuccess: () => nextKeyword(),
},
);
const posts = useInfiniteQuery<FeedData>(
['keyword_post', keyword.value],
({ pageParam }) =>
request(`${apiUrl}/graphql`, KEYWORD_FEED_QUERY, {
keyword: keyword.value,
first: 4,
after: pageParam,
}),
{
getNextPageParam: (lastPage) =>
lastPage.page.pageInfo.hasNextPage && lastPage.page.pageInfo.endCursor,
},
);
const onAllow = () => {
setCurrentAction('allow');
return allowKeyword();
};
const onSynonym = () => setCurrentAction('synonym');
const onDeny = () => {
setCurrentAction('deny');
return denyKeyword();
};
const disableActions = !!currentAction;
return (
<ResponsivePageContainer style={{ paddingBottom: sizeN(23) }}>
<NextSeo title="Pending Keywords" nofollow noindex />
<h1 className="m-0 font-bold typo-title2">{keyword.value}</h1>
<div className="flex justify-between items-center my-1 text-theme-label-tertiary typo-callout">
<span>Occurrences: {keyword.occurrences}</span>
<span>{subtitle}</span>
</div>
<ActivitySection
title="Keyword Posts"
query={posts}
emptyScreen={
<div className="font-bold typo-title3" data-testid="emptyPosts">
No posts
</div>
}
elementToNode={(post) => (
<Link
href={post.commentsPermalink}
passHref
key={post.id}
prefetch={false}
>
<a
target="_blank"
rel="noopener noreferrer"
aria-label={post.title}
className="flex items-start tablet:items-center py-3 no-underline"
>
<LazyImage
imgSrc={smallPostImage(post.image)}
imgAlt="Post cover image"
className="w-16 h-16 rounded-2xl"
/>
<p
className="flex-1 self-center p-0 tablet:mr-6 ml-4 whitespace-pre-wrap break-words-overflow text-theme-label-primary typo-callout multi-truncate"
style={{
maxHeight: '3.75rem',
maxWidth: '19.25rem',
lineClamp: 3,
}}
>
{post.title}
</p>
</a>
</Link>
)}
/>
<div
className={classNames(
'fixed flex left-0 right-0 bottom-0 w-full items-center justify-between mx-auto py-6 px-4 bg-theme-bg-primary',
styles.buttons,
)}
>
<Button
loading={currentAction === 'allow'}
onClick={onAllow}
disabled={disableActions}
className="btn-primary"
>
Allow
</Button>
<Button
disabled={disableActions}
onClick={onSynonym}
className="btn-secondary"
>
Synonym
</Button>
<Button
loading={currentAction === 'deny'}
onClick={onDeny}
disabled={disableActions}
className="btn-primary-ketchup"
>
Deny
</Button>
</div>
{(windowLoaded || currentAction === 'synonym') && (
<KeywordSynonymModal
isOpen={currentAction === 'synonym'}
selectedKeyword={keyword.value}
onRequestClose={nextKeyword}
/>
)}
</ResponsivePageContainer>
);
}
Example #25
Source File: useFeed.ts From apps with GNU Affero General Public License v3.0 | 4 votes |
export default function useFeed<T>(
feedQueryKey: unknown[],
pageSize: number,
adSpot: number,
placeholdersPerPage: number,
showOnlyUnreadPosts: boolean,
query?: string,
variables?: T,
): FeedReturnType {
const { user, tokenRefreshed } = useContext(AuthContext);
const queryClient = useQueryClient();
const feedQuery = useInfiniteQuery<FeedData>(
feedQueryKey,
({ pageParam }) =>
request(`${apiUrl}/graphql`, query, {
...variables,
first: pageSize,
after: pageParam,
loggedIn: !!user,
unreadOnly: showOnlyUnreadPosts,
}),
{
enabled: query && tokenRefreshed,
refetchOnMount: false,
refetchOnReconnect: false,
refetchOnWindowFocus: false,
getNextPageParam: (lastPage) =>
lastPage.page.pageInfo.hasNextPage && lastPage.page.pageInfo.endCursor,
},
);
const adsQuery = useInfiniteQuery<Ad>(
['ads', ...feedQueryKey],
async () => {
const res = await fetch(`${apiUrl}/v1/a`);
const ads: Ad[] = await res.json();
return ads[0];
},
{
getNextPageParam: () => Date.now(),
enabled: query && tokenRefreshed,
refetchOnMount: false,
refetchOnReconnect: false,
refetchOnWindowFocus: false,
},
);
useEffect(() => {
if (
!adsQuery.isFetching &&
adsQuery.data?.pages?.length < feedQuery.data?.pages?.length
) {
adsQuery.fetchNextPage();
}
}, [adsQuery.data, feedQuery.data, adsQuery.isFetching]);
const items = useMemo(() => {
let newItems: FeedItem[] = [];
if (feedQuery.data) {
newItems = feedQuery.data.pages.flatMap(
({ page }, pageIndex): FeedItem[] => {
const posts: FeedItem[] = page.edges.map(({ node }, index) => ({
type: 'post',
post: node,
page: pageIndex,
index,
}));
if (adsQuery.data?.pages[pageIndex]) {
posts.splice(adSpot, 0, {
type: 'ad',
ad: adsQuery.data?.pages[pageIndex],
});
} else {
posts.splice(adSpot, 0, {
type: 'placeholder',
});
}
return posts;
},
);
}
if (feedQuery.isFetching) {
newItems.push(
...Array(placeholdersPerPage).fill({ type: 'placeholder' }),
);
}
return newItems;
}, [
feedQuery.data,
feedQuery.isFetching,
adsQuery.data,
adsQuery.isFetching,
]);
const updatePost = updateCachedPost(feedQueryKey, queryClient);
useSubscription(
() => ({
query: POSTS_ENGAGED_SUBSCRIPTION,
}),
{
next: (data: PostsEngaged) => {
const { pageIndex, index } = findIndexOfPostInData(
feedQuery.data,
data.postsEngaged.id,
);
if (index > -1) {
updatePost(pageIndex, index, {
...feedQuery.data.pages[pageIndex].page.edges[index].node,
...data.postsEngaged,
});
}
},
},
);
return {
items,
fetchPage: async () => {
const adPromise = adsQuery.fetchNextPage();
await feedQuery.fetchNextPage();
await adPromise;
},
updatePost,
removePost: removeCachedPost(feedQueryKey, queryClient),
canFetchMore: feedQuery.hasNextPage,
emptyFeed:
!feedQuery?.data?.pages[0]?.page.edges.length && !feedQuery.isFetching,
};
}
Example #26
Source File: PostsSection.tsx From apps with GNU Affero General Public License v3.0 | 4 votes |
export default function PostsSection({
userId,
isSameUser,
numPosts,
}: PostsSectionProps): ReactElement {
const { windowLoaded } = useContext(ProgressiveEnhancementContext);
const { user, updateUser, tokenRefreshed } = useContext(AuthContext);
const formRef = useRef<HTMLFormElement>(null);
const [disableSubmit, setDisableSubmit] = useState<boolean>(true);
const [twitterHint, setTwitterHint] = useState<string>();
const [showAccountDetails, setShowAccountDetails] = useState(false);
const updateDisableSubmit = () => {
if (formRef.current) {
setDisableSubmit(!formRef.current.checkValidity());
}
};
const onSubmit = async (event: FormEvent): Promise<void> => {
event.preventDefault();
setDisableSubmit(true);
const data = formToJson<UserProfile>(
formRef.current,
loggedUserToProfile(user),
);
const res = await updateProfile(data);
if ('error' in res) {
if ('code' in res && res.code === 1) {
if (res.field === 'twitter') {
setTwitterHint('This Twitter handle is already used');
} else {
setTwitterHint('Please contact us [email protected]');
}
}
} else {
await updateUser({ ...user, ...res });
setDisableSubmit(false);
}
};
const posts = useInfiniteQuery<FeedData>(
['user_posts', userId],
({ pageParam }) =>
request(`${apiUrl}/graphql`, AUTHOR_FEED_QUERY, {
userId,
first: 3,
after: pageParam,
}),
{
enabled: !!userId && tokenRefreshed,
getNextPageParam: (lastPage) =>
lastPage.page.pageInfo.hasNextPage && lastPage.page.pageInfo.endCursor,
},
);
let postsEmptyScreen: ReactNode;
if (!isSameUser) {
postsEmptyScreen = (
<EmptyMessage data-testid="emptyPosts">No articles yet.</EmptyMessage>
);
} else if (user.twitter) {
postsEmptyScreen = (
<p
className="typo-callout text-theme-label-tertiary"
data-testid="emptyPosts"
>
No articles yet.
<br />
<br />
<a
className="no-underline text-theme-label-link"
href={ownershipGuide}
target="_blank"
rel="noopener"
>
How daily.dev picks up new articles
</a>
<br />
<br />
Do you have articles you wrote that got picked up by daily.dev in the
past?
<br />
<br />
<a
className="no-underline text-theme-label-link"
href="mailto:[email protected]?subject=Add my articles retroactively&body=README: To add your articles retroactively, please reply with your username or a link to your profile on daily.dev. Keep in mind that we can only add articles that we're already picked up by daily.dev. Not sure if your article appeared in our feed? Try searching its headline here: https://app.daily.dev/search"
target="_blank"
rel="noopener"
>
Email us to add your articles retroactively
</a>
</p>
);
} else {
postsEmptyScreen = (
<>
<EmptyMessage data-testid="emptyPosts">
{`Track when articles you published are getting picked by
daily.dev. Set up your Twitter handle and we'll do the rest ?`}
</EmptyMessage>
{user.email && user.username ? (
<form
className="flex flex-col items-start mt-6"
ref={formRef}
onSubmit={onSubmit}
>
<TextField
inputId="twitter"
name="twitter"
label="Twitter"
value={user.twitter}
hint={twitterHint}
valid={!twitterHint}
placeholder="handle"
pattern="(\w){1,15}"
maxLength={15}
validityChanged={updateDisableSubmit}
valueChanged={() => twitterHint && setTwitterHint(null)}
className="self-stretch mb-4"
style={{ maxWidth: sizeN(78) }}
/>
<Button
className="btn-primary"
type="submit"
disabled={disableSubmit}
style={{ width: sizeN(30) }}
>
Save
</Button>
</form>
) : (
<>
<button
type="button"
className="self-start mt-4 btn-primary"
onClick={() => setShowAccountDetails(true)}
>
Complete your profile
</button>
{(windowLoaded || showAccountDetails) && (
<AccountDetailsModal
isOpen={showAccountDetails}
onRequestClose={() => setShowAccountDetails(false)}
/>
)}
</>
)}
</>
);
}
return (
<ActivitySection
title={`${isSameUser ? 'Your ' : ''}Articles`}
query={posts}
count={numPosts}
emptyScreen={postsEmptyScreen}
elementToNode={(post) => (
<Link
href={post.commentsPermalink}
passHref
key={post.id}
prefetch={false}
>
<a
className={`${commentContainerClass} pl-3 no-underline`}
aria-label={post.title}
>
<div className="relative">
<LazyImage
imgSrc={smallPostImage(post.image)}
imgAlt="Post cover image"
className={`rounded-2xl ${styles.postImage}`}
/>
<LazyImage
imgSrc={post.source.image}
imgAlt={post.source.name}
className={`top-1/2 left-0 w-8 h-8 bg-theme-bg-primary rounded-full -translate-x-1/2 -translate-y-1/2 ${styles.sourceImage}`}
absolute
/>
</div>
<div className={commentInfoClass}>
<CommentContent className={styles.postContent}>
{post.title}
</CommentContent>
<div className="grid grid-flow-col auto-cols-max gap-x-4 mt-3 tablet:mt-0 tablet:ml-auto">
{post.views !== null && (
<PostStat>
<EyeIcon className={postStatIconClass} />
{largeNumberFormat(post.views)}
</PostStat>
)}
<PostStat>
<UpvoteIcon className={postStatIconClass} />
{largeNumberFormat(post.numUpvotes)}
</PostStat>
<PostStat>
<CommentIcon className={postStatIconClass} />
{largeNumberFormat(post.numComments)}
</PostStat>
</div>
</div>
</a>
</Link>
)}
/>
);
}
Example #27
Source File: MessagesScreen.tsx From vsinder with Apache License 2.0 | 4 votes |
MessagesScreen: React.FC<MatchesStackNav<"messages">> = ({
route: { params },
navigation,
}) => {
const qKey = `/messages/${params.id}`;
const {
data,
isLoading,
isFetchingMore,
fetchMore,
} = useInfiniteQuery<IMessageResponse>(
qKey,
(key, cursor = "") =>
defaultQueryFn(`${key}/${cursor}`).then((x) => ({
hasMore: x.hasMore,
messages: x.messages.map((m: Message) =>
messageToGiftedMessage(m, { me: meData!.user!, them: params })
),
})),
{
staleTime: 0,
getFetchMore: ({ messages, hasMore }) =>
hasMore && messages.length
? messages[messages.length - 1].createdAt
: "",
}
);
const { data: meData } = useQuery<MeResponse>("/me", defaultQueryFn);
const cache = useQueryCache();
const [mutate] = useMutation(defaultMutationFn);
const {
inputForeground,
inputBackground,
buttonBackground,
buttonForeground,
buttonSecondaryBackground,
buttonSecondaryForeground,
buttonForegroundDarker,
buttonSecondaryForegroundDarker,
} = useTheme();
useOnWebSocket((e) => {
if (e.type === "new-message" && e.message.senderId === params.id) {
cache.setQueryData<IMessageResponse[]>(qKey, (d) => {
return produce(d!, (x) => {
x[0].messages = GiftedChat.append(x[0].messages, [
messageToGiftedMessage(e.message, {
me: meData!.user!,
them: params,
}),
]);
});
});
} else if (e.type === "unmatch") {
if (e.userId === params.id) {
navigation.goBack();
}
}
});
useEffect(() => {
getSocket().send(
JSON.stringify({ type: "message-open", userId: params.id })
);
const d = cache.getQueryData<MatchesResponse>("/matches/0");
if (d && d.matches.find((x) => x.userId === params.id && !x.read)) {
cache.setQueryData<MatchesResponse>("/matches/0", {
matches: d.matches.map((m) =>
m.userId === params.id ? { ...m, read: true } : m
),
});
}
return () => {
getSocket().send(JSON.stringify({ type: "message-open", userId: null }));
};
}, []);
if (isLoading) {
return <FullscreenLoading />;
}
if (!meData?.user) {
return null;
}
const messages = data ? data.map((x) => x.messages).flat() : [];
return (
<ScreenWrapper noPadding>
<GiftedChat
alignTop
loadEarlier={data?.[data?.length - 1]?.hasMore}
onPressAvatar={(u) =>
navigation.navigate("viewCard", { id: u._id.toString() })
}
isLoadingEarlier={!!isFetchingMore}
renderLoadEarlier={({ isLoadingEarlier }) =>
isLoadingEarlier ? (
<Loading />
) : (
<MyButton onPress={() => fetchMore()}>load more</MyButton>
)
}
listViewProps={{
showsVerticalScrollIndicator: false,
}}
timeTextStyle={{
left: { color: buttonSecondaryForegroundDarker },
right: { color: buttonForegroundDarker },
}}
renderBubble={(props) => {
return (
<Bubble
{...props}
textStyle={{
right: {
color: buttonForeground,
},
left: {
color: buttonSecondaryForeground,
},
}}
wrapperStyle={{
left: {
backgroundColor: buttonSecondaryBackground,
},
right: {
backgroundColor: buttonBackground,
},
}}
/>
);
}}
renderSend={(props) => (
<Send
{...props}
containerStyle={{
justifyContent: "center",
alignItems: "center",
alignSelf: "center",
marginRight: 15,
}}
>
<MaterialIcons name="send" size={24} color={buttonBackground} />
</Send>
)}
// @ts-ignore
containerStyle={{
backgroundColor: inputBackground,
}}
textInputStyle={{
color: inputForeground,
backgroundColor: inputBackground,
}}
// @ts-ignore
renderTicks={() => null}
messages={messages}
onSend={(messages) => {
messages.forEach((m) => {
mutate([
"/message",
{ recipientId: params.id, text: m.text, matchId: params.matchId },
"POST",
]).then(({ message: newMessage }) => {
cache.setQueryData<IMessageResponse[]>(qKey, (d) => {
return produce(d!, (x) => {
x[0].messages = GiftedChat.append(x[0].messages, [
messageToGiftedMessage(newMessage, {
me: meData.user!,
them: params,
}),
]);
});
});
const d = cache.getQueryData<MatchesResponse>("/matches/0");
if (d) {
cache.setQueryData<MatchesResponse>("/matches/0", {
matches: d.matches.map((m) =>
m.userId === params.id
? {
...m,
message: {
text: newMessage.text,
createdAt: newMessage.createdAt,
},
}
: m
),
});
}
});
});
}}
user={{
_id: meData.user.id,
}}
/>
</ScreenWrapper>
);
}
Example #28
Source File: index.tsx From basement-grotesque with SIL Open Font License 1.1 | 4 votes |
DataColumns = ({ tweets: initialTweets, releases }: DataColumnsProps) => {
const {
data: queriedTweets,
fetchNextPage,
hasNextPage,
isFetching
} = useInfiniteQuery(['tweets'], ({ pageParam }) => getTweets(pageParam), {
enabled: !!initialTweets,
refetchOnWindowFocus: false,
initialData: {
pageParams: [undefined],
pages: [initialTweets]
},
getNextPageParam: (lastPage) => (lastPage ? lastPage.meta.next_token : null)
})
const [activeSection, setActiveSection] = useState<Sections>('releases')
return (
<Section background="black" noMargin>
<Container maxWidth>
<SectionInner
css={{
display: 'none',
'@bp3': {
display: 'grid'
}
}}
>
<Column>
<Text css={{ fontSize: '$3' }} uppercase>
CHANGELOG
</Text>
<Text css={{ fontSize: '$7' }} heading uppercase>
Version History
</Text>
<div>
{releases.map(({ date, text, version }, idx) => (
<Release version={version} date={date} text={text} key={idx} />
))}
</div>
</Column>
<Column>
<Text css={{ fontSize: '$3' }} uppercase>
Stats
</Text>
<Text css={{ fontSize: '$7' }} heading uppercase>
Features status
</Text>
<div>
<Feature title="Family styles" score={1} />
<Feature title="Character set" score={4} />
<Feature title="Spacing & Kerning" score={3} />
<Feature title="Hinting" score={4} />
</div>
</Column>
<Column>
<Text css={{ fontSize: '$3' }} uppercase>
#BASEMENTGROTESQUE
</Text>
<Text css={{ fontSize: '$7' }} heading uppercase>
Tweets
</Text>
<div>
{queriedTweets?.pages?.map((page, pageIdx) => {
if (!page) return null
return (
<Fragment key={pageIdx}>
{page.data.map((tweet) => (
<Tweet tweet={tweet} key={tweet.id} />
))}
</Fragment>
)
})}
{hasNextPage && (
<LoadMore onClick={() => fetchNextPage()} disabled={isFetching}>
<Text uppercase heading>
Load more
</Text>
<ArrowDown
css={{
marginLeft: 8,
color: 'white',
path: { fill: 'white' },
$$size: '16px'
}}
/>
</LoadMore>
)}
</div>
</Column>
</SectionInner>
</Container>
<SectionInner
css={{
'@bp3': {
display: 'none'
}
}}
>
<Box>
<Container css={{ borderBottom: '1px solid $white' }}>
<SectionPicker>
<select
onChange={(e) => {
setActiveSection(e.target.value as Sections)
}}
>
{[
{ key: 'releases', label: 'Version History' },
{ key: 'features', label: 'Feature Status' },
{ key: 'tweets', label: 'Tweets' }
].map(({ key, label }) => (
<option value={key} key={key}>
{label}
</option>
))}
</select>
<svg
width="20"
height="13"
viewBox="0 0 20 13"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M1 1.5L10 10.5L19 1.5"
stroke="white"
strokeWidth="2.5"
/>
</svg>
</SectionPicker>
</Container>
</Box>
<Container>
<Column
css={{ display: activeSection === 'releases' ? 'block' : 'none' }}
>
{releases.map(({ date, text, version }, idx) => (
<Release version={version} date={date} text={text} key={idx} />
))}
</Column>
<Column
css={{ display: activeSection === 'features' ? 'block' : 'none' }}
>
<Feature title="Family styles" score={1} />
<Feature title="Character set" score={4} />
<Feature title="Spacing & Kerning" score={3} />
<Feature title="Hinting" score={4} />
</Column>
<Column
css={{ display: activeSection === 'tweets' ? 'block' : 'none' }}
>
{queriedTweets?.pages?.map((page, pageIdx) => {
if (!page) return null
return (
<Fragment key={pageIdx}>
{page.data.map((tweet) => (
<Tweet tweet={tweet} key={tweet.id} />
))}
</Fragment>
)
})}
{hasNextPage && (
<LoadMore onClick={() => fetchNextPage()} disabled={isFetching}>
<Text uppercase heading>
Load more
</Text>
<ArrowDown
css={{
marginLeft: 8,
color: 'white',
path: { fill: 'white' },
$$size: '16px'
}}
/>
</LoadMore>
)}
</Column>
</Container>
</SectionInner>
</Section>
)
}