-
Notifications
You must be signed in to change notification settings - Fork 0
/
runtimeUtils.ts
150 lines (129 loc) · 4.65 KB
/
runtimeUtils.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
import { Gas, NEAR } from 'near-units'
import { atcb_action as addToCalendar } from 'add-to-calendar-button'
import settings from '../../config/settings.json'
import { signIn } from '../../src/near'
import { TenK } from '../../src/near/contracts'
import { TenkData } from "../../src/hooks/useTenk"
import { saleStatuses, userStatuses } from './Locale'
import { Locale } from '../../src/hooks/useLocales'
type Timestamp = number
type Data = TenkData & {
currentUser: string
locale: Locale
numberToMint?: number
saleStatus: typeof saleStatuses[number]
userStatus: typeof userStatuses[number]
}
function formatNumber(
num: number | string,
/**
* `undefined` will default to browser's locale (may not work correctly in Node during build)
*/
locale?: string,
) {
return new Intl.NumberFormat(locale, {
maximumSignificantDigits: 3,
}).format(Number(num))
}
function formatCurrency(
num: number | string,
currency: string = 'NEAR',
/**
* `undefined` will default to browser's locale (may not work correctly in Node during build)
*/
locale?: string,
) {
return `${formatNumber(num, locale)} ${currency}`
}
function formatDate(
d: Timestamp | Date,
/**
* `undefined` will default to browser's locale (may not work correctly in Node during build)
*/
locale?: string,
options: Intl.DateTimeFormatOptions = {}
): string {
const date = typeof d === "number" ? new Date(d) : d
return new Intl.DateTimeFormat(locale, {
dateStyle: 'short',
timeStyle: 'short',
...options,
}).format(date)
}
const replacers = {
CURRENT_USER: (d: Data) => d.currentUser,
PRESALE_START: (d: Data) => formatDate(d.saleInfo.presale_start),
SALE_START: (d: Data) => formatDate(d.saleInfo.sale_start),
MINT_LIMIT: (d: Data) => d.remainingAllowance ?? 0,
MINT_PRICE: (d: Data) => formatCurrency(
NEAR.from(d.saleInfo.price).mul(NEAR.from('' + (d.numberToMint ?? 1))).toHuman().split(' ')[0]
),
MINT_RATE_LIMIT: (d: Data) => d.mintRateLimit,
INITIAL_COUNT: (d: Data) => formatNumber(d.saleInfo.token_final_supply),
REMAINING_COUNT: (d: Data) => formatNumber(d.tokensLeft),
} as const
export const placeholderStrings = Object.keys(replacers)
export type PlaceholderString = keyof typeof replacers
const placeholderRegex = new RegExp(`(${placeholderStrings.join('|')})`, 'gm')
export function fill(text: string, data: Data): string {
return text.replace(placeholderRegex, (match) => {
return String(replacers[match as PlaceholderString](data))
})
}
// add-to-calendar-button has strange strict requirements on time format
function formatDatesForAtcb(d: Timestamp) {
let [start, end] = new Date(d).toISOString().split('T')
return [
start,
end.replace(/:\d\d\..*$/, '') // strip seconds, ms, & TZ
]
}
// add-to-calendar-button doesn't allow passing simple ISO strings for start/end
function getStartAndEnd(d: Timestamp) {
const [startDate, startTime] = formatDatesForAtcb(d)
const [endDate, endTime] = formatDatesForAtcb(d + 3600000)
return { startDate, startTime, endDate, endTime }
}
const actions = {
'ADD_TO_CALENDAR(SALE_START)': (d: Data) => addToCalendar({
name: d.locale.calendarEvent!,
...getStartAndEnd(d.saleInfo.sale_start),
options: ['Google', 'iCal', 'Apple', 'Microsoft365', 'MicrosoftTeams', 'Outlook.com', 'Yahoo'],
timeZone: "UTC",
trigger: 'click',
}),
'ADD_TO_CALENDAR(PRESALE_START)': (d: Data) => addToCalendar({
name: d.locale.calendarEvent!,
...getStartAndEnd(d.saleInfo.presale_start),
options: ['Google', 'iCal', 'Apple', 'Microsoft365', 'MicrosoftTeams', 'Outlook.com', 'Yahoo'],
timeZone: "UTC",
trigger: 'click',
}),
'SIGN_IN': signIn,
'MINT': (d: Data) => TenK.nft_mint_many({ num: d.numberToMint ?? 1 }, {
gas: Gas.parse('40 Tgas').mul(Gas.from('' + d.numberToMint)),
attachedDeposit: NEAR.from(d.saleInfo.price).mul(NEAR.from('' + d.numberToMint)),
}),
'GO_TO_PARAS': () => window.open(`https://paras.id/search?q=${settings.contractName}&sort=priceasc&pmin=.01&is_verified=true`),
}
export type Action = keyof typeof actions
export function act(action: Action, data: Data): void {
actions[action](data)
}
export function can(action: Action, data: Data): boolean {
if (action === 'MINT') {
return Boolean(data.currentUser) && (
(data.saleStatus === 'presale' &&
data.remainingAllowance !== undefined &&
data.remainingAllowance > 0
) ||
(data.saleStatus === 'saleOpen' && (
// users are added to the whitelist as they mint during saleOpen;
// undefined means they haven't minted yet
data.remainingAllowance === undefined ||
data.remainingAllowance > 0
))
)
}
return true
}