swr#mutate TypeScript Examples
The following examples show how to use
swr#mutate.
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: Buttons.tsx From frames with Mozilla Public License 2.0 | 7 votes |
BackButton = ({response}: { response: { mediaId: number, episodeName: string | null, logo: string | null, name: string } }) => {
const router = useRouter();
const {disconnect} = useGroupWatch();
const routeOut = async (ev: any) => {
ev.stopPropagation();
await disconnect();
const url = '/' + (response.episodeName ? 'show' : 'movie') + '=' + response.name.replace(/\s/g, '+');
document.body.removeAttribute('style');
await router.push('/info?mediaId=' + response.mediaId, url);
await mutate('/api/load/continue');
}
return (
<svg className={styles.bb} viewBox="0 0 512 512" onClick={routeOut}>
<path d="M256,0C114.844,0,0,114.844,0,256s114.844,256,256,256s256-114.844,256-256S397.156,0,256,0z M256,490.667
C126.604,490.667,21.333,385.396,21.333,256S126.604,21.333,256,21.333S490.667,126.604,490.667,256S385.396,490.667,256,490.667
z"/>
<path d="M394.667,245.333H143.083l77.792-77.792c4.167-4.167,4.167-10.917,0-15.083c-4.167-4.167-10.917-4.167-15.083,0l-96,96
c-4.167,4.167-4.167,10.917,0,15.083l96,96c2.083,2.083,4.813,3.125,7.542,3.125c2.729,0,5.458-1.042,7.542-3.125
c4.167-4.167,4.167-10.917,0-15.083l-77.792-77.792h251.583c5.896,0,10.667-4.771,10.667-10.667S400.563,245.333,394.667,245.333
z"/>
</svg>
)
}
Example #2
Source File: useQuestions.ts From office-hours with GNU General Public License v3.0 | 6 votes |
export function useQuestions(qid: number): UseQuestionReturn {
const key = qid && `/api/v1/queues/${qid}/questions`;
// Subscribe to sse
const isLive = useEventSource(
qid && `/api/v1/queues/${qid}/sse`,
"question",
useCallback(
(data: SSEQueueResponse) => {
if (data.questions) {
mutate(
key,
plainToClass(ListQuestionsResponse, data.questions),
false
);
}
},
[key]
)
);
const {
data: questions,
error: questionsError,
mutate: mutateQuestions,
} = useSWR(key, async () => API.questions.index(Number(qid)), {
refreshInterval: isLive ? 0 : 10 * 1000,
});
return { questions, questionsError, mutateQuestions };
}
Example #3
Source File: Buttons.tsx From frames with Mozilla Public License 2.0 | 6 votes |
Seen = ({id, seen}: ButtonInterfaces) => {
const [hover, setHover] = useState(false);
const [see, setSee] = useState(seen);
async function seenHandler() {
setSee(!see)
await fetch(`/api/media/seen?id=${id}`);
await mutate('/api/load/continue')
}
return (
<button
title={!see ? "seen ?" : "seen"}
onClick={() => seenHandler()}
className={(see && !hover) || (!see && hover) ? styles.roundGuys : `${styles.roundGuys} ${styles.noFill}`}
onMouseEnter={() => setHover(true)}
onMouseLeave={() => setHover(false)}
>
{(see && !hover) || (!see && hover) ? (
<svg viewBox="0 0 426.667 426.667">
<g>
<path
d="M421.876,56.307c-6.548-6.78-17.352-6.968-24.132-0.42c-0.142,0.137-0.282,0.277-0.42,0.42L119.257,334.375
l-90.334-90.334c-6.78-6.548-17.584-6.36-24.132,0.42c-6.388,6.614-6.388,17.099,0,23.713l102.4,102.4
c6.665,6.663,17.468,6.663,24.132,0L421.456,80.44C428.236,73.891,428.424,63.087,421.876,56.307z"
/>
</g>
</svg>
) : (
<svg viewBox="0 0 24 24" id="notSeenSvg">
<polyline points="9 11 12 14 22 4"/>
<path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"/>
</svg>
)}
</button>
);
}
Example #4
Source File: likebutton.tsx From samuelkraft-next with MIT License | 6 votes |
LikeButton = ({ slug }: { slug: string }): JSX.Element | null => {
const [mounted, setMounted] = useState(false)
const { data } = useSWR(`/api/likes?slug=${slug}`, fetcher)
const likes = data?.likes
const liked = localStorage.getItem(slug) === 'true'
useEffect(() => setMounted(true), [])
const onLike = async () => {
localStorage.setItem(slug, 'true')
mutate(`/api/likes?slug=${slug}`, { ...data, likes: likes + 1 }, false)
await fetch(`/api/likes?slug=${slug}`, { method: 'POST' })
}
if (!mounted) return null
return (
<Button disabled={liked} onClick={onLike} type="button" variant="like">
<Heart className={liked ? styles.icon : ''} /> {typeof likes === 'undefined' ? '--' : likes}
</Button>
)
}
Example #5
Source File: Buttons.tsx From frames with Mozilla Public License 2.0 | 6 votes |
BackButton = ({response}: { response?: SpringPlay }) => {
const router = useRouter();
const {disconnect} = useGroupWatch();
const routeOut = async (ev: any) => {
ev.stopPropagation();
await disconnect();
if (response) {
const url = '/' + (response.episodeName ? 'show' : 'movie') + '=' + response.name.replace(/\s/g, '+');
document.body.removeAttribute('style');
await router.push('/info?id=' + response.mediaId, url);
} else await router.back();
await mutate('/api/load/continue');
}
return (
<svg className={styles.bb} viewBox="0 0 512 512" onClick={routeOut}>
<path d="M256,0C114.844,0,0,114.844,0,256s114.844,256,256,256s256-114.844,256-256S397.156,0,256,0z M256,490.667
C126.604,490.667,21.333,385.396,21.333,256S126.604,21.333,256,21.333S490.667,126.604,490.667,256S385.396,490.667,256,490.667
z"/>
<path d="M394.667,245.333H143.083l77.792-77.792c4.167-4.167,4.167-10.917,0-15.083c-4.167-4.167-10.917-4.167-15.083,0l-96,96
c-4.167,4.167-4.167,10.917,0,15.083l96,96c2.083,2.083,4.813,3.125,7.542,3.125c2.729,0,5.458-1.042,7.542-3.125
c4.167-4.167,4.167-10.917,0-15.083l-77.792-77.792h251.583c5.896,0,10.667-4.771,10.667-10.667S400.563,245.333,394.667,245.333
z"/>
</svg>
)
}
Example #6
Source File: Buttons.tsx From frames with Mozilla Public License 2.0 | 6 votes |
Template = ({id, type, name, onClick, onHover}: ButtonInterfaces) => {
if (name === 'see details')
mutate('/api/load/continue');
return (
<button title={name} onMouseEnter={() => onHover ? onHover(true) : null}
onMouseLeave={() => onHover ? onHover(false) : null}
className={`${(id === 0 ? styles.playButton : id === 1 ? styles.trailerButton : styles.roundGuys)} ${type === 'add' ? '' : styles.noFill}`}
onClick={onClick}>
{type === 'down' ? <svg viewBox="0 0 24 24">
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>
<polyline points="7 10 12 15 17 10"/>
<line x1="12" y1="15" x2="12" y2="3"/>
</svg> : type === 'scan' ? <svg viewBox="0 0 24 24">
<polyline points="23 4 23 10 17 10"/>
<polyline points="1 20 1 14 7 14"/>
<path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"/>
</svg> : type === 'edit' ? <svg viewBox="0 0 24 24">
<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/>
<path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/>
</svg> : type === 'add' ? <svg viewBox="0 0 409.6 409.6">
<g>
<path
d="M392.533,187.733H221.867V17.067C221.867,7.641,214.226,0,204.8,0s-17.067,7.641-17.067,17.067v170.667H17.067
C7.641,187.733,0,195.374,0,204.8s7.641,17.067,17.067,17.067h170.667v170.667c0,9.426,7.641,17.067,17.067,17.067
s17.067-7.641,17.067-17.067V221.867h170.667c9.426,0,17.067-7.641,17.067-17.067S401.959,187.733,392.533,187.733z"
/>
</g>
</svg> : type === 'none' ? null : <svg viewBox="0 0 24 24">
<circle cx="12" cy="12" r="10"/>
<line x1="12" y1="8" x2="12" y2="12"/>
<line x1="12" y1="16" x2="12.01" y2="16"/>
</svg>}
{id !== 2 && name}
</button>
)
}
Example #7
Source File: Api.ts From takeout-app with MIT License | 6 votes |
function activateCandidateTrackCard(data: GetConferenceResponse) {
console.log("Start activating candidate TrackCard");
const now = dayjs().unix();
let updated = false;
for (const [, track] of Object.entries(data.conference.tracks)) {
const candidate = track.card_candidate;
if (!candidate) continue;
if (now >= candidate.at) {
console.log(`Activating candidate TrackCard for track=${track.slug}`, track.card_candidate);
updated = true;
track.card = candidate;
track.card_candidate = null;
} else {
console.log(`Skipping candidate activation for track=${track.slug}`, track.card_candidate);
}
}
if (updated) {
console.log("Mutating /api/conference due to candidate TrackCard activation");
mutate(API_CONFERENCE, { ...data }, false);
}
}
Example #8
Source File: usePolicyPerformance.ts From yasd with MIT License | 6 votes |
usePolicyPerformance = () => {
const isSupported = useVersionSupport({
ios: '4.9.5',
macos: '4.2.4',
})
const { data, error } = useSWR<PolicyBenchmarkResults>(
isSupported ? '/policies/benchmark_results' : null,
fetcher,
{
revalidateOnFocus: false,
revalidateOnReconnect: false,
refreshInterval: 0,
},
)
return {
data,
error,
mutate: mutatePolicyPerformanceResults,
}
}
Example #9
Source File: FirestoreEmulatedApiProvider.tsx From firebase-tools-ui with Apache License 2.0 | 6 votes |
export function useRecursiveDelete() {
const { baseEmulatorUrl } = useFirestoreRestApi();
const fetcher = useFetcher({
method: 'DELETE',
});
return async (
ref:
| firebase.firestore.CollectionReference
| firebase.firestore.DocumentReference
) => {
mutate('*');
const encodedPath = encodePath(ref.path);
const url = `${baseEmulatorUrl}/documents/${encodedPath}`;
return await fetcher(url);
};
}
Example #10
Source File: FirestoreEmulatedApiProvider.tsx From firebase-tools-ui with Apache License 2.0 | 6 votes |
export function useEjector() {
const { baseEmulatorUrl } = useFirestoreRestApi();
const url = `${baseEmulatorUrl}/documents`;
const fetcher = useFetcher({
method: 'DELETE',
});
return async () => {
mutate('*');
return await fetcher(url);
};
}
Example #11
Source File: ControlApi.tsx From takeout-app with MIT License | 5 votes |
ControlApi = {
useConference() {
return useSWR<ControlGetConferenceResponse, ApiError>("/api/control/conference", swrFetcher, {
revalidateOnFocus: false,
});
},
async createControlSession(password: string) {
const resp = await request("/api/session/take_control", "POST", null, {
password,
});
mutate("/api/session");
return resp.json();
},
useTrackCards(slug: TrackSlug) {
return useSWR<ControlGetTrackCardsResponse, ApiError>(
`/api/control/tracks/${encodeURIComponent(slug)}/cards`,
swrFetcher,
);
},
async createTrackCard(card: TrackCard) {
const url = `/api/control/tracks/${encodeURIComponent(card.track)}/cards`;
const resp = await request(url, "POST", null, {
track_card: card,
});
mutate(url);
return resp.json();
},
useAttendeeList(query: string | null) {
return useSWR<ControlListAttendeesResponse, ApiError>(
query !== null ? `/api/control/attendees?query=${encodeURIComponent(query)}` : null,
swrFetcher,
{
revalidateOnFocus: false,
revalidateOnReconnect: false,
},
);
},
useAttendee(id: number) {
return useSWR<ControlGetAttendeeResponse, ApiError>(id ? `/api/control/attendees/${id}` : null, swrFetcher, {
revalidateOnFocus: false,
revalidateOnReconnect: false,
});
},
async updateAttendee(id: number, params: ControlUpdateAttendeeRequestAttendee) {
const url = `/api/control/attendees/${id}`;
const resp = await request(url, "PUT", null, { attendee: params });
mutate(`/api/control/attendees/${id}`);
return resp.json();
},
}
Example #12
Source File: index.tsx From yasd with MIT License | 5 votes |
Page: React.FC = () => {
const { t } = useTranslation()
const { data: modules, error: modulesError } = useSWR<Modules>(
'/modules',
fetcher,
)
const [isLoading, setIsLoading] = useState(false)
const isChecked = (name: string): boolean => {
return modules?.enabled.includes(name) === true
}
const toggle = useCallback(
(name: string, newVal: boolean) => {
setIsLoading(true)
fetcher({
url: '/modules',
method: 'POST',
data: {
[name]: newVal,
},
})
.then(() => {
toast.success(t('common.success_interaction'))
return mutate('/modules')
})
.catch((err) => {
toast.success(t('common.failed_interaction'))
console.error(err)
})
.finally(() => {
setIsLoading(false)
})
},
[setIsLoading, t],
)
return (
<PageContainer>
<PageTitle title={t('home.modules')} />
<div tw="divide-y divide-gray-200">
{modules &&
modules.available.map((mod) => {
return (
<div key={mod} tw="flex items-center justify-between p-3">
<div tw="truncate leading-normal text-gray-700">{mod}</div>
<div tw="flex items-center">
<Toggle
noMargin
label=""
labelChecked="on"
labelUnchecked="off"
disabled={isLoading}
checked={isChecked(mod)}
onChange={() => toggle(mod, !isChecked(mod))}
/>
</div>
</div>
)
})}
</div>
</PageContainer>
)
}
Example #13
Source File: usePolicyPerformance.ts From yasd with MIT License | 5 votes |
mutatePolicyPerformanceResults = () =>
mutate('/policies/benchmark_results')
Example #14
Source File: Api.ts From takeout-app with MIT License | 5 votes |
export function consumeIvsMetadata(metadata: IvsMetadata) {
mutate(
API_CONFERENCE,
(known: GetConferenceResponse) => {
known = JSON.parse(JSON.stringify(known));
let updated = false;
metadata.i?.forEach((item) => {
console.log(item);
if (item.c) {
const cardUpdate = item.c;
const cardKey = cardUpdate.candidate ? "card_candidate" : "card";
if (cardUpdate.clear) {
const track = known.conference.tracks[cardUpdate.clear];
if (track?.[cardKey]) {
console.log("Clearing card", { key: cardKey, cardUpdate });
track[cardKey] = null;
updated = true;
}
} else if (cardUpdate.card) {
const track = known.conference.tracks[cardUpdate.card.track];
if (track) {
console.log("Updating card", { key: cardKey, cardUpdate });
track[cardKey] = cardUpdate.card;
updated = true;
}
}
}
if (item.p) {
const presence = item.p;
const track = known.conference.tracks[presence.track];
if (track) {
const was = track.presences[presence.kind]?.at ?? 0;
console.log("Updating stream presence", { presence, was });
track.presences[presence.kind] = presence;
updated = true;
}
}
if (item.n) {
const track = known.conference.tracks[item.n.track];
if (track) {
console.log("Updating viewerCount", item.n);
track.viewerCount = item.n;
updated = true;
}
}
});
if (updated) known.requested_at = 0;
return known;
},
false,
);
}
Example #15
Source File: Api.ts From takeout-app with MIT License | 5 votes |
export function consumeChatAdminControl(adminControl: ChatAdminControl) {
console.log("consumeChatAdminControl", adminControl);
if (adminControl.pin) {
mutate(
`/api/tracks/${encodeURIComponent(adminControl.pin.track)}/chat_message_pin`,
{ track: adminControl.pin.track, pin: adminControl.pin },
false,
);
}
if (adminControl.spotlights) {
const spotlights = adminControl.spotlights;
mutate(
API_CONFERENCE,
(known: GetConferenceResponse) => {
known = JSON.parse(JSON.stringify(known));
spotlights.forEach((spotlight) => {
const track = known.conference.tracks[spotlight.track];
if (!track) return;
const idx = track.spotlights.findIndex((v) => v.id === spotlight.id);
if (idx !== -1) {
track.spotlights[idx] = spotlight;
} else {
track.spotlights.push(spotlight);
}
});
return known;
},
false,
);
}
if (adminControl.presences) {
const presences = adminControl.presences;
mutate(
API_CONFERENCE,
(known2: GetConferenceResponse) => {
const known = JSON.parse(JSON.stringify(known2));
presences.forEach((presence) => {
const track = known.conference.tracks[presence.track];
if (track) {
const was = track.presences[presence.kind]?.at ?? 0;
if (was < presence.at) {
console.log("Updating stream presence (chat)", presence);
track.presences[presence.kind] = presence;
}
}
});
return known;
},
false,
);
}
}
Example #16
Source File: Buttons.tsx From frames with Mozilla Public License 2.0 | 5 votes |
MyList = ({id, myList}: ButtonInterfaces) => {
const [hover, setHover] = useState(false);
const [list, setList] = useState(myList);
useEffect(() => {
setList(myList);
}, [myList]);
async function listHandler() {
setList(!list);
await fetch(`/api/media/addToList?mediaId=${id}`);
await mutate('/api/load/myList');
}
return (
<button
className={styles.roundGuys}
onMouseEnter={() => setHover(true)}
onMouseLeave={() => setHover(false)}
onClick={listHandler}
title={list ? "remove" : "add to list"}
>
{list ? (
!hover ? (
<svg viewBox="0 0 426.667 426.667">
<g>
<path
d="M421.876,56.307c-6.548-6.78-17.352-6.968-24.132-0.42c-0.142,0.137-0.282,0.277-0.42,0.42L119.257,334.375
l-90.334-90.334c-6.78-6.548-17.584-6.36-24.132,0.42c-6.388,6.614-6.388,17.099,0,23.713l102.4,102.4
c6.665,6.663,17.468,6.663,24.132,0L421.456,80.44C428.236,73.891,428.424,63.087,421.876,56.307z"
/>
</g>
</svg>
) : (
<svg viewBox="0 0 409.806 409.806">
<g>
<path
d="M228.929,205.01L404.596,29.343c6.78-6.548,6.968-17.352,0.42-24.132c-6.548-6.78-17.352-6.968-24.132-0.42
c-0.142,0.137-0.282,0.277-0.42,0.42L204.796,180.878L29.129,5.21c-6.78-6.548-17.584-6.36-24.132,0.42
c-6.388,6.614-6.388,17.099,0,23.713L180.664,205.01L4.997,380.677c-6.663,6.664-6.663,17.468,0,24.132
c6.664,6.662,17.468,6.662,24.132,0l175.667-175.667l175.667,175.667c6.78,6.548,17.584,6.36,24.132-0.42
c6.387-6.614,6.387-17.099,0-23.712L228.929,205.01z"
/>
</g>
</svg>
)
) : (
<svg viewBox="0 0 409.6 409.6">
<g>
<path
d="M392.533,187.733H221.867V17.067C221.867,7.641,214.226,0,204.8,0s-17.067,7.641-17.067,17.067v170.667H17.067
C7.641,187.733,0,195.374,0,204.8s7.641,17.067,17.067,17.067h170.667v170.667c0,9.426,7.641,17.067,17.067,17.067
s17.067-7.641,17.067-17.067V221.867h170.667c9.426,0,17.067-7.641,17.067-17.067S401.959,187.733,392.533,187.733z"
/>
</g>
</svg>
)}
</button>
);
}
Example #17
Source File: useQueue.ts From office-hours with GNU General Public License v3.0 | 5 votes |
/**
* Get data for a queue.
* @param qid Queue ID to get data for
* @param onUpdate Optional callback to listen for when data is refetched, whether via HTTP or SSE
*/
export function useQueue(qid: number, onUpdate?: OnUpdate): UseQueueReturn {
const key = qid && `/api/v1/queues/${qid}`;
if (!(key in REFRESH_INFO)) {
REFRESH_INFO[key] = {
lastUpdated: null,
onUpdates: new Set(),
};
}
// Register onUpdate callback
useEffect(() => {
if (onUpdate) {
const refreshInfo = REFRESH_INFO[key];
refreshInfo.onUpdates.add(onUpdate);
onUpdate(refreshInfo.lastUpdated);
return () => {
refreshInfo.onUpdates.delete(onUpdate);
};
}
}, [onUpdate, key]);
const isLive = useEventSource(
qid && `/api/v1/queues/${qid}/sse`,
"queue",
useCallback(
(data: SSEQueueResponse) => {
if (data.queue) {
mutate(key, plainToClass(GetQueueResponse, data.queue), false);
REFRESH_INFO[key].lastUpdated = new Date();
callOnUpdates(key);
}
},
[key]
)
);
const { data: queue, error: queueError, mutate: mutateQueue } = useSWR(
key,
useCallback(async () => API.queues.get(Number(qid)), [qid]),
{
refreshInterval: isLive ? 0 : 10 * 1000,
onSuccess: (_, key) => {
REFRESH_INFO[key].lastUpdated = new Date();
callOnUpdates(key);
},
}
);
return {
queue,
queueError,
mutateQueue,
isLive,
};
}
Example #18
Source File: Buttons.tsx From frames with Mozilla Public License 2.0 | 5 votes |
Seen = ({id, seen}: ButtonInterfaces) => {
const [hover, setHover] = useState(false);
const [see, setSee] = useState(seen);
useEffect(() => {
setSee(seen);
}, [seen]);
async function seenHandler() {
setSee(!see)
await fetch(`/api/media/seen?mediaId=${id}`);
await mutate('/api/load/continue')
}
return (
<button
title={!see ? "seen ?" : "seen"}
onClick={() => seenHandler()}
className={(see && !hover) || (!see && hover) ? styles.roundGuys : `${styles.roundGuys} ${styles.noFill}`}
onMouseEnter={() => setHover(true)}
onMouseLeave={() => setHover(false)}
>
{(see && !hover) || (!see && hover) ? (
<svg viewBox="0 0 426.667 426.667">
<g>
<path
d="M421.876,56.307c-6.548-6.78-17.352-6.968-24.132-0.42c-0.142,0.137-0.282,0.277-0.42,0.42L119.257,334.375
l-90.334-90.334c-6.78-6.548-17.584-6.36-24.132,0.42c-6.388,6.614-6.388,17.099,0,23.713l102.4,102.4
c6.665,6.663,17.468,6.663,24.132,0L421.456,80.44C428.236,73.891,428.424,63.087,421.876,56.307z"
/>
</g>
</svg>
) : (
<svg viewBox="0 0 24 24" id="notSeenSvg">
<polyline points="9 11 12 14 22 4"/>
<path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"/>
</svg>
)}
</button>
);
}
Example #19
Source File: Buttons.tsx From frames with Mozilla Public License 2.0 | 5 votes |
Template = ({id, type, name, onClick, onHover}: ButtonInterfaces) => {
if (name === 'see details')
mutate('/api/load/continue');
return (
<button title={name} onMouseEnter={() => onHover ? onHover(true) : null}
onMouseLeave={() => onHover ? onHover(false) : null}
className={`${(id === 0 ? styles.playButton : id === 1 ? styles.trailerButton : styles.roundGuys)} ${type === 'add' || type === 'play' ? '' : styles.noFill}`}
onClick={onClick}>
{type === 'down' && <svg viewBox="0 0 24 24">
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>
<polyline points="7 10 12 15 17 10"/>
<line x1="12" y1="15" x2="12" y2="3"/>
</svg>}
{type === 'scan' && <svg viewBox="0 0 24 24">
<polyline points="23 4 23 10 17 10"/>
<polyline points="1 20 1 14 7 14"/>
<path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"/>
</svg>}
{type === 'edit' && <svg viewBox="0 0 24 24">
<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/>
<path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/>
</svg>}
{type === 'add' && <svg viewBox="0 0 409.6 409.6">
<g>
<path
d="M392.533,187.733H221.867V17.067C221.867,7.641,214.226,0,204.8,0s-17.067,7.641-17.067,17.067v170.667H17.067
C7.641,187.733,0,195.374,0,204.8s7.641,17.067,17.067,17.067h170.667v170.667c0,9.426,7.641,17.067,17.067,17.067
s17.067-7.641,17.067-17.067V221.867h170.667c9.426,0,17.067-7.641,17.067-17.067S401.959,187.733,392.533,187.733z"
/>
</g>
</svg>}
{type === 'play' && <svg viewBox="0 0 494.148 494.148">
<g>
<path
d="M405.284,201.188L130.804,13.28C118.128,4.596,105.356,0,94.74,0C74.216,0,61.52,16.472,61.52,44.044v406.124
c0,27.54,12.68,43.98,33.156,43.98c10.632,0,23.2-4.6,35.904-13.308l274.608-187.904c17.66-12.104,27.44-28.392,27.44-45.884
C432.632,229.572,422.964,213.288,405.284,201.188z"
data-original="#000000"
className="active-path"
data-old_color="#000000"
/>
</g>
</svg>}
{type === 'info' && <svg viewBox="0 0 24 24">
<circle cx="12" cy="12" r="10"/>
<line x1="12" y1="8" x2="12" y2="12"/>
<line x1="12" y1="16" x2="12.01" y2="16"/>
</svg>}
{id !== 2 && name}
</button>
)
}
Example #20
Source File: EmulatorConfigProvider.tsx From firebase-tools-ui with Apache License 2.0 | 5 votes |
EmulatorConfigProvider: React.FC<
React.PropsWithChildren<{ refreshInterval: number }>
> = ({ refreshInterval, children }) => {
// We don't use suspense here since the provider should never be suspended --
// it merely creates a context for hooks (e.g. useConfig) who do suspend.
const { data } = useSwr<Config | null>(CONFIG_API, configFetcher, {
// Keep refreshing config to detect when emulators are stopped or restarted
// with a different config (e.g. different emulators or host / ports).
refreshInterval,
// Emulator UI works fully offline. Even if the browser cannot reach LAN or
// any router, this page and the CONFIG_API will still work if they are on
// the same physical device (e.g. localhost/127.0.0.1/containers/VMs). See:
// https://developer.mozilla.org/en-US/docs/Web/API/NavigatorOnLine/onLine
refreshWhenOffline: true,
onErrorRetry(_, __, ___, revalidate, { retryCount }) {
if (retryCount < 2) {
// If the Emulator Hub is running locally, we are very unlikely to get
// any errors at all. However, an occasional blip may happen when using
// Emulator UI remotely (or through port forwarding). Let's retry once.
// Note that swr will keep `data` while retrying, so UI won't unload.
// (This overwrites swr's built-in exponential backoff timeouts.)
setTimeout(() => revalidate({ retryCount }), refreshInterval);
} else {
// We cannot reach the discovery API for 2+ times. This usually means
// Emulator Hub is stopped or unreachable. swr won't clear data on error
// so we manually set data to null to represent this situation.
// Tell swr to revalidate (retry the fetch). No need for setTimeout.
mutate(CONFIG_API, null, /* shouldRevalidate= */ true);
}
},
});
const promise = useOnChangePromise(data);
return (
<emulatorConfigContext.Provider
value={{
config: data,
promise,
}}
>
{children}
</emulatorConfigContext.Provider>
);
}
Example #21
Source File: Buttons.tsx From frames with Mozilla Public License 2.0 | 5 votes |
MyList = ({id, myList}: ButtonInterfaces) => {
const [hover, setHover] = useState(false);
const [list, setList] = useState(myList);
async function listHandler() {
setList(!list);
await fetch(`/api/media/list?id=${id}`);
await mutate('/api/load/myList');
}
return (
<button
className={styles.roundGuys}
onMouseEnter={() => setHover(true)}
onMouseLeave={() => setHover(false)}
onClick={listHandler}
title={list ? "remove" : "add to list"}
>
{list ? (
!hover ? (
<svg viewBox="0 0 426.667 426.667">
<g>
<path
d="M421.876,56.307c-6.548-6.78-17.352-6.968-24.132-0.42c-0.142,0.137-0.282,0.277-0.42,0.42L119.257,334.375
l-90.334-90.334c-6.78-6.548-17.584-6.36-24.132,0.42c-6.388,6.614-6.388,17.099,0,23.713l102.4,102.4
c6.665,6.663,17.468,6.663,24.132,0L421.456,80.44C428.236,73.891,428.424,63.087,421.876,56.307z"
/>
</g>
</svg>
) : (
<svg viewBox="0 0 409.806 409.806">
<g>
<path
d="M228.929,205.01L404.596,29.343c6.78-6.548,6.968-17.352,0.42-24.132c-6.548-6.78-17.352-6.968-24.132-0.42
c-0.142,0.137-0.282,0.277-0.42,0.42L204.796,180.878L29.129,5.21c-6.78-6.548-17.584-6.36-24.132,0.42
c-6.388,6.614-6.388,17.099,0,23.713L180.664,205.01L4.997,380.677c-6.663,6.664-6.663,17.468,0,24.132
c6.664,6.662,17.468,6.662,24.132,0l175.667-175.667l175.667,175.667c6.78,6.548,17.584,6.36,24.132-0.42
c6.387-6.614,6.387-17.099,0-23.712L228.929,205.01z"
/>
</g>
</svg>
)
) : (
<svg viewBox="0 0 409.6 409.6">
<g>
<path
d="M392.533,187.733H221.867V17.067C221.867,7.641,214.226,0,204.8,0s-17.067,7.641-17.067,17.067v170.667H17.067
C7.641,187.733,0,195.374,0,204.8s7.641,17.067,17.067,17.067h170.667v170.667c0,9.426,7.641,17.067,17.067,17.067
s17.067-7.641,17.067-17.067V221.867h170.667c9.426,0,17.067-7.641,17.067-17.067S401.959,187.733,392.533,187.733z"
/>
</g>
</svg>
)}
</button>
);
}
Example #22
Source File: CapabilityTile.tsx From yasd with MIT License | 5 votes |
CapabilityTile: React.FC<CapabilityTileProps> = ({
api,
title,
link,
}) => {
const { t } = useTranslation()
const profile = useProfile()
const { data: capability } = useSWR<Capability>(
profile !== undefined ? api : null,
fetcher,
)
const history = useHistory()
const toggle: ChangeEventHandler<HTMLButtonElement> = useCallback(
(e) => {
e.stopPropagation()
e.preventDefault()
fetcher({
method: 'POST',
url: api,
data: {
enabled: !capability?.enabled,
},
})
.then(() => {
return mutate(api)
})
.catch((err) => {
console.error(err)
})
},
[api, capability],
)
return (
<MenuTile onClick={link ? () => history.push(link) : undefined}>
<MenuTileTitle title={t(`home.${title}`)} />
<MenuTileContent css={[tw`flex justify-end`]}>
<Toggle
noMargin
label=""
labelChecked={t('common.on')}
labelUnchecked={t('common.off')}
checked={capability?.enabled}
onChange={toggle}
/>
</MenuTileContent>
</MenuTile>
)
}
Example #23
Source File: EditPiggybankModal.tsx From coindrop with GNU General Public License v3.0 | 4 votes |
EditPiggybankModal: FunctionComponent<Props> = ({ isOpen, onClose }) => {
const [isSubmitting, setIsSubmitting] = useState(false);
const { colors } = useTheme();
const { user } = useUser();
const { colorMode } = useColorMode();
const accentColorLevelInitial = getAccentColorLevelInitial(colorMode);
const accentColorLevelHover = getAccentColorLevelHover(colorMode);
const { push: routerPush, query: { piggybankName } } = useRouter();
const initialPiggybankId = Array.isArray(piggybankName) ? piggybankName[0] : piggybankName;
const { piggybankDbData } = useContext(PublicPiggybankDataContext);
const { avatar_storage_id: currentAvatarStorageId } = piggybankDbData;
const initialPaymentMethodsDataFieldArray = convertPaymentMethodsDataToFieldArray(piggybankDbData.paymentMethods);
const initialAccentColor = piggybankDbData.accentColor ?? 'orange';
const {
register,
handleSubmit,
setValue,
watch,
control,
formState: { isDirty },
} = useForm({
defaultValues: {
piggybankId: initialPiggybankId,
accentColor: initialAccentColor,
website: piggybankDbData.website ?? '',
name: piggybankDbData.name ?? '',
verb: piggybankDbData.verb ?? 'pay',
paymentMethods: sortByIsPreferredThenAlphabetical(initialPaymentMethodsDataFieldArray),
},
});
const paymentMethodsFieldArrayName = "paymentMethods";
const { fields, append, remove } = useFieldArray({
control,
name: paymentMethodsFieldArrayName,
});
const {
accentColor: watchedAccentColor,
piggybankId: watchedPiggybankId,
} = watch(["accentColor", "piggybankId"]);
const isAccentColorDirty = initialAccentColor !== watchedAccentColor;
const isUrlUnchanged = initialPiggybankId === watchedPiggybankId;
const { isPiggybankIdAvailable, setIsAddressTouched } = useContext(AdditionalValidation);
const onSubmit = async (formData) => {
try {
setIsSubmitting(true);
const dataToSubmit = {
...formData,
paymentMethods: convertPaymentMethodsFieldArrayToDbMap(formData.paymentMethods ?? []),
owner_uid: user.id,
avatar_storage_id: currentAvatarStorageId ?? null,
};
if (isUrlUnchanged) {
await db.collection('piggybanks').doc(initialPiggybankId).set(dataToSubmit);
mutate(['publicPiggybankData', initialPiggybankId], dataToSubmit);
} else {
await axios.post(
'/api/createPiggybank',
{
oldPiggybankName: initialPiggybankId,
newPiggybankName: formData.piggybankId,
piggybankData: dataToSubmit,
},
{
headers: {
token: user.token,
},
},
);
try {
await db.collection('piggybanks').doc(initialPiggybankId).delete();
} catch (err) {
console.log('error deleting old Coindrop page');
}
routerPush(`/${formData.piggybankId}`);
}
fetch(`/${initialPiggybankId}`, { headers: { isToForceStaticRegeneration: "true" }});
onClose();
} catch (error) {
setIsSubmitting(false);
// TODO: handle errors
throw error;
}
};
const handleAccentColorChange = (e) => {
e.preventDefault();
setValue("accentColor", e.target.dataset.colorname);
};
useEffect(() => {
register("accentColor");
}, [register]);
const formControlTopMargin = 2;
return (
<Modal
isOpen={isOpen}
onClose={onClose}
size="xl"
closeOnOverlayClick={false}
>
<ModalOverlay />
<ModalContent>
<ModalHeader>Configure</ModalHeader>
<ModalCloseButton />
<form id="configure-coindrop-form" onSubmit={handleSubmit(onSubmit)}>
<ModalBody>
<AvatarInput />
<FormControl
isRequired
mt={formControlTopMargin}
>
<FormLabel htmlFor="input-piggybankId">URL</FormLabel>
<EditUrlInput
register={register}
value={watchedPiggybankId}
/>
</FormControl>
<FormControl
mt={formControlTopMargin}
>
<FormLabel
htmlFor="input-accentColor"
>
Theme
</FormLabel>
<Flex wrap="wrap" justify="center">
{themeColorOptions.map(colorName => {
const isColorSelected = watchedAccentColor === colorName;
const accentColorInitial = colors[colorName][accentColorLevelInitial];
const accentColorHover = colors[colorName][accentColorLevelHover];
return (
<Box
key={colorName}
as="button"
bg={isColorSelected ? accentColorHover : accentColorInitial}
_hover={{
bg: accentColorHover,
}}
w="36px"
h="36px"
borderRadius="50%"
mx={1}
my={1}
onClick={handleAccentColorChange}
data-colorname={colorName}
>
{isColorSelected && (
<CheckIcon color="#FFF" />
)}
</Box>
);
})}
</Flex>
</FormControl>
<FormControl
isRequired
mt={formControlTopMargin}
>
<FormLabel
htmlFor="input-name"
>
Name
</FormLabel>
<Input
id="input-name"
name="name"
ref={register}
/>
</FormControl>
<FormControl
isRequired
mt={formControlTopMargin}
>
<FormLabel
htmlFor="input-verb"
>
Payment action name
</FormLabel>
<Select
id="input-verb"
name="verb"
ref={register}
>
<option value="pay">Pay</option>
<option value="donate to">Donate to</option>
<option value="support">Support</option>
<option value="tip">Tip</option>
</Select>
</FormControl>
<FormControl
mt={formControlTopMargin}
>
<FormLabel
htmlFor="input-website"
>
Website
</FormLabel>
<Input
id="input-website"
name="website"
ref={register}
placeholder="http://"
type="url"
/>
</FormControl>
<FormControl
mt={formControlTopMargin}
>
<FormLabel
htmlFor="input-paymentmethods"
>
Payment Methods
</FormLabel>
<PaymentMethodsInput
fields={fields}
control={control}
register={register}
remove={remove}
append={append}
fieldArrayName={paymentMethodsFieldArrayName}
/>
</FormControl>
</ModalBody>
<Flex
id="modal-footer"
justify="space-between"
m={6}
>
<DeleteButton
piggybankName={initialPiggybankId}
/>
<Flex>
<Button
variant="ghost"
onClick={onClose}
>
Cancel
</Button>
<Button
id="save-configuration-btn"
colorScheme="green"
mx={1}
type="submit"
isLoading={isSubmitting}
loadingText="Saving"
isDisabled={
(
!isDirty
&& !isAccentColorDirty // controlled accentColor field is not showing up in formState.dirtyFields
)
|| !isPiggybankIdAvailable
|| !initialPiggybankId
}
onClick={() => setIsAddressTouched(true)}
>
Save
</Button>
</Flex>
</Flex>
</form>
</ModalContent>
</Modal>
);
}
Example #24
Source File: StudentQueue.tsx From office-hours with GNU General Public License v3.0 | 4 votes |
export default function StudentQueue({
qid,
cid,
}: StudentQueueProps): ReactElement {
const { queue } = useQueue(qid);
const { questions, mutateQuestions } = useQuestions(qid);
const { studentQuestion, studentQuestionIndex } = useStudentQuestion(qid);
const [isFirstQuestion, setIsFirstQuestion] = useLocalStorage(
"isFirstQuestion",
true
);
const [showJoinPopconfirm, setShowJoinPopconfirm] = useState(false);
const { deleteDraftQuestion } = useDraftQuestion();
const [isJoining, setIsJoining] = useState(
questions &&
studentQuestion &&
studentQuestion?.status !== OpenQuestionStatus.Queued
);
const [popupEditQuestion, setPopupEditQuestion] = useState(false);
const router = useRouter();
const editQuestionQueryParam = Boolean(router.query.edit_question as string);
useEffect(() => {
if (editQuestionQueryParam && studentQuestion) {
mutate(`/api/v1/queues/${qid}/questions`);
setPopupEditQuestion(true);
router.push(`/course/${cid}/queue/${qid}`);
}
}, [editQuestionQueryParam, qid, studentQuestion]);
const studentQuestionId = studentQuestion?.id;
const studentQuestionStatus = studentQuestion?.status;
const leaveQueue = useCallback(async () => {
await API.questions.update(studentQuestionId, {
status: ClosedQuestionStatus.ConfirmedDeleted,
});
setIsJoining(false);
await mutateQuestions();
}, [mutateQuestions, studentQuestionId]);
const rejoinQueue = useCallback(async () => {
await API.questions.update(studentQuestionId, {
status: OpenQuestionStatus.PriorityQueued,
});
await mutateQuestions();
}, [mutateQuestions, studentQuestionId]);
const finishQuestion = useCallback(
async (text: string, questionType: QuestionType, groupable: boolean) => {
const updateStudent = {
text,
questionType,
groupable,
status:
studentQuestionStatus === OpenQuestionStatus.Drafting
? OpenQuestionStatus.Queued
: studentQuestionStatus,
};
const updatedQuestionFromStudent = await API.questions.update(
studentQuestionId,
updateStudent
);
const newQuestionsInQueue = questions?.queue?.map((question: Question) =>
question.id === studentQuestionId
? updatedQuestionFromStudent
: question
);
// questions are the old questions and newQuestionsInQueue are questions that've been added since.
mutateQuestions({
...questions,
yourQuestion: updatedQuestionFromStudent,
queue: newQuestionsInQueue,
});
},
[studentQuestionStatus, studentQuestionId, questions, mutateQuestions]
);
const joinQueueAfterDeletion = useCallback(async () => {
await API.questions.update(studentQuestion?.id, {
status: ClosedQuestionStatus.ConfirmedDeleted,
});
await mutateQuestions();
const newQuestion = await API.questions.create({
text: studentQuestion.text,
questionType: studentQuestion?.questionType,
queueId: qid,
location: studentQuestion?.location,
force: true,
groupable: false,
});
await API.questions.update(newQuestion.id, {
status: OpenQuestionStatus.Queued,
});
await mutateQuestions();
}, [mutateQuestions, qid, studentQuestion]);
const openEditModal = useCallback(async () => {
mutate(`/api/v1/queues/${qid}/questions`);
setPopupEditQuestion(true);
}, [qid]);
const closeEditModal = useCallback(() => {
setPopupEditQuestion(false);
setIsJoining(false);
}, []);
const leaveQueueAndClose = useCallback(() => {
//delete draft when they leave the queue
deleteDraftQuestion();
leaveQueue();
closeEditModal();
}, [deleteDraftQuestion, leaveQueue, closeEditModal]);
const joinQueueOpenModal = useCallback(
async (force: boolean) => {
try {
const createdQuestion = await API.questions.create({
queueId: Number(qid),
text: "",
force: force,
questionType: null,
groupable: false,
});
const newQuestionsInQueue = [...questions?.queue, createdQuestion];
await mutateQuestions({ ...questions, queue: newQuestionsInQueue });
setPopupEditQuestion(true);
return true;
} catch (e) {
if (
e.response?.data?.message?.includes(
ERROR_MESSAGES.questionController.createQuestion.oneQuestionAtATime
)
) {
return false;
}
return true;
// TODO: how should we handle error that happens for another reason?
}
},
[mutateQuestions, qid, questions]
);
const finishQuestionAndClose = useCallback(
(
text: string,
qt: QuestionType,
groupable: true,
router: Router,
cid: number
) => {
deleteDraftQuestion();
finishQuestion(text, qt, groupable);
closeEditModal();
if (isFirstQuestion) {
notification.warn({
style: { cursor: "pointer" },
message: "Enable Notifications",
className: "hide-in-percy",
description:
"Turn on notifications for when it's almost your turn to get help.",
placement: "bottomRight",
duration: 0,
onClick: () => {
notification.destroy();
setIsFirstQuestion(false);
router.push(`/settings?cid=${cid}`);
},
});
}
},
[
deleteDraftQuestion,
finishQuestion,
closeEditModal,
isFirstQuestion,
setIsFirstQuestion,
]
);
useHotkeys(
"shift+e",
() => {
if (studentQuestion) {
openEditModal();
}
},
[studentQuestion]
);
useHotkeys(
"shift+n",
() => {
if (!studentQuestion && queue?.allowQuestions && !queue?.isDisabled) {
joinQueueOpenModal(false).then((res) => setShowJoinPopconfirm(!res));
}
},
[studentQuestion, queue]
);
if (queue && questions) {
if (!queue.isOpen) {
return <h1 style={{ marginTop: "50px" }}>The Queue is Closed!</h1>;
}
return (
<>
<Container>
<CantFindModal
visible={studentQuestion?.status === LimboQuestionStatus.CantFind}
leaveQueue={leaveQueue}
rejoinQueue={rejoinQueue}
/>
<StudentRemovedFromQueueModal
question={studentQuestion}
leaveQueue={leaveQueue}
joinQueue={joinQueueAfterDeletion}
/>
<QueueInfoColumn
queueId={qid}
isStaff={false}
buttons={
!studentQuestion && (
<Popconfirm
title={
<PopConfirmTitle>
You already have a question in a queue for this course, so
your previous question will be deleted in order to join
this queue. Do you want to continue?
</PopConfirmTitle>
}
onConfirm={() => joinQueueOpenModal(true)}
okText="Yes"
cancelText="No"
disabled
visible={showJoinPopconfirm}
onVisibleChange={setShowJoinPopconfirm}
>
<JoinButton
type="primary"
disabled={!queue?.allowQuestions || queue?.isDisabled}
data-cy="join-queue-button"
onClick={async () =>
setShowJoinPopconfirm(!(await joinQueueOpenModal(false)))
}
>
Join Queue
</JoinButton>
</Popconfirm>
)
}
/>
<VerticalDivider />
<QueueListContainer>
{studentQuestion && (
<>
<StudentBanner
queueId={qid}
editQuestion={openEditModal}
leaveQueue={leaveQueue}
/>
<div style={{ marginTop: "40px" }} />
</>
)}
<QueueQuestions
questions={questions?.queue}
studentQuestion={studentQuestion}
/>
</QueueListContainer>
</Container>
<QuestionForm
visible={
(questions && !studentQuestion && isJoining) ||
// && studentQuestion.status !== QuestionStatusKeys.Drafting)
popupEditQuestion
}
question={studentQuestion}
leaveQueue={leaveQueueAndClose}
finishQuestion={finishQuestionAndClose}
position={studentQuestionIndex + 1}
cancel={closeEditModal}
/>
</>
);
} else {
return <div />;
}
}
Example #25
Source File: index.tsx From gonear-name with The Unlicense | 4 votes |
Profile = () => {
useTopScroll()
const toMarket = useToMarket()
const [tab, setTab] = useState<number>(0)
const [disableRewards, setDisableRewards] = useState<boolean>(false)
const { near }: { near: INearProps | null } = useContext(NearContext)
let { accountId } = useParams<{ accountId: string | undefined }>();
const { data: profile } = useSWR(
['get_profile', near?.signedAccountId, accountId],
() => near?.api.get_profile(accountId || near?.signedAccountId)
)
const grabRewards = async () => {
if (!near || !profile) return null
setDisableRewards(true)
lowRewards = true
profile.profitTaken += profile.availableRewards
profile.availableRewards = 0
await mutate(['get_profile', near?.signedAccountId, accountId], profile, false)
await near.api.collect_rewards()
}
if (!near || !profile) return null
if (!accountId && near.signedAccountId) accountId = near.signedAccountId
if (!accountId) {
return <Redirect to="/" />
}
const { numAcquisitions, numBets, numClaims, numOffers, availableRewards, betsVolume, profitTaken, acquisitions, participation} = profile
let lowRewards = profile && profile.availableRewards < 0.1
return (
<Container>
<BackButton onClick={toMarket} />
<Main>
<Title>{ accountId }</Title>
<Bread>
<BreadItem>{numOffers} Offer{numOffers !== 1 && 's'}</BreadItem>
<BreadDot />
<BreadItem>{numBets} Bid{numBets !== 1 && 's'}</BreadItem>
<BreadDot />
<BreadItem>{numClaims} Claim{numClaims !== 1 && 's'}</BreadItem>
<BreadDot />
<BreadItem>{numAcquisitions} Aacquisition{numAcquisitions !== 1 && 's'}</BreadItem>
</Bread>
<Cards>
<Card type="bag">
<CardTitle>Bids volume:</CardTitle>
<CardValue>
<MoneyIcon />
<Value>{betsVolume.toFixed(2)}</Value>
</CardValue>
</Card>
<Card type="coin">
<CardTitle>Profit taken</CardTitle>
<CardValue>
<MoneyIcon />
<Value>{profitTaken.toFixed(2)}</Value>
</CardValue>
</Card>
<Card type="cup">
<CardTitle>Available rewards</CardTitle>
<CardValue>
<MoneyIcon />
<Value>{availableRewards.toFixed(2)}</Value>
</CardValue>
</Card>
</Cards>
{accountId === near.signedAccountId && (
<Collect>
<DetailsButton disabled={lowRewards || disableRewards} onClick={grabRewards}>
Collect Rewards
</DetailsButton>
{lowRewards && <LowRewards>
Accumulate at least<Value>0.1</Value> <InlineMoneyIcon /> to collect rewards
</LowRewards>}
</Collect>
)}
<TableHeaders>
<TableHeader active={tab === 0} onClick={() => setTab(0)}>
<TableTitle>Participating</TableTitle>
{participation.length > 0 ? <TableIndex>{participation.length}</TableIndex> : ''}
</TableHeader>
<TableHeader active={tab === 1} onClick={() => setTab(1)}>
<TableTitle>Successful claims</TableTitle>
{acquisitions.length > 0 ? <TableIndex>{acquisitions.length}</TableIndex> : ''}
</TableHeader>
</TableHeaders>
<ProfileTable list={tab === 0 ? participation : acquisitions} isAcquisition={tab === 1} />
</Main>
</Container>
)
}
Example #26
Source File: Api.ts From takeout-app with MIT License | 4 votes |
Api = {
useSession() {
return useSWR<GetSessionResponse, ApiError>("/api/session", swrFetcher, {
revalidateOnFocus: false,
});
},
useAppVersion() {
return useSWR<GetAppVersionResponse, ApiError>("/api/app_version", swrFetcher, {
revalidateOnFocus: true,
revalidateOnReconnect: true,
focusThrottleInterval: 60000,
refreshInterval: 90 * 1000, // TODO:
});
},
useConference() {
// TODO: Error handling
const swr = useSWR<GetConferenceResponse, ApiError>(API_CONFERENCE, swrFetcher, {
revalidateOnFocus: true,
revalidateOnReconnect: true,
//focusThrottleInterval: 15 * 1000, // TODO:
compare(knownData, newData) {
if (!knownData || !newData) return false;
try {
mergeConferenceData(newData, knownData);
} catch (e) {
console.warn(e);
throw e;
}
const res = dequal(newData, knownData);
return false; //res;
},
});
// Schedule candidate TrackCard activation
const { data } = swr;
useEffect(() => {
if (!data) return;
const earliestCandidateActivationAt = determineEarliestCandidateActivationAt(data);
if (!earliestCandidateActivationAt) return;
const timeout = (earliestCandidateActivationAt - dayjs().unix()) * 1000 + 500;
console.log(
`Scheduling candidate TrackCard activation; earliest will happen at ${dayjs(
new Date(earliestCandidateActivationAt * 1000),
).toISOString()}, in ${timeout / 1000}s`,
);
const timer = setTimeout(() => activateCandidateTrackCard(data), timeout);
return () => clearTimeout(timer);
}, [data]);
return swr;
},
// XXX: this is not an API
useTrackStreamOptions(): TrackStreamOptionsState {
const browserStateKey = "rk-takeout-app--TrackStreamOption";
let options: TrackStreamOptions = { interpretation: false, caption: false, chat: true };
const browserState = window.localStorage?.getItem(browserStateKey);
if (browserState) {
try {
options = JSON.parse(browserState);
} catch (e) {
console.warn(e);
}
if (!options.hasOwnProperty("chat")) {
options.chat = true;
}
} else {
const acceptJapanese = navigator.languages.findIndex((v) => /^ja($|-)/.test(v)) !== -1;
options.interpretation = !acceptJapanese;
}
const [state, setState] = useState(options);
return [
state,
(x: TrackStreamOptions) => {
try {
window.localStorage?.setItem(browserStateKey, JSON.stringify(x));
} catch (e) {
console.warn(e);
}
setState(x);
},
];
},
async createSession(email: string, reference: string): Promise<CreateSessionResponse> {
const resp = await request("/api/session", "POST", null, {
email,
reference,
});
mutate("/api/session");
return resp.json();
},
async updateAttendee(name: string, gravatar_email: string): Promise<UpdateAttendeeResponse> {
const resp = await request("/api/attendee", "PUT", null, {
name,
gravatar_email,
});
mutate("/api/session");
return resp.json();
},
useStream(slug: TrackSlug, interpretation: boolean) {
return useSWR<GetStreamResponse, ApiError>(
`/api/streams/${slug}?interpretation=${interpretation ? "1" : "0"}`,
swrFetcher,
{
revalidateOnFocus: true,
revalidateOnReconnect: true,
focusThrottleInterval: 60 * 15 * 1000,
compare(knownData, newData) {
// Accept new data only if expired
if (!knownData?.stream || !newData?.stream) return false;
const now = dayjs().unix() + 180;
return !(knownData.stream.expiry < newData.stream.expiry && knownData.stream.expiry <= now);
},
},
);
},
useChatSession(attendeeId: number | undefined) {
// attendeeId for cache buster
return useSWR<GetChatSessionResponse, ApiError>(
attendeeId ? `/api/chat_session?i=${attendeeId}&p=${CACHE_BUSTER}` : null,
swrFetcher,
{
revalidateOnFocus: true,
revalidateOnReconnect: true,
focusThrottleInterval: 60 * 40 * 1000,
refreshInterval: 60 * 80 * 1000,
refreshWhenHidden: false,
refreshWhenOffline: false,
},
);
},
useChatMessagePin(track: TrackSlug | undefined) {
return useSWR<GetChatMessagePinResponse, ApiError>(
track ? `/api/tracks/${encodeURIComponent(track)}/chat_message_pin` : null,
swrFetcher,
{
revalidateOnFocus: true,
revalidateOnReconnect: true,
focusThrottleInterval: 60 * 1000,
},
);
},
async sendChatMessage(track: TrackSlug, message: string, asAdmin?: boolean) {
const resp = await request(`/api/tracks/${encodeURIComponent(track)}/chat_messages`, "POST", null, {
message,
as_admin: !!asAdmin,
});
return resp.json();
},
async pinChatMessage(track: TrackSlug, chatMessage: ChatMessage | null) {
const resp = await request(`/api/tracks/${encodeURIComponent(track)}/chat_admin_message_pin`, "PUT", null, {
chat_message: chatMessage,
});
return resp.json();
},
async createCaptionChatMembership(track: TrackSlug) {
const resp = await request(`/api/tracks/${encodeURIComponent(track)}/caption_chat_membership`, "POST", null, {});
return resp.json();
},
async deleteCaptionChatMembership(track: TrackSlug) {
const resp = await request(`/api/tracks/${encodeURIComponent(track)}/caption_chat_membership`, "DELETE", null, {});
return resp.json();
},
useConferenceSponsorships() {
return useSWR<GetConferenceSponsorshipsResponse, ApiError>(
`/api/conference_sponsorships?p=${CACHE_BUSTER}`,
swrFetcher,
);
},
}
Example #27
Source File: AdvancedSettingsTab.tsx From staticshield with MIT License | 4 votes |
AdvancedSettingsTab: React.FC<{ siteData: HarperDBRecord }> = ({
siteData,
}) => {
const { setVisible, bindings } = useModal();
const [disableDeleteButton, setDisableDeleteButton] = useState(true);
const [siteNameDeletingInput, setSiteNameDeletingInput] = useState('');
const [isBlocked, setIsBlocked] = useState(siteData.is_login_blocked);
const [_, setToast] = useToasts();
const router = useRouter();
useEffect(() => {
if (siteNameDeletingInput === siteData.site_name) {
setDisableDeleteButton(false);
} else {
setDisableDeleteButton(true);
}
}, [siteData.site_name, siteNameDeletingInput]);
return (
<div>
<Card className='!mt-10'>
<Text className='text-xl font-bold'>Block logins</Text>
<Text p>
Block logins temporarily. Existing logged in users will lose access to
website once after the <Code>Login expiration time </Code> finishes
</Text>
<Text>
The <Code>login expiration time </Code> can be changed in{' '}
<Code> General Settings Tab </Code>
</Text>
<span>
Allow logins
<Toggle
size='large'
className='mx-3'
initialChecked={siteData?.is_login_blocked}
onChange={(e) => {
setIsBlocked(e.target.checked);
}}
/>
Block logins
</span>
<Card.Footer className='!bg-warmgray-100'>
<div className='flex items-center justify-between w-full'>
<div>
<Text>Please use 48 characters at maximum</Text>
</div>
<div>
<Button
size='small'
type='secondary'
auto
onClick={async () => {
console.log(isBlocked);
const res = await blockLogins(isBlocked, siteData?.id);
if (isBlocked && res.success) {
setToast({
text: 'Logins blocked successfully',
type: 'warning',
});
} else if (!isBlocked && res.success) {
setToast({
text: 'Logins enabled successfully',
type: 'success',
});
} else {
setToast({
text: 'An unexpected error occured',
type: 'error',
});
}
mutate(
'/api/get-site-from-site-id/?siteId=' + siteData?.id,
{ ...siteData, is_login_blocked: isBlocked },
false
);
}}>
Save
</Button>
</div>
</div>
</Card.Footer>
</Card>
<Card className='!mt-10 !border !border-red-500'>
<Text className='text-xl font-bold'>Delete site</Text>
<Text p>
<span className='text-red-500'>Permanently remove</span> this website
and all of its contents from the StaticShield. This action is not
reversible, so please continue with caution.
</Text>
<Text>
Also do not forget to remove StaticShield's code from your
website.
</Text>
<Card.Footer className='!bg-warmgray-100'>
<div className='flex items-center justify-between w-full'>
<div>
<Text>This action is irreversible</Text>
</div>
<div>
<Button
size='small'
type='error'
auto
onClick={() => setVisible(true)}>
Delete permanantely
</Button>
</div>
</div>
</Card.Footer>
</Card>
<Modal {...bindings}>
<Modal.Title>Delete site</Modal.Title>
<Modal.Subtitle>
This deletion process cannot be reversed
</Modal.Subtitle>
<Modal.Content>
Enter <Code>{siteData.site_name}</Code> to continue
<Input
width='100%'
className='my-3 mt-5'
label='Name of site →'
onChange={(e) => setSiteNameDeletingInput(e.target.value)}
onPaste={(e) => e.preventDefault()}
/>
</Modal.Content>
<Modal.Action passive onClick={() => setVisible(false)}>
Cancel
</Modal.Action>
<Modal.Action
type='error'
onClick={() => {
deleteSite(siteData.id)
.then(() => {
router.replace('/dashboard/?mutate=1');
setToast({
text: `Successfully deleted ${siteData?.site_name}`,
type: 'success',
});
})
.catch(() =>
setToast({ text: 'An unexpected error occured', type: 'error' })
);
}}
disabled={disableDeleteButton}>
Delete
</Modal.Action>
</Modal>
</div>
);
}
Example #28
Source File: validateAndUpdateSiteData.ts From staticshield with MIT License | 4 votes |
export default async function validateAndUpdateSiteData(
data: GeneralSiteSettingsFormValues,
field:
| 'site_name'
| 'site_desc'
| 'password'
| 'expiration_days'
| 'cap'
| 'title'
| 'logo_url',
siteId: string,
previousData: GeneralSiteSettingsFormValues
): Promise<{ success: boolean }> {
if (!schema.safeParse(data).success) {
return {
success: false,
};
}
// -------------------------------------------------------------
console.log(data);
if (field === 'site_name') {
try {
await axios.post('/api/site/update-name', {
siteName: data.site_name,
siteId: siteId,
});
} catch (_) {
return {
success: false,
};
}
mutate(
'/api/get-site-from-site-id/?siteId=' + siteId,
{ ...previousData, site_name: data.site_name },
false
);
return {
success: true,
};
}
// -------------------------------------------------------------
else if (field === 'site_desc') {
try {
await axios.post('/api/site/update-site-desc', {
siteDesc: data.site_desc,
siteId: siteId,
});
} catch (_) {
return {
success: false,
};
}
mutate(
'/api/get-site-from-site-id/?siteId=' + siteId,
{ ...previousData, site_desc: data.site_desc },
false
);
return {
success: true,
};
}
// -------------------------------------------------------------
else if (field === 'expiration_days') {
try {
await axios.post('/api/site/update-max-login-duration', {
max_login_duration: data.expiration_days,
siteId: siteId,
});
} catch (_) {
return {
success: false,
};
}
mutate(
'/api/get-site-from-site-id/?siteId=' + siteId,
{ ...previousData, expiration_days: data.expiration_days },
false
);
return {
success: true,
};
}
// -------------------------------------------------------------
else if (field === 'password') {
if (data.password === 'A-str0ng-p@55w0rd') {
return {
success: false,
};
}
try {
await axios.post('/api/site/update-site-password', {
password: data.password,
siteId: siteId,
});
} catch (_) {
return {
success: false,
};
}
mutate(
'/api/get-site-from-site-id/?siteId=' + siteId,
{ ...previousData, password: data.password },
false
);
return {
success: true,
};
}
// -------------------------------------------------------------
else if (field === 'logo_url') {
try {
await axios.post('/api/site/update-logo-url', {
logoUrl: data.logo_url,
siteId: siteId,
});
} catch (_) {
return {
success: false,
};
}
mutate(
'/api/get-site-from-site-id/?siteId=' + siteId,
{ ...previousData, logo_url: data.logo_url },
false
);
return {
success: true,
};
}
// -------------------------------------------------------------
else if (field === 'cap') {
try {
await axios.post('/api/site/update-caption', {
cap: data.cap,
siteId: siteId,
});
} catch (_) {
return {
success: false,
};
}
mutate(
'/api/get-site-from-site-id/?siteId=' + siteId,
{ ...previousData, cap: data.cap },
false
);
return {
success: true,
};
} else if (field === 'title') {
try {
await axios.post('/api/site/update-title', {
title: data.title,
siteId: siteId,
});
} catch (_) {
return {
success: false,
};
}
mutate(
'/api/get-site-from-site-id/?siteId=' + siteId,
{ ...previousData, title: data.title },
false
);
return {
success: true,
};
}
// -------------------------------------------------------------
}
Example #29
Source File: useEtherSWR.ts From ether-swr with MIT License | 4 votes |
function useEtherSWR<Data = any, Error = any>(
...args: any[]
): SWRResponse<Data, Error> {
let _key: ethKeyInterface
let fn: any //fetcherFn<Data> | undefined
let config: EthSWRConfigInterface<Data, Error> = { subscribe: [] }
let isMulticall = false
if (args.length >= 1) {
_key = args[0]
isMulticall = Array.isArray(_key[0])
}
if (args.length > 2) {
fn = args[1]
//FIXME we lost default value subscriber = []
config = args[2]
} else {
if (typeof args[1] === 'function') {
fn = args[1]
} else if (typeof args[1] === 'object') {
config = args[1]
}
}
config = Object.assign({}, useContext(EthSWRConfigContext), config)
if (fn === undefined) {
fn = config.fetcher || etherJsFetcher(config.web3Provider, config.ABIs)
}
// TODO LS implement a getTarget and change subscribe interface {subscribe: {name: "Transfer", target: 0x01}}
const [target] = isMulticall
? [_key[0][0]] // pick the first element of the list.
: _key
const { cache } = useSWRConfig()
// we need to serialize the key as string otherwise
// a new array is created everytime the component is rendered
// we follow SWR format
const normalizeKey = isMulticall ? JSON.stringify(_key) : _key
// base methods (e.g. getBalance, getBlockNumber, etc)
useEffect(() => {
if (!config.web3Provider || !config.subscribe || Array.isArray(target)) {
// console.log('skip')
return () => ({})
}
// console.log('effect!')
const contract = buildContract(target, config)
const subscribers = Array.isArray(config.subscribe)
? config.subscribe
: [config.subscribe]
const instance: Contract | Provider = contract || config.web3Provider
subscribers.forEach(subscribe => {
let filter
const internalKey = unstable_serialize(normalizeKey)
if (typeof subscribe === 'string') {
filter = subscribe
instance.on(filter, () => {
// console.log('on(string):', { filter }, Array.from(cache.keys()))
mutate(internalKey, undefined, true)
})
} else if (typeof subscribe === 'object' && !Array.isArray(subscribe)) {
const { name, topics, on } = subscribe
const args = topics || []
filter = contract ? contract.filters[name](...args) : name
// console.log('subscribe:', filter)
instance.on(filter, (...args) => {
if (on) {
// console.log('on(object):', { filter }, Array.from(cache.keys()))
on(cache.get(internalKey), ...args)
} else {
// auto refresh
// console.log('auto(refresh):', { filter }, Array.from(cache.keys()))
mutate(internalKey, undefined, true)
}
})
}
})
return () => {
subscribers.forEach(filter => {
instance.removeAllListeners(filter)
})
}
}, [unstable_serialize(normalizeKey), target])
return useSWR(normalizeKey, fn, config)
}