react-query#useQueryClient TypeScript Examples
The following examples show how to use
react-query#useQueryClient.
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: useCreateAddress.ts From wildduck-ui with MIT License | 8 votes |
useCreateAddress = () => {
const queryClient = useQueryClient();
return useMutation(
({ userId, addressDetails }: IProp) => api.addressApi.createUserAddress(userId, addressDetails),
{
onError: () => {
AppEvents.publish(Events.Error, 'Error');
},
onSuccess: ({ data }) => {
handleError(data);
queryClient.invalidateQueries('useAddress');
},
},
);
}
Example #2
Source File: useRefetchQueries.ts From anchor-web-app with Apache License 2.0 | 6 votes |
export function useRefetchQueries(refetchMap?: TxRefetchMap) {
const queryClient = useQueryClient();
const { refetchMap: terraRefetchMap } = useApp();
return useCallback(
(txKey: string) => {
const queryRefetches = (refetchMap ?? terraRefetchMap)[txKey];
if (queryRefetches) {
for (const queryRefetch of queryRefetches) {
runRefetch(queryRefetch).then((queryKey) => {
queryClient.invalidateQueries(queryKey, {
refetchActive: true,
refetchInactive: false,
});
});
}
}
},
[queryClient, refetchMap, terraRefetchMap],
);
}
Example #3
Source File: mutation.ts From RareCamp with Apache License 2.0 | 6 votes |
useEditTaskMutation = (
{ taskId, projectId, programId },
cb?: Function,
) => {
const queryClient = useQueryClient()
return useMutation(
(task: any) => {
return axios.put(`/projects/${projectId}/tasks/${taskId}`, {
task,
})
},
{
onSuccess: async (resp: any) => {
const { task } = resp.data
queryClient.setQueryData(['task', task.taskId], {
data: resp.data,
})
const programData: any = queryClient.getQueryData([
'program',
programId,
])
if (programData) {
updateProgramTask(programData.data.program, task)
queryClient.setQueryData(['program', programId], {
data: programData.data,
})
}
if (cb) cb()
},
onError: (err: Error, variables) =>
notification.error({
duration: 2,
message: `Task ${variables.name} was not updated`,
description: err.message,
}),
},
)
}
Example #4
Source File: useFetchResource.ts From ke with MIT License | 6 votes |
useFetchResource = <ResourceData = unknown>(
userConfigOrKey: ResourceOptionsOrKey<FetchResourceOptions<ResourceData>>
): ((requestOptions?: FetchOptions<ResourceData>) => Promise<ResourceData>) => {
const userConfig = configResolver(userConfigOrKey)
const {
fetchResource: { fn },
} = useDefaultResourceConfig<ResourceData>()
const client = useQueryClient()
return useCallback(
(requestOptions: FetchOptions<ResourceData> = {}) => {
const { key, ...config } = userConfig
const { requestConfig = {}, ...queryOptions } = deepmerge(config, requestOptions)
return client.fetchQuery(
[key, requestConfig.lookupField, requestConfig.params],
() => fn(key, requestConfig),
queryOptions
)
},
[client, fn, userConfig]
)
}
Example #5
Source File: useBackgroundPaginatedRequest.ts From apps with GNU Affero General Public License v3.0 | 6 votes |
useBackgroundPaginatedRequest = <T extends InfiniteData<unknown>>(
queryKey: QueryKey,
): T => {
const client = useQueryClient();
const data = client.getQueryData<T>(queryKey);
useBackgroundRequest(queryKey, {
callback: ({ res, req }) => {
if (!res) {
return;
}
const current = client.getQueryData(queryKey) as T;
const updated = { ...current } as T;
const index = updated.pages.length - 1;
updated.pageParams[index] = req.variables.after;
updated.pages[index] = res;
client.setQueryData(queryKey, updated);
},
});
return data;
}
Example #6
Source File: useUserSettings.ts From tailchat with GNU General Public License v3.0 | 6 votes |
/**
* 用户设置hooks
*/
export function useUserSettings() {
const client = useQueryClient();
const { data: settings, isLoading } = useQuery(
['useUserSettings'],
() => getUserSettings(),
{
staleTime: 1 * 60 * 1000, // 缓存1分钟
}
);
const [{ loading: saveLoading }, setSettings] = useAsyncRequest(
async (settings: UserSettings) => {
const newSettings = await setUserSettings(settings);
client.setQueryData(['useUserSettings'], () => newSettings);
},
[client]
);
return {
settings: settings ?? {},
setSettings,
loading: isLoading || saveLoading,
};
}
Example #7
Source File: Members.tsx From Riakuto-StartingReact-ja3.1 with Apache License 2.0 | 6 votes |
EnhancedMembers: VFC<{ enablePrefetch?: boolean }> = ({
enablePrefetch = false,
}) => {
const queryClient = useQueryClient();
const prefetch = (orgCode: string): void => {
const load = async (): Promise<void> => {
try {
await Promise.all([
queryClient.prefetchQuery([orgCode, 'organization'], () =>
getOrganization(orgCode),
),
queryClient.prefetchQuery([orgCode, 'members'], () =>
getMembers(orgCode),
),
]);
} catch (error) {
console.error(error); // eslint-disable-line no-console
}
};
void load();
};
const membersProps = enablePrefetch
? { orgCodeList, prefetch }
: { orgCodeList };
// eslint-disable-next-line react/jsx-props-no-spreading
return <Members {...membersProps} />;
}
Example #8
Source File: [id].tsx From next-crud with MIT License | 6 votes |
UserCreate: NextPage<IProps> = ({ user }) => {
const toast = useToast()
const { replace } = useRouter()
const queryClient = useQueryClient()
const onSubmit = async (values: IFormValues) => {
try {
const userData = await fetch(`/api/users/${user.id}`, {
method: 'PUT',
body: JSON.stringify(values),
headers: {
'Content-Type': 'application/json',
},
}).then((res) => res.json())
toast({
status: 'success',
description: 'User successfully updated',
duration: 2000,
})
replace('/users')
queryClient.setQueryData<
InfiniteData<TPaginationResult<User>> | undefined
>('users', (data) => {
const page = data?.pages.find((page) =>
page.data.some((userElem) => userElem.id === user.id)
)
if (page) {
const elemIdx = page.data.findIndex((data) => data.id === user.id)
page.data[elemIdx] = userData
}
return data
})
} catch (e) {
toast({
status: 'error',
description: 'Failed to update user',
duration: 2000,
})
}
}
return (
<Layout title={user.username} backRoute="/users">
<VStack spacing={4} width="100%">
<Heading>User edition</Heading>
<UserForm
initialValues={{ username: user.username }}
onSubmit={onSubmit}
/>
</VStack>
</Layout>
)
}
Example #9
Source File: logout.tsx From rcvr-app with GNU Affero General Public License v3.0 | 6 votes |
export default function BusinessIndexPage() {
const { t } = usePageLocale('business/logout')
const router = useRouter()
const queryClient = useQueryClient()
React.useEffect(() => {
localStorage.removeItem('rcvr_olt')
queryClient.clear()
router.replace('/business')
}, [router, queryClient])
return (
<MobileApp pageTitle={t('title')} logoVariant="big">
<Text as="h2" variant="h2">
{t('title')}
</Text>
<Loading show />
</MobileApp>
)
}
Example #10
Source File: useCreateAllowedDomain.ts From wildduck-ui with MIT License | 6 votes |
useCreateAllowedDomain = () => {
const queryClient = useQueryClient();
return useMutation(
(domain: { tag: string; domain: string }) =>
api.domainAccessApi.createAllowedDomain(domain.tag, { domain: domain.domain }),
{
onSuccess: () => {
AppEvents.publish(Events.Success, 'Success');
queryClient.invalidateQueries('query-allowList');
queryClient.invalidateQueries('query-blockList');
},
onError: () => {
AppEvents.publish(Events.Error, 'Error');
},
},
);
}
Example #11
Source File: FusePoolPage.tsx From rari-dApp with GNU Affero General Public License v3.0 | 5 votes |
PendingAdminAlert = ({ comptroller }: { comptroller?: string }) => {
const { address, fuse } = useRari();
const toast = useToast();
const queryClient = useQueryClient();
const [isAccepting, setIsAccepting] = useState(false);
const isPendingAdmin = useIsComptrollerPendingAdmin(comptroller);
const acceptAdmin = async () => {
if (!comptroller) return;
const unitroller = createUnitroller(comptroller, fuse);
setIsAccepting(true);
try {
await testForComptrollerErrorAndSend(
unitroller.methods._acceptAdmin(),
address,
""
);
LogRocket.track("Fuse-AcceptAdmin");
queryClient.refetchQueries();
setIsAccepting(false);
} catch (e) {
setIsAccepting(false);
handleGenericError(e, toast);
}
};
return (
<>
{isPendingAdmin && (
<AdminAlert
isAdmin={isPendingAdmin}
isAdminText="You are the pending admin of this Fuse Pool! Click to Accept Admin"
rightAdornment={
<Button
h="100%"
p={3}
ml="auto"
color="black"
onClick={acceptAdmin}
disabled={isAccepting}
>
<HStack>
<Text fontWeight="bold">
{isAccepting} ? Accepting... : Accept Admin{" "}
</Text>
</HStack>
</Button>
}
/>
)}
</>
);
}
Example #12
Source File: index.tsx From strapi-plugin-comments with MIT License | 5 votes |
DiscussionThreadItemApprovalFlowActions = ({
id,
allowedActions: { canModerate },
queryToInvalidate,
}) => {
const toggleNotification = useNotification();
const queryClient = useQueryClient();
const { lockApp, unlockApp } = useOverlayBlocker();
const onSuccess = (message) => async () => {
if (queryToInvalidate) {
await queryClient.invalidateQueries(queryToInvalidate);
}
toggleNotification({
type: "success",
message: `${pluginId}.${message}`,
});
unlockApp();
};
const onError = (err) => {
handleAPIError(err, toggleNotification);
};
const approveItemMutation = useMutation(approveItem, {
onSuccess: onSuccess(
"page.details.actions.comment.approve.confirmation.success"
),
onError,
refetchActive: false,
});
const rejectItemMutation = useMutation(rejectItem, {
onSuccess: onSuccess(
"page.details.actions.comment.reject.confirmation.success"
),
onError,
refetchActive: false,
});
const handleApproveClick = () => {
if (canModerate) {
lockApp();
approveItemMutation.mutate(id);
}
};
const handleRejectClick = () => {
if (canModerate) {
lockApp();
rejectItemMutation.mutate(id);
}
};
if (canModerate) {
return (
<>
<IconButton
icon={<Check />}
label={getMessage("page.details.actions.comment.approve", "Approve")}
onClick={handleApproveClick}
/>
<IconButton
icon={<Cross />}
label={getMessage("page.details.actions.comment.reject", "Reject")}
onClick={handleRejectClick}
/>
</>
);
}
return null;
}
Example #13
Source File: useTezos.ts From homebase-app with MIT License | 5 votes |
useTezos = (): WalletConnectReturn => {
const {
state: { tezos, network, account, wallet },
dispatch,
} = useContext(TezosContext);
const queryClient = useQueryClient()
const connect = useCallback(async (newNetwork?: Network) => {
const { wallet } = await connectWithBeacon(newNetwork || network);
const newTezos = new TezosToolkit(rpcNodes[newNetwork || network]);
newTezos.setPackerProvider(new MichelCodecPacker());
newTezos.addExtension(new Tzip16Module());
newTezos.setProvider({ wallet });
const account = await newTezos.wallet.pkh();
dispatch({
type: TezosActionType.UPDATE_TEZOS,
payload: {
network: newNetwork || network,
tezos: newTezos,
account,
wallet
},
});
mixpanel.identify(account)
return newTezos;
}, [dispatch, network]);
return {
tezos,
connect,
reset: useCallback(async () => {
if(!wallet) {
throw new Error("No Wallet Connected")
}
await wallet.disconnect()
dispatch({
type: TezosActionType.RESET_TEZOS,
});
}, [dispatch, wallet]),
changeNetwork: async (newNetwork: Network) => {
mixpanel.register({'Network': newNetwork});
localStorage.setItem("homebase:network", newNetwork);
if (!("_pkh" in tezos.wallet)) {
const Tezos = new TezosToolkit(rpcNodes[newNetwork]);
Tezos.setPackerProvider(new MichelCodecPacker());
Tezos.addExtension(new Tzip16Module());
dispatch({
type: TezosActionType.UPDATE_TEZOS,
payload: {
network: newNetwork,
tezos: Tezos,
account,
wallet: undefined
},
});
} else {
await connect(newNetwork);
}
queryClient.resetQueries()
},
account,
network,
};
}
Example #14
Source File: CompanionEngagements.tsx From apps with GNU Affero General Public License v3.0 | 5 votes |
export function CompanionEngagements({
post,
commentsNum,
isCommentsOpen,
onCommentsClick,
onUpvotesClick,
}: CompanionEngagementsProps): ReactElement {
if (!post) {
return null;
}
const client = useQueryClient();
useRawBackgroundRequest(({ res, key }) => {
if (!Array.isArray(key)) {
return;
}
if (key[0] !== 'readingRank') {
return;
}
client.setQueryData(key, res);
});
return (
<div
className="flex gap-x-4 justify-between items-center text-theme-label-tertiary typo-callout"
data-testid="statsBar"
>
{post.numUpvotes <= 0 && <span>Be the first to upvote</span>}
{post.numUpvotes > 0 && (
<ClickableText onClick={onUpvotesClick}>
{post.numUpvotes} Upvote{post.numUpvotes > 1 ? 's' : ''}
</ClickableText>
)}
<Button
buttonSize="small"
className={isCommentsOpen ? 'btn-secondary' : 'btn-primary'}
rightIcon={
<ArrowIcon
className={classNames(
'ml-2 w-6 h-6 transition-transform',
!isCommentsOpen && 'rotate-180',
)}
/>
}
onClick={onCommentsClick}
>
{commentsNum.toLocaleString()}
{` Comment${commentsNum === 1 ? '' : 's'}`}
</Button>
</div>
);
}
Example #15
Source File: index.tsx From interbtc-ui with Apache License 2.0 | 5 votes |
ClaimRewardsButton = ({
className,
claimableRewardAmount,
...rest
}: CustomProps & InterlayDenimOrKintsugiMidnightContainedButtonProps): JSX.Element => {
const { address } = useSelector((state: StoreType) => state.general);
const queryClient = useQueryClient();
const claimRewardsMutation = useMutation<void, Error, void>(
() => {
return window.bridge.escrow.withdrawRewards();
},
{
onSuccess: () => {
queryClient.invalidateQueries([GENERIC_FETCHER, 'escrow', 'getRewardEstimate', address]);
queryClient.invalidateQueries([GENERIC_FETCHER, 'escrow', 'getRewards', address]);
}
}
);
const handleClaimRewards = () => {
claimRewardsMutation.mutate();
};
return (
<>
<InterlayDenimOrKintsugiSupernovaContainedButton
className={clsx('w-full', 'px-6', 'py-3', 'text-base', 'rounded-md', className)}
onClick={handleClaimRewards}
pending={claimRewardsMutation.isLoading}
{...rest}
>
Claim {claimableRewardAmount} {GOVERNANCE_TOKEN_SYMBOL} Rewards
</InterlayDenimOrKintsugiSupernovaContainedButton>
{claimRewardsMutation.isError && (
<ErrorModal
open={claimRewardsMutation.isError}
onClose={() => {
claimRewardsMutation.reset();
}}
title='Error'
description={claimRewardsMutation.error?.message || ''}
/>
)}
</>
);
}
Example #16
Source File: index.tsx From livepeer-com with MIT License | 5 votes |
useTableState = <T extends Record<string, unknown>>({
tableId,
pageSize = 20,
}: {
tableId: string;
pageSize?: number;
}) => {
const [order, setOrder] = useState("");
const [cursor, setCursor] = useState("");
const [prevCursors, setPrevCursors] = useState<string[]>([]);
const [nextCursor, setNextCursor] = useState("default");
const [filters, setFilters] = useState<TFilter[]>([]);
const [selectedRows, setSelectedRows] = useState<Row<T>[]>([]);
const queryClient = useQueryClient();
const stringifiedFilters = useMemo(() => {
const formatted = formatFiltersForApiRequest(filters);
return JSON.stringify(formatted);
}, [filters]);
const stateSetter: StateSetter<T> = useMemo(
() => ({
setOrder,
setCursor,
setPrevCursors,
setNextCursor,
setFilters,
setSelectedRows,
}),
[]
);
const state: State<T> = useMemo(
() => ({
tableId,
order,
cursor,
prevCursors,
nextCursor,
filters,
stringifiedFilters,
selectedRows,
pageSize,
invalidate: () => queryClient.invalidateQueries(tableId),
}),
[
order,
cursor,
prevCursors,
nextCursor,
filters,
stringifiedFilters,
selectedRows,
pageSize,
queryClient,
tableId,
]
);
return { state, stateSetter };
}
Example #17
Source File: delete-device-modal.tsx From homebridge-zigbee-nt with Apache License 2.0 | 5 votes |
export function DeleteDeviceModal(props: Props) {
const queryClient = useQueryClient();
const [state, setState] = useState<State>({
isDeletingDevice: false,
error: null,
});
const { selectedDevice } = props;
return (
<Dialog
isShown={props.isDeleteConfirmationShown}
title="Unpair confirmation"
onConfirm={async () => {
setState({ ...state, isDeletingDevice: true });
const response = await DevicesService.deleteDevice(selectedDevice.ieeeAddr);
if (response.result === 'success') {
await queryClient.invalidateQueries(DEVICES_QUERY_KEY);
setState({ ...state, isDeletingDevice: false });
props.onSuccess();
} else {
setState({ ...state, isDeletingDevice: false, error: response.error });
}
}}
isConfirmLoading={state.isDeletingDevice}
onCancel={props.onCancel}
cancelLabel="Cancel"
confirmLabel={state.isDeletingDevice ? 'Unpairing...' : 'Unpair'}
>
{selectedDevice && !!state.error && (
<Paragraph size={300}>
Are you sure you want to unpair device {selectedDevice.modelID} ({selectedDevice.ieeeAddr}
)?
</Paragraph>
)}
{state.error && (
<Paragraph size={300}>
An error occurred: {state.error}
</Paragraph>
)}
{state.isDeletingDevice && renderSpinner()}
</Dialog>
);
}
Example #18
Source File: DeleteModal.tsx From frontend with MIT License | 5 votes |
export default function DeleteModal({ id, onClose, onDelete }: Props) {
const queryClient = useQueryClient()
const router = useRouter()
const { t } = useTranslation()
const mutationFn = useDeleteCampaignById(id)
const deleteMutation = useMutation<AxiosResponse<null>, AxiosError<ApiErrors>, string>({
mutationFn,
onError: () => AlertStore.show(t('campaigns:alerts:error'), 'error'),
onSuccess: () => {
AlertStore.show(t('Кампанията беше преместена в кошчето.'), 'warning')
queryClient.removeQueries(endpoints.campaign.viewCampaignById(id).url)
onDelete()
router.push(routes.admin.campaigns.index)
},
})
function deleteHandler() {
deleteMutation.mutate(id)
}
return (
<Dialog open onClose={onClose} sx={{ top: '-35%' }}>
<DialogTitle>{t('campaigns:deleteTitle')}</DialogTitle>
<Card>
<CardContent>
<Typography variant="body1" sx={{ marginBottom: '16px', textAlign: 'center' }}>
{t('campaigns:deleteContent')}
</Typography>
<Box sx={{ display: 'flex', justifyContent: 'center' }}>
<Button color="error" onClick={deleteHandler}>
{t('campaigns:cta:delete')}
</Button>
<Button onClick={onClose}>{t('campaigns:cta:cancel')}</Button>
</Box>
</CardContent>
</Card>
</Dialog>
)
}
Example #19
Source File: success.tsx From rcvr-app with GNU Affero General Public License v3.0 | 5 votes |
SetupSuccessPage: React.FC<WithOwnerProps> = ({ owner }) => {
const { t } = usePageLocale('business/setup/success')
const router = useRouter()
const queryClient = useQueryClient()
const generateKey = async (redirectTarget: string) => {
if (!owner) return
let { publicKey, privateKey } = owner
if (!publicKey || !privateKey) {
const keys = generateKeys()
privateKey = keys.privateKey
publicKey = keys.publicKey
// the backend will receive the publicKey when the onboarding
// process has been done completely.
// meanwhile we store it in the frontend as setupPublicKey.
// so when a user freshly logs in (or reloads or whatever)
// and doesn't have a public_key assigned in the backend
// the frontend needs to restart the onboarding process!
await updateOwner(queryClient, {
...owner,
privateKey,
setupPublicKey: publicKey,
})
}
router.push(redirectTarget)
}
return (
<MobileApp pageTitle={t('title')} logoVariant="big">
<Text as="h3" variant="h3">
{t('title')} (2/3)
</Text>
<Box height={6} />
<Row justifyContent="center">
<KeyPaper />
</Row>
<Box height={4} />
<Card variant="form" mx={-4}>
<Text as="h2" variant="h2">
2.
{t('headline')}
</Text>
<Box height={4} />
<Text>
<PrivateKeyExplanation />
</Text>
<Box height={6} />
<Text>
<ContactInformation />
</Text>
<Box height={6} />
<Button
onClick={() => {
generateKey('/business/setup/verify-key')
}}
right={<ArrowsRight color="green" />}
>
{t('downloadKeyButtonText')}
</Button>
<Box height={6} />
<Button
onClick={() => {
generateKey('/business/setup/keys')
}}
right={<ArrowsRight color="green" />}
css={{ textAlign: 'center' }}
>
{t('printKeyButtonText')}
</Button>
</Card>
</MobileApp>
)
}
Example #20
Source File: index.tsx From RareCamp with Apache License 2.0 | 4 votes |
export default function EditProgram({ program }) {
const [isEditProgramVisible, setIsEditProgramVisible] =
useState(false)
const deleteProgram = () =>
confirm({
okButtonProps: {
style: { backgroundColor: '#e53935', borderColor: '#e53935' },
},
title: 'Are you sure you want to delete this program?',
centered: true,
icon: <ExclamationCircleOutlined />,
content:
'Program will be immediately deleted. You cannot undo this action.',
okText: 'Delete',
onOk: deleteProgramMutation.mutateAsync,
onCancel() {},
})
const menu = (
<Menu>
<Menu.Item>
<Button
onClick={() => setIsEditProgramVisible(true)}
type="text"
>
Edit Program Details
</Button>
</Menu.Item>
<Menu.Item>
<Button onClick={deleteProgram} type="text">
<Text type="danger">Delete Program</Text>
</Button>
</Menu.Item>
</Menu>
)
const router = useRouter()
const queryClient = useQueryClient()
const deleteProgramMutation = useMutation(
() =>
axios.delete(
`/workspaces/${program.workspaceId}/programs/${program.programId}`,
),
{
onSuccess: async () => {
notification.success({
duration: 2,
message: `Program ${program.name} has been deleted successfully`,
})
const { data } = queryClient.getQueryData<any>(
'defaultWorkspace',
)
queryClient.invalidateQueries('defaultWorkspace')
if (data.workspace?.programs?.length) {
await router.push('/workspace/intro')
} else {
await router.push('/')
}
},
onError: (err: Error) =>
notification.error({
duration: 2,
message: `Program ${program.name} was not deleted`,
description: err.message,
}),
},
)
const editProgramMutation = useMutation(
(values: { name: string; description: string }) => {
const data = { ...program, ...values }
return axios.put(
`/workspaces/${program.workspaceId}/programs/${program.programId}`,
{
program: { name: data.name, description: data.description },
},
)
},
{
onSuccess: async (resp) => {
queryClient.setQueryData(['program', program.programId], {
data: resp.data,
})
await queryClient.invalidateQueries('defaultWorkspace')
notification.success({
duration: 2,
message: `Program ${program.name} has been updated successfully`,
})
setIsEditProgramVisible(false)
},
onError: (err: Error) =>
notification.error({
duration: 2,
message: `Program ${program.name} was not updated`,
description: err.message,
}),
},
)
const [editProgramForm] = Form.useForm()
return (
<EditProgramDropdown>
<Dropdown overlay={menu}>
<MoreOutlined />
</Dropdown>
<Modal
centered
title="Edit Program Details"
visible={isEditProgramVisible}
okText="Save"
onOk={() => {
editProgramForm.validateFields().then((values) => {
// editProgramForm.resetFields()
editProgramMutation.mutate(values)
})
}}
confirmLoading={editProgramMutation.isLoading}
onCancel={() => setIsEditProgramVisible(false)}
>
<Form
name="editProgram"
initialValues={{
name: program.name,
description: program.description,
}}
form={editProgramForm}
layout="vertical"
>
<Form.Item
label="Program Name"
name="name"
rules={[
{
required: true,
message: 'Please input program name',
},
]}
>
<Input />
</Form.Item>
<Form.Item
label="Description"
name="description"
rules={[{ message: 'Please input program description' }]}
>
<Input.TextArea rows={4} />
</Form.Item>
</Form>
</Modal>
</EditProgramDropdown>
)
}
Example #21
Source File: FusePoolPage.tsx From rari-dApp with GNU Affero General Public License v3.0 | 4 votes |
AssetSupplyRow = ({
assets,
index,
comptrollerAddress,
supplyIncentives,
rewardTokensData,
isPaused,
}: {
assets: USDPricedFuseAsset[];
index: number;
comptrollerAddress: string;
supplyIncentives: CTokenRewardsDistributorIncentivesWithRates[];
rewardTokensData: TokensDataMap;
isPaused: boolean;
}) => {
const {
isOpen: isModalOpen,
onOpen: openModal,
onClose: closeModal,
} = useDisclosure();
const authedOpenModal = useAuthedCallback(openModal);
const asset = assets[index];
const { fuse, address } = useRari();
const tokenData = useTokenData(asset.underlyingToken);
const supplyAPY = convertMantissaToAPY(asset.supplyRatePerBlock, 365);
const queryClient = useQueryClient();
const toast = useToast();
const onToggleCollateral = async () => {
const comptroller = createComptroller(comptrollerAddress, fuse);
let call;
if (asset.membership) {
call = comptroller.methods.exitMarket(asset.cToken);
} else {
call = comptroller.methods.enterMarkets([asset.cToken]);
}
let response = await call.call({ from: address });
// For some reason `response` will be `["0"]` if no error but otherwise it will return a string number.
if (response[0] !== "0") {
if (asset.membership) {
toast({
title: "Error! Code: " + response,
description:
"You cannot disable this asset as collateral as you would not have enough collateral posted to keep your borrow. Try adding more collateral of another type or paying back some of your debt.",
status: "error",
duration: 9000,
isClosable: true,
position: "top-right",
});
} else {
toast({
title: "Error! Code: " + response,
description:
"You cannot enable this asset as collateral at this time.",
status: "error",
duration: 9000,
isClosable: true,
position: "top-right",
});
}
return;
}
await call.send({ from: address });
LogRocket.track("Fuse-ToggleCollateral");
queryClient.refetchQueries();
};
const isStakedOHM =
asset.underlyingToken.toLowerCase() ===
"0x04F2694C8fcee23e8Fd0dfEA1d4f5Bb8c352111F".toLowerCase();
const { data: stakedOHMApyData } = useQuery("sOHM_APY", async () => {
const data = (
await fetch("https://api.rari.capital/fuse/pools/18/apy")
).json();
return data as Promise<{ supplyApy: number; supplyWpy: number }>;
});
const isMobile = useIsMobile();
const { t } = useTranslation();
const hasSupplyIncentives = !!supplyIncentives.length;
const totalSupplyAPR =
supplyIncentives?.reduce((prev, incentive) => {
const apr = incentive.supplyAPR;
return prev + apr;
}, 0) ?? 0;
const [hovered, setHovered] = useState<number>(-1);
const handleMouseEnter = (index: number) => setHovered(index);
const handleMouseLeave = () => setHovered(-1);
const displayedSupplyAPR =
hovered >= 0 ? supplyIncentives[hovered].supplyAPR : totalSupplyAPR;
const displayedSupplyAPRLabel =
hovered >= 0
? `${supplyIncentives[hovered].supplyAPR.toFixed(2)} % APR in ${
rewardTokensData[supplyIncentives[hovered].rewardToken].symbol
} distributions.`
: `${displayedSupplyAPR.toFixed(
2
)}% total APR distributed in ${supplyIncentives
.map((incentive) => rewardTokensData[incentive.rewardToken].symbol)
.join(", ")}
`;
const _hovered = hovered > 0 ? hovered : 0;
const color =
rewardTokensData[supplyIncentives?.[_hovered]?.rewardToken]?.color ??
"white";
const symbol = getSymbol(tokenData, asset);
return (
<>
<PoolModal
defaultMode={Mode.SUPPLY}
comptrollerAddress={comptrollerAddress}
assets={assets}
index={index}
isOpen={isModalOpen}
onClose={closeModal}
isBorrowPaused={asset.isPaused}
/>
<Row
mainAxisAlignment="flex-start"
crossAxisAlignment="flex-start"
width="100%"
px={4}
py={1.5}
className="hover-row"
>
{/* Underlying Token Data */}
<Column
mainAxisAlignment="flex-start"
crossAxisAlignment="flex-start"
width="27%"
>
<Row
mainAxisAlignment="flex-start"
crossAxisAlignment="center"
width="100%"
as="button"
onClick={authedOpenModal}
>
<Avatar
bg="#FFF"
boxSize="37px"
name={symbol}
src={
tokenData?.logoURL ??
"https://raw.githubusercontent.com/feathericons/feather/master/icons/help-circle.svg"
}
/>
<Text fontWeight="bold" fontSize="lg" ml={2} flexShrink={0}>
{symbol}
</Text>
</Row>
{/* <Row
mainAxisAlignment="flex-start"
crossAxisAlignment="center"
width="100%"
>
<Text fontSize="sm" ml={2} flexShrink={0}>
{shortUsdFormatter(asset.liquidityUSD)}
</Text>
</Row> */}
</Column>
{/* APY */}
{isMobile ? null : (
<Column
mainAxisAlignment="flex-start"
crossAxisAlignment="flex-end"
width="27%"
as="button"
onClick={authedOpenModal}
>
<Text
color={tokenData?.color ?? "#FF"}
fontWeight="bold"
fontSize="17px"
>
{isStakedOHM
? stakedOHMApyData
? (stakedOHMApyData.supplyApy * 100).toFixed(2)
: "?"
: supplyAPY.toFixed(2)}
%
</Text>
{/* Demo Supply Incentives */}
{hasSupplyIncentives && (
<Row
// ml={1}
// mb={.5}
crossAxisAlignment="center"
mainAxisAlignment="flex-end"
py={2}
>
<Text fontWeight="bold" mr={1}>
+
</Text>
<AvatarGroup size="xs" max={30} ml={2} mr={1} spacing={1}>
{supplyIncentives?.map((supplyIncentive, i) => {
return (
<SimpleTooltip label={displayedSupplyAPRLabel}>
<CTokenIcon
address={supplyIncentive.rewardToken}
boxSize="20px"
onMouseEnter={() => handleMouseEnter(i)}
onMouseLeave={() => handleMouseLeave()}
_hover={{
zIndex: 9,
border: ".5px solid white",
transform: "scale(1.3);",
}}
/>
</SimpleTooltip>
);
})}
</AvatarGroup>
<SimpleTooltip label={displayedSupplyAPRLabel}>
<Text color={color} fontWeight="bold" pl={1} fontSize="sm">
{/* {(supplyIncentive.supplySpeed / 1e18).toString()}% */}
{displayedSupplyAPR.toFixed(2)}% APR
</Text>
</SimpleTooltip>
</Row>
)}
{/* Incentives */}
{/* {hasSupplyIncentives && (
<Column
mainAxisAlignment="flex-start"
crossAxisAlignment="flex-end"
py={1}
>
{supplyIncentives?.map((supplyIncentive) => {
return (
<Row
ml={1}
py={0.5}
// mb={.5}
crossAxisAlignment="center"
mainAxisAlignment="flex-end"
>
<Text fontWeight="bold" mr={2}>
+
</Text>
<CTokenIcon
address={supplyIncentive.rewardToken}
boxSize="20px"
/>
<Text fontWeight="bold" mr={2}></Text>
<Text
color={
rewardTokensData[supplyIncentive.rewardToken].color ??
"white"
}
fontWeight="bold"
>
{(supplyIncentive.supplySpeed / 1e18).toString()}%
</Text>
</Row>
);
})}
</Column>
)} */}
<SimpleTooltip
label={t(
"The Collateral Factor (CF) ratio defines the maximum amount of tokens in the pool that can be borrowed with a specific collateral. It’s expressed in percentage: if in a pool ETH has 75% LTV, for every 1 ETH worth of collateral, borrowers will be able to borrow 0.75 ETH worth of other tokens in the pool."
)}
>
<Text fontSize="sm">{asset.collateralFactor / 1e16}% CF</Text>
</SimpleTooltip>
{/* Incentives under APY
<Column
mainAxisAlignment="flex-start"
crossAxisAlignment="flex-end"
my={1}
>
{supplyIncentives?.map((supplyIncentive) => {
return (
<Row
mainAxisAlignment="space-between"
crossAxisAlignment="center"
w="100%"
>
<Avatar
src={
rewardTokensData[supplyIncentive.rewardToken].logoURL ?? ""
}
boxSize="20px"
/>
<Text
ml={2}
fontWeight="bold"
color={
rewardTokensData[supplyIncentive.rewardToken].color ?? ""
}
>
{(supplyIncentive.supplySpeed / 1e18).toString()}%
</Text>
</Row>
);
})}
</Column>
*/}
</Column>
)}
{/* Incentives */}
{/* <Column mainAxisAlignment="flex-start" crossAxisAlignment="flex-start">
{supplyIncentives?.map((supplyIncentive) => {
return (
<Row mainAxisAlignment="flex-start" crossAxisAlignment="center">
<Avatar
src={rewardTokensData[supplyIncentive.rewardToken].logoURL}
boxSize="15px"
/>
<Box>
{(supplyIncentive.supplySpeed / 1e18).toString()}% APY
</Box>
</Row>
);
})}
</Column> */}
<Column
mainAxisAlignment="flex-start"
crossAxisAlignment="flex-end"
width={isMobile ? "40%" : "27%"}
as="button"
onClick={authedOpenModal}
>
<Text
color={tokenData?.color ?? "#FFF"}
fontWeight="bold"
fontSize="17px"
>
{smallUsdFormatter(asset.supplyBalanceUSD)}
</Text>
<Text fontSize="sm">
{smallUsdFormatter(
asset.supplyBalance / 10 ** asset.underlyingDecimals
).replace("$", "")}{" "}
{symbol}
</Text>
</Column>
{/* Set As Collateral */}
<Row
width={isMobile ? "34%" : "20%"}
mainAxisAlignment="flex-end"
crossAxisAlignment="center"
>
<SwitchCSS symbol={symbol} color={tokenData?.color} />
<Switch
isChecked={asset.membership}
className={symbol + "-switch"}
onChange={onToggleCollateral}
size="md"
mt={1}
mr={5}
/>
</Row>
</Row>
</>
);
}
Example #22
Source File: index.tsx From strapi-plugin-comments with MIT License | 4 votes |
DiscussionThreadItemActions = ({
allowedActions: { canModerate, canAccessReports, canReviewReports },
...item
}) => {
const {
id,
blocked,
removed,
blockedThread,
gotThread,
threadItemsCount,
threadFirstItemId,
pinned,
preview,
reports = [],
approvalStatus,
} = item;
const [blockConfirmationVisible, setBlockConfirmationVisible] =
useState(false);
const [blockThreadConfirmationVisible, setBlockThreadConfirmationVisible] =
useState(false);
const [blockButtonsDisabled, setBlockButtonsDisabled] =
useState(blockedThread);
const { push } = useHistory();
const toggleNotification = useNotification();
const queryClient = useQueryClient();
const { lockApp, unlockApp } = useOverlayBlocker();
const onSuccess =
(message, stateAction = () => {}) =>
async () => {
await queryClient.invalidateQueries("get-details-data");
toggleNotification({
type: "success",
message: `${pluginId}.${message}`,
});
stateAction(false);
unlockApp();
};
const onError = (err) => {
handleAPIError(err, toggleNotification);
};
const blockItemMutation = useMutation(blockItem, {
onSuccess: onSuccess(
"page.details.actions.comment.block.confirmation.success",
setBlockConfirmationVisible
),
onError,
refetchActive: false,
});
const unblockItemMutation = useMutation(unblockItem, {
onSuccess: onSuccess(
"page.details.actions.comment.unblock.confirmation.success"
),
onError,
refetchActive: false,
});
const blockItemThreadMutation = useMutation(blockItemThread, {
onSuccess: onSuccess(
"page.details.actions.thread.block.confirmation.success",
setBlockThreadConfirmationVisible
),
onError,
refetchActive: false,
});
const unblockItemThreadMutation = useMutation(unblockItemThread, {
onSuccess: onSuccess(
"page.details.actions.thread.unblock.confirmation.success"
),
onError,
refetchActive: false,
});
const gotApprovalFlow = !isNil(approvalStatus);
const needsApproval = gotApprovalFlow && approvalStatus === "PENDING";
const isBlocked = blocked || blockedThread;
const isRejected = gotApprovalFlow && approvalStatus === "REJECTED";
const openReports = reports?.filter((_) => !_.resolved);
const hasReports = !isEmpty(openReports);
const reviewFlowEnabled =
(canAccessReports || canReviewReports) && hasReports;
const hasActiveThread =
gotThread && !(removed || preview || pinned || blockedThread);
const isStatusBadgeVisible = isBlocked || reviewFlowEnabled;
const renderStatus = (props) => {
const status = resolveCommentStatus({ ...props, reviewFlowEnabled });
const color = resolveCommentStatusColor(status);
return (
<StatusBadge
backgroundColor={`${color}100`}
textColor={`${color}700`}
color={color}
>
{getMessage(
{
id: `page.common.item.status.${status}`,
props: {
count: openReports.length,
},
},
status
)}
</StatusBadge>
);
};
const handleBlockClick = () => setBlockConfirmationVisible(true);
const handleBlockConfirm = async () => {
if (canModerate) {
lockApp();
blockItemMutation.mutate(id);
}
};
const handleBlockCancel = () => {
setBlockConfirmationVisible(false);
};
const handleUnblockClick = async () => {
if (canModerate) {
lockApp();
unblockItemMutation.mutate(id);
}
};
const handleBlockThreadClick = () => setBlockThreadConfirmationVisible(true);
const handleBlockThreadConfirm = async () => {
if (canModerate) {
lockApp();
blockItemThreadMutation.mutate(id);
}
};
const handleBlockThreadCancel = () => {
setBlockThreadConfirmationVisible(false);
};
const handleUnblockThreadClick = async () => {
if (canModerate) {
lockApp();
unblockItemThreadMutation.mutate(id);
}
};
const handleDrilldownClick = () => {
if (hasActiveThread) {
push(getUrl(`discover/${threadFirstItemId}`));
}
};
const handleBlockActionClick = async (mutation, onCallback) => {
lockApp();
await mutation.mutateAsync(id);
onCallback();
};
const handleBlockButtonsStateChange = (disabled) =>
setBlockButtonsDisabled(disabled);
const anyGroupButtonsVisible =
needsApproval || reviewFlowEnabled || !blockedThread;
const isLoading =
unblockItemMutation.isLoading ||
blockItemMutation.isLoading ||
blockItemThreadMutation.isLoading ||
unblockItemThreadMutation.isLoading;
if (removed || isRejected || !canModerate) {
return (
<DiscussionThreadItemActionsWrapper as={Flex} direction="row">
{renderStatus(item)}
</DiscussionThreadItemActionsWrapper>
);
}
return (
<>
<DiscussionThreadItemActionsWrapper as={Flex} direction="row">
{isStatusBadgeVisible && renderStatus(item)}
{!blockedThread && (gotThread || pinned) && (
<ActionButton
onClick={handleBlockThreadClick}
startIcon={<LockIcon />}
loading={blockItemThreadMutation.isLoading}
variant="danger"
>
{getMessage("page.details.actions.thread.block", "Block thread")}
</ActionButton>
)}
{blockedThread && (gotThread || pinned) && (
<ActionButton
onClick={handleUnblockThreadClick}
startIcon={<UnlockIcon />}
loading={unblockItemThreadMutation.isLoading}
variant="success"
>
{getMessage(
"page.details.actions.thread.unblock",
"Unblock thread"
)}
</ActionButton>
)}
{anyGroupButtonsVisible && (
<IconButtonGroupStyled isSingle withMargin>
{!blockedThread && !(blocked || needsApproval) && (
<IconButton
onClick={handleBlockClick}
loading={blockItemMutation.isLoading}
icon={<LockIcon />}
label={getMessage(
"page.details.actions.comment.block",
"Block"
)}
/>
)}
{!blockedThread && blocked && (
<IconButton
onClick={handleUnblockClick}
loading={unblockItemMutation.isLoading}
icon={<UnlockIcon />}
label={getMessage(
"page.details.actions.comment.unblock",
"Unblock"
)}
/>
)}
{needsApproval && (
<DiscussionThreadItemApprovalFlowActions
id={id}
allowedActions={{ canModerate }}
queryToInvalidate="get-details-data"
/>
)}
<DiscussionThreadItemReviewAction
item={item}
queryToInvalidate="get-details-data"
areBlockButtonsDisabled={blockButtonsDisabled}
isLoading={isLoading}
allowedActions={{
canModerate,
canAccessReports,
canReviewReports,
}}
blockItemMutation={blockItemMutation}
blockItemThreadMutation={blockItemThreadMutation}
onBlockButtonsStateChange={handleBlockButtonsStateChange}
onBlockActionClick={handleBlockActionClick}
/>
</IconButtonGroupStyled>
)}
{hasActiveThread && (
<IconButtonGroupStyled isSingle withMargin>
<IconButton
onClick={handleDrilldownClick}
icon={<Eye />}
label={getMessage(
"page.details.panel.discussion.nav.drilldown",
"Drilldown thread"
)}
style={
blocked && !blockedThread
? { marginTop: "1px", marginRight: ".5rem" }
: {}
}
/>
</IconButtonGroupStyled>
)}
</DiscussionThreadItemActionsWrapper>
{!blocked && (
<ConfirmationDialog
isVisible={blockConfirmationVisible}
isActionAsync={blockItemMutation.isLoading}
header={getMessage(
"page.details.actions.comment.block.confirmation.header"
)}
labelConfirm={getMessage(
"page.details.actions.comment.block.confirmation.button.confirm"
)}
iconConfirm={<LockIcon />}
onConfirm={handleBlockConfirm}
onCancel={handleBlockCancel}
>
{getMessage(
"page.details.actions.comment.block.confirmation.description"
)}
</ConfirmationDialog>
)}
{!blockedThread && (
<ConfirmationDialog
isVisible={blockThreadConfirmationVisible}
isActionAsync={blockItemThreadMutation.isLoading}
header={getMessage(
"page.details.actions.thread.block.confirmation.header"
)}
labelConfirm={getMessage(
"page.details.actions.thread.block.confirmation.button.confirm"
)}
iconConfirm={<LockIcon />}
onConfirm={handleBlockThreadConfirm}
onCancel={handleBlockThreadCancel}
>
{getMessage(
"page.details.actions.thread.block.confirmation.description"
)}
</ConfirmationDialog>
)}
</>
);
}
Example #23
Source File: index.tsx From react-query-auth with MIT License | 4 votes |
export function initReactQueryAuth< User = unknown, Error = unknown, LoginCredentials = unknown, RegisterCredentials = unknown >(config: AuthProviderConfig<User, Error>) { const AuthContext = React.createContext<AuthContextValue< User, Error, LoginCredentials, RegisterCredentials > | null>(null); AuthContext.displayName = 'AuthContext'; const { loadUser, loginFn, registerFn, logoutFn, key = 'auth-user', waitInitial = true, LoaderComponent = () => <div>Loading...</div>, ErrorComponent = (error: any) => ( <div style={{ color: 'tomato' }}>{JSON.stringify(error, null, 2)}</div> ), } = config; function AuthProvider({ children }: AuthProviderProps): JSX.Element { const queryClient = useQueryClient(); const { data: user, error, status, isLoading, isIdle, isSuccess, refetch, } = useQuery<User, Error>({ queryKey: key, queryFn: loadUser, }); const setUser = React.useCallback( (data: User) => queryClient.setQueryData(key, data), [queryClient] ); const loginMutation = useMutation({ mutationFn: loginFn, onSuccess: user => { setUser(user); }, }); const registerMutation = useMutation({ mutationFn: registerFn, onSuccess: user => { setUser(user); }, }); const logoutMutation = useMutation({ mutationFn: logoutFn, onSuccess: () => { queryClient.clear(); }, }); const value = React.useMemo( () => ({ user, error, refetchUser: refetch, login: loginMutation.mutateAsync, isLoggingIn: loginMutation.isLoading, logout: logoutMutation.mutateAsync, isLoggingOut: logoutMutation.isLoading, register: registerMutation.mutateAsync, isRegistering: registerMutation.isLoading, }), [ user, error, refetch, loginMutation.mutateAsync, loginMutation.isLoading, logoutMutation.mutateAsync, logoutMutation.isLoading, registerMutation.mutateAsync, registerMutation.isLoading, ] ); if (isSuccess || !waitInitial) { return ( <AuthContext.Provider value={value}>{children}</AuthContext.Provider> ); } if (isLoading || isIdle) { return <LoaderComponent />; } if (error) { return <ErrorComponent error={error} />; } return <div>Unhandled status: {status}</div>; } function useAuth() { const context = React.useContext(AuthContext); if (!context) { throw new Error(`useAuth must be used within an AuthProvider`); } return context; } return { AuthProvider, AuthConsumer: AuthContext.Consumer, useAuth }; }
Example #24
Source File: useOriginate.ts From homebase-app with MIT License | 4 votes |
useOriginate = (template: DAOTemplate) => {
const queryClient = useQueryClient();
const [states, setStates] = useState(INITIAL_STATES);
const [activeState, setActiveState] = useState<number>();
const { tezos, connect, network, account } = useTezos();
const result = useMutation<
ContractAbstraction<ContractProvider | Wallet>,
Error,
OriginateParams
>(
async ({ metadataParams, params }) => {
const updatedStates = INITIAL_STATES;
updatedStates[0] = {
activeText: "Deploying Metadata Carrier Contract",
completedText: "",
};
setActiveState(0);
setStates(updatedStates);
let tezosToolkit = tezos;
if (!account) {
tezosToolkit = await connect();
}
mixpanel.track("Started DAO origination", {
contract: "MetadataCarrier",
daoName: params.orgSettings.name,
daoType: params.template
})
const metadata = await deployMetadataCarrier({
...metadataParams,
tezos: tezosToolkit,
connect,
});
if (!metadata) {
throw new Error(
`Could not deploy ${template}DAO because MetadataCarrier contract deployment failed`
);
}
updatedStates[0] = {
...updatedStates[0],
completedText: `Deployed Metadata Carrier with address "${metadata.deployAddress}" and key "${metadata.keyName}"`,
};
updatedStates[1] = {
activeText: `Deploying ${template} DAO Contract`,
completedText: "",
};
setActiveState(1);
setStates(updatedStates);
mixpanel.track("Started DAO origination", {
contract: "BaseDAO",
daoName: params.orgSettings.name
})
console.log(params, network, metadata)
const contract = await BaseDAO.baseDeploy(template, {
tezos: tezosToolkit,
metadata,
params,
network,
});
if (!contract) {
throw new Error(`Error deploying ${template}DAO`);
}
updatedStates[1] = {
...updatedStates[1],
completedText: `Deployed ${template} DAO contract with address "${contract.address}"`,
};
updatedStates[2] = {
activeText: `Waiting for DAO to be indexed`,
completedText: "",
};
setActiveState(2);
setStates(updatedStates);
mixpanel.track("Completed DAO creation", {
daoName: params.orgSettings.name,
daoType: params.template
})
mixpanel.track("Waiting for DAO indexation", {
daoName: params.orgSettings.name,
daoType: params.template
})
const indexed = await waitForIndexation(contract.address);
updatedStates[2] = {
...updatedStates[2],
completedText: indexed
? `Deployed ${metadataParams.metadata.unfrozenToken.name} successfully`
: `Deployed ${metadataParams.metadata.unfrozenToken.name} successfully, but metadata has not been indexed yet. This usually takes a few minutes, your DAO page may not be available yet.`,
};
setActiveState(3);
setStates(updatedStates);
mixpanel.track("Completed DAO indexation", {
daoName: params.orgSettings.name,
daoType: params.template
})
return contract;
},
{
onSuccess: () => {
queryClient.resetQueries();
},
}
);
console.log(result)
return { mutation: result, states, activeState };
}
Example #25
Source File: Companion.tsx From apps with GNU Affero General Public License v3.0 | 4 votes |
export default function Companion({
postData,
companionHelper,
companionExpanded,
onOptOut,
}: CompanionProps): ReactElement {
const client = useQueryClient();
const containerRef = useRef<HTMLDivElement>();
const [isCommentsOpen, setIsCommentsOpen] = useState(false);
const [assetsLoaded, setAssetsLoaded] = useState(isTesting);
const [post, setPost] = useState<PostBootData>(postData);
const [companionState, setCompanionState] =
useState<boolean>(companionExpanded);
const { user, closeLogin, loadingUser, shouldShowLogin, loginState } =
useContext(AuthContext);
const routeChangedCallbackRef = useTrackPageView();
useEffect(() => {
if (!assetsLoaded) {
return;
}
client.setQueryData(REQUEST_PROTOCOL_KEY, {
requestMethod: companionRequest,
fetchMethod: companionFetch,
});
}, [assetsLoaded]);
useEffect(() => {
if (routeChangedCallbackRef.current) {
routeChangedCallbackRef.current();
}
}, [routeChangedCallbackRef]);
useEffect(() => {
if (!containerRef?.current || assetsLoaded) {
return;
}
const checkAssets = () => {
if (containerRef?.current?.offsetLeft === 0) {
return setTimeout(checkAssets, 10);
}
return setTimeout(() => setAssetsLoaded(true), 10);
};
checkAssets();
routeChangedCallbackRef.current();
}, [containerRef]);
const onOpenComments = (value: boolean) => {
if (value && !companionState) {
setCompanionState(true);
}
setIsCommentsOpen(value);
};
return (
<Container
containerRef={containerRef}
companionExpanded={companionState}
shouldLoad={assetsLoaded}
>
{!user && !loadingUser && shouldShowLogin && (
<LoginModal
parentSelector={getCompanionWrapper}
isOpen
onRequestClose={closeLogin}
contentLabel="Login Modal"
{...loginState}
/>
)}
<CompanionMenu
post={post}
companionHelper={companionHelper}
setPost={setPost}
onOptOut={onOptOut}
companionState={companionState}
setCompanionState={setCompanionState}
onOpenComments={() => onOpenComments(true)}
/>
<CompanionContent
post={post}
viewComments={isCommentsOpen}
onViewComments={onOpenComments}
/>
</Container>
);
}