commit
2b2d467e74
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "sengi",
|
||||
"version": "0.23.1",
|
||||
"version": "0.24.0",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"main": "main-electron.js",
|
||||
"description": "A multi-account desktop client for Mastodon and Pleroma",
|
||||
|
@ -456,7 +456,11 @@ export class CreateStatusComponent implements OnInit, OnDestroy {
|
||||
let globalUniqueMentions = [];
|
||||
for (let mention of uniqueMentions) {
|
||||
if (!mention.includes('@')) {
|
||||
mention += `@${providerInfo.instance}`;
|
||||
if (providerInfo) {
|
||||
mention += `@${providerInfo.instance}`;
|
||||
} else {
|
||||
mention += `@${status.url.replace('https://', '').split('/')[0]}`;
|
||||
}
|
||||
}
|
||||
globalUniqueMentions.push(mention);
|
||||
}
|
||||
|
@ -94,6 +94,15 @@
|
||||
<span class="sub-section__title" *ngIf="contentWarningPolicyChanged"><br/>this settings needs a <a href (click)="reload()">reload</a> to be effective.</span>
|
||||
</div>
|
||||
|
||||
<h4 class="panel__subtitle">Other</h4>
|
||||
<div class="sub-section">
|
||||
<input class="sub-section__checkbox" [(ngModel)]="disableRemoteStatusFetchingEnabled"
|
||||
(change)="onDisableRemoteStatusFetchingChanged()" type="checkbox" name="disableRemoteFetching" value="disableRemoteFetching"
|
||||
id="disableRemoteFetching">
|
||||
<label class="noselect sub-section__label" for="disableRemoteFetching">disable remote status fetching</label>
|
||||
<br>
|
||||
</div>
|
||||
|
||||
<h4 class="panel__subtitle">About</h4>
|
||||
<p class="version">
|
||||
Sengi version: {{version}}<br />
|
||||
|
@ -21,6 +21,7 @@ export class SettingsComponent implements OnInit {
|
||||
notificationForm: FormGroup;
|
||||
|
||||
disableAutofocusEnabled: boolean;
|
||||
disableRemoteStatusFetchingEnabled: boolean;
|
||||
disableAvatarNotificationsEnabled: boolean;
|
||||
disableSoundsEnabled: boolean;
|
||||
version: string;
|
||||
@ -78,6 +79,7 @@ export class SettingsComponent implements OnInit {
|
||||
this.disableAutofocusEnabled = settings.disableAutofocus;
|
||||
this.disableAvatarNotificationsEnabled = settings.disableAvatarNotifications;
|
||||
this.disableSoundsEnabled = settings.disableSounds;
|
||||
this.disableRemoteStatusFetchingEnabled = settings.disableRemoteStatusFetching;
|
||||
|
||||
if (!settings.columnSwitchingWinAlt) {
|
||||
this.columnShortcutEnabled = ColumnShortcut.Ctrl;
|
||||
@ -172,6 +174,12 @@ export class SettingsComponent implements OnInit {
|
||||
this.toolsService.saveSettings(settings);
|
||||
}
|
||||
|
||||
onDisableRemoteStatusFetchingChanged() {
|
||||
let settings = this.toolsService.getSettings();
|
||||
settings.disableRemoteStatusFetching = this.disableRemoteStatusFetchingEnabled;
|
||||
this.toolsService.saveSettings(settings);
|
||||
}
|
||||
|
||||
onDisableAvatarNotificationsChanged() {
|
||||
let settings = this.toolsService.getSettings();
|
||||
settings.disableAvatarNotifications = this.disableAvatarNotificationsEnabled;
|
||||
|
@ -76,14 +76,18 @@ export class ActionBarComponent implements OnInit, OnDestroy {
|
||||
ngOnInit() {
|
||||
this.displayedStatus = this.statusWrapper.status;
|
||||
const account = this.statusWrapper.provider;
|
||||
let accountId = 'remote';
|
||||
if (account) {
|
||||
accountId = account.id;
|
||||
}
|
||||
|
||||
if (this.displayedStatus.reblog) {
|
||||
this.displayedStatus = this.displayedStatus.reblog;
|
||||
}
|
||||
|
||||
this.favoriteStatePerAccountId[account.id] = this.displayedStatus.favourited;
|
||||
this.bootedStatePerAccountId[account.id] = this.displayedStatus.reblogged;
|
||||
this.bookmarkStatePerAccountId[account.id] = this.displayedStatus.bookmarked;
|
||||
this.favoriteStatePerAccountId[accountId] = this.displayedStatus.favourited;
|
||||
this.bootedStatePerAccountId[accountId] = this.displayedStatus.reblogged;
|
||||
this.bookmarkStatePerAccountId[accountId] = this.displayedStatus.bookmarked;
|
||||
|
||||
this.analyseMemoryStatus();
|
||||
|
||||
@ -134,8 +138,13 @@ export class ActionBarComponent implements OnInit, OnDestroy {
|
||||
private checkStatus(accounts: AccountInfo[]): void {
|
||||
const status = this.statusWrapper.status;
|
||||
const provider = this.statusWrapper.provider;
|
||||
this.selectedAccounts = accounts.filter(x => x.isSelected);
|
||||
this.isProviderSelected = this.selectedAccounts.filter(x => x.id === provider.id).length > 0;
|
||||
this.selectedAccounts = accounts.filter(x => x.isSelected);
|
||||
|
||||
if (!this.statusWrapper.isRemote) {
|
||||
this.isProviderSelected = this.selectedAccounts.filter(x => x.id === provider.id).length > 0;
|
||||
} else {
|
||||
this.isProviderSelected = false;
|
||||
}
|
||||
|
||||
if (status.visibility === 'direct' || status.visibility === 'private') {
|
||||
this.isBoostLocked = true;
|
||||
|
@ -84,6 +84,9 @@
|
||||
<div class="status__labels--label status__labels--old" title="this status is old" *ngIf="isOld && !statusWrapper.status.pinned">
|
||||
old
|
||||
</div>
|
||||
<div class="status__labels--label status__labels--remote" title="this status isn't federated with this instance" *ngIf="isRemote">
|
||||
remote
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
@ -99,6 +99,11 @@
|
||||
&--old {
|
||||
background-color: rgb(150, 0, 0);
|
||||
}
|
||||
&--remote {
|
||||
background-color: rgb(161, 64, 140);
|
||||
background-color: rgb(33, 69, 136);
|
||||
background-color: rgb(38, 77, 148);
|
||||
}
|
||||
}
|
||||
&__name {
|
||||
display: inline-block;
|
||||
|
@ -39,6 +39,7 @@ export class StatusComponent implements OnInit {
|
||||
contentWarningText: string;
|
||||
isDirectMessage: boolean;
|
||||
isSelected: boolean;
|
||||
isRemote: boolean;
|
||||
|
||||
hideStatus: boolean = false;
|
||||
|
||||
@ -58,9 +59,9 @@ export class StatusComponent implements OnInit {
|
||||
@Input('statusWrapper')
|
||||
set statusWrapper(value: StatusWrapper) {
|
||||
this._statusWrapper = value;
|
||||
// console.warn(value.status);
|
||||
this.status = value.status;
|
||||
this.isSelected = value.isSelected;
|
||||
this.isRemote = value.isRemote;
|
||||
|
||||
if (this.status.reblog) {
|
||||
this.reblog = true;
|
||||
@ -72,6 +73,7 @@ export class StatusComponent implements OnInit {
|
||||
this.isDirectMessage = this.displayedStatus.visibility === 'direct';
|
||||
let cwPolicy = this.toolsService.checkContentWarning(this.displayedStatus);
|
||||
this.displayedStatusWrapper = new StatusWrapper(cwPolicy.status, value.provider, cwPolicy.applyCw, cwPolicy.hide);
|
||||
this.displayedStatusWrapper.isRemote = value.isRemote;
|
||||
|
||||
this.checkLabels(this.displayedStatus);
|
||||
this.setContentWarning(this.displayedStatusWrapper);
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { Component, OnInit, OnDestroy, Input, Output, EventEmitter, ViewChildren, QueryList, ViewChild, ElementRef } from '@angular/core';
|
||||
import { HttpErrorResponse } from '@angular/common/http';
|
||||
import { HttpErrorResponse, HttpClient, HttpHeaders } from '@angular/common/http';
|
||||
import { Subscription } from 'rxjs';
|
||||
|
||||
import { MastodonWrapperService } from '../../../services/mastodon-wrapper.service';
|
||||
@ -22,6 +22,7 @@ export class ThreadComponent implements OnInit, OnDestroy {
|
||||
isLoading = true;
|
||||
isThread = true;
|
||||
hasContentWarnings = false;
|
||||
private remoteStatusFetchingDisabled = false;
|
||||
|
||||
bufferStream: Status[] = []; //html compatibility only
|
||||
|
||||
@ -51,11 +52,15 @@ export class ThreadComponent implements OnInit, OnDestroy {
|
||||
private goToTopSubscription: Subscription;
|
||||
|
||||
constructor(
|
||||
private readonly httpClient: HttpClient,
|
||||
private readonly notificationService: NotificationService,
|
||||
private readonly toolsService: ToolsService,
|
||||
private readonly mastodonService: MastodonWrapperService) { }
|
||||
|
||||
ngOnInit() {
|
||||
let settings = this.toolsService.getSettings();
|
||||
this.remoteStatusFetchingDisabled = settings.disableRemoteStatusFetching;
|
||||
|
||||
if (this.refreshEventEmitter) {
|
||||
this.refreshSubscription = this.refreshEventEmitter.subscribe(() => {
|
||||
this.refresh();
|
||||
@ -130,7 +135,7 @@ export class ThreadComponent implements OnInit, OnDestroy {
|
||||
if (status.visibility === 'public' || status.visibility === 'unlisted') {
|
||||
var statusPromise: Promise<Status> = Promise.resolve(status);
|
||||
|
||||
if (sourceAccount.id !== currentAccount.id) {
|
||||
if (!sourceAccount || sourceAccount.id !== currentAccount.id) {
|
||||
statusPromise = this.toolsService.getInstanceInfo(currentAccount)
|
||||
.then(instance => {
|
||||
let version: 'v1' | 'v2' = 'v1';
|
||||
@ -148,7 +153,7 @@ export class ThreadComponent implements OnInit, OnDestroy {
|
||||
|
||||
this.retrieveThread(currentAccount, statusPromise);
|
||||
|
||||
} else if (sourceAccount.id === currentAccount.id) {
|
||||
} else if (sourceAccount && sourceAccount.id === currentAccount.id) {
|
||||
var statusPromise = Promise.resolve(status);
|
||||
this.retrieveThread(currentAccount, statusPromise);
|
||||
} else {
|
||||
@ -161,33 +166,52 @@ export class ThreadComponent implements OnInit, OnDestroy {
|
||||
pipeline
|
||||
.then((status: Status) => {
|
||||
return this.mastodonService.getStatusContext(currentAccount, status.id)
|
||||
.then((context: Context) => {
|
||||
.then((context: Context) => {
|
||||
let contextStatuses = [...context.ancestors, status, ...context.descendants]
|
||||
const position = context.ancestors.length;
|
||||
|
||||
for (let i = 0; i < contextStatuses.length; i++) {
|
||||
let localStatuses = [];
|
||||
for (let i = 0; i < contextStatuses.length; i++) {
|
||||
let s = contextStatuses[i];
|
||||
let cwPolicy = this.toolsService.checkContentWarning(s);
|
||||
const wrapper = new StatusWrapper(cwPolicy.status, currentAccount, cwPolicy.applyCw, cwPolicy.hide);
|
||||
|
||||
if(i == position) wrapper.isSelected = true;
|
||||
const wrapper = new StatusWrapper(cwPolicy.status, currentAccount, cwPolicy.applyCw, cwPolicy.hide);
|
||||
|
||||
this.statuses.push(wrapper);
|
||||
if (i == position) wrapper.isSelected = true;
|
||||
|
||||
// this.statuses.push(wrapper);
|
||||
localStatuses.push(wrapper);
|
||||
}
|
||||
return localStatuses;
|
||||
})
|
||||
.then(async (localStatuses: StatusWrapper[]) => {
|
||||
|
||||
let remoteStatuses = await this.retrieveRemoteThread(status);
|
||||
let unknownStatuses = remoteStatuses.filter(x => !localStatuses.find(y => y.status.uri == x.uri));
|
||||
|
||||
for(let s of unknownStatuses){
|
||||
let cwPolicy = this.toolsService.checkContentWarning(s);
|
||||
let wrapper = new StatusWrapper(s, null, cwPolicy.applyCw, cwPolicy.hide);
|
||||
wrapper.isRemote = true;
|
||||
localStatuses.push(wrapper);
|
||||
}
|
||||
|
||||
localStatuses.sort((a,b) => {
|
||||
if(a.status.created_at > b.status.created_at) return 1;
|
||||
if(a.status.created_at < b.status.created_at) return -1;
|
||||
return 0;
|
||||
});
|
||||
|
||||
this.statuses = localStatuses;
|
||||
this.hasContentWarnings = this.statuses.filter(x => x.applyCw).length > 1;
|
||||
|
||||
return position;
|
||||
let newPosition = this.statuses.findIndex(x => x.isSelected);
|
||||
return newPosition;
|
||||
});
|
||||
|
||||
})
|
||||
.then((position: number) => {
|
||||
setTimeout(() => {
|
||||
const el = this.statusChildren.toArray()[position];
|
||||
|
||||
//el.elem.nativeElement.scrollIntoViewIfNeeded({ behavior: 'auto', block: 'start', inline: 'nearest' });
|
||||
|
||||
scrollIntoView(el.elem.nativeElement, { behavior: 'smooth', block: 'nearest'});
|
||||
//el.elem.nativeElement.scrollIntoViewIfNeeded({ behavior: 'auto', block: 'start', inline: 'nearest' });
|
||||
scrollIntoView(el.elem.nativeElement, { behavior: 'smooth', block: 'nearest' });
|
||||
}, 250);
|
||||
})
|
||||
.catch((err: HttpErrorResponse) => {
|
||||
@ -198,6 +222,35 @@ export class ThreadComponent implements OnInit, OnDestroy {
|
||||
});
|
||||
}
|
||||
|
||||
private async retrieveRemoteThread(status: Status): Promise<Status[]> {
|
||||
if(this.remoteStatusFetchingDisabled) return [];
|
||||
|
||||
try {
|
||||
let url = status.url;
|
||||
let splitUrl = url.replace('https://', '').split('/');
|
||||
let id = splitUrl[splitUrl.length - 1];
|
||||
let instance = splitUrl[0];
|
||||
|
||||
//Pleroma
|
||||
if(url.includes('/objects/')){
|
||||
var webpage = await this.httpClient.get(url, { responseType: 'text' }).toPromise();
|
||||
id = webpage.split(`<meta content="https://${instance}/notice/`)[1].split('" property="og:url"')[0];
|
||||
}
|
||||
|
||||
let context = await this.mastodonService.getRemoteStatusContext(instance, id);
|
||||
let remoteStatuses = [...context.ancestors, ...context.descendants];
|
||||
remoteStatuses.forEach(s => {
|
||||
if(!s.account.acct.includes('@')){
|
||||
s.account.acct += `@${instance}`;
|
||||
}
|
||||
});
|
||||
return remoteStatuses;
|
||||
|
||||
} catch (err) {
|
||||
return [];
|
||||
};
|
||||
}
|
||||
|
||||
refresh(): any {
|
||||
this.isLoading = true;
|
||||
this.displayError = null;
|
||||
@ -225,9 +278,9 @@ export class ThreadComponent implements OnInit, OnDestroy {
|
||||
const statuses = this.statusChildren.toArray();
|
||||
statuses.forEach(x => {
|
||||
x.removeContentWarning();
|
||||
if(x.isSelected){
|
||||
if (x.isSelected) {
|
||||
setTimeout(() => {
|
||||
scrollIntoView(x.elem.nativeElement, { behavior: 'auto', block: 'nearest'});
|
||||
scrollIntoView(x.elem.nativeElement, { behavior: 'auto', block: 'nearest' });
|
||||
}, 0);
|
||||
}
|
||||
});
|
||||
|
@ -21,4 +21,5 @@ export class StatusWrapper {
|
||||
) { }
|
||||
|
||||
public isSelected: boolean;
|
||||
public isRemote: boolean;
|
||||
}
|
@ -141,6 +141,10 @@ export class MastodonWrapperService {
|
||||
});
|
||||
}
|
||||
|
||||
getRemoteStatusContext(instance: string, targetStatusId: string): Promise<Context> {
|
||||
return this.mastodonService.getRemoteStatusContext(instance, targetStatusId);
|
||||
}
|
||||
|
||||
getFavorites(account: AccountInfo, maxId: string = null): Promise<FavoriteResult> { //, minId: string = null
|
||||
return this.refreshAccountIfNeeded(account)
|
||||
.then((refreshedAccount: AccountInfo) => {
|
||||
|
@ -167,6 +167,13 @@ export class MastodonService {
|
||||
return this.httpClient.get<Context>(route, { headers: headers }).toPromise();
|
||||
}
|
||||
|
||||
getRemoteStatusContext(instance: string, targetStatusId: string): Promise<Context> {
|
||||
const params = this.apiRoutes.getStatusContext.replace('{0}', targetStatusId);
|
||||
const route = `https://${instance}${params}`;
|
||||
|
||||
return this.httpClient.get<Context>(route).toPromise();
|
||||
}
|
||||
|
||||
getFavorites(account: AccountInfo, maxId: string = null): Promise<FavoriteResult> { //, minId: string = null
|
||||
let route = `https://${account.instance}${this.apiRoutes.getFavourites}`; //?limit=${limit}
|
||||
|
||||
|
@ -205,7 +205,10 @@ export class ToolsService {
|
||||
}
|
||||
|
||||
getStatusUsableByAccount(account: AccountInfo, originalStatus: StatusWrapper): Promise<Status> {
|
||||
const isProvider = originalStatus.provider.id === account.id;
|
||||
let isProvider = false;
|
||||
if(!originalStatus.isRemote){
|
||||
isProvider = originalStatus.provider.id === account.id;
|
||||
}
|
||||
|
||||
let statusPromise: Promise<Status> = Promise.resolve(originalStatus.status);
|
||||
|
||||
|
@ -51,14 +51,15 @@ export class GlobalSettings {
|
||||
disableAutofocus = false;
|
||||
disableAvatarNotifications = false;
|
||||
disableSounds = false;
|
||||
|
||||
disableRemoteStatusFetching = false;
|
||||
|
||||
notificationSoundFileId: string = '0';
|
||||
|
||||
contentWarningPolicy: ContentWarningPolicy = new ContentWarningPolicy();
|
||||
|
||||
columnSwitchingWinAlt = false;
|
||||
|
||||
accountSettings: AccountSettings[] = [];
|
||||
accountSettings: AccountSettings[] = [];
|
||||
}
|
||||
|
||||
export interface SettingsStateModel {
|
||||
@ -143,6 +144,7 @@ export class SettingsState {
|
||||
newSettings.disableSounds = oldSettings.disableSounds;
|
||||
newSettings.notificationSoundFileId = oldSettings.notificationSoundFileId;
|
||||
newSettings.columnSwitchingWinAlt = oldSettings.columnSwitchingWinAlt;
|
||||
newSettings.disableRemoteStatusFetching = oldSettings.disableRemoteStatusFetching;
|
||||
|
||||
return newSettings;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user