2023-10-12 07:48:09 +03:00
|
|
|
import { createRestAPIClient, createStreamingAPIClient } from 'masto';
|
2023-02-05 19:17:19 +03:00
|
|
|
|
|
|
|
import store from './store';
|
2023-09-01 10:40:00 +03:00
|
|
|
import {
|
|
|
|
getAccount,
|
|
|
|
getAccountByAccessToken,
|
|
|
|
getCurrentAccount,
|
|
|
|
saveAccount,
|
|
|
|
} from './store-utils';
|
2023-02-05 19:17:19 +03:00
|
|
|
|
|
|
|
// Default *fallback* instance
|
|
|
|
const DEFAULT_INSTANCE = 'mastodon.social';
|
|
|
|
|
|
|
|
// Per-instance masto instance
|
|
|
|
// Useful when only one account is logged in
|
|
|
|
// I'm not sure if I'll ever allow multiple logged-in accounts but oh well...
|
|
|
|
// E.g. apis['mastodon.social']
|
|
|
|
const apis = {};
|
|
|
|
|
|
|
|
// Per-account masto instance
|
|
|
|
// Note: There can be many accounts per instance
|
|
|
|
// Useful when multiple accounts are logged in or when certain actions require a specific account
|
|
|
|
// Just in case if I need this one day.
|
|
|
|
// E.g. accountApis['mastodon.social']['ACCESS_TOKEN']
|
|
|
|
const accountApis = {};
|
2023-09-01 10:40:00 +03:00
|
|
|
window.__ACCOUNT_APIS__ = accountApis;
|
2023-02-05 19:17:19 +03:00
|
|
|
|
|
|
|
// Current account masto instance
|
|
|
|
let currentAccountApi;
|
|
|
|
|
|
|
|
export function initClient({ instance, accessToken }) {
|
|
|
|
if (/^https?:\/\//.test(instance)) {
|
|
|
|
instance = instance
|
|
|
|
.replace(/^https?:\/\//, '')
|
|
|
|
.replace(/\/+$/, '')
|
|
|
|
.toLowerCase();
|
|
|
|
}
|
|
|
|
const url = instance ? `https://${instance}` : `https://${DEFAULT_INSTANCE}`;
|
|
|
|
|
2023-10-12 07:48:09 +03:00
|
|
|
const masto = createRestAPIClient({
|
2023-02-05 19:17:19 +03:00
|
|
|
url,
|
|
|
|
accessToken, // Can be null
|
|
|
|
timeout: 30_000, // Unfortunatly this is global instead of per-request
|
|
|
|
});
|
|
|
|
|
2023-10-12 07:48:09 +03:00
|
|
|
const client = {
|
|
|
|
masto,
|
|
|
|
instance,
|
|
|
|
accessToken,
|
|
|
|
};
|
2023-02-05 19:17:19 +03:00
|
|
|
apis[instance] = client;
|
|
|
|
if (!accountApis[instance]) accountApis[instance] = {};
|
|
|
|
if (accessToken) accountApis[instance][accessToken] = client;
|
|
|
|
|
|
|
|
return client;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get the instance information
|
|
|
|
// The config is needed for composing
|
2023-05-09 13:48:19 +03:00
|
|
|
export async function initInstance(client, instance) {
|
2023-10-12 07:48:09 +03:00
|
|
|
console.log('INIT INSTANCE', client, instance);
|
|
|
|
const { masto, accessToken } = client;
|
2023-02-05 19:17:19 +03:00
|
|
|
// Request v2, fallback to v1 if fail
|
|
|
|
let info;
|
|
|
|
try {
|
|
|
|
info = await masto.v2.instance.fetch();
|
|
|
|
} catch (e) {}
|
|
|
|
if (!info) {
|
|
|
|
try {
|
2023-10-12 07:48:09 +03:00
|
|
|
info = await masto.v1.instance.fetch();
|
2023-02-05 19:17:19 +03:00
|
|
|
} catch (e) {}
|
|
|
|
}
|
|
|
|
if (!info) return;
|
|
|
|
console.log(info);
|
|
|
|
const {
|
|
|
|
// v1
|
|
|
|
uri,
|
|
|
|
urls: { streamingApi } = {},
|
|
|
|
// v2
|
|
|
|
domain,
|
|
|
|
configuration: { urls: { streaming } = {} } = {},
|
|
|
|
} = info;
|
2023-05-09 13:48:19 +03:00
|
|
|
const instances = store.local.getJSON('instances') || {};
|
2023-02-05 19:17:19 +03:00
|
|
|
if (uri || domain) {
|
|
|
|
instances[
|
|
|
|
(domain || uri)
|
|
|
|
.replace(/^https?:\/\//, '')
|
|
|
|
.replace(/\/+$/, '')
|
|
|
|
.toLowerCase()
|
|
|
|
] = info;
|
|
|
|
}
|
2023-05-09 13:48:19 +03:00
|
|
|
if (instance) {
|
|
|
|
instances[instance.toLowerCase()] = info;
|
|
|
|
}
|
|
|
|
store.local.setJSON('instances', instances);
|
2023-02-05 19:17:19 +03:00
|
|
|
// This is a weird place to put this but here's updating the masto instance with the streaming API URL set in the configuration
|
|
|
|
// Reason: Streaming WebSocket URL may change, unlike the standard API REST URLs
|
2023-10-12 07:48:09 +03:00
|
|
|
const supportsWebSocket = 'WebSocket' in window;
|
|
|
|
if (supportsWebSocket && (streamingApi || streaming)) {
|
2023-02-07 19:31:46 +03:00
|
|
|
console.log('🎏 Streaming API URL:', streaming || streamingApi);
|
2023-10-12 07:48:09 +03:00
|
|
|
// masto.config.props.streamingApiUrl = streaming || streamingApi;
|
|
|
|
// Legacy masto.ws
|
|
|
|
const streamClient = createStreamingAPIClient({
|
|
|
|
streamingApiUrl: streaming || streamingApi,
|
|
|
|
accessToken,
|
|
|
|
implementation: WebSocket,
|
|
|
|
});
|
|
|
|
client.streaming = streamClient;
|
|
|
|
// masto.ws = streamClient;
|
|
|
|
console.log('🎏 Streaming API client:', client);
|
2023-02-05 19:17:19 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get the account information and store it
|
2023-09-01 10:40:00 +03:00
|
|
|
export async function initAccount(client, instance, accessToken, vapidKey) {
|
2023-10-12 07:48:09 +03:00
|
|
|
const { masto } = client;
|
2023-02-05 19:17:19 +03:00
|
|
|
const mastoAccount = await masto.v1.accounts.verifyCredentials();
|
|
|
|
|
2023-10-12 07:48:09 +03:00
|
|
|
console.log('CURRENTACCOUNT SET', mastoAccount.id);
|
2023-04-16 08:52:16 +03:00
|
|
|
store.session.set('currentAccount', mastoAccount.id);
|
|
|
|
|
2023-02-05 19:17:19 +03:00
|
|
|
saveAccount({
|
|
|
|
info: mastoAccount,
|
|
|
|
instanceURL: instance.toLowerCase(),
|
|
|
|
accessToken,
|
2023-09-01 10:40:00 +03:00
|
|
|
vapidKey,
|
2023-02-05 19:17:19 +03:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2023-02-09 18:59:57 +03:00
|
|
|
// Get preferences
|
|
|
|
export async function initPreferences(client) {
|
|
|
|
try {
|
2023-10-12 07:48:09 +03:00
|
|
|
const { masto } = client;
|
2023-02-09 18:59:57 +03:00
|
|
|
const preferences = await masto.v1.preferences.fetch();
|
|
|
|
store.account.set('preferences', preferences);
|
|
|
|
} catch (e) {
|
|
|
|
// silently fail
|
|
|
|
console.error(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-05 19:17:19 +03:00
|
|
|
// Get the masto instance
|
|
|
|
// If accountID is provided, get the masto instance for that account
|
|
|
|
export function api({ instance, accessToken, accountID, account } = {}) {
|
2023-02-22 04:46:50 +03:00
|
|
|
// Always lowercase and trim the instance
|
|
|
|
if (instance) {
|
|
|
|
instance = instance.toLowerCase().trim();
|
|
|
|
}
|
|
|
|
|
2023-02-05 19:17:19 +03:00
|
|
|
// If instance and accessToken are provided, get the masto instance for that account
|
|
|
|
if (instance && accessToken) {
|
2023-10-12 07:48:09 +03:00
|
|
|
const client =
|
|
|
|
accountApis[instance]?.[accessToken] ||
|
|
|
|
initClient({ instance, accessToken });
|
|
|
|
const { masto, streaming } = client;
|
2023-02-05 19:17:19 +03:00
|
|
|
return {
|
2023-10-12 07:48:09 +03:00
|
|
|
masto,
|
|
|
|
streaming,
|
|
|
|
client,
|
2023-02-05 19:17:19 +03:00
|
|
|
authenticated: true,
|
|
|
|
instance,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2023-09-01 10:40:00 +03:00
|
|
|
if (accessToken) {
|
|
|
|
// If only accessToken is provided, get the masto instance for that accessToken
|
|
|
|
console.log('X 1', accountApis);
|
|
|
|
for (const instance in accountApis) {
|
|
|
|
if (accountApis[instance][accessToken]) {
|
|
|
|
console.log('X 2', accountApis, instance, accessToken);
|
2023-10-12 07:48:09 +03:00
|
|
|
const client = accountApis[instance][accessToken];
|
|
|
|
const { masto, streaming } = client;
|
2023-09-01 10:40:00 +03:00
|
|
|
return {
|
2023-10-12 07:48:09 +03:00
|
|
|
masto,
|
|
|
|
streaming,
|
|
|
|
client,
|
2023-09-01 10:40:00 +03:00
|
|
|
authenticated: true,
|
|
|
|
instance,
|
|
|
|
};
|
|
|
|
} else {
|
|
|
|
console.log('X 3', accountApis, instance, accessToken);
|
|
|
|
const account = getAccountByAccessToken(accessToken);
|
|
|
|
if (account) {
|
|
|
|
const accessToken = account.accessToken;
|
|
|
|
const instance = account.instanceURL.toLowerCase().trim();
|
2023-10-12 07:48:09 +03:00
|
|
|
const client = initClient({ instance, accessToken });
|
|
|
|
const { masto, streaming } = client;
|
2023-09-01 10:40:00 +03:00
|
|
|
return {
|
2023-10-12 07:48:09 +03:00
|
|
|
masto,
|
|
|
|
streaming,
|
|
|
|
client,
|
2023-09-01 10:40:00 +03:00
|
|
|
authenticated: true,
|
|
|
|
instance,
|
|
|
|
};
|
|
|
|
} else {
|
2023-10-03 10:07:47 +03:00
|
|
|
throw new Error(`Access token not found`);
|
2023-09-01 10:40:00 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-05 19:17:19 +03:00
|
|
|
// If account is provided, get the masto instance for that account
|
|
|
|
if (account || accountID) {
|
|
|
|
account = account || getAccount(accountID);
|
|
|
|
if (account) {
|
|
|
|
const accessToken = account.accessToken;
|
2023-02-22 04:46:50 +03:00
|
|
|
const instance = account.instanceURL.toLowerCase().trim();
|
2023-10-12 07:48:09 +03:00
|
|
|
const client =
|
|
|
|
accountApis[instance]?.[accessToken] ||
|
|
|
|
initClient({ instance, accessToken });
|
|
|
|
const { masto, streaming } = client;
|
2023-02-05 19:17:19 +03:00
|
|
|
return {
|
2023-10-12 07:48:09 +03:00
|
|
|
masto,
|
|
|
|
streaming,
|
|
|
|
client,
|
2023-02-05 19:17:19 +03:00
|
|
|
authenticated: true,
|
|
|
|
instance,
|
|
|
|
};
|
|
|
|
} else {
|
|
|
|
throw new Error(`Account ${accountID} not found`);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-11-01 12:02:52 +03:00
|
|
|
const currentAccount = getCurrentAccount();
|
|
|
|
|
2023-02-05 19:17:19 +03:00
|
|
|
// If only instance is provided, get the masto instance for that instance
|
|
|
|
if (instance) {
|
2023-11-01 12:02:52 +03:00
|
|
|
if (currentAccountApi?.instance === instance) {
|
|
|
|
return {
|
|
|
|
masto: currentAccountApi.masto,
|
|
|
|
streaming: currentAccountApi.streaming,
|
|
|
|
client: currentAccountApi,
|
|
|
|
authenticated: true,
|
|
|
|
instance,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
if (currentAccount?.instanceURL === instance) {
|
|
|
|
const { accessToken } = currentAccount;
|
|
|
|
currentAccountApi =
|
|
|
|
accountApis[instance]?.[accessToken] ||
|
|
|
|
initClient({ instance, accessToken });
|
|
|
|
return {
|
|
|
|
masto: currentAccountApi.masto,
|
|
|
|
streaming: currentAccountApi.streaming,
|
|
|
|
client: currentAccountApi,
|
|
|
|
authenticated: true,
|
|
|
|
instance,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2023-10-12 07:48:09 +03:00
|
|
|
const client = apis[instance] || initClient({ instance });
|
|
|
|
const { masto, streaming, accessToken } = client;
|
2023-02-05 19:17:19 +03:00
|
|
|
return {
|
|
|
|
masto,
|
2023-10-12 07:48:09 +03:00
|
|
|
streaming,
|
|
|
|
client,
|
|
|
|
authenticated: !!accessToken,
|
2023-02-05 19:17:19 +03:00
|
|
|
instance,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
// If no instance is provided, get the masto instance for the current account
|
2023-02-22 04:46:50 +03:00
|
|
|
if (currentAccountApi) {
|
2023-02-05 19:17:19 +03:00
|
|
|
return {
|
2023-10-12 07:48:09 +03:00
|
|
|
masto: currentAccountApi.masto,
|
|
|
|
streaming: currentAccountApi.streaming,
|
|
|
|
client: currentAccountApi,
|
2023-02-05 19:17:19 +03:00
|
|
|
authenticated: true,
|
2023-10-12 07:48:09 +03:00
|
|
|
instance: currentAccountApi.instance,
|
2023-02-05 19:17:19 +03:00
|
|
|
};
|
2023-02-22 04:46:50 +03:00
|
|
|
}
|
2023-02-05 19:17:19 +03:00
|
|
|
if (currentAccount) {
|
|
|
|
const { accessToken, instanceURL: instance } = currentAccount;
|
|
|
|
currentAccountApi =
|
|
|
|
accountApis[instance]?.[accessToken] ||
|
|
|
|
initClient({ instance, accessToken });
|
|
|
|
return {
|
2023-10-12 07:48:09 +03:00
|
|
|
masto: currentAccountApi.masto,
|
|
|
|
streaming: currentAccountApi.streaming,
|
|
|
|
client: currentAccountApi,
|
2023-02-05 19:17:19 +03:00
|
|
|
authenticated: true,
|
|
|
|
instance,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
// If no instance is provided and no account is logged in, get the masto instance for DEFAULT_INSTANCE
|
2023-10-12 07:48:09 +03:00
|
|
|
const client =
|
|
|
|
apis[DEFAULT_INSTANCE] || initClient({ instance: DEFAULT_INSTANCE });
|
|
|
|
const { masto, streaming } = client;
|
2023-02-05 19:17:19 +03:00
|
|
|
return {
|
2023-10-12 07:48:09 +03:00
|
|
|
masto,
|
|
|
|
streaming,
|
|
|
|
client,
|
2023-02-05 19:17:19 +03:00
|
|
|
authenticated: false,
|
|
|
|
instance: DEFAULT_INSTANCE,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
window.__API__ = {
|
|
|
|
currentAccountApi,
|
|
|
|
apis,
|
|
|
|
accountApis,
|
|
|
|
};
|