From 4ce30be7110e1c358a914caec4318c7c1bb349af Mon Sep 17 00:00:00 2001 From: Midgard Date: Thu, 26 Mar 2020 23:25:35 +0100 Subject: [PATCH] Complete server manager and load channel list --- assets/main.css | 9 ++- index.html | 23 +++++-- main.js | 177 +++++++++++++++++++++++++++++++++--------------- 3 files changed, 146 insertions(+), 63 deletions(-) diff --git a/assets/main.css b/assets/main.css index 333b45f..58537ca 100644 --- a/assets/main.css +++ b/assets/main.css @@ -7,10 +7,13 @@ body { line-height: 1.5; } -input { +input, button { font: inherit; color: inherit; } +button { + cursor: pointer; +} h1 { text-align: center; @@ -23,8 +26,12 @@ h1 img { } #login { + display: none; text-align: center; } +#login:target { + display: block; +} #login table { margin: 0 auto 1em; } diff --git a/index.html b/index.html index 4107c26..9ba0a3c 100644 --- a/index.html +++ b/index.html @@ -14,26 +14,35 @@

Feathermost

-
-

Add a server

+ +
+
    + +
    + +
    +

    Add a server

    - - + +
    Server
    UsernameEmail or username
    Password
    - + +
    - +
    -
    
    +		
    +
    +		
    diff --git a/main.js b/main.js index 7bbd88a..6c6a632 100644 --- a/main.js +++ b/main.js @@ -10,34 +10,37 @@ function byId(id, nullOk=false) { const LOCALSTORAGE_KEY_SERVER = "mattermostServer"; -const RE_SERVER_ITEM = new RegExp(`^${LOCALSTORAGE_KEY_SERVER}_(.*)\$`, "").compile(); -class Storage { +const RE_SERVER_ITEM = new RegExp(`^${LOCALSTORAGE_KEY_SERVER}_(.*)\$`, ""); +const Storage = { getServers() { let servers = []; for (var i = 0; i < window.localStorage.length; i++) { const key = window.localStorage.key(i); const matches = key.match(RE_SERVER_ITEM); if (matches) { - servers.push(matches[1]); + const endpoint = matches[1]; + console.debug(`Found logged in endpoint ${endpoint}`); + let stored = JSON.parse(window.localStorage.getItem(Storage._key_for(endpoint))); + servers.push({...stored, endpoint}); } } return servers; - } + }, _key_for(endpoint) { return `${LOCALSTORAGE_KEY_SERVER}_${endpoint}`; - } + }, clear(endpoint) { - window.localStorage.removeItem(this._key_for(endpoint)); - } + window.localStorage.removeItem(Storage._key_for(endpoint)); + }, store(endpoint, login_id, token) { - window.localStorage.setItem(this._key_for(endpoint), JSON.stringify({login_id, token})); - } + window.localStorage.setItem(Storage._key_for(endpoint), JSON.stringify({login_id, token})); + }, get(endpoint) { - return JSON.parse(window.localStorage.getItem(this._key_for(endpoint)) || "null"); + return JSON.parse(window.localStorage.getItem(Storage._key_for(endpoint)) || "null"); } } @@ -124,11 +127,13 @@ class MattermostClient { } async myTeams() { + const stored = this.storage.get(this.api.id); const response = await this.api.get("/users/me/teams", stored.token); return response.responseJson; } async myChannels(team_id) { + const stored = this.storage.get(this.api.id); const response = await this.api.get(`/users/me/teams/${team_id}/channels`, stored.token); return response.responseJson; } @@ -148,82 +153,144 @@ function normalizedEndpoint(endpoint) { return `${protocol}${domain}${path}`; } +function humanReadableEndpoint(endpoint) { + let matches = endpoint.match(/^(https?:\/\/.+)\/api\/v4$/i); + if (!matches) throw Error("Invalid endpoint URL"); + + return matches[1]; +} function createClient(endpoint) { const api = new MattermostApi(normalizedEndpoint(endpoint)); - const storage = new Storage(); - return new MattermostClient(api, storage); + return new MattermostClient(api, Storage); } function buttonDisable(element, text) { - element.value = text; + if (!element.dataset.originalText) { + element.dataset.originalText = element.innerText; + } + element.innerText = text; element.disabled = true; } -function buttonEnable(element, text) { - element.value = text; +function buttonEnable(element) { + element.innerText = element.dataset.originalText; element.disabled = false; } +function populateServerSelectionList() { + const servers = Storage.getServers(); + + let nodes = []; + for (let server of servers) { + const li = document.createElement("li"); + const endpoint = humanReadableEndpoint(server.endpoint); + li.innerText = `${server.login_id}@${endpoint} `; + + const logoutButton = document.createElement("button"); + logoutButton.className = "logout"; + logoutButton.innerText = "Log out"; + logoutButton.addEventListener("click", e => logOut(endpoint, e.currentTarget)); + + li.appendChild(logoutButton); + nodes.push(li); + } + byId("server_selection_list").innerHTML = ""; + byId("server_selection_list").append(...nodes); +} +populateServerSelectionList(); + +function populateChannelList() { + async function addChannelItems(endpoint) { + const client = createClient(endpoint); + + const teams = await client.myTeams(); + console.log(teams); + + for (let team of teams) { + let nodes = []; + const channels = await client.myChannels(team.id); + console.log(channels); + for (let channel of channels) { + const li = document.createElement("li"); + const a = document.createElement("a"); + a.href = "javascript:void(0)"; + switch (channel.type) { + case "O": // Public channel + a.innerText = `${team.name}/${channel.name}`; + break; + case "P": // Private channel + a.innerText = `🔒 ${team.name}/${channel.name}`; + break; + case "D": // Direct message + a.innerText = `👤 ...`; + break; + case "G": // Group chat + a.innerText = `👥 ${channel.display_name}`; + break; + default: // Unsupported + a.innerText = `${channel.type} ${team.name}/${channel.name}`; + break; + } + a.addEventListener("click", () => switchToChannel(team.id, channel.id)); + li.appendChild(a); + nodes.push(li); + } + byId("channel_list").append(...nodes); + } + } + + const servers = Storage.getServers(); + + byId("channel_list").innerHTML = ""; + for (let server of servers) { + addChannelItems(server.endpoint); + } +} +populateChannelList(); + function logIn() { - byId("user_json").innerText = ""; + const client = createClient(byId("login_server").value); - const client = createClient(byId("server").value); + buttonDisable(byId("login_button"), "Logging in..."); - buttonDisable(byId("login"), "Logging in..."); - - client.logIn(byId("username").value, byId("password").value) + client.logIn(byId("login_login_id").value, byId("login_password").value) .then(json => { - buttonEnable(byId("login"), "Logged in"); + buttonEnable(byId("login_button")); byId("login_message").innerText = ""; - byId("user_json").innerText = JSON.stringify(json, null, 2); + byId("channel_list").innerText = `Logged in as ${json.username}`; + window.location = "#"; + populateServerSelectionList(); + populateChannelList(); }) .catch(error => { console.error(error); - buttonEnable(byId("login"), "Could not log in"); + buttonEnable(byId("login_button")); byId("login_message").innerText = `${error}`; }); } -function logOut() { - const client = createClient(byId("server").value); +function logOut(endpoint, button) { + const client = createClient(endpoint); - buttonDisable(byId("logout"), "Logging out..."); + buttonDisable(button, "Logging out..."); client.logOut() .then(response => { - buttonEnable(byId("logout"), "Logged out"); - byId("logout_message").innerText = ""; - byId("user_json").innerText = ""; + console.info("Succesfully logged out"); + populateServerSelectionList(); + populateChannelList(); }) .catch(error => { console.error(error); - buttonEnable(byId("logout"), "Could not log out"); - byId("logout_message").innerText = `${error}`; + const span = document.createElement("span"); + span.innerText = `Failed to log out: ${error.message}`; + button.parentElement.appendChild(span); + buttonEnable(button); }); } -function validateToken() { - byId("user_json").innerText = ""; - - const client = createClient(byId("server").value); - - buttonDisable(byId("validate"), "Validating token..."); - - let cred = client.storage.get(client.api.id); - if (!cred || !cred.token) { - buttonEnable(byId("validate"), "No token, log in first"); - return; - } - - client.usersMe() - .then(json => { - buttonEnable(byId("validate"), "Validation succeeded"); - byId("validate_message").innerText = ""; - byId("user_json").innerText = JSON.stringify(json, null, 2); - }) - .catch(error => { - console.error(error); - buttonEnable(byId("validate"), "Validation failed"); - byId("validate_message").innerText = `${error}`; - }); +function switchToChannel(team_id, channel_id) { + window.location = "#channel_contents"; } + +byId("login_button").addEventListener("click", logIn);