Skip to content

Commit 898ab42

Browse files
committed
Finish trust level implementation
Implementing chat trust level Implementing logic for trusted contacts Implementing automatic deletion of untrusted contacts
1 parent cba3d95 commit 898ab42

12 files changed

Lines changed: 621 additions & 47 deletions

File tree

android/tinySSB/app/src/main/assets/web/prod/chat.js

Lines changed: 55 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -393,30 +393,66 @@ function load_chat_list() {
393393
load_chat_item(nm)
394394
}
395395

396-
function load_chat_item(nm) { // appends a button for conversation with name nm to the conv list
397-
var cl, mem, item, bg, row, badge, badgeId, cnt;
398-
cl = document.getElementById('lst:chats');
399-
// console.log(nm)
400-
if (nm == "ALL")
401-
mem = "ALL";
402-
else
403-
mem = "🔒 " + recps2display(tremola.chats[nm].members);
404-
item = document.createElement('div');
405-
// item.style = "padding: 0px 5px 10px 5px; margin: 3px 3px 6px 3px;";
406-
item.setAttribute('class', 'chat_item_div'); // old JS (SDK 23)
407-
if (tremola.chats[nm].forgotten) bg = ' gray'; else bg = ' light';
408-
row = "<button class='chat_item_button w100" + bg + "' onclick='load_chat(\"" + nm + "\");' style='overflow: hidden; position: relative;'>";
409-
row += "<div style='white-space: nowrap;'><div style='text-overflow: ellipsis; overflow: hidden;'>" + tremola.chats[nm].alias + "</div>";
410-
row += "<div style='text-overflow: clip; overflow: ellipsis;'><font size=-2>" + escapeHTML(mem) + "</font></div></div>";
411-
badgeId = nm + "-badge"
412-
badge = "<div id='" + badgeId + "' style='display: none; position: absolute; right: 0.5em; bottom: 0.9em; text-align: center; border-radius: 1em; height: 2em; width: 2em; background: var(--red); color: white; font-size: small; line-height:2em;'>&gt;9</div>";
396+
function load_chat_item(nm) {
397+
var cl = document.getElementById('lst:chats');
398+
var item = document.createElement('div');
399+
item.setAttribute('class', 'chat_item_div');
400+
401+
var mem = (nm == "ALL") ? "ALL" : "🔒 " + recps2display(tremola.chats[nm].members);
402+
var bg = (tremola.chats[nm].forgotten) ? ' gray' : ' light';
403+
404+
// Get the current trust level
405+
let trustLevel = getChatTrustLevel(nm);
406+
let trustCircle = "🔴";
407+
if (trustLevel == 1) {
408+
trustCircle = "🟠";
409+
} else if (trustLevel == 2) {
410+
trustCircle = "🟢";
411+
}
412+
413+
var row = "<button class='chat_item_button w100" + bg + "' onclick='load_chat(\"" + nm + "\");' style='overflow: hidden; position: relative;'>";
414+
row += "<div style='white-space: nowrap;'><div style='text-overflow: ellipsis; overflow: hidden;'>";
415+
row += tremola.chats[nm].alias + " ";
416+
// Insert the trust circle with a unique ID
417+
row += "<span id='trustCircle_" + nm + "'>" + trustCircle + "</span>";
418+
row += "</div><div style='text-overflow: clip; overflow: ellipsis;'><font size=-2>" + escapeHTML(mem) + "</font></div></div>";
419+
420+
var badgeId = nm + "-badge";
421+
var badge = "<div id='" + badgeId + "' style='display: none; position: absolute; right: 0.5em; bottom: 0.9em; text-align: center; border-radius: 1em; height: 2em; width: 2em; background: var(--red); color: white; font-size: small; line-height:2em;'>&gt;9</div>";
413422
row += badge + "</button>";
414-
row += ""
415423
item.innerHTML = row;
416424
cl.appendChild(item);
417-
set_chats_badge(nm)
425+
426+
set_chats_badge(nm);
427+
}
428+
429+
function reloadChatTrustLevels() {
430+
if (!tremola || !tremola.chats) {
431+
console.error("tremola.chats not found.");
432+
return;
433+
}
434+
435+
for (var nm in tremola.chats) {
436+
if (!tremola.chats.hasOwnProperty(nm)) continue;
437+
438+
var trustLevel = getChatTrustLevel(nm); // assume this returns 0,1,2
439+
var trustCircle = "🔴"; // default
440+
if (trustLevel == 1) {
441+
trustCircle = "🟠";
442+
} else if (trustLevel == 2) {
443+
trustCircle = "🟢";
444+
}
445+
446+
// Update the DOM element directly
447+
var elem = document.getElementById("trustCircle_" + nm);
448+
if (elem) {
449+
elem.textContent = trustCircle;
450+
}
451+
}
418452
}
419453

454+
455+
420456
function new_conversation() {
421457
// { "alias":"local notes (for my eyes only)", "posts":{}, "members":[myId], "touched": millis }
422458
var recps = []

android/tinySSB/app/src/main/assets/web/prod/contacts.js

Lines changed: 107 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -75,19 +75,34 @@ function load_contact_item(c) { // [ id, { "alias": "thealias", "initial": "T",
7575
document.getElementById('lst:contacts-trustLevelTwo').appendChild(item);
7676
}
7777
}
78+
79+
function unforget_contact(id) {
80+
console.log("unforgetting contact " + id);
81+
tremola.contacts[id].forgotten = false;
82+
persist();
83+
load_contact_list();
84+
closeOverlay();
85+
}
86+
7887
// reload trust levels of all contacts, parameter is a list of contact IDs and their trust levels
7988
function reloadContactsTrustLevels(contactsTrustLevelsJSON) {
8089
const contactsTrustLevels = JSON.parse(contactsTrustLevelsJSON);
8190
for (const contactId in contactsTrustLevels) {
8291
const id = encodeScuttlebuttId(contactId);
8392
console.log(tremola.contacts);
84-
//check if contact trust level is different from the one in the contacts list
85-
if (tremola.contacts[id]["trusted"] != contactsTrustLevels[contactId]) {
93+
//check if contact trust level is lower than the one in the contacts list
94+
if (tremola.contacts[id]["trusted"] < contactsTrustLevels[contactId]) {
95+
//if contact is forgotten
96+
if (tremola.contacts[id].forgotten) {
97+
// ask the user if they want to unforget the contact
98+
launch_toast("Unforget Contact", "This contact is in your forgotten list, do you also want to unforget it?", function() { unforget_contact(id); }, closeOverlay);
99+
}
86100
launch_snackbar("Updated trust level for " + tremola.contacts[id].alias + " to " + contactsTrustLevels[contactId]);
87101
}
88102
tremola.contacts[id]["trusted"] = contactsTrustLevels[contactId];
89103
console.log("contactsTrustLevel: " + contactId + " " + contactsTrustLevels[contactId]);
90104
}
105+
reloadChatTrustLevels();
91106
persist();
92107
load_contact_list();
93108
}
@@ -145,7 +160,33 @@ function encodeScuttlebuttId(hexString) {
145160
}
146161
}
147162

163+
function deleteContactButton(contactID) {
164+
backend("contacts:delete " + decodeScuttlebuttId(new_contact_id));
165+
closeOverlay();
166+
}
167+
168+
function getChatTrustLevel(chat) {
169+
if (chat != "ALL") {
170+
let contacts = tremola.chats[chat].members;
171+
//get the minimum trust level of all contacts
172+
let trustLevel = 2;
173+
for (var i = 0; i < contacts.length; i++) {
174+
let contact = tremola.contacts[contacts[i]];
175+
if (contact.trusted < trustLevel) {
176+
trustLevel = contact.trusted;
177+
}
178+
}
179+
return trustLevel
180+
} else {
181+
return 0
182+
}
183+
}
184+
148185
function show_contact_details(id) {
186+
//for each entry in tremola.chats
187+
for (var chat in tremola.chats) {
188+
console.log("chat: " + chat + "trust levell: " + getChatTrustLevel(chat));
189+
}
149190
if (id == myId) {
150191
document.getElementById('old_contact_alias_hdr').innerHTML = "Alias: (own name, visible to others)"
151192
} else {
@@ -161,27 +202,35 @@ function show_contact_details(id) {
161202
details += '<br><div>IAM-Alias: &nbsp;' + (c.iam != "" ? c.iam : "&mdash;") + '</div>\n';
162203
details += '<br><div>Shortname: &nbsp;' + id2b32(id) + '</div>\n';
163204
details += '<br><div style="word-break: break-all;">SSB identity: &nbsp;<tt>' + id + '</tt></div>\n';
164-
details += '<br><div class=settings style="padding: 0px;"><div class=settingsText>Forget this contact</div><div style="float: right;"><label class="switch"><input id="hide_contact" type="checkbox" onchange="toggle_forget_contact(this);"><span class="slider round"></span></label></div></div>'
165-
166-
// Add slider with colored gradient
167-
details += '<br><div>Trust Level:</div>\n';
168-
details += '<div style="display: flex; align-items: center;">';
169-
details += '<input type="range" id="slider" min="0" max="2" step="1" value="' + c['trusted'] + '" style="flex: 1; margin-right: 10px; appearance: none; -webkit-appearance: none; background: linear-gradient(to right, red 0%, orange 50%, green 100%); height: 8px; border-radius: 5px;">';
170-
details += '<button id="setButton" style="flex: 0;">Set</button>\n';
171-
details += '</div>';
172-
173-
// Add the numbers under the slider with precise positioning using percentage for better alignment
174-
details += '<div style="position: relative; width: 100%; padding: 0;">';
175-
details += '<span style="position: absolute; left: 0;">0</span>';
176-
details += '<span style="position: absolute; left: 40%; transform: translateX(-40%);">1</span>';
177-
details += '<span style="position: absolute; right: 20%;">2</span>';
178-
details += '</div>';
205+
if (id != myId)
206+
details += '<br><div class=settings style="padding: 0px;"><div class=settingsText>Forget this contact</div><div style="float: right;"><label class="switch"><input id="hide_contact" type="checkbox" onchange="toggle_forget_contact(this);"><span class="slider round"></span></label></div></div>'
207+
208+
if (id != myId) {
209+
// Add slider with colored gradient
210+
details += '<br><div>Trust Level:</div>\n';
211+
details += '<div style="display: flex; align-items: center;">';
212+
details += '<input type="range" id="slider" min="0" max="2" step="1" value="' + c['trusted'] + '" style="flex: 1; margin-right: 10px; appearance: none; -webkit-appearance: none; background: linear-gradient(to right, red 0%, orange 50%, green 100%); height: 8px; border-radius: 5px;">';
213+
details += '<button id="setButton" style="flex: 0;">Set</button>\n';
214+
details += '</div>';
215+
216+
// Add the numbers under the slider with precise positioning using percentage for better alignment
217+
details += '<div style="position: relative; width: 100%; padding: 0;">';
218+
details += '<span style="position: absolute; left: 0;">0</span>';
219+
details += '<span style="position: absolute; left: 40%; transform: translateX(-40%);">1</span>';
220+
details += '<span style="position: absolute; right: 20%;">2</span>';
221+
details += '</div>';
222+
}
179223

180224
// Add error message field to the details HTML
181225
details += '<br><div id="errorMessage" class="error" style="color: red;"></div>\n';
182226

227+
// Add button to delete contact if id is not myId
228+
if (id != myId)
229+
details += '<br><button class="button" onclick="launch_toast(\'Delete Contact\', \'Are you sure you want to delete this contact?\', function() { deleteContactButton(\'' + decodeScuttlebuttId(id) + '\'); }, closeOverlay);">Delete Contact</button>';
230+
183231
document.getElementById('old_contact_details').innerHTML = details;
184232
document.getElementById('old_contact-overlay').style.display = 'initial';
233+
185234
document.getElementById('overlay-bg').style.display = 'initial';
186235
document.getElementById('hide_contact').checked = c.forgotten;
187236

@@ -220,7 +269,6 @@ function show_contact_details(id) {
220269
// Attach event listener to the button after it is added to the DOM
221270
document.getElementById("setButton").addEventListener("click", setSliderValue);
222271

223-
document.getElementById('old_contact_alias').focus();
224272
overlayIsActive = true;
225273
}
226274

@@ -239,8 +287,36 @@ function changeTrustLevel(contactID, value) {
239287
}
240288

241289
function toggle_forget_contact(e) {
242-
var c = tremola.contacts[new_contact_id];
243-
c.forgotten = !c.forgotten;
290+
if (e.checked) {
291+
//get the contrast's trust level
292+
let trustLevel = tremola.contacts[new_contact_id]["trusted"];
293+
if (trustLevel == 2 || trustLevel == 1) {
294+
var c = tremola.contacts[new_contact_id];
295+
c.forgotten = !c.forgotten;
296+
persist();
297+
closeOverlay();
298+
load_contact_list();
299+
return;
300+
} else if (trustLevel == 0) {
301+
//send backend request to delete the contact's feed
302+
backend("contacts:delete " + decodeScuttlebuttId(new_contact_id));
303+
closeOverlay();
304+
//reload the contact list
305+
load_contact_list();
306+
}
307+
} else {
308+
var c = tremola.contacts[new_contact_id];
309+
c.forgotten = !c.forgotten;
310+
persist();
311+
closeOverlay();
312+
load_contact_list();
313+
}
314+
}
315+
316+
//function to forget contact with id as paramter
317+
function forget_contact(contact_id) {
318+
var c = tremola.contacts[encodeScuttlebuttId(contact_id)];
319+
c.forgotten = true;
244320
persist();
245321
closeOverlay();
246322
load_contact_list();
@@ -290,4 +366,15 @@ function save_content_alias() {
290366
closeOverlay();
291367
}
292368

369+
function deleteContact(contactID) {
370+
//convert the contactID to scuttlebutt format
371+
contactID = encodeScuttlebuttId(contactID);
372+
//remove the contact from the contacts list
373+
delete tremola.contacts[contactID];
374+
persist();
375+
closeOverlay();
376+
load_contact_list();
377+
launch_snackbar("Contact deleted successfully");
378+
}
379+
293380
// --- eof

android/tinySSB/app/src/main/assets/web/tremola.css

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,43 @@
399399
animation: fadein 0.5s, fadeout 0.5s 2.5s;
400400
}
401401

402+
#toast-overlay {
403+
display: none;
404+
position: fixed;
405+
top: 40vh;
406+
background-color: var(--neutralCol); /* Black background color */
407+
z-index: 1; /* Add a z-index if needed */
408+
409+
}
410+
411+
#toast-button-yes {
412+
background-color: var(--lightA);
413+
color: var(--fontCol);
414+
border: none;
415+
border-radius: 4px;
416+
padding: 10px 24px;
417+
text-align: center;
418+
text-decoration: none;
419+
display: inline-block;
420+
font-size: 16px;
421+
margin: 4px 2px;
422+
cursor: pointer;
423+
}
424+
425+
#toast-button-no {
426+
background-color: var(--lightB);
427+
color: var(--fontCol);
428+
border: none;
429+
border-radius: 4px;
430+
padding: 10px 24px;
431+
text-align: center;
432+
text-decoration: none;
433+
display: inline-block;
434+
font-size: 16px;
435+
margin: 4px 2px;
436+
cursor: pointer;
437+
}
438+
402439
/* Animations to fade the snackbar in and out */
403440
@-webkit-keyframes fadein {
404441
from {bottom: 0; opacity: 0;}

android/tinySSB/app/src/main/assets/web/tremola.html

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,15 @@ <h3><span class="circle red"></span> Other Contacts</h3>
191191
&nbsp;<br><div style="word-break: break-all;">SSB identifier: &nbsp;@sdfjdskfhdshfjkdhfhdjskfhdshfhdsjfkdhf.ed25519</div>
192192
</div>
193193
</div>
194+
195+
<div id="toast-overlay" class="qr-overlay">
196+
<div id="toast-title" style="margin-bottom: 25px;">Confirmation</div>
197+
<div id="toast-text" style="margin-bottom: 25px;">Are you sure you want to delete this item?</div>
198+
<div style="text-align: right; margin-top: 20px;">
199+
<button id ="toast-button-yes" onclick="confirmation_yes()">Yes</button>
200+
<button id="toast-button-no" onclick="confirmation_no()">No</button>
201+
</div>
202+
</div>
194203
&nbsp;
195204
<div id='confirm_contact-overlay' class='qr-overlay'>
196205
Enter desired alias:<br>

android/tinySSB/app/src/main/assets/web/tremola.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -646,11 +646,8 @@ function b2f_new_event(e) { // incoming SSB log event: we get map with three ent
646646
ch["posts"][e.header.ref] = p;
647647
// console.log(`chat add tips ${a[1]}`)
648648
ch.timeline.add(e.header.ref, a[1])
649-
if (ch["touched"] < e.header.tst)
650-
ch["touched"] = e.header.tst
651649
if (curr_scenario == "posts" && curr_chat == conv_name) {
652650
load_chat(conv_name); // reload all messages (not very efficient ...)
653-
ch.lastRead = Object.keys(ch.posts).length; // Date.now();
654651
}
655652
set_chats_badge(conv_name)
656653
} else {
@@ -752,6 +749,7 @@ function b2f_new_contact(fid, trustLevel="untrusted", alias="") {
752749
"color": colors[Math.floor(colors.length * Math.random())],
753750
"iam": "", "forgotten": false, "trusted": trusted
754751
};
752+
backend("contacts:checkDeleted " + decodeScuttlebuttId(fid));
755753
console.log(tremola.contacts[fid]);
756754
persist();
757755
load_contact_list();

android/tinySSB/app/src/main/assets/web/tremola_ui.js

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,7 @@ function closeOverlay() {
309309
document.getElementById('div:modal_img').style.display = 'none';
310310
document.getElementById('connection-overlay').style.display = 'none';
311311
document.getElementById('import-id-overlay').style.display = 'none';
312+
document.getElementById('toast-overlay').style.display = 'none';
312313

313314
// kanban overlays
314315
document.getElementById('div:menu_history').style.display = 'none';
@@ -376,6 +377,20 @@ function launch_snackbar(txt) {
376377
}, 3000);
377378
}
378379

380+
function launch_toast(title, description, function_yes, function_no) {
381+
closeOverlay();
382+
var ts = document.getElementById("toast-overlay");
383+
//set the title
384+
document.getElementById("toast-title").innerHTML = title;
385+
//set the description
386+
document.getElementById("toast-text").innerHTML = description;
387+
//set the functions
388+
document.getElementById("toast-button-yes").onclick = function_yes;
389+
document.getElementById("toast-button-no").onclick = function_no;
390+
//show the toast
391+
ts.style.display = 'inline';
392+
}
393+
379394
function chat_open_attachments_menu() {
380395
closeOverlay()
381396
document.getElementById('overlay-bg').style.display = 'initial'
@@ -449,7 +464,11 @@ function qr_scan_success(s) {
449464
}
450465
backend("contacts:setTrust " + decodeScuttlebuttId(new_contact_id) + " 2" + " " + tips);
451466
} else if (tremola.contacts[new_contact_id].trusted == 2) {
452-
launch_snackbar("This contact already exists and is verified");
467+
if (tremola.contacts[new_contact_id].forgotten) {
468+
launch_toast("Unforget Contact", "This contact is in your forgotten list, do you also want to unforget it?", function() { unforget_contact(new_contact_id); }, closeOverlay);
469+
} else {
470+
launch_snackbar("This contact already exists and is verified");
471+
}
453472
}
454473
return;
455474
}

0 commit comments

Comments
 (0)