ThreadListAvatars: add private thread indicator
When a thread belongs to a private forum, we can't obtain its avatars.
This changes makes this clear by inserting a "key" icon where the
avatars should be shown.
Bug: twpowertools:30
Change-Id: Idec33f277b12282df0fd271eebe0156865474bf4
diff --git a/src/contentScripts/communityConsole/avatars.js b/src/contentScripts/communityConsole/avatars.js
index d0a91df..093e217 100644
--- a/src/contentScripts/communityConsole/avatars.js
+++ b/src/contentScripts/communityConsole/avatars.js
@@ -54,19 +54,19 @@
// get its avatars since it makes an anonymomus call to get the contents of
// the thread.
//
- // This function returns whether avatars should be retrieved depending on if
- // the thread belongs to a known private forum.
- shouldRetrieveAvatars(thread) {
+ // This function returns whether the thread belongs to a known private forum.
+ isPrivateThread(thread) {
return this.getPrivateForums().then(privateForums => {
- if (privateForums.includes(thread.forum)) return false;
+ if (privateForums.includes(thread.forum)) return true;
- return this.db.isForumUnauthorized(thread.forum).then(res => !res);
+ return this.db.isForumUnauthorized(thread.forum);
});
}
// Get an object with the author of the thread, an array of the first |num|
// replies from the thread |thread|, and additional information about the
- // thread.
+ // thread. It also returns whether the thread is private, in which case the
+ // previous properties will be missing.
getFirstMessages(thread, num = 15) {
return CCApi(
'ViewThread', {
@@ -97,7 +97,9 @@
.then(response => {
if (response.unauthorized)
return this.db.putUnauthorizedForum(thread.forum).then(() => {
- throw new Error('Permission denied to load thread.');
+ return {
+ isPrivate: true,
+ };
});
var data = response.body;
@@ -119,6 +121,7 @@
'Author isn\'t included in the ViewThread response.');
return {
+ isPrivate: false,
messages,
author,
@@ -130,9 +133,17 @@
});
}
- // Get a list of at most |num| avatars for thread |thread| by calling the API
+ // Get the following data:
+ // - |isPrivate|: whether the thread is private.
+ // - |avatars|: a list of at most |num| avatars for thread |thread| by calling
+ // the API, if |isPrivate| is false.
getVisibleAvatarsFromServer(thread, num) {
return this.getFirstMessages(thread).then(result => {
+ if (result.isPrivate)
+ return {
+ isPrivate: true,
+ };
+
var messages = result.messages;
var author = result.author;
var lastMessageId = result.lastMessageId;
@@ -160,7 +171,10 @@
lastUsedTimestamp: Math.floor(Date.now() / 1000),
});
- return avatarUrls;
+ return {
+ isPrivate: false,
+ avatars: avatarUrls,
+ };
});
}
@@ -228,13 +242,17 @@
});
}
- // Get a list of at most |num| avatars for thread |thread|
+ // Get an object with the following data:
+ // - |state|: 'ok' (the avatars list could be retrieved) or 'private' (the
+ // thread is private, so the avatars list could not be retrieved).
+ // - |avatars|: list of at most |num| avatars for thread |thread|
getVisibleAvatars(thread, num = 3) {
- return this.shouldRetrieveAvatars(thread).then(shouldRetrieve => {
- if (!shouldRetrieve) {
- console.debug('[threadListAvatars] Skipping thread', thread);
- return [];
- }
+ return this.isPrivateThread(thread).then(isPrivate => {
+ if (isPrivate)
+ return {
+ state: 'private',
+ avatars: [],
+ };
return this.getVisibleAvatarsFromCacheAfterInvalidations(thread, num)
.then(res => {
@@ -243,7 +261,10 @@
err.name = 'notCached';
throw err;
}
- return res.entry.avatarUrls;
+ return {
+ state: 'ok',
+ avatars: res.entry.avatarUrls,
+ };
})
.catch(err => {
// If the name is "notCached", then this is not an actual error so
@@ -253,7 +274,18 @@
'[threadListAvatars] Error while accessing avatars cache:',
err);
- return this.getVisibleAvatarsFromServer(thread, num);
+ return this.getVisibleAvatarsFromServer(thread, num).then(res => {
+ if (res.isPrivate)
+ return {
+ state: 'private',
+ avatars: [],
+ };
+
+ return {
+ state: 'ok',
+ avatars: res.avatars,
+ };
+ });
});
});
}
@@ -275,17 +307,24 @@
}
this.getVisibleAvatars(thread)
- .then(avatarUrls => {
+ .then(res => {
var avatarsContainer = document.createElement('div');
avatarsContainer.classList.add('TWPT-avatars');
- var count = Math.floor(Math.random() * 4);
+ var avatarUrls = res.avatars;
- for (var i = 0; i < avatarUrls.length; ++i) {
+ if (res.state == 'private') {
var avatar = document.createElement('div');
- avatar.classList.add('TWPT-avatar');
- avatar.style.backgroundImage = 'url(\'' + avatarUrls[i] + '\')';
+ avatar.classList.add('TWPT-avatar-private-placeholder');
+ avatar.textContent = 'vpn_key';
avatarsContainer.appendChild(avatar);
+ } else {
+ for (var i = 0; i < avatarUrls.length; ++i) {
+ var avatar = document.createElement('div');
+ avatar.classList.add('TWPT-avatar');
+ avatar.style.backgroundImage = 'url(\'' + avatarUrls[i] + '\')';
+ avatarsContainer.appendChild(avatar);
+ }
}
header.appendChild(avatarsContainer);
diff --git a/src/static/css/thread_list_avatars.css b/src/static/css/thread_list_avatars.css
index 2dcb29b..beacbf1 100644
--- a/src/static/css/thread_list_avatars.css
+++ b/src/static/css/thread_list_avatars.css
@@ -6,19 +6,32 @@
margin-inline-start: 14px;
}
-.TWPT-avatars .TWPT-avatar {
+.TWPT-avatars .TWPT-avatar,
+ .TWPT-avatars .TWPT-avatar-private-placeholder {
height: 28px;
width: 28px;
align-self: center;
border-width: 0;
border-radius: 50%;
margin-inline-start: 6px;
+}
+
+.TWPT-avatars .TWPT-avatar {
background-color: white;
background-position: center;
background-size: contain;
background-repeat: no-repeat;
}
+.TWPT-avatars .TWPT-avatar-private-placeholder {
+ line-height: 28px;
+ text-align: center;
+ color: #35363a;
+ background-color: #dadce0;
+ font-size: 20px;
+ font-family: 'Google Material Icons';
+}
+
/*
* Changing styles of existing elements so the avatars fit.
*/