Skip to content

Commit 39657e4

Browse files
authored
Merge pull request #57 from TRACE-Digital/jack/export
Export, database clear, and account closure
2 parents 3c95a17 + f862336 commit 39657e4

File tree

9 files changed

+311
-63
lines changed

9 files changed

+311
-63
lines changed

src/components/AccountCardList/AccountCardList.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import AccountCard from 'components/AccountCard/AccountCard';
22
import { rejectAccount } from 'components/AccountCard/AccountCard';
33
import { claimAccount } from 'components/AccountCard/AccountCard';
4-
import React, { useState, useEffect, useRef } from 'react';
4+
import React, { useState, useEffect, useRef, Fragment } from 'react';
55
import { Dropdown, DropdownItem, DropdownMenu, DropdownToggle, Row } from 'reactstrap';
66
import { tags } from 'trace-search';
77
import NotificationAlert from "react-notification-alert";
@@ -213,7 +213,7 @@ function AccountCardList(props) {
213213
</div>
214214
<Row>
215215
{sortedAccounts.map((account) => (
216-
<>
216+
<Fragment key={account.id || account}>
217217
{typeof account !== "string" && (<AccountCard
218218
key={account.id}
219219
account={account}
@@ -230,7 +230,7 @@ function AccountCardList(props) {
230230
<div key={account} className='titled-separator' style={{ marginBottom: '20px' }}>
231231
<h3>{account}</h3>
232232
</div>}
233-
</>
233+
</Fragment>
234234
))}
235235
</Row>
236236
</>

src/components/AddSitePopup/AddSitePopup.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -101,19 +101,19 @@ const AddPopup = (props) => {
101101
<Col md={6}>
102102
<FormGroup>
103103
<Label for="siteName">Site Name</Label><span className="asterisk">*</span>
104-
<Input type="text" name="site" id="input" placeholder="eg. Instagram" onChange={(e) => onSiteChange(`${e.target.value}`)}/>
104+
<Input type="text" name="site" id="siteName" placeholder="eg. Instagram" onChange={(e) => onSiteChange(`${e.target.value}`)}/>
105105
</FormGroup>
106106
</Col>
107107
<Col md={6}>
108108
<FormGroup>
109109
<Label for="username">Username</Label><span className="asterisk">*</span>
110-
<Input type="text" name="username" id="input" placeholder="eg. coraychan" onChange={(e) => onUsernameChange(`${e.target.value}`)}/>
110+
<Input type="text" name="username" id="username" placeholder="eg. coraychan" onChange={(e) => onUsernameChange(`${e.target.value}`)}/>
111111
</FormGroup>
112112
</Col>
113113
</Row>
114114
<FormGroup>
115115
<Label for="siteUrl">Account URL</Label><span className="asterisk">*</span>
116-
<Input type="text" name="url" id="input" placeholder="eg. http://www.instagram.com/coraychan" onChange={(e) => onUrlChange(`${e.target.value}`)}/>
116+
<Input type="text" name="url" id="siteUrl" placeholder="eg. http://www.instagram.com/coraychan" onChange={(e) => onUrlChange(`${e.target.value}`)}/>
117117
</FormGroup>
118118
<Label for="tags">Tags</Label>
119119
<Row>

src/components/Navbars/AdminNavbar.js

Lines changed: 9 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,8 @@ function AdminNavbar(props) {
5252
const isChrome = window.navigator.userAgent.includes('Chrome');
5353
const isFirefox = window.navigator.userAgent.includes('Firefox');
5454

55-
let extensionUrl = 'https://addons.mozilla.org/en-US/firefox/addon/trace-digital/';
56-
if (isChrome) extensionUrl = 'https://chrome.google.com/webstore/detail/TRACE/klhmocgplcpemcdfeefpaikihedmikgk';
55+
let extensionUrl = 'https://github.com/TRACE-Digital/TRACE-ext';
56+
if (isChrome) extensionUrl = 'https://chrome.google.com/webstore/detail/trace/klhmocgplcpemcdfeefpaikihedmikgk';
5757
if (isFirefox) extensionUrl = 'https://addons.mozilla.org/en-US/firefox/addon/trace-digital/';
5858

5959
useEffect(() => {
@@ -152,18 +152,6 @@ function AdminNavbar(props) {
152152
<p className="d-lg-none"></p>
153153
</DropdownToggle>
154154
<DropdownMenu className="dropdown-navbar" right tag="ul">
155-
<NavLink tag="li">
156-
<DropdownItem className="nav-item">Profile</DropdownItem>
157-
</NavLink>
158-
<DropdownItem divider tag="li" />
159-
160-
<NavLink tag="li">
161-
<DropdownItem className="nav-item">Settings</DropdownItem>
162-
</NavLink>
163-
<DropdownItem tag="li" className="">
164-
165-
</DropdownItem>
166-
<DropdownItem divider tag="li" />
167155
<NavLink tag="li">
168156
<a href={extensionUrl} target='blank'>
169157
<DropdownItem className="nav-item">
@@ -174,33 +162,20 @@ function AdminNavbar(props) {
174162
</DropdownItem>
175163
</a>
176164
</NavLink>
177-
<NavLink tag="li">
178-
<DropdownItem className="nav-item" onClick={async () => {
179-
const confirmation = window.confirm('Are you sure? This action is irreversible and will delete ALL your data.');
180-
if (!confirmation) {
181-
return;
182-
}
183-
184-
try {
185-
await resetDb();
186-
if (currentUsername) {
187-
await setRemoteUser(await Auth.currentUserPoolUser());
188-
await resetRemoteDb();
189-
}
190-
window.location.reload();
191-
} catch (e) {
192-
console.error(e);
193-
}
194-
}}>Delete my data</DropdownItem>
165+
166+
<NavLink to="/settings" tag={Link} style={{paddingBottom: 0}}>
167+
<DropdownItem className="nav-item">Settings</DropdownItem>
195168
</NavLink>
169+
196170
<DropdownItem divider tag="li" />
171+
197172
{!currentUsername &&
198173
<NavLink to="/login" tag={Link}>
199174
<DropdownItem className="nav-item">Log In</DropdownItem>
200175
</NavLink>}
201176
{currentUsername &&
202-
<NavLink onClick={() => {
203-
signOut();
177+
<NavLink onClick={async () => {
178+
await signOut();
204179
window.location.href = "/landing";
205180
}} tag="li">
206181
<DropdownItem className="nav-item">
Lines changed: 245 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,245 @@
1+
import Auth from "@aws-amplify/auth";
2+
import { useEffect, useState } from "react";
3+
import { Badge, Card, CardBody, CardHeader, Col, Row } from "reactstrap";
4+
import { destroyDb, destroyRemoteDb, getDb, getRemoteDb, setRemoteUser, exportToJson, exportToCsv } from "trace-search";
5+
6+
function Settings() {
7+
const [currentUser, setCurrentUser] = useState(null);
8+
const [localDbInfo, setLocalDbInfo] = useState(null);
9+
const [remoteDbInfo, setRemoteDbInfo] = useState(null);
10+
const [currentSettings, setCurrentSettings] = useState(null);
11+
const [refreshData, setRefreshData] = useState(false);
12+
13+
useEffect(() => {
14+
(async () => {
15+
try {
16+
const user = await Auth.currentUserPoolUser();
17+
setCurrentUser(user);
18+
19+
await setRemoteUser(user);
20+
const db = await getRemoteDb();
21+
const dbInfo = await db.info();
22+
23+
setRemoteDbInfo(dbInfo);
24+
25+
} catch (e) {
26+
console.error(e);
27+
}
28+
29+
try {
30+
const db = await getDb();
31+
const dbInfo = await db.info();
32+
const settings = await db.get('settings');
33+
34+
console.log(dbInfo);
35+
console.log(settings);
36+
37+
setLocalDbInfo(dbInfo);
38+
setCurrentSettings(settings);
39+
} catch (e) {
40+
console.error(e);
41+
}
42+
})();
43+
}, [refreshData]);
44+
45+
const isChrome = window.navigator.userAgent.includes('Chrome');
46+
const isFirefox = window.navigator.userAgent.includes('Firefox');
47+
48+
let extensionUrl = 'https://github.com/TRACE-Digital/TRACE-ext';
49+
if (isChrome) extensionUrl = 'https://chrome.google.com/webstore/detail/trace/klhmocgplcpemcdfeefpaikihedmikgk';
50+
if (isFirefox) extensionUrl = 'https://addons.mozilla.org/en-US/firefox/addon/trace-digital/';
51+
52+
const downloadJson = async () => {
53+
const text = await exportToJson();
54+
openBlob(text, '.json', 'application/json');
55+
};
56+
57+
const downloadCsv = async () => {
58+
const text = await exportToCsv();
59+
openBlob(text, '.csv', 'text/plain');
60+
};
61+
62+
const openBlob = (text, extension, contentType) => {
63+
const fileName = `trace-${new Date().toJSON()}${extension}`;
64+
const props = { type: contentType };
65+
66+
let blob;
67+
try {
68+
// Use File in newer browsers so we can specify a name
69+
blob = new File([text], fileName, props);
70+
} catch (e) {
71+
console.log(e);
72+
blob = new Blob([text], props);
73+
}
74+
75+
const url = URL.createObjectURL(blob);
76+
console.log(url);
77+
78+
// Trigger the download
79+
const link = document.createElement('a');
80+
link.href = url;
81+
link.download = fileName;
82+
link.target = 'blank';
83+
link.click();
84+
85+
// Also show the content
86+
window.open(url, 'blank');
87+
};
88+
89+
const closeAccount = async () => {
90+
if (!window.confirm('Are you sure you want to permanently close your account? ALL account data will be deleted.')) {
91+
return;
92+
}
93+
94+
await destroyDb();
95+
await destroyRemoteDb();
96+
97+
try {
98+
const remoteDb = await getRemoteDb();
99+
const settings = {
100+
_id: 'settings',
101+
accountClosed: true
102+
};
103+
await remoteDb.put(settings);
104+
} catch (e) {
105+
alert(`Could not close account!\n${e}`);
106+
}
107+
108+
setRefreshData(prev => !prev);
109+
110+
await Auth.signOut();
111+
window.location.href = '/login';
112+
};
113+
114+
const deleteLocalData = async () => {
115+
if (!confirmDelete()) {
116+
return;
117+
}
118+
119+
try {
120+
await destroyDb();
121+
} catch (e) {
122+
alert(`Could not delete local data!\n${e}`);
123+
}
124+
125+
setRefreshData(prev => !prev);
126+
};
127+
128+
const deleteRemoteData = async () => {
129+
if (!confirmDelete()) {
130+
return;
131+
}
132+
133+
try {
134+
if (currentUser) {
135+
await destroyRemoteDb();
136+
}
137+
} catch (e) {
138+
alert(`Could not delete remote data!\n${e}`);
139+
}
140+
141+
setRefreshData(prev => !prev);
142+
};
143+
144+
const confirmDelete = () => {
145+
const confirmation = window.confirm('Are you sure? This action is irreversible.');
146+
return confirmation;
147+
}
148+
149+
return (
150+
<div className="content" style={{ lineHeight: '2.3' }}>
151+
<div className="header">
152+
<h1>Settings</h1>
153+
</div>
154+
<Card>
155+
<CardHeader>TRACE</CardHeader>
156+
<CardBody>
157+
<Row>
158+
<Col>Account name:</Col>
159+
<Col>{currentUser ? currentUser.attributes.email : 'None (local only)'}</Col>
160+
</Row>
161+
<Row>
162+
<Col>Account ID:</Col>
163+
<Col>{currentUser ? currentUser.attributes.sub : 'None (local only)'}</Col>
164+
</Row>
165+
<Row>
166+
<Col>Version:</Col>
167+
<Col>{currentSettings?.version}</Col>
168+
</Row>
169+
<Row>
170+
<Col>Browser extension:</Col>
171+
<Col>
172+
<a href={extensionUrl} target='blank'>
173+
{window.__TRACE_EXTENSION_HOOK__ ?
174+
'v' + window.__TRACE_EXTENSION_HOOK__.getVersionStr() :
175+
'Install the extension'
176+
}
177+
</a>
178+
</Col>
179+
</Row>
180+
</CardBody>
181+
</Card>
182+
183+
<Card>
184+
<CardHeader>Your Data</CardHeader>
185+
<CardBody>
186+
<Row>
187+
<Col>Local data:</Col>
188+
<Col>
189+
{localDbInfo && localDbInfo.doc_count + ' document' + (localDbInfo.doc_count === 1 ? '' : 's')}
190+
</Col>
191+
</Row>
192+
<Row>
193+
<Col>Sync enabled:</Col>
194+
<Col>
195+
{currentSettings?.syncEnabled ? 'Yes' : 'No'}
196+
</Col>
197+
</Row>
198+
<Row>
199+
<Col>Remote data:</Col>
200+
<Col>
201+
{remoteDbInfo && remoteDbInfo.doc_count + ' document' + (remoteDbInfo.doc_count === 1 ? '' : 's')}
202+
</Col>
203+
</Row>
204+
<Row>
205+
<Col>Export:</Col>
206+
<Col>
207+
<Badge href="#" color="info" onClick={downloadJson}>Export to JSON</Badge>
208+
<Badge href="#" color="info" onClick={downloadCsv}>Export to CSV</Badge>
209+
</Col>
210+
</Row>
211+
</CardBody>
212+
</Card>
213+
214+
<Card>
215+
<CardHeader>Danger Zone</CardHeader>
216+
<CardBody>
217+
<Row>
218+
<Col>Local data:</Col>
219+
<Col>
220+
<Badge href="#" color="danger" onClick={deleteLocalData}>Delete local data</Badge>
221+
</Col>
222+
</Row>
223+
{currentUser &&
224+
<>
225+
<Row>
226+
<Col>Remote data:</Col>
227+
<Col>
228+
<Badge href="#" color="danger" onClick={deleteRemoteData}>Delete remote data</Badge>
229+
</Col>
230+
</Row>
231+
<Row>
232+
<Col>Close your account:</Col>
233+
<Col>
234+
<Badge href="#" color="danger" onClick={closeAccount}>Close account</Badge>
235+
</Col>
236+
</Row>
237+
</>
238+
}
239+
</CardBody>
240+
</Card>
241+
</div>
242+
);
243+
}
244+
245+
export default Settings;

src/routes.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import Analytics from "views/Analytics.js";
2121
import UserProfile from "views/UserProfile.js";
2222
import Editor from "views/Editor.js";
2323
import Contact from "views/Contact.js";
24+
import Settings from "components/Settings/Settings";
2425

2526
var routes = [
2627
{
@@ -55,6 +56,14 @@ var routes = [
5556
component: Editor,
5657
layout: "",
5758
},
59+
{
60+
path: "/settings",
61+
name: "Settings",
62+
rtlName: "",
63+
icon: "tim-icons icon-settings",
64+
component: Settings,
65+
layout: "",
66+
},
5867
{
5968
path: "/contact",
6069
name: "Contact Us",

0 commit comments

Comments
 (0)