migrate to unified "capabilities" API
This commit is contained in:
parent
7779845056
commit
191212c940
|
@ -1,6 +1,6 @@
|
||||||
<script>
|
<script>
|
||||||
import Feed from './Feed.svelte';
|
import Feed from './Feed.svelte';
|
||||||
import { Client, server_types } from './client/client.js';
|
import { Client } from './client/client.js';
|
||||||
|
|
||||||
let ready = Client.get().app && Client.get().app.token;
|
let ready = Client.get().app && Client.get().app.token;
|
||||||
|
|
||||||
|
@ -25,7 +25,6 @@
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function log_out() {
|
function log_out() {
|
||||||
Client.get().logout().then(() => {
|
Client.get().logout().then(() => {
|
||||||
ready = false;
|
ready = false;
|
||||||
|
|
|
@ -1,98 +0,0 @@
|
||||||
import { Client } from '../client/client.js';
|
|
||||||
import Post from '../post/post.js';
|
|
||||||
import User from '../user/user.js';
|
|
||||||
import Emoji from '../emoji.js';
|
|
||||||
|
|
||||||
import * as mastodonAPI from './mastodon.js';
|
|
||||||
|
|
||||||
export async function createApp(host) {
|
|
||||||
return await mastodonAPI.createApp(host);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getOAuthUrl() {
|
|
||||||
return mastodonAPI.getOAuthUrl();
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getToken(code) {
|
|
||||||
return await mastodonAPI.getToken(code);
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function revokeToken() {
|
|
||||||
return await mastodonAPI.revokeToken();
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getTimeline(last_post_id) {
|
|
||||||
return await mastodonAPI.getTimeline(last_post_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getPost(post_id, num_replies) {
|
|
||||||
return await mastodonAPI.getPost(post_id, num_replies);
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function parsePost(data, num_replies) {
|
|
||||||
let client = Client.get();
|
|
||||||
let post = new Post()
|
|
||||||
post.id = data.id;
|
|
||||||
post.created_at = new Date(data.created_at);
|
|
||||||
post.user = await Client.get().api.parseUser(data.account);
|
|
||||||
post.text = data.text;
|
|
||||||
post.warning = data.spoiler_text;
|
|
||||||
post.boost_count = data.reblogs_count;
|
|
||||||
post.reply_count = data.replies_count;
|
|
||||||
post.mentions = data.mentions;
|
|
||||||
post.reactions = data.reactions;
|
|
||||||
post.files = data.media_attachments;
|
|
||||||
post.url = data.url;
|
|
||||||
post.reply = data.in_reply_to_id && num_replies > 0 ? await getPost(data.in_reply_to_id, num_replies - 1) : null;
|
|
||||||
post.boost = data.reblog ? await parsePost(data.reblog, 1) : null;
|
|
||||||
post.emojis = [];
|
|
||||||
data.emojis.forEach(emoji_data => {
|
|
||||||
let name = emoji_data.shortcode.split('@')[0];
|
|
||||||
post.emojis.push(Client.get().api.parseEmoji({
|
|
||||||
id: name + '@' + post.user.host,
|
|
||||||
name: name,
|
|
||||||
host: post.user.host,
|
|
||||||
url: emoji_data.url,
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
post.reactions = [];
|
|
||||||
data.reactions.forEach(reaction_data => {
|
|
||||||
if (/^[\w\-.@]+$/g.exec(reaction_data.name)) {
|
|
||||||
let name = reaction_data.name.split('@')[0];
|
|
||||||
let host = reaction_data.name.includes('@') ? reaction_data.name.split('@')[1] : client.instance.host;
|
|
||||||
post.reactions.push({
|
|
||||||
count: reaction_data.count,
|
|
||||||
emoji: Client.get().api.parseEmoji({
|
|
||||||
id: name + '@' + host,
|
|
||||||
name: name,
|
|
||||||
host: host,
|
|
||||||
url: reaction_data.url,
|
|
||||||
}),
|
|
||||||
me: reaction_data.me,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
if (reaction_data.name == '❤') reaction_data.name = '❤️'; // stupid heart unicode
|
|
||||||
post.reactions.push({
|
|
||||||
count: reaction_data.count,
|
|
||||||
emoji: {
|
|
||||||
html: reaction_data.name,
|
|
||||||
name: reaction_data.name,
|
|
||||||
},
|
|
||||||
me: reaction_data.me,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return post;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function parseUser(data) {
|
|
||||||
return mastodonAPI.parseUser(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function parseEmoji(data) {
|
|
||||||
return mastodonAPI.parseEmoji(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getUser(user_id) {
|
|
||||||
return mastodonAPI.getUser(user_id);
|
|
||||||
}
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { Client } from '../client/client.js';
|
import { Client } from '../client/client.js';
|
||||||
|
import { capabilities } from '../client/instance.js';
|
||||||
import Post from '../post/post.js';
|
import Post from '../post/post.js';
|
||||||
import User from '../user/user.js';
|
import User from '../user/user.js';
|
||||||
import Emoji from '../emoji.js';
|
import Emoji from '../emoji.js';
|
||||||
|
@ -94,13 +95,15 @@ export async function getTimeline(last_post_id) {
|
||||||
let posts = [];
|
let posts = [];
|
||||||
for (let i in data) {
|
for (let i in data) {
|
||||||
const post_data = data[i];
|
const post_data = data[i];
|
||||||
const post = await client.api.parsePost(post_data, 1);
|
const post = await parsePost(post_data, 1);
|
||||||
if (!post) {
|
if (!post) {
|
||||||
if (post_data.id) {
|
if (post === null || post === undefined) {
|
||||||
console.warn("Failed to parse post #" + post_data.id);
|
if (post_data.id) {
|
||||||
} else {
|
console.warn("Failed to parse post #" + post_data.id);
|
||||||
console.warn("Failed to parse post:");
|
} else {
|
||||||
console.warn(post_data);
|
console.warn("Failed to parse post:");
|
||||||
|
console.warn(post_data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -115,10 +118,12 @@ export async function getPost(post_id, num_replies) {
|
||||||
const data = await fetch(url, {
|
const data = await fetch(url, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
headers: { "Authorization": "Bearer " + client.app.token }
|
headers: { "Authorization": "Bearer " + client.app.token }
|
||||||
}).then(res => res.json());
|
}).then(res => { res.ok ? res.json() : false });
|
||||||
|
|
||||||
const post = await client.api.parsePost(data, num_replies);
|
if (!data) return null;
|
||||||
if (!post) {
|
|
||||||
|
const post = await parsePost(data, num_replies);
|
||||||
|
if (post === null || post === undefined) {
|
||||||
if (data.id) {
|
if (data.id) {
|
||||||
console.warn("Failed to parse post data #" + data.id);
|
console.warn("Failed to parse post data #" + data.id);
|
||||||
} else {
|
} else {
|
||||||
|
@ -133,56 +138,72 @@ export async function getPost(post_id, num_replies) {
|
||||||
export async function parsePost(data, num_replies) {
|
export async function parsePost(data, num_replies) {
|
||||||
let client = Client.get();
|
let client = Client.get();
|
||||||
let post = new Post()
|
let post = new Post()
|
||||||
|
|
||||||
post.id = data.id;
|
post.id = data.id;
|
||||||
post.created_at = new Date(data.created_at);
|
post.created_at = new Date(data.created_at);
|
||||||
post.user = await Client.get().api.parseUser(data.account);
|
post.user = await parseUser(data.account);
|
||||||
post.text = data.content;
|
|
||||||
|
if (client.instance.capabilities.includes(capabilities.MARKDOWN_CONTENT))
|
||||||
|
post.text = data.text;
|
||||||
|
else
|
||||||
|
post.text = data.content;
|
||||||
|
|
||||||
post.warning = data.spoiler_text;
|
post.warning = data.spoiler_text;
|
||||||
post.boost_count = data.reblogs_count;
|
post.boost_count = data.reblogs_count;
|
||||||
post.reply_count = data.replies_count;
|
post.reply_count = data.replies_count;
|
||||||
post.mentions = data.mentions;
|
post.mentions = data.mentions;
|
||||||
post.reactions = data.reactions;
|
|
||||||
post.files = data.media_attachments;
|
post.files = data.media_attachments;
|
||||||
post.url = data.url;
|
post.url = data.url;
|
||||||
post.reply = data.in_reply_to_id && num_replies > 0 ? await getPost(data.in_reply_to_id, num_replies - 1) : null;
|
|
||||||
|
post.reply = null;
|
||||||
|
if (data.in_reply_to_id && num_replies > 0) {
|
||||||
|
post.reply = await getPost(data.in_reply_to_id, num_replies - 1);
|
||||||
|
// if the post returns null, we probably don't have permission to read it.
|
||||||
|
// we'll respect the thread's privacy, and leave it alone :)
|
||||||
|
if (post.reply === null) return false;
|
||||||
|
}
|
||||||
post.boost = data.reblog ? await parsePost(data.reblog, 1) : null;
|
post.boost = data.reblog ? await parsePost(data.reblog, 1) : null;
|
||||||
|
|
||||||
post.emojis = [];
|
post.emojis = [];
|
||||||
data.emojis.forEach(emoji_data => {
|
data.emojis.forEach(emoji_data => {
|
||||||
let name = emoji_data.shortcode.split('@')[0];
|
let name = emoji_data.shortcode.split('@')[0];
|
||||||
post.emojis.push(Client.get().api.parseEmoji({
|
post.emojis.push(parseEmoji({
|
||||||
id: name + '@' + post.user.host,
|
id: name + '@' + post.user.host,
|
||||||
name: name,
|
name: name,
|
||||||
host: post.user.host,
|
host: post.user.host,
|
||||||
url: emoji_data.url,
|
url: emoji_data.url,
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
post.reactions = [];
|
|
||||||
data.reactions.forEach(reaction_data => {
|
if (client.instance.capabilities.includes(capabilities.REACTIONS)) {
|
||||||
if (/^[\w\-.@]+$/g.exec(reaction_data.name)) {
|
post.reactions = [];
|
||||||
let name = reaction_data.name.split('@')[0];
|
data.reactions.forEach(reaction_data => {
|
||||||
let host = reaction_data.name.includes('@') ? reaction_data.name.split('@')[1] : client.instance.host;
|
if (/^[\w\-.@]+$/g.exec(reaction_data.name)) {
|
||||||
post.reactions.push({
|
let name = reaction_data.name.split('@')[0];
|
||||||
count: reaction_data.count,
|
let host = reaction_data.name.includes('@') ? reaction_data.name.split('@')[1] : client.instance.host;
|
||||||
emoji: Client.get().api.parseEmoji({
|
post.reactions.push({
|
||||||
id: name + '@' + host,
|
count: reaction_data.count,
|
||||||
name: name,
|
emoji: parseEmoji({
|
||||||
host: host,
|
id: name + '@' + host,
|
||||||
url: reaction_data.url,
|
name: name,
|
||||||
}),
|
host: host,
|
||||||
me: reaction_data.me,
|
url: reaction_data.url,
|
||||||
});
|
}),
|
||||||
} else {
|
me: reaction_data.me,
|
||||||
if (reaction_data.name == '❤') reaction_data.name = '❤️'; // stupid heart unicode
|
});
|
||||||
post.reactions.push({
|
} else {
|
||||||
count: reaction_data.count,
|
if (reaction_data.name == '❤') reaction_data.name = '❤️'; // stupid heart unicode
|
||||||
emoji: {
|
post.reactions.push({
|
||||||
html: reaction_data.name,
|
count: reaction_data.count,
|
||||||
name: reaction_data.name,
|
emoji: {
|
||||||
},
|
html: reaction_data.name,
|
||||||
me: reaction_data.me,
|
name: reaction_data.name,
|
||||||
});
|
},
|
||||||
}
|
me: reaction_data.me,
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
return post;
|
return post;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -201,7 +222,7 @@ export async function parseUser(data) {
|
||||||
emoji_data.id = emoji_data.shortcode + '@' + user.host;
|
emoji_data.id = emoji_data.shortcode + '@' + user.host;
|
||||||
emoji_data.name = emoji_data.shortcode;
|
emoji_data.name = emoji_data.shortcode;
|
||||||
emoji_data.host = user.host;
|
emoji_data.host = user.host;
|
||||||
user.emojis.push(Client.get().api.parseEmoji(emoji_data));
|
user.emojis.push(parseEmoji(emoji_data));
|
||||||
});
|
});
|
||||||
Client.get().putCacheUser(user);
|
Client.get().putCacheUser(user);
|
||||||
return user;
|
return user;
|
||||||
|
@ -226,8 +247,8 @@ export async function getUser(user_id) {
|
||||||
headers: { "Authorization": "Bearer " + client.app.token }
|
headers: { "Authorization": "Bearer " + client.app.token }
|
||||||
}).then(res => res.json());
|
}).then(res => res.json());
|
||||||
|
|
||||||
const user = await Client.get().api.parseUser(data);
|
const user = await parseUser(data);
|
||||||
if (!post) {
|
if (user === null || user === undefined) {
|
||||||
if (data.id) {
|
if (data.id) {
|
||||||
console.warn("Failed to parse user data #" + data.id);
|
console.warn("Failed to parse user data #" + data.id);
|
||||||
} else {
|
} else {
|
|
@ -1,27 +1,11 @@
|
||||||
import * as mastodonAPI from '../api/mastodon.js';
|
import { Instance, server_types } from './instance.js';
|
||||||
import * as firefishAPI from '../api/firefish.js';
|
import * as api from './api.js';
|
||||||
|
|
||||||
let client = false;
|
let client = false;
|
||||||
|
|
||||||
export const server_types = {
|
|
||||||
INCOMPATIBLE: "incompatible",
|
|
||||||
MASTODON: "mastodon",
|
|
||||||
FIREFISH: "firefish",
|
|
||||||
};
|
|
||||||
|
|
||||||
const save_name = "spacesocial";
|
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 {
|
export class Client {
|
||||||
#api;
|
|
||||||
instance;
|
instance;
|
||||||
app;
|
app;
|
||||||
#cache;
|
#cache;
|
||||||
|
@ -40,44 +24,29 @@ export class Client {
|
||||||
client = new Client();
|
client = new Client();
|
||||||
window.peekie = client;
|
window.peekie = client;
|
||||||
client.load();
|
client.load();
|
||||||
if (client.instance && client.instance !== server_types.INCOMPATIBLE)
|
|
||||||
client.#configureAPI();
|
|
||||||
return client;
|
return client;
|
||||||
}
|
}
|
||||||
|
|
||||||
async init(host) {
|
async init(host) {
|
||||||
if (host.startsWith("https://")) host = host.substring(8);
|
if (host.startsWith("https://")) host = host.substring(8);
|
||||||
const url = `https://${host}/api/v1/instance`;
|
const url = `https://${host}/api/v1/instance`;
|
||||||
const data = await fetch(url).then(res => res.json()).catch(error => { console.log(error) });
|
const data = await fetch(url).then(res => res.json()).catch(error => { console.error(error) });
|
||||||
if (!data) {
|
if (!data) {
|
||||||
console.error(`Failed to connect to ${host}`);
|
console.error(`Failed to connect to ${host}`);
|
||||||
alert(`Failed to connect to ${host}! Please try again later.`);
|
alert(`Failed to connect to ${host}! Please try again later.`);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
this.instance = {
|
|
||||||
host: host,
|
this.instance = new Instance(host, data.version);
|
||||||
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) {
|
if (this.instance.type == server_types.INCOMPATIBLE) {
|
||||||
console.error(`Server ${host} is not supported - ${data.version}`);
|
console.error(`Server ${host} is not supported - ${data.version}`);
|
||||||
alert(`Sorry, this app is not compatible with ${host}!`);
|
alert(`Sorry, this app is not compatible with ${host}!`);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`Server is "${client.instance.type}" (or compatible).`);
|
console.log(`Server is "${this.instance.type}" (or compatible) with capabilities: [${this.instance.capabilities}].`);
|
||||||
|
|
||||||
this.#configureAPI();
|
this.app = await api.createApp(host);
|
||||||
this.app = await this.api.createApp(host);
|
|
||||||
|
|
||||||
if (!this.app || !this.instance) {
|
if (!this.app || !this.instance) {
|
||||||
console.error("Failed to create app. Check the network logs for details.");
|
console.error("Failed to create app. Check the network logs for details.");
|
||||||
|
@ -89,29 +58,12 @@ export class Client {
|
||||||
return true;
|
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() {
|
getOAuthUrl() {
|
||||||
return this.api.getOAuthUrl(this.app.secret);
|
return api.getOAuthUrl(this.app.secret);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getToken(code) {
|
async getToken(code) {
|
||||||
const token = await this.api.getToken(code);
|
const token = await api.getToken(code);
|
||||||
if (!token) {
|
if (!token) {
|
||||||
console.error("Failed to obtain access token");
|
console.error("Failed to obtain access token");
|
||||||
return false;
|
return false;
|
||||||
|
@ -120,15 +72,15 @@ export class Client {
|
||||||
}
|
}
|
||||||
|
|
||||||
async revokeToken() {
|
async revokeToken() {
|
||||||
return await this.api.revokeToken();
|
return await api.revokeToken();
|
||||||
}
|
}
|
||||||
|
|
||||||
async getTimeline(last_post_id) {
|
async getTimeline(last_post_id) {
|
||||||
return await this.api.getTimeline(last_post_id);
|
return await api.getTimeline(last_post_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getPost(post_id, num_replies) {
|
async getPost(post_id, num_replies) {
|
||||||
return await this.api.getPost(post_id, num_replies);
|
return await api.getPost(post_id, num_replies);
|
||||||
}
|
}
|
||||||
|
|
||||||
putCacheUser(user) {
|
putCacheUser(user) {
|
||||||
|
@ -139,7 +91,7 @@ export class Client {
|
||||||
let user = this.cache.users[user_id];
|
let user = this.cache.users[user_id];
|
||||||
if (user) return user;
|
if (user) return user;
|
||||||
|
|
||||||
user = await this.api.getUser(user_id);
|
user = await api.getUser(user_id);
|
||||||
if (user) return user;
|
if (user) return user;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
@ -166,7 +118,10 @@ export class Client {
|
||||||
|
|
||||||
save() {
|
save() {
|
||||||
localStorage.setItem(save_name, JSON.stringify({
|
localStorage.setItem(save_name, JSON.stringify({
|
||||||
instance: this.instance,
|
instance: {
|
||||||
|
host: this.instance.host,
|
||||||
|
version: this.instance.version,
|
||||||
|
},
|
||||||
app: this.app,
|
app: this.app,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
@ -175,13 +130,13 @@ export class Client {
|
||||||
let json = localStorage.getItem(save_name);
|
let json = localStorage.getItem(save_name);
|
||||||
if (!json) return false;
|
if (!json) return false;
|
||||||
let saved = JSON.parse(json);
|
let saved = JSON.parse(json);
|
||||||
this.instance = saved.instance;
|
this.instance = new Instance(saved.instance.host, saved.instance.version);
|
||||||
this.app = saved.app;
|
this.app = saved.app;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
async logout() {
|
async logout() {
|
||||||
if (!this.instance || !this.app || !this.api) return;
|
if (!this.instance || !this.app) return;
|
||||||
if (!await this.revokeToken()) {
|
if (!await this.revokeToken()) {
|
||||||
console.warn("Failed to log out correctly; ditching the old tokens anyways.");
|
console.warn("Failed to log out correctly; ditching the old tokens anyways.");
|
||||||
}
|
}
|
||||||
|
|
68
src/client/instance.js
Normal file
68
src/client/instance.js
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
export const server_types = {
|
||||||
|
UNSUPPORTED: "unsupported",
|
||||||
|
MASTODON: "mastodon",
|
||||||
|
GLITCHSOC: "glitchsoc",
|
||||||
|
CHUCKYA: "chuckya",
|
||||||
|
FIREFISH: "firefish",
|
||||||
|
ICESHRIMP: "iceshrimp",
|
||||||
|
SHARKEY: "sharkey",
|
||||||
|
};
|
||||||
|
|
||||||
|
export const capabilities = {
|
||||||
|
MARKDOWN_CONTENT: "mdcontent",
|
||||||
|
REACTIONS: "reactions",
|
||||||
|
};
|
||||||
|
|
||||||
|
export class Instance {
|
||||||
|
host;
|
||||||
|
version;
|
||||||
|
capabilities;
|
||||||
|
type = server_types.UNSUPPORTED;
|
||||||
|
|
||||||
|
constructor(host, version) {
|
||||||
|
this.host = host;
|
||||||
|
this.version = version;
|
||||||
|
this.#setType(version);
|
||||||
|
this.capabilities = this.#getCapabilities(this.type);
|
||||||
|
}
|
||||||
|
|
||||||
|
#setType(version) {
|
||||||
|
if (version.constructor !== String) return;
|
||||||
|
let version_lower = version.toLowerCase();
|
||||||
|
for (let i = 1; i < Object.keys(server_types).length; i++) {
|
||||||
|
const check_type = Object.values(server_types)[i];
|
||||||
|
if (version_lower.includes(check_type)) {
|
||||||
|
this.type = check_type;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this.type === server_types.UNSUPPORTED) return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#getCapabilities(type) {
|
||||||
|
let c = [];
|
||||||
|
switch (type) {
|
||||||
|
case server_types.MASTODON:
|
||||||
|
break;
|
||||||
|
case server_types.GLITCHSOC:
|
||||||
|
c.push(capabilities.REACTIONS);
|
||||||
|
break;
|
||||||
|
case server_types.CHUCKYA:
|
||||||
|
c.push(capabilities.REACTIONS);
|
||||||
|
break;
|
||||||
|
case server_types.FIREFISH:
|
||||||
|
c.push(capabilities.REACTIONS);
|
||||||
|
break;
|
||||||
|
case server_types.ICESHRIMP:
|
||||||
|
c.push(capabilities.MARKDOWN_CONTENT);
|
||||||
|
c.push(capabilities.REACTIONS);
|
||||||
|
break;
|
||||||
|
case server_types.SHARKEY:
|
||||||
|
c.push(capabilities.REACTIONS);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
import { Client, server_types } from '../client/client.js';
|
import { Client } from '../client/client.js';
|
||||||
|
import { capabilities, server_types } from '../client/instance.js';
|
||||||
import { parseOne as parseEmoji, EMOJI_REGEX } from '../emoji.js';
|
import { parseOne as parseEmoji, EMOJI_REGEX } from '../emoji.js';
|
||||||
|
|
||||||
export default class Post {
|
export default class Post {
|
||||||
|
@ -20,33 +21,36 @@ export default class Post {
|
||||||
async rich_text() {
|
async rich_text() {
|
||||||
let text = this.text;
|
let text = this.text;
|
||||||
if (!text) return text;
|
if (!text) return text;
|
||||||
|
let client = Client.get();
|
||||||
|
|
||||||
const markdown_tokens = [
|
const markdown_tokens = [
|
||||||
{ tag: "pre", token: "```" },
|
{ tag: "pre", token: "```" },
|
||||||
{ tag: "code", token: "`" },
|
{ tag: "code", token: "`" },
|
||||||
{ tag: "strong", token: "**", regex: /\*{2}/g },
|
{ tag: "strong", token: "**" },
|
||||||
{ tag: "strong", token: "__" },
|
{ tag: "strong", token: "__" },
|
||||||
{ tag: "em", token: "*", regex: /\*/g },
|
{ tag: "em", token: "*" },
|
||||||
{ tag: "em", token: "_" },
|
{ tag: "em", token: "_" },
|
||||||
];
|
];
|
||||||
|
|
||||||
let response = "";
|
let response = "";
|
||||||
let current;
|
let md_layer;
|
||||||
let index = 0;
|
let index = 0;
|
||||||
while (index < text.length) {
|
while (index < text.length) {
|
||||||
let sample = text.substring(index);
|
let sample = text.substring(index);
|
||||||
let allow_new = !current || !current.nostack;
|
let md_nostack = !(md_layer && md_layer.nostack);
|
||||||
|
|
||||||
// handle newlines
|
// handle newlines
|
||||||
if (allow_new && sample.startsWith('\n')) {
|
if (md_nostack && sample.startsWith('\n')) {
|
||||||
response += "<br>";
|
response += "<br>";
|
||||||
index++;
|
index++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// handle mentions
|
// handle mentions
|
||||||
// TODO: setup a better system for handling different server capabilities
|
if (client.instance.capabilities.includes(capabilities.MARKDOWN_CONTENT)
|
||||||
if (Client.get().instance.type !== server_types.MASTODON && allow_new && sample.match(/^@[\w\-.]+@[\w\-.]+/g)) {
|
&& md_nostack
|
||||||
|
&& sample.match(/^@[\w\-.]+@[\w\-.]+/g)
|
||||||
|
) {
|
||||||
// find end of the mention
|
// find end of the mention
|
||||||
let length = 1;
|
let length = 1;
|
||||||
while (index + length < text.length && /[a-z0-9-_.]/.test(text[index + length])) length++;
|
while (index + length < text.length && /[a-z0-9-_.]/.test(text[index + length])) length++;
|
||||||
|
@ -56,12 +60,12 @@ export default class Post {
|
||||||
let mention = text.substring(index, index + length);
|
let mention = text.substring(index, index + length);
|
||||||
|
|
||||||
// attempt to resolve mention to a user
|
// attempt to resolve mention to a user
|
||||||
let user = await Client.get().getUserByMention(mention);
|
let user = await client.getUserByMention(mention);
|
||||||
if (user) {
|
if (user) {
|
||||||
const out = `<a href="/${user.mention}" class="mention">` +
|
const out = `<a href="/${user.mention}" class="mention">` +
|
||||||
`<img src="${user.avatar_url}" class="mention-avatar" width="20" height="20">` +
|
`<img src="${user.avatar_url}" class="mention-avatar" width="20" height="20">` +
|
||||||
'@' + user.username + '@' + user.host + "</a>";
|
'@' + user.username + '@' + user.host + "</a>";
|
||||||
if (current) current.text += out;
|
if (md_layer) md_layer.text += out;
|
||||||
else response += out;
|
else response += out;
|
||||||
} else {
|
} else {
|
||||||
response += mention;
|
response += mention;
|
||||||
|
@ -71,72 +75,75 @@ export default class Post {
|
||||||
}
|
}
|
||||||
|
|
||||||
// handle links
|
// handle links
|
||||||
if (Client.get().instance.type !== server_types.MASTODON && allow_new && sample.match(/^[a-z]{3,6}:\/\/[^\s]+/g)) {
|
if (client.instance.capabilities.includes(capabilities.MARKDOWN_CONTENT)
|
||||||
|
&& md_nostack
|
||||||
|
&& sample.match(/^[a-z]{3,6}:\/\/[^\s]+/g)
|
||||||
|
) {
|
||||||
// get length of link
|
// get length of link
|
||||||
let length = text.substring(index).search(/\s|$/g);
|
let length = text.substring(index).search(/\s|$/g);
|
||||||
let url = text.substring(index, index + length);
|
let url = text.substring(index, index + length);
|
||||||
let out = `<a href="${url}">${url}</a>`;
|
let out = `<a href="${url}">${url}</a>`;
|
||||||
if (current) current.text += out;
|
if (md_layer) md_layer.text += out;
|
||||||
else response += out;
|
else response += out;
|
||||||
index += length;
|
index += length;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// handle emojis
|
// handle emojis
|
||||||
if (allow_new && sample.match(/^:[\w\-.]{0,32}:/g)) {
|
if (md_nostack && sample.match(/^:[\w\-.]{0,32}:/g)) {
|
||||||
// find the emoji name
|
// find the emoji name
|
||||||
let length = text.substring(index + 1).search(':');
|
let length = text.substring(index + 1).search(':');
|
||||||
if (length <= 0) return text;
|
if (length <= 0) return text;
|
||||||
let emoji_name = text.substring(index + 1, index + length + 1);
|
let emoji_name = text.substring(index + 1, index + length + 1);
|
||||||
let emoji = Client.get().getEmoji(emoji_name + '@' + this.user.host);
|
let emoji = client.getEmoji(emoji_name + '@' + this.user.host);
|
||||||
|
|
||||||
index += length + 2;
|
index += length + 2;
|
||||||
|
|
||||||
if (!emoji) {
|
if (!emoji) {
|
||||||
let out = ':' + emoji_name + ':';
|
let out = ':' + emoji_name + ':';
|
||||||
if (current) current.text += out;
|
if (md_layer) md_layer.text += out;
|
||||||
else response += out;
|
else response += out;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let out = emoji.html;
|
let out = emoji.html;
|
||||||
if (current) current.text += out;
|
if (md_layer) md_layer.text += out;
|
||||||
else response += out;
|
else response += out;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// handle markdown
|
// handle markdown
|
||||||
// TODO: handle misskey-flavoured markdown
|
// TODO: handle misskey-flavoured markdown(?)
|
||||||
if (current) {
|
if (md_layer) {
|
||||||
// try to pop stack
|
// try to pop layer
|
||||||
if (sample.startsWith(current.token)) {
|
if (sample.startsWith(md_layer.token)) {
|
||||||
index += current.token.length;
|
index += md_layer.token.length;
|
||||||
let out = `<${current.tag}>${current.text}</${current.tag}>`;
|
let out = `<${md_layer.tag}>${md_layer.text}</${md_layer.tag}>`;
|
||||||
if (current.token === '```')
|
if (md_layer.token === '```')
|
||||||
out = `<code><pre>${current.text}</pre></code>`;
|
out = `<code><pre>${md_layer.text}</pre></code>`;
|
||||||
if (current.parent) current.parent.text += out;
|
if (md_layer.parent) md_layer.parent.text += out;
|
||||||
else response += out;
|
else response += out;
|
||||||
current = current.parent;
|
md_layer = md_layer.parent;
|
||||||
} else {
|
} else {
|
||||||
current.text += sample[0];
|
md_layer.text += sample[0];
|
||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
} else if (allow_new) {
|
} else if (md_nostack) {
|
||||||
// can we add to stack?
|
// should we add a layer?
|
||||||
let pushed = false;
|
let pushed = false;
|
||||||
for (let i = 0; i < markdown_tokens.length; i++) {
|
for (let i = 0; i < markdown_tokens.length; i++) {
|
||||||
let item = markdown_tokens[i];
|
let item = markdown_tokens[i];
|
||||||
if (sample.startsWith(item.token)) {
|
if (sample.startsWith(item.token)) {
|
||||||
let new_current = {
|
let new_md_layer = {
|
||||||
token: item.token,
|
token: item.token,
|
||||||
tag: item.tag,
|
tag: item.tag,
|
||||||
text: "",
|
text: "",
|
||||||
parent: current,
|
parent: md_layer,
|
||||||
};
|
};
|
||||||
if (item.token === '```' || item.token === '`') new_current.nostack = true;
|
if (item.token === '```' || item.token === '`') new_md_layer.nostack = true;
|
||||||
current = new_current;
|
md_layer = new_md_layer;
|
||||||
pushed = true;
|
pushed = true;
|
||||||
index += current.token.length;
|
index += md_layer.token.length;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -148,11 +155,11 @@ export default class Post {
|
||||||
}
|
}
|
||||||
|
|
||||||
// destroy the remaining stack
|
// destroy the remaining stack
|
||||||
while (current) {
|
while (md_layer) {
|
||||||
let out = current.token + current.text;
|
let out = md_layer.token + md_layer.text;
|
||||||
if (current.parent) current.parent.text += out;
|
if (md_layer.parent) md_layer.parent.text += out;
|
||||||
else response += out;
|
else response += out;
|
||||||
current = current.parent;
|
md_layer = md_layer.parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
|
|
Loading…
Reference in a new issue