Fun with funretro.io
Just a few functions whipped up while bending the rules on https://funretro.io.
function parseUrl() {
const match = window.location.href.match(/board[/](?<teamId>[^/]+)[/](?<boardId>[^/?]+)/);
if (match) {
return match.groups;
}
throw new Error();
}
function getBoardId() {
return parseUrl().boardId;
}
/** @returns [string] ID of the closest message/board/vote ID */
function getMessageId(element) {
const match = closest(element, '[messageid]');
return match ? match.getAttribute('messageid') : match;
}
/** @returns [null|Element] Closest match of selector from element to document root */
function closest(element, selector) {
if (element === null) {
return element;
}
const match = element.querySelector(selector);
return match
? match
: closest(element.parentElement, selector);
}
function getVoteCount(id) {
const element = document.querySelector(`[messageid="${id}"] .show-vote-count`);
return Number( element.innerText.trim() );
}
function getUserStorageKey() {
const keys =
Object.keys(localStorage)
.filter(k => !k.startsWith('firebase'));
if (keys.length === 0) {
return getBoardId();
}
if (keys.length !== 1) {
throw new Error();
}
return keys[0];
}
/** @returns { key: string, item: any } */
function getUserStorage() {
const key = getUserStorageKey();
return {
key,
item: JSON.parse( localStorage.getItem( key ) ) || {},
};
}
function setUserStorage(key, obj) {
localStorage.setItem(key, JSON.stringify(obj));
}
function setAvailableVotes(id, value) {
const { key, item } = getUserStorage();
item[id] = value;
setUserStorage(key, item);
}
//------------------------------------------------------------- firebase -----
/** @returns [Promise<Message>] */
async function getMessages(boardId) {
const messages =
await firebase
.database()
.ref(`messages/${boardId}`)
.once('value');
return messages.toJSON();
}
/** @returns [String|null] */
function getCurrentUserId() {
const user = firebase.auth().currentUser;
return user ? user.uid : user;
}
/** @returns [Promise<String[]>] */
async function getTeamIds() {
const userId = getCurrentUserId();
const teams =
await firebase
.database()
.ref(`/users/${userId}/teams`)
.once('value');
return Object.keys(teams.toJSON() || {});
}
/** @returns [Promise<Object>] */
async function getTeamMembers(teamId) {
const team =
await firebase
.database()
.ref(`/teams/${teamId}`)
.once('value');
const { members, teamName, user_id: managerId } = team.toJSON();
return {
...members,
[managerId]: `Manager of "${teamName}"`,
};
}
/** @returns [Promise<Object>] */
async function getAllTeamMembers() {
const teamIds = await getTeamIds();
const members = await Promise.all(teamIds.map(getTeamMembers));
return members.reduce(
function merge(result, teamMembers) {
for (const [ userId, emailAddress ] of Object.entries(teamMembers)) {
if (typeof emailAddress !== 'string') throw new Error();
result[userId]
? result[userId].add(emailAddress)
: result[userId] = new Set([emailAddress])
}
return result;
},
{},
);
}
/** @returns [Void] **/
async function addAuthors() {
const boardId = getBoardId();
const messages = await getMessages(boardId);
const emailsByUserId = await getAllTeamMembers();
Object.keys(messages)
.forEach(id => {
const messageUserId = messages[id].user_id;
const emailAddresses = emailsByUserId[messageUserId];
if (!emailAddresses) {
debugger;
}
const author = emailAddresses
? Array.from(emailAddresses)[0]
: 'Unknown';
const front = document.querySelector(`[messageid="${id}"] .front`);
let a = front.querySelector('.author');
if (!a) {
a = document.createElement('div');
a.style.cssText = 'bottom: 0; font-size: 0.8em; left: 0; position: absolute;';
a.className = 'author';
}
a.innerHTML = `> ${author}`;
front.appendChild(a);
})
}
With the functions above, you can tell funretro.io that you've made the votes on an inspected element:
var id = getMessageId($0);
setAvailableVotes( id, getVoteCount(id) );
// The refresh is required to update Angular's data model
window.location.reload(true);
Add the authors to the messages on a board:
addAuthors()
``