2018-10-31 05:02:03 +01:00
|
|
|
import { Injectable } from '@angular/core';
|
|
|
|
import { Store } from '@ngxs/store';
|
|
|
|
|
|
|
|
import { AccountInfo } from '../states/accounts.state';
|
2019-10-02 06:14:40 +02:00
|
|
|
import { MastodonWrapperService } from './mastodon-wrapper.service';
|
2023-04-23 22:48:07 +02:00
|
|
|
import { Account, Results, Status, Emoji, Instancev2, Instancev1 } from "./models/mastodon.interfaces";
|
2019-03-06 04:46:50 +01:00
|
|
|
import { StatusWrapper } from '../models/common.model';
|
2020-04-20 06:19:56 +02:00
|
|
|
import { AccountSettings, SaveAccountSettings, GlobalSettings, SaveSettings, ContentWarningPolicy, SaveContentWarningPolicy, ContentWarningPolicyEnum, TimeLineModeEnum, TimeLineHeaderEnum } from '../states/settings.state';
|
2021-02-28 08:38:42 +01:00
|
|
|
import { SettingsService } from './settings.service';
|
2018-10-31 05:02:03 +01:00
|
|
|
|
|
|
|
@Injectable({
|
|
|
|
providedIn: 'root'
|
|
|
|
})
|
2020-04-01 08:29:51 +02:00
|
|
|
export class ToolsService {
|
2019-09-06 07:18:41 +02:00
|
|
|
private accountAvatar: { [id: string]: string; } = {};
|
2019-09-25 06:09:10 +02:00
|
|
|
private instanceInfos: { [id: string]: InstanceInfo } = {};
|
2019-09-06 07:18:41 +02:00
|
|
|
|
2018-11-03 02:35:33 +01:00
|
|
|
constructor(
|
2021-02-28 08:38:42 +01:00
|
|
|
private readonly settingsService: SettingsService,
|
2019-10-02 06:14:40 +02:00
|
|
|
private readonly mastodonService: MastodonWrapperService,
|
2018-11-03 02:35:33 +01:00
|
|
|
private readonly store: Store) { }
|
2018-10-31 05:02:03 +01:00
|
|
|
|
2020-04-01 08:29:51 +02:00
|
|
|
checkContentWarning(status: Status): StatusWithCwPolicyResult {
|
|
|
|
if(!status) {
|
|
|
|
return new StatusWithCwPolicyResult(status, false, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
let applyCw = false;
|
|
|
|
let hideStatus = false;
|
|
|
|
|
2021-02-28 08:38:42 +01:00
|
|
|
let cwPolicy = this.settingsService.getSettings().contentWarningPolicy;
|
2020-04-01 08:29:51 +02:00
|
|
|
|
|
|
|
let splittedContent = [];
|
|
|
|
if ((cwPolicy.policy === ContentWarningPolicyEnum.HideAll && cwPolicy.addCwOnContent.length > 0)
|
|
|
|
|| (cwPolicy.policy === ContentWarningPolicyEnum.AddOnAllContent && cwPolicy.removeCwOnContent.length > 0)
|
|
|
|
|| (cwPolicy.hideCompletlyContent && cwPolicy.hideCompletlyContent.length > 0)) {
|
|
|
|
let parser = new DOMParser();
|
|
|
|
let dom = parser.parseFromString((status.content + ' ' + status.spoiler_text).replace("<br/>", " ").replace("<br>", " ").replace(/\n/g, ' '), 'text/html')
|
|
|
|
let contentToParse = dom.body.textContent;
|
|
|
|
splittedContent = contentToParse.toLowerCase().split(' ');
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cwPolicy.policy === ContentWarningPolicyEnum.None && (status.sensitive || status.spoiler_text)) {
|
|
|
|
applyCw = true;
|
|
|
|
} else if (cwPolicy.policy === ContentWarningPolicyEnum.HideAll) {
|
|
|
|
let detected = cwPolicy.addCwOnContent.filter(x => splittedContent.find(y => y == x || y == `#${x}`));
|
|
|
|
if (!detected || detected.length === 0) {
|
|
|
|
applyCw = false;
|
|
|
|
} else {
|
|
|
|
if (!status.spoiler_text) {
|
|
|
|
status.spoiler_text = detected.join(' ');
|
|
|
|
}
|
|
|
|
applyCw = true;
|
|
|
|
}
|
|
|
|
} else if (cwPolicy.policy === ContentWarningPolicyEnum.AddOnAllContent) {
|
|
|
|
let detected = cwPolicy.removeCwOnContent.filter(x => splittedContent.find(y => y == x || y == `#${x}`));
|
|
|
|
|
|
|
|
if (detected && detected.length > 0) {
|
|
|
|
applyCw = false;
|
|
|
|
} else {
|
|
|
|
applyCw = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cwPolicy.hideCompletlyContent && cwPolicy.hideCompletlyContent.length > 0) {
|
|
|
|
let detected = cwPolicy.hideCompletlyContent.filter(x => splittedContent.find(y => y == x || y == `#${x}`));
|
|
|
|
if (detected && detected.length > 0) {
|
|
|
|
hideStatus = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return new StatusWithCwPolicyResult(status, applyCw, hideStatus);
|
|
|
|
}
|
|
|
|
|
2019-09-25 06:09:10 +02:00
|
|
|
getInstanceInfo(acc: AccountInfo): Promise<InstanceInfo> {
|
|
|
|
if (this.instanceInfos[acc.instance]) {
|
|
|
|
return Promise.resolve(this.instanceInfos[acc.instance]);
|
|
|
|
} else {
|
|
|
|
return this.mastodonService.getInstance(acc.instance)
|
2023-08-06 07:17:04 +02:00
|
|
|
.then(instance => {
|
|
|
|
const splittedVersion = instance.version.split('.');
|
|
|
|
let major = +splittedVersion[0];
|
|
|
|
let minor = +splittedVersion[1];
|
|
|
|
|
|
|
|
let altMajor = 0;
|
|
|
|
let altMinor = 0;
|
|
|
|
|
2023-04-23 22:09:44 +02:00
|
|
|
let type = InstanceType.Mastodon;
|
2023-08-06 07:17:04 +02:00
|
|
|
|
|
|
|
const version = instance.version.toLowerCase();
|
|
|
|
|
|
|
|
if (version.includes('pleroma')) {
|
2019-09-25 06:09:10 +02:00
|
|
|
type = InstanceType.Pleroma;
|
2023-08-06 07:17:04 +02:00
|
|
|
|
|
|
|
const pleromaVersion = version.split('pleroma ')[1].split('.');
|
|
|
|
altMajor = +pleromaVersion[0];
|
|
|
|
altMinor = +pleromaVersion[1];
|
|
|
|
|
|
|
|
} else if (version.includes('+glitch')) {
|
2019-09-25 06:09:10 +02:00
|
|
|
type = InstanceType.GlitchSoc;
|
2023-08-06 07:17:04 +02:00
|
|
|
} else if (version.includes('+florence')) {
|
2019-09-25 06:09:10 +02:00
|
|
|
type = InstanceType.Florence;
|
2023-08-06 07:17:04 +02:00
|
|
|
} else if (version.includes('pixelfed')) {
|
2019-09-28 03:55:40 +02:00
|
|
|
type = InstanceType.Pixelfed;
|
2023-08-06 07:17:04 +02:00
|
|
|
} else if (version.includes('takahe')) {
|
|
|
|
type = InstanceType.Takahe;
|
|
|
|
major = 1; //FIXME: when a clearer set of feature are available
|
|
|
|
minor = 0; //FIXME: when a clearer set of feature are available
|
2019-09-25 06:09:10 +02:00
|
|
|
|
2023-08-06 07:17:04 +02:00
|
|
|
const takaheVersion = version.split('takahe/')[1].split('.');
|
|
|
|
altMajor = +takaheVersion[0];
|
|
|
|
altMinor = +takaheVersion[1];
|
|
|
|
|
|
|
|
} else if (version.includes('akkoma')) {
|
|
|
|
type = InstanceType.Akkoma;
|
|
|
|
|
|
|
|
const akkomaVersion = version.split('akkoma ')[1].split('.');
|
|
|
|
altMajor = +akkomaVersion[0];
|
|
|
|
altMinor = +akkomaVersion[1];
|
|
|
|
}
|
2019-09-25 06:09:10 +02:00
|
|
|
|
2023-04-23 22:09:44 +02:00
|
|
|
let streamingApi = "";
|
2023-04-23 22:48:07 +02:00
|
|
|
|
|
|
|
if (major >= 4) {
|
|
|
|
const instanceV2 = <Instancev2>instance;
|
|
|
|
|
|
|
|
if (instanceV2
|
|
|
|
&& instanceV2.configuration
|
|
|
|
&& instanceV2.configuration.urls)
|
|
|
|
streamingApi = instanceV2.configuration.urls.streaming;
|
|
|
|
} else {
|
|
|
|
const instanceV1 = <Instancev1>instance;
|
|
|
|
if (instanceV1 && instanceV1.urls)
|
|
|
|
streamingApi = instanceV1.urls.streaming_api;
|
|
|
|
}
|
2023-04-23 22:09:44 +02:00
|
|
|
|
2023-08-06 07:17:04 +02:00
|
|
|
let instanceInfo = new InstanceInfo(type, major, minor, streamingApi, altMajor, altMinor);
|
2019-09-25 06:09:10 +02:00
|
|
|
this.instanceInfos[acc.instance] = instanceInfo;
|
2019-09-28 03:55:40 +02:00
|
|
|
|
2019-09-25 06:09:10 +02:00
|
|
|
return instanceInfo;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
2018-10-31 05:02:03 +01:00
|
|
|
|
2023-08-06 07:17:04 +02:00
|
|
|
isBookmarksAreAvailable(account: AccountInfo): Promise<boolean> {
|
|
|
|
return this.getInstanceInfo(account)
|
|
|
|
.then((instance: InstanceInfo) => {
|
|
|
|
if (instance.major == 3 && instance.minor >= 1
|
|
|
|
|| instance.major > 3
|
|
|
|
|| instance.type === InstanceType.Pleroma && instance.altMajor >= 2 && instance.altMinor >= 5
|
|
|
|
|| instance.type === InstanceType.Akkoma && instance.altMajor >= 3 && instance.altMinor >= 9
|
|
|
|
|| instance.type === InstanceType.Takahe && instance.altMajor >= 0 && instance.altMinor >= 9) {
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.catch(err => {
|
|
|
|
console.error(err);
|
|
|
|
return false;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2019-09-06 07:18:41 +02:00
|
|
|
getAvatar(acc: AccountInfo): Promise<string> {
|
|
|
|
if (this.accountAvatar[acc.id]) {
|
|
|
|
return Promise.resolve(this.accountAvatar[acc.id]);
|
|
|
|
} else {
|
|
|
|
return this.mastodonService.retrieveAccountDetails(acc)
|
|
|
|
.then((result: Account) => {
|
|
|
|
this.accountAvatar[acc.id] = result.avatar;
|
|
|
|
return result.avatar;
|
|
|
|
})
|
|
|
|
.catch((err) => {
|
|
|
|
return "";
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-31 05:02:03 +01:00
|
|
|
getSelectedAccounts(): AccountInfo[] {
|
2019-09-30 01:21:43 +02:00
|
|
|
let regAccounts = <AccountInfo[]>this.store.snapshot().registeredaccounts.accounts;
|
2018-10-31 05:02:03 +01:00
|
|
|
return regAccounts.filter(x => x.isSelected);
|
|
|
|
}
|
2019-07-30 03:20:12 +02:00
|
|
|
|
2020-09-12 20:01:35 +02:00
|
|
|
getAllAccounts(): AccountInfo[] {
|
|
|
|
let regAccounts = <AccountInfo[]>this.store.snapshot().registeredaccounts.accounts;
|
|
|
|
return regAccounts;
|
|
|
|
}
|
|
|
|
|
2019-11-17 06:57:58 +01:00
|
|
|
getAccountById(accountId: string): AccountInfo {
|
|
|
|
let regAccounts = <AccountInfo[]>this.store.snapshot().registeredaccounts.accounts;
|
|
|
|
return regAccounts.find(x => x.id === accountId);
|
|
|
|
}
|
|
|
|
|
2018-11-03 02:35:33 +01:00
|
|
|
findAccount(account: AccountInfo, accountName: string): Promise<Account> {
|
2020-03-12 01:54:32 +01:00
|
|
|
let findAccountFunc = (result: Results) => {
|
|
|
|
if (accountName[0] === '@') accountName = accountName.substr(1);
|
|
|
|
|
|
|
|
const foundAccount = result.accounts.find(
|
|
|
|
x => (x.acct.toLowerCase() === accountName.toLowerCase()
|
|
|
|
||
|
|
|
|
(x.acct.toLowerCase().split('@')[0] === accountName.toLowerCase().split('@')[0])
|
|
|
|
&& x.url.replace('https://', '').split('/')[0] === accountName.toLowerCase().split('@')[1])
|
|
|
|
);
|
|
|
|
return foundAccount;
|
|
|
|
};
|
|
|
|
|
|
|
|
let searchVersion: 'v1' | 'v2' = 'v1';
|
2019-09-25 06:09:10 +02:00
|
|
|
return this.getInstanceInfo(account)
|
|
|
|
.then(instance => {
|
2020-03-12 01:54:32 +01:00
|
|
|
//let version: 'v1' | 'v2' = 'v1';
|
|
|
|
if (instance.major >= 3) searchVersion = 'v2';
|
|
|
|
return this.mastodonService.search(account, accountName, searchVersion, true);
|
2019-09-25 06:09:10 +02:00
|
|
|
})
|
2020-03-12 01:54:32 +01:00
|
|
|
.then((results: Results) => {
|
|
|
|
return findAccountFunc(results);
|
|
|
|
})
|
|
|
|
.then((foundAccount: Account) => {
|
|
|
|
if (foundAccount != null) return Promise.resolve(foundAccount);
|
|
|
|
|
|
|
|
let fullName = `https://${accountName.split('@')[1]}/@${accountName.split('@')[0]}`;
|
|
|
|
return this.mastodonService.search(account, fullName, searchVersion, true)
|
|
|
|
.then((results: Results) => {
|
|
|
|
return findAccountFunc(results);
|
|
|
|
});
|
2018-11-03 02:35:33 +01:00
|
|
|
});
|
2020-03-12 01:54:32 +01:00
|
|
|
}
|
2018-11-03 02:35:33 +01:00
|
|
|
|
2019-07-30 03:20:12 +02:00
|
|
|
getStatusUsableByAccount(account: AccountInfo, originalStatus: StatusWrapper): Promise<Status> {
|
2020-04-07 04:06:36 +02:00
|
|
|
let isProvider = false;
|
|
|
|
if(!originalStatus.isRemote){
|
|
|
|
isProvider = originalStatus.provider.id === account.id;
|
|
|
|
}
|
2019-02-12 04:28:15 +01:00
|
|
|
|
|
|
|
let statusPromise: Promise<Status> = Promise.resolve(originalStatus.status);
|
|
|
|
|
|
|
|
if (!isProvider) {
|
2019-09-25 06:09:10 +02:00
|
|
|
statusPromise = statusPromise
|
|
|
|
.then((foreignStatus: Status) => {
|
2020-08-29 03:35:50 +02:00
|
|
|
const statusUri = foreignStatus.uri;
|
|
|
|
const statusUrl = foreignStatus.url;
|
2019-09-25 06:09:10 +02:00
|
|
|
return this.getInstanceInfo(account)
|
|
|
|
.then(instance => {
|
|
|
|
let version: 'v1' | 'v2' = 'v1';
|
|
|
|
if (instance.major >= 3) version = 'v2';
|
2020-08-29 03:35:50 +02:00
|
|
|
return this.mastodonService.search(account, statusUri, version, true)
|
|
|
|
.then((results: Results) => {
|
|
|
|
if(results && results.statuses.length > 0) return results;
|
|
|
|
return this.mastodonService.search(account, statusUrl, version, true);
|
|
|
|
});
|
2019-09-25 06:09:10 +02:00
|
|
|
})
|
|
|
|
.then((results: Results) => {
|
|
|
|
return results.statuses[0];
|
|
|
|
});
|
|
|
|
});
|
2019-02-12 04:28:15 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return statusPromise;
|
|
|
|
}
|
2019-04-01 00:30:38 +02:00
|
|
|
|
2019-07-12 01:02:15 +02:00
|
|
|
getAccountFullHandle(account: Account): string {
|
|
|
|
let fullHandle = account.acct.toLowerCase();
|
|
|
|
if (!fullHandle.includes('@')) {
|
|
|
|
fullHandle += `@${account.url.replace('https://', '').split('/')[0]}`;
|
|
|
|
}
|
|
|
|
return `@${fullHandle}`;
|
|
|
|
}
|
2019-07-30 03:05:37 +02:00
|
|
|
|
2019-07-30 03:20:12 +02:00
|
|
|
private emojiCache: { [id: string]: Emoji[] } = {};
|
|
|
|
getCustomEmojis(account: AccountInfo): Promise<Emoji[]> {
|
|
|
|
if (this.emojiCache[account.id]) {
|
|
|
|
return Promise.resolve(this.emojiCache[account.id]);
|
|
|
|
} else {
|
|
|
|
return this.mastodonService.getCustomEmojis(account)
|
|
|
|
.then(emojis => {
|
|
|
|
this.emojiCache[account.id] = emojis.filter(x => x.visible_in_picker);
|
|
|
|
return this.emojiCache[account.id];
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
2019-02-19 04:44:21 +01:00
|
|
|
}
|
2019-02-12 04:28:15 +01:00
|
|
|
|
2019-02-19 04:44:21 +01:00
|
|
|
export class OpenThreadEvent {
|
|
|
|
constructor(
|
|
|
|
public status: Status,
|
|
|
|
public sourceAccount: AccountInfo
|
|
|
|
) {
|
|
|
|
}
|
2018-10-31 05:02:03 +01:00
|
|
|
}
|
2019-09-25 06:09:10 +02:00
|
|
|
|
|
|
|
export class InstanceInfo {
|
|
|
|
constructor(
|
|
|
|
public readonly type: InstanceType,
|
|
|
|
public readonly major: number,
|
2023-04-23 22:09:44 +02:00
|
|
|
public readonly minor: number,
|
2023-08-06 07:17:04 +02:00
|
|
|
public readonly streamingApi: string,
|
|
|
|
public readonly altMajor: number,
|
|
|
|
public readonly altMinor: number) {
|
2019-09-25 06:09:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export enum InstanceType {
|
|
|
|
Mastodon = 1,
|
2023-08-06 07:17:04 +02:00
|
|
|
Pleroma = 2, // "2.7.2 (compatible; Pleroma 2.5.1)"
|
|
|
|
GlitchSoc = 3, // "4.1.5+glitch_0801_3b49b5a"
|
2019-09-28 03:55:40 +02:00
|
|
|
Florence = 4,
|
2023-08-06 07:17:04 +02:00
|
|
|
Pixelfed = 5,
|
|
|
|
Takahe = 6, // "takahe/0.9.0"
|
|
|
|
Akkoma = 7, // "2.7.2 (compatible; Akkoma 3.9.2-develop)"
|
2020-04-01 08:29:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
export class StatusWithCwPolicyResult {
|
|
|
|
constructor(public status: Status, public applyCw: boolean, public hide: boolean) {
|
|
|
|
}
|
2019-09-25 06:09:10 +02:00
|
|
|
}
|