@@ -1,5 +1,5 @@
1import { UnspawnedPoints } from '@urbit/roller-api';
1import { UnspawnedPoints } from '@urbit/roller-api';
2−import { DEFAULT_CSV_NAME, POINT_DOMINIONS } from 'lib/constants';
2+import { POINT_DOMINIONS } from 'lib/constants';
3import { deriveNetworkSeedFromUrbitWallet } from 'lib/keys';
3import { deriveNetworkSeedFromUrbitWallet } from 'lib/keys';
4import * as need from 'lib/need';
4import * as need from 'lib/need';
5import * as azimuth from 'azimuth-js';
5import * as azimuth from 'azimuth-js';
@@ -25,7 +25,7 @@ import {
25import { useWallet } from 'store/wallet';
25import { useWallet } from 'store/wallet';
26import { usePointCache } from 'store/pointCache';
26import { usePointCache } from 'store/pointCache';
27import create from 'zustand';
27import create from 'zustand';
28−import { generateCsvLine, generateCsvName } from 'lib/utils/invite';
28+import { generateCsvLine } from 'lib/utils/invite';
30const inviteTemplate = (
30const inviteTemplate = (
31 planet: number,
31 planet: number,
@@ -45,20 +45,24 @@ export type InviteGeneratingStatus =
45 | 'finished'
45 | 'finished'
46 | 'errored';
46 | 'errored';
49+ generatingNum: number;
50+ status: InviteGeneratingStatus;
48interface InviteStore {
53interface InviteStore {
49 invites: Invites;
54 invites: Invites;
50− generatingStatus: InviteGeneratingStatus;
55+ inviteJobs: {
51− generatingNum: number;
56+ [point: number]: InviteJob;
52− loading: boolean;
57+ };
53 addInvite: (point: number, invite: Invite) => void;
58 addInvite: (point: number, invite: Invite) => void;
54 setInvites: (points: number, invites: Invite[]) => void;
59 setInvites: (points: number, invites: Invite[]) => void;
60+ updateJob: (point: number, updates: Partial<InviteJob>) => void;
57export const useInviteStore = create<InviteStore>((set, get) => ({
63export const useInviteStore = create<InviteStore>((set, get) => ({
58 invites: {},
64 invites: {},
59− generatingStatus: 'initial',
65+ inviteJobs: {},
62 addInvite: (point: number, invite: Invite) => {
66 addInvite: (point: number, invite: Invite) => {
63 set(state => {
67 set(state => {
64 const newInvites: Invites = {};
68 const newInvites: Invites = {};
@@ -76,6 +80,17 @@ export const useInviteStore = create<InviteStore>((set, get) => ({
76 newInvites[point] = invites;
80 newInvites[point] = invites;
77 return { invites: Object.assign(state.invites, newInvites) };
81 return { invites: Object.assign(state.invites, newInvites) };
83+ updateJob: (point: number, updates: Partial<InviteJob>) => {
85+ const jobs = get().inviteJobs;
86+ const job = jobs[point] || { status: 'initial', generatingNum: 0 };
87+ const newJob = Object.assign({}, job, updates);
88+ jobs[point] = newJob;
90+ //console.log({ point, oldJob: job, updates, newJob });
92+ set({ inviteJobs: jobs });
81export function useInvites() {
96export function useInvites() {
@@ -87,8 +102,13 @@ export function useInvites() {
87 const getDetails = allPoints?.getDetails;
102 const getDetails = allPoints?.getDetails;
88 const { api, ls } = useRoller();
103 const { api, ls } = useRoller();
89 const { point, pointList, setPendingTransactions } = useRollerStore();
104 const { point, pointList, setPendingTransactions } = useRollerStore();
90− const { addInvite, setInvites } = useInviteStore();
111+ } = useInviteStore();
91− const updatingInvites = useRef<number[]>([]);
93 const generateInviteInfo = async (planet: number, _authToken: string) => {
113 const generateInviteInfo = async (planet: number, _authToken: string) => {
94 const { ticket, inviteWallet } = await generateInviteWallet(
114 const { ticket, inviteWallet } = await generateInviteWallet(
@@ -100,13 +120,14 @@ export function useInvites() {
102 const getInvites = useCallback(async () => {
122 const getInvites = useCallback(async () => {
103− if (updatingInvites.current.includes(point.value)) {
123+ const inviteJob = inviteJobs[point.value];
124+ if (inviteJob?.status === 'generating') {
107− updatingInvites.current.push(point.value);
109− useInviteStore.setState({ loading: true });
129+ updateJob(point.value, { status: 'generating' });
110 const curPoint: number = Number(need.point(pointCursor));
131 const curPoint: number = Number(need.point(pointCursor));
111 const _authToken = authToken.getOrElse(null);
132 const _authToken = authToken.getOrElse(null);
112 const _contracts = contracts.getOrElse(null);
133 const _contracts = contracts.getOrElse(null);
@@ -145,7 +166,7 @@ export function useInvites() {
145 const storedInvites = getStoredInvites(ls);
166 const storedInvites = getStoredInvites(ls);
147 for (let i = 0; i < invitePlanets.length; i++) {
168 for (let i = 0; i < invitePlanets.length; i++) {
148− useInviteStore.setState({ generatingNum: i + 1 });
169+ updateJob(point.value, { generatingNum: i + 1 });
149 const planet = invitePlanets[i];
170 const planet = invitePlanets[i];
150 const storedInvite = storedInvites[planet];
171 const storedInvite = storedInvites[planet];
151 const invite =
172 const invite =
@@ -169,16 +190,13 @@ export function useInvites() {
172− useInviteStore.setState({ loading: false, generatingNum: 0 });
193+ updateJob(point.value, { status: 'finished', generatingNum: 0 });
173 return newInvites;
194 return newInvites;
175 } catch (error) {
196 } catch (error) {
176 console.warn('ERROR GETTING INVITES', error);
197 console.warn('ERROR GETTING INVITES', error);
177− useInviteStore.setState({ loading: false, generatingNum: 0 });
198+ updateJob(point.value, { status: 'errored', generatingNum: 0 });
180− const pointIndex = updatingInvites.current.indexOf(point.value);
181− updatingInvites.current.splice(pointIndex, 1);
184 authToken,
202 authToken,
@@ -190,6 +208,7 @@ export function useInvites() {
190 addInvite,
208 addInvite,
191 setInvites,
209 setInvites,
192 setPendingTransactions,
210 setPendingTransactions,
195 const generateInviteCodes = useCallback(
214 const generateInviteCodes = useCallback(
@@ -233,7 +252,7 @@ export function useInvites() {
235 for (let i = 0; i < numInvites && planets[i]; i++) {
254 for (let i = 0; i < numInvites && planets[i]; i++) {
236− useInviteStore.setState({ generatingNum: i + 1 });
255+ updateJob(point.value, { generatingNum: i + 1 });
237 const planet = planets[i];
256 const planet = planets[i];
238 const nonceInc = i + nonce;
257 const nonceInc = i + nonce;
@@ -327,38 +346,23 @@ export function useInvites() {
327 getDetails,
346 getDetails,
329 getInvites,
348 getInvites,
333− const generateCsv = useCallback(async () => {
353+ const generateCsv = useCallback(() => {
334− const invites = await getInvites();
354+ const pointInvites = invites[point.value];
336− if (!invites) {
356+ if (!pointInvites) {
337 throw new Error('There was an error creating the CSV');
357 throw new Error('There was an error creating the CSV');
340− const csv = invites.reduce(
360+ return pointInvites.reduce(
341 (csvData, { ticket, planet }, ind) =>
361 (csvData, { ticket, planet }, ind) =>
342 (csvData += generateCsvLine(ind, ticket, planet)),
362 (csvData += generateCsvLine(ind, ticket, planet)),
343 'Number,Planet,Invite URL,Point,Ticket\n'
363 'Number,Planet,Invite URL,Point,Ticket\n'
345−
365+ }, [invites, point]);
346− document.getElementById('csv')?.remove();
347− const hiddenElement = document.createElement('a');
348− hiddenElement.href = `data:text/csv;charset=utf-8,${encodeURIComponent(
351− hiddenElement.target = '_blank';
352− hiddenElement.id = 'csv';
354− //provide the name for the CSV file to be downloaded
355− hiddenElement.download = generateCsvName(
357− point.patp?.slice(1) || 'bridge'
360− return hiddenElement;
361− }, [getInvites, point]);
364 getInvites,
368 getInvites,