Skip to content

Commit 3befd40

Browse files
authored
Merge pull request gitcoinco#5947 from gitcoinco/3box-integration
3box integration
2 parents 9c5aaba + e5ef20a commit 3befd40

File tree

15 files changed

+1772
-15
lines changed

15 files changed

+1772
-15
lines changed

.eslintignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,5 @@ app/assets/v2/js/dataviz/*
1717
app/assets/v2/js/ethereumjs-accounts.js
1818
app/assets/onepager/js/tx.js
1919
app/assets/onepager/js/bignumber.js
20+
21+
app/assets/v2/js/lib/emoji-button.js

app/app/urls.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,8 @@
115115
url(r'^api/v0.1/profile/(.*)?/viewers.csv', dashboard.views.profile_viewers, name='profile_viewers'),
116116
url(r'^api/v0.1/profile/(.*)?/spent.csv', dashboard.views.profile_spent, name='profile_spent'),
117117
url(r'^api/v0.1/profile/banner', dashboard.views.change_user_profile_banner, name='change_user_profile_banner'),
118+
url(r'^api/v0.1/profile/settings', dashboard.views.profile_settings, name='profile_settings'),
119+
url(r'^api/v0.1/profile/backup', dashboard.views.profile_backup, name='profile_backup'),
118120
path('api/v0.1/activity/<int:activity_id>', townsquare.views.api, name='townsquare_api'),
119121
path('api/v0.1/emailsettings/', townsquare.views.emailsettings, name='townsquare_emailsettings'),
120122
url(r'^api/v0.1/activity', retail.views.create_status_update, name='create_status_update'),

app/assets/v2/css/base.css

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1662,10 +1662,11 @@ div.busyOverlay {
16621662
background: rgba(12, 3, 59, 0.1);
16631663
border-radius: 4px;
16641664

1665-
.emoji-picker__tab {
1666-
font-size: 32px!important;
1667-
}
1665+
.emoji-picker__tab {
1666+
font-size: 32px!important;
1667+
}
16681668

1669-
.emoji-picker__emoji {
1670-
font-size: 1.6rem!important;
1671-
}
1669+
.emoji-picker__emoji {
1670+
font-size: 1.6rem!important;
1671+
}
1672+
}

app/assets/v2/css/profile.css

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,23 @@
286286
background-color: transparent;
287287
}
288288

289+
.profile-header__sync {
290+
margin-top: -140px;
291+
position: relative;
292+
}
293+
294+
.profile-header__sync.inactive {
295+
display: none;
296+
}
297+
298+
.profile-header__sync img {
299+
width: 32px;
300+
}
301+
302+
.profile-header__sync img.loading {
303+
display: none;
304+
}
305+
289306
.card-header::after {
290307
content: '';
291308
position: relative;

app/assets/v2/images/3box.svg

Lines changed: 37 additions & 0 deletions
Loading

app/assets/v2/js/lib/emoji-button.js

Lines changed: 1044 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app/assets/v2/js/pages/profile.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ $(document).ready(function() {
133133
.y(function(d) {
134134
return y(d.close);
135135
});
136-
136+
137137
// Adds the svg canvas
138138
var svg = d3.select('#earn_dataviz')
139139
.append('svg')
@@ -178,7 +178,6 @@ $(document).ready(function() {
178178

179179
}
180180

181-
182181
$(document).on('click', '.load-more', function() {
183182
var address = $('#preferred-address').prop('title');
184183
var link = $(this);
@@ -219,4 +218,4 @@ $(document).ready(function() {
219218
}
220219
});
221220
});
222-
}(jQuery));
221+
}(jQuery));

app/assets/v2/js/profile-backup.js

Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
(function($) {
2+
const syncComplete = res => {
3+
console.log('sync complete');
4+
};
5+
6+
const syncFailure = () => {
7+
_alert(
8+
{ message: gettext('Failed to backup profile data to 3Box. Please try again.') },
9+
'error'
10+
);
11+
switchIcons(false);
12+
};
13+
14+
const openBox = callback => {
15+
window.ethereum.enable().then(addresses => {
16+
window.Box.openBox(addresses[0], window.ethereum, {}).then(box => {
17+
box.onSyncDone(syncComplete);
18+
window.box = box;
19+
window.curentEthAddr = addresses[0];
20+
console.log('openBox succeeded');
21+
callback(box);
22+
}).catch(err => {
23+
syncFailure();
24+
});
25+
});
26+
};
27+
28+
const openSpace = (box, callback) => {
29+
const name = 'GitCoin';
30+
31+
window.currentSpace = name;
32+
const opts = {
33+
onSyncDone: () => {
34+
console.log('sync done in space', name);
35+
callback(box, box.spaces[name]);
36+
}
37+
};
38+
39+
box.openSpace(name, opts).catch(err => {
40+
syncFailure();
41+
});
42+
};
43+
44+
const backupProfile = async space => {
45+
const data = await fetchProfieData();
46+
47+
if (data) {
48+
let profile = data;
49+
50+
console.log('profile', profile);
51+
52+
if (profile) {
53+
// get public key-value
54+
const public_keys = Object.keys(profile).filter(k => k[0] !== '_');
55+
const public_values = public_keys.map(k => profile[k]);
56+
// get private key-value
57+
let private_keys = Object.keys(profile).filter(k => k[0] === '_');
58+
const private_values = private_keys.map(k => profile[k]);
59+
60+
private_keys = private_keys.map(k => k.substring(1));
61+
62+
// save data to space
63+
const r_public = await space.public.setMultiple(public_keys, public_values);
64+
const r_private = await space.private.setMultiple(private_keys, private_values);
65+
66+
// remove the unused key/value pairs from the space
67+
await removeUnusedFields(space, public_keys, private_keys);
68+
69+
if (r_public && r_private) {
70+
const three_box_link = `https://3box.io/${window.curentEthAddr}/data`;
71+
72+
_alert(
73+
{
74+
message: gettext(`<span>Your profile data has been synchronized to 3Box -- </span> <a href="${three_box_link}" target="_blank">Check out the details on 3Box Hub</a>.`)},
75+
'success'
76+
);
77+
} else {
78+
syncFailure();
79+
}
80+
} else {
81+
syncFailure();
82+
}
83+
} else {
84+
syncFailure();
85+
}
86+
87+
switchIcons(false);
88+
};
89+
90+
const removeUnusedFields = async(space, public_keys, private_keys) => {
91+
const public_data = await space.public.all();
92+
const private_data = await space.private.all();
93+
94+
const unused_public_keys = Object.keys(public_data).filter(x => !public_keys.includes(x));
95+
const unused_private_keys = Object.keys(private_data).filter(x => !private_keys.includes(x));
96+
97+
await removeFields(space.public, unused_public_keys);
98+
await removeFields(space.private, unused_private_keys);
99+
100+
const count = unused_public_keys.length + unused_private_keys.length;
101+
102+
console.log(`remove ${count} outdated fields from space`, unused_public_keys, unused_private_keys);
103+
};
104+
105+
const removeFields = async(subspace, keys) => {
106+
if (keys && keys.length > 0) {
107+
for (let x of keys) {
108+
await subspace.remove(x);
109+
}
110+
}
111+
};
112+
113+
const toggleAutomaticUpdateFlag = async() => {
114+
const data = new FormData();
115+
116+
data.append('csrfmiddlewaretoken', $('input[name="csrfmiddlewaretoken"]').val());
117+
try {
118+
const response = await fetch('/api/v0.1/profile/settings', {
119+
method: 'post',
120+
body: data
121+
});
122+
123+
if (response.status === 200) {
124+
const result = await response.json();
125+
const automatic_backup = result.automatic_backup ? 'ENABLED' : 'DISABLED';
126+
127+
_alert(
128+
{ message: gettext(`Profile automatic backup has been ${automatic_backup}`) },
129+
'success'
130+
);
131+
} else {
132+
_alert(
133+
{ message: gettext('An error occurred. Please try again.') },
134+
'error'
135+
);
136+
}
137+
138+
} catch (err) {
139+
console.log('Error when toggling automatic backup flag', err);
140+
}
141+
};
142+
143+
const fetchProfieData = async() => {
144+
const data = new FormData();
145+
146+
data.append('csrfmiddlewaretoken', $('input[name="csrfmiddlewaretoken"]').val());
147+
try {
148+
const response = await fetch('/api/v0.1/profile/backup', {
149+
method: 'post',
150+
body: data
151+
});
152+
153+
if (response.status === 200) {
154+
const result = await response.json();
155+
156+
return result.data;
157+
}
158+
} catch (err) {
159+
console.log('Error when fetching profile data', err);
160+
}
161+
return null;
162+
};
163+
164+
const startProfileDataBackup = async() => {
165+
console.log('start sync data to 3box');
166+
167+
const data = await fetchProfieData();
168+
169+
console.log('data', data);
170+
171+
// User is prompted to approve the messages inside their wallet (openBox() and
172+
// openSpace() methods via 3Box.js). This logs them in to 3Box.
173+
174+
try {
175+
if (window.Box) {
176+
// 1. Open box and space
177+
// 2. Backing up my Gitcoin data to 3Box, inside of a "Gitcoin" space
178+
switchIcons(true);
179+
openBox(box => {
180+
openSpace(box, (box, space) => {
181+
console.log('backup data into space');
182+
backupProfile(space);
183+
});
184+
});
185+
} else {
186+
setTimeout(() => {
187+
startProfileDataBackup();
188+
}, 1000);
189+
}
190+
} catch (err) {
191+
console.log('Error when backing up profile data', err);
192+
syncFailure();
193+
}
194+
};
195+
196+
const switchIcons = (loading) => {
197+
if (loading) {
198+
$('.profile-header__sync img.loading').show();
199+
$('.profile-header__sync img.action').hide();
200+
} else {
201+
$('.profile-header__sync img.loading').hide();
202+
$('.profile-header__sync img.action').show();
203+
}
204+
};
205+
206+
// add click listener
207+
$('#sync-to-3box').on('click', (event) => {
208+
if (!long_pressed) {
209+
startProfileDataBackup();
210+
}
211+
long_pressed = false;
212+
});
213+
214+
// add long press listener
215+
let timer = null;
216+
let long_pressed = false;
217+
218+
$('#sync-to-3box').on('mousedown', () => {
219+
timer = setTimeout(() => {
220+
long_pressed = true;
221+
toggleAutomaticUpdateFlag();
222+
}, 500);
223+
}).on('mouseup mouseleave', () => {
224+
clearTimeout(timer);
225+
});
226+
227+
$(document).ready(function() {
228+
setTimeout(() => {
229+
console.log('check profile backup flag', window.profile_automatic_backup);
230+
// backup automatically if the flag is true
231+
if (window.profile_automatic_backup) {
232+
startProfileDataBackup();
233+
}
234+
}, 3000);
235+
});
236+
237+
}(jQuery));

app/assets/v2/js/status.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -153,17 +153,17 @@ $(document).ready(function() {
153153
.catch(err => console.log('Error ', err));
154154
}
155155
});
156-
window.addEventListener('DOMContentLoaded', function () {
156+
window.addEventListener('DOMContentLoaded', function() {
157157
var button = document.querySelector('#emoji-button');
158158
var picker = new EmojiButton({
159159
position: 'left-end'
160160
});
161161

162-
picker.on('emoji', function (emoji) {
162+
picker.on('emoji', function(emoji) {
163163
document.querySelector('textarea').value += emoji;
164164
});
165165

166-
button.addEventListener('click', function () {
166+
button.addEventListener('click', function() {
167167
picker.pickerVisible ? picker.hidePicker() : picker.showPicker(button);
168168
});
169169
});

0 commit comments

Comments
 (0)