Skip to content

Commit 47632da

Browse files
OETH/OUSD Geofence modal support (#1717)
* Add in support for geofence check in oeth * Add ousd * Navigate to ousd.com for ousd dapp * Fix ousd build * Use lato for font * Update regulatory copy * Wrap * Clean up regulatory copy * Revise regulatory copy --------- Co-authored-by: Micah Alcorn <[email protected]>
1 parent 83d9365 commit 47632da

File tree

7 files changed

+906
-28
lines changed

7 files changed

+906
-28
lines changed

dapp-oeth/pages/_app.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import React, { useEffect, useState } from 'react'
2+
import dynamic from 'next/dynamic'
23
import { useRouter } from 'next/router'
34
import Head from 'next/head'
45
import { useCookies } from 'react-cookie'
@@ -79,6 +80,10 @@ const client = createClient({
7980
connectors,
8081
})
8182

83+
const GeoFenceCheck = dynamic(() => import('components/GeoFenceCheck'), {
84+
ssr: false,
85+
})
86+
8287
function App({ Component, pageProps, err }) {
8388
const { address: account, isConnected: active } = useAccount()
8489
const [locale, setLocale] = useState('en_US')
@@ -125,6 +130,7 @@ function App({ Component, pageProps, err }) {
125130
<link rel="canonical" href={canonicalUrl} />
126131
</Head>
127132
<QueryClientProvider client={queryClient}>
133+
<GeoFenceCheck />
128134
<AccountListener />
129135
<TransactionListener />
130136
<UserActivityListener />
Lines changed: 241 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,241 @@
1+
import { useState } from 'react'
2+
import { Modal } from 'react-bootstrap'
3+
import useLocalStorage from 'hooks/useLocalStorage'
4+
5+
const GeoFenceCheck = () => {
6+
const { data: hasConfirmedGeoLocation, onSetItem } = useLocalStorage(
7+
'@originprotocol/oeth-geo-check',
8+
false
9+
)
10+
11+
const [isChecked, setIsChecked] = useState(false)
12+
13+
const onAckGeoFence = () => {
14+
onSetItem(true)
15+
}
16+
17+
return (
18+
<>
19+
<Modal
20+
show={!hasConfirmedGeoLocation}
21+
size="lg"
22+
aria-labelledby="geofence-modal"
23+
centered
24+
>
25+
<div className="d-flex flex-column geofence-modal">
26+
<header className="header">
27+
<h1 className="title">Restricted Access</h1>
28+
</header>
29+
<div className="body">
30+
<p className="info">
31+
The Origin Ether dapp is not available to restricted
32+
jurisdictions. Before proceeding, please carefully read the
33+
following:
34+
</p>
35+
<div className="accept-criteria">
36+
<ul className="list">
37+
<li className="item">
38+
You confirm that you are not a resident of, citizen of,
39+
located in, incorporated in, or have a registered office in
40+
the United States or any country or region currently currently
41+
subject to sanctions by the United States.
42+
</li>
43+
<li className="item">
44+
You affirm that you are not a subject of economic or trade
45+
sanctions administered or enforced by any governmental
46+
authority or otherwise designated on any list of prohibited or
47+
restricted parties, including the list maintained by the
48+
Office of Foreign Assets Control of the U.S. Department of the
49+
Treasury.
50+
</li>
51+
<li className="item">
52+
You agree not to use any VPN or other privacy or anonymization
53+
tools or techniques to attempt to circumvent these eligibility
54+
restrictions.
55+
</li>
56+
<li className="item">
57+
You are lawfully permitted to access this site. You understand
58+
and accept the risks associated with using Origin Ether.
59+
</li>
60+
</ul>
61+
</div>
62+
<div className="ack">
63+
<label className="ack-label">
64+
<div className="ack-container">
65+
<input
66+
className="ack-checkbox"
67+
type="checkbox"
68+
checked={isChecked}
69+
onChange={(e) => {
70+
setIsChecked(e.target.checked)
71+
}}
72+
/>
73+
</div>
74+
75+
<span className="label-text">
76+
I have read and agree to the above terms{' '}
77+
</span>
78+
</label>
79+
</div>
80+
</div>
81+
<footer className="footer">
82+
<a className="footer-action" href="https://oeth.com">
83+
Exit
84+
</a>
85+
<button
86+
className="footer-action"
87+
onClick={onAckGeoFence}
88+
disabled={!isChecked}
89+
>
90+
I agree
91+
</button>
92+
</footer>
93+
</div>
94+
</Modal>
95+
<style jsx>{`
96+
.geofence-modal {
97+
background-color: #1e1f25;
98+
color: #fafbfb;
99+
border-radius: 8px;
100+
max-width: 469px;
101+
width: 100%;
102+
margin: 0 auto;
103+
}
104+
105+
.geofence-modal .header {
106+
display: flex;
107+
align-items: center;
108+
height: 72px;
109+
border-bottom: 1px solid #101113;
110+
padding: 0 24px;
111+
}
112+
113+
.geofence-modal .footer {
114+
display: flex;
115+
align-items: center;
116+
justify-content: center;
117+
padding: 0 24px;
118+
margin-bottom: 24px;
119+
}
120+
121+
.geofence-modal .footer .footer-action {
122+
display: flex;
123+
align-items: center;
124+
justify-content: center;
125+
cursor: pointer;
126+
padding: 8px 16px;
127+
border-radius: 56px;
128+
background-image: linear-gradient(
129+
90deg,
130+
#8c66fc -28.99%,
131+
#0274f1 144.97%
132+
);
133+
border: none;
134+
color: #fafbfb;
135+
width: 100%;
136+
}
137+
138+
.geofence-modal .footer .footer-action[disabled] {
139+
opacity: 0.5;
140+
cursor: not-allowed;
141+
}
142+
143+
.geofence-modal .footer .footer-action + .footer-action {
144+
margin-left: 12px;
145+
}
146+
147+
.geofence-modal .title {
148+
color: #fafbfb;
149+
font-family: Inter;
150+
font-size: 16px;
151+
font-weight: 700;
152+
line-height: 28px;
153+
letter-spacing: 0em;
154+
text-align: left;
155+
margin: 0;
156+
}
157+
158+
.geofence-modal .body {
159+
padding: 24px 24px 0 24px;
160+
text-align: left;
161+
}
162+
163+
.geofence-modal .info {
164+
font-family: Inter;
165+
font-size: 14px;
166+
font-weight: 500;
167+
line-height: 23px;
168+
letter-spacing: 0em;
169+
text-align: left;
170+
}
171+
172+
.geofence-modal .info.sub {
173+
font-family: Inter;
174+
font-size: 12px;
175+
font-weight: 400;
176+
line-height: 20px;
177+
letter-spacing: 0em;
178+
text-align: left;
179+
}
180+
181+
.geofence-modal .accept-criteria {
182+
padding: 12px 24px 12px 24px;
183+
border-radius: 4px;
184+
background-color: #51546633;
185+
font-family: Inter;
186+
font-size: 12px;
187+
font-weight: 400;
188+
line-height: 20px;
189+
letter-spacing: 0em;
190+
text-align: left;
191+
}
192+
193+
.geofence-modal .accept-criteria .list {
194+
padding: 0 0 0 24px;
195+
margin: 0;
196+
}
197+
198+
.geofence-modal .accept-criteria .list .item + .item {
199+
margin-top: 18px;
200+
}
201+
202+
.geofence-modal .ack {
203+
display: inline-flex;
204+
align-items: center;
205+
margin: 18px 0;
206+
}
207+
208+
.geofence-modal .ack .ack-label {
209+
display: inline-flex;
210+
align-items: center;
211+
font-family: Inter;
212+
font-size: 12px;
213+
font-weight: 400;
214+
line-height: 20px;
215+
letter-spacing: 0em;
216+
text-align: left;
217+
}
218+
219+
.geofence-modal .ack .label-text {
220+
margin-left: 16px;
221+
}
222+
223+
.geofence-modal .ack .ack-container {
224+
width: 24px;
225+
height: 24px;
226+
border-radius: 4px;
227+
overflow: hidden;
228+
border: 1px solid #000000;
229+
background-color: #fafbfb;
230+
}
231+
232+
.geofence-modal .ack .ack-checkbox {
233+
width: 100%;
234+
height: 100%;
235+
}
236+
`}</style>
237+
</>
238+
)
239+
}
240+
241+
export default GeoFenceCheck
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
import { useCallback, useEffect, useState } from 'react'
2+
3+
const LOCAL_STORAGE_CHANGE_EVENT_NAME = 'onLocalStorageChange'
4+
5+
const isTypeOfLocalStorageChanged = (evt) => {
6+
return !!evt && evt.type === LOCAL_STORAGE_CHANGE_EVENT_NAME
7+
}
8+
9+
const isBrowser = () => {
10+
return typeof window !== 'undefined' && typeof window.document !== 'undefined'
11+
}
12+
13+
const writeStorage = (key, value) => {
14+
if (!isBrowser()) {
15+
return
16+
}
17+
18+
try {
19+
window.localStorage.setItem(
20+
key,
21+
typeof value === 'object' ? JSON.stringify(value) : `${value}`
22+
)
23+
window.dispatchEvent(
24+
new CustomEvent(LOCAL_STORAGE_CHANGE_EVENT_NAME, {
25+
detail: { key, value },
26+
})
27+
)
28+
} catch (err) {
29+
if (
30+
err instanceof TypeError &&
31+
err.message.includes('circular structure')
32+
) {
33+
throw new TypeError(
34+
'The object that was given to the writeStorage function has circular references.\n' +
35+
'For more information, check here: ' +
36+
'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Cyclic_object_value'
37+
)
38+
}
39+
throw err
40+
}
41+
}
42+
43+
const deleteFromStorage = (key) => {
44+
if (!isBrowser()) {
45+
return
46+
}
47+
window.localStorage.removeItem(key)
48+
window.dispatchEvent(
49+
new CustomEvent(LOCAL_STORAGE_CHANGE_EVENT_NAME, {
50+
detail: { key, value: null },
51+
})
52+
)
53+
}
54+
55+
const tryParse = (value) => {
56+
try {
57+
return JSON.parse(value)
58+
} catch {
59+
return value
60+
}
61+
}
62+
63+
const useLocalStorage = (key, defaultValue) => {
64+
const [localState, updateLocalState] = useState(
65+
window.localStorage.getItem(key) === null
66+
? defaultValue
67+
: tryParse(window.localStorage.getItem(key))
68+
)
69+
70+
const onLocalStorageChange = useCallback(
71+
(event) => {
72+
if (isTypeOfLocalStorageChanged(event)) {
73+
if (event.detail.key === key) {
74+
updateLocalState(event.detail.value)
75+
}
76+
} else {
77+
if (event.key === key) {
78+
updateLocalState(
79+
event.newValue === null ? null : tryParse(event.newValue)
80+
)
81+
}
82+
}
83+
},
84+
[updateLocalState, key]
85+
)
86+
87+
useEffect(() => {
88+
if (!isBrowser()) {
89+
return
90+
}
91+
92+
const listener = (e) => {
93+
onLocalStorageChange(e)
94+
}
95+
96+
window.addEventListener(LOCAL_STORAGE_CHANGE_EVENT_NAME, listener)
97+
98+
// The storage event only works in the context of other documents (eg. other browser tabs)
99+
window.addEventListener('storage', listener)
100+
101+
// Write default value to the local storage if there currently isn't any value there.
102+
if (window.localStorage.getItem(key) === null && defaultValue !== null) {
103+
writeStorage(key, defaultValue)
104+
}
105+
106+
return () => {
107+
window.removeEventListener(LOCAL_STORAGE_CHANGE_EVENT_NAME, listener)
108+
window.removeEventListener('storage', listener)
109+
}
110+
}, [key, defaultValue, onLocalStorageChange])
111+
112+
const writeState = useCallback(
113+
(value) =>
114+
value instanceof Function
115+
? writeStorage(key, value(localState))
116+
: writeStorage(key, value),
117+
[key]
118+
)
119+
120+
const deleteState = useCallback(() => deleteFromStorage(key), [key])
121+
122+
return {
123+
data: localState ?? defaultValue,
124+
onSetItem: writeState,
125+
onRemoveItem: deleteState,
126+
}
127+
}
128+
129+
export default useLocalStorage

0 commit comments

Comments
 (0)