import * as mastodonAPI from '../api/mastodon.js'; import * as firefishAPI from '../api/firefish.js'; let client = false; export const server_types = { INCOMPATIBLE: "incompatible", MASTODON: "mastodon", FIREFISH: "firefish", }; const save_name = "spacesocial"; const versions_types = [ { version: "mastodon", type: server_types.MASTODON }, { version: "glitchsoc", type: server_types.MASTODON }, { version: "chuckya", type: server_types.MASTODON }, { version: "firefish", type: server_types.FIREFISH }, { version: "iceshrimp", type: server_types.FIREFISH }, { version: "sharkey", type: server_types.FIREFISH }, ]; export class Client { #api; instance; app; #cache; constructor() { this.instance = null; this.app = null; this.cache = { users: {}, emojis: {}, }; } static get() { if (client) return client; client = new Client(); window.peekie = client; client.load(); if (client.instance && client.instance !== server_types.INCOMPATIBLE) client.#configureAPI(); return client; } async init(host) { if (host.startsWith("https://")) host = host.substring(8); const url = `https://${host}/api/v1/instance`; const data = await fetch(url).then(res => res.json()).catch(error => { console.log(error) }); if (!data) { console.error(`Failed to connect to ${host}`); alert(`Failed to connect to ${host}! Please try again later.`); return false; } this.instance = { host: host, version: data.version, type: server_types.INCOMPATIBLE, }; for (let index in versions_types) { const pair = versions_types[index]; if (data.version.toLowerCase().includes(pair.version)) { this.instance.type = pair.type; break; } } if (this.instance.type == server_types.INCOMPATIBLE) { console.error(`Server ${host} is not supported - ${data.version}`); alert(`Sorry, this app is not compatible with ${host}!`); return false; } console.log(`Server is "${client.instance.type}" (or compatible).`); this.#configureAPI(); this.app = await this.api.createApp(host); if (!this.app || !this.instance) { console.error("Failed to create app. Check the network logs for details."); return false; } this.save(); return true; } #configureAPI() { switch (this.instance.type) { case server_types.MASTODON: this.api = mastodonAPI; break; case server_types.FIREFISH: this.api = firefishAPI; break; /* not opening that can of worms for a while case server_types.MISSKEY: this.api = misskeyAPI; break; */ default: break; } } getOAuthUrl() { return this.api.getOAuthUrl(this.app.secret); } async getToken(code) { const token = await this.api.getToken(code); if (!token) { console.error("Failed to obtain access token"); return false; } this.app.token = token; } async revokeToken() { return await this.api.revokeToken(); } async getTimeline(last_post_id) { return await this.api.getTimeline(last_post_id); } async getPost(post_id, num_replies) { return await this.api.getPost(post_id, num_replies); } putCacheUser(user) { this.cache.users[user.id] = user; } async getUser(user_id) { let user = this.cache.users[user_id]; if (user) return user; user = await this.api.getUser(user_id); if (user) return user; return false; } async getUserByMention(mention) { let users = Object.values(this.cache.users); for (let i in users) { const user = users[i]; if (user.mention == mention) return user; } return false; } putCacheEmoji(emoji) { this.cache.emojis[emoji.id] = emoji; } getEmoji(emoji_id) { let emoji = this.cache.emojis[emoji_id]; if (!emoji) return false; return emoji; } save() { localStorage.setItem(save_name, JSON.stringify({ instance: this.instance, app: this.app, })); } load() { let json = localStorage.getItem(save_name); if (!json) return false; let saved = JSON.parse(json); this.instance = saved.instance; this.app = saved.app; return true; } async logout() { if (!this.instance || !this.app || !this.api) return; if (!await this.revokeToken()) { console.warn("Failed to log out correctly; ditching the old tokens anyways."); } localStorage.removeItem(save_name); client = new Client(); console.log("Logged out successfully."); } }