commit
822ef21985
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "sengi",
|
"name": "sengi",
|
||||||
"version": "1.6.0",
|
"version": "1.7.0",
|
||||||
"license": "AGPL-3.0-or-later",
|
"license": "AGPL-3.0-or-later",
|
||||||
"main": "main-electron.js",
|
"main": "main-electron.js",
|
||||||
"description": "A multi-account desktop client for Mastodon and Pleroma",
|
"description": "A multi-account desktop client for Mastodon and Pleroma",
|
||||||
|
|
|
@ -15,7 +15,7 @@ import { NavigationService } from '../../services/navigation.service';
|
||||||
import { NotificationService } from '../../services/notification.service';
|
import { NotificationService } from '../../services/notification.service';
|
||||||
import { MastodonService } from '../../services/mastodon.service';
|
import { MastodonService } from '../../services/mastodon.service';
|
||||||
import { AuthService } from '../../services/auth.service';
|
import { AuthService } from '../../services/auth.service';
|
||||||
|
import { SettingsState } from '../../states/settings.state';
|
||||||
|
|
||||||
describe('CreateStatusComponent', () => {
|
describe('CreateStatusComponent', () => {
|
||||||
let component: CreateStatusComponent;
|
let component: CreateStatusComponent;
|
||||||
|
@ -33,7 +33,8 @@ describe('CreateStatusComponent', () => {
|
||||||
NgxsModule.forRoot([
|
NgxsModule.forRoot([
|
||||||
RegisteredAppsState,
|
RegisteredAppsState,
|
||||||
AccountsState,
|
AccountsState,
|
||||||
StreamsState
|
StreamsState,
|
||||||
|
SettingsState
|
||||||
]),
|
]),
|
||||||
],
|
],
|
||||||
providers: [NavigationService, NotificationService, MastodonService, AuthService],
|
providers: [NavigationService, NotificationService, MastodonService, AuthService],
|
||||||
|
|
|
@ -127,6 +127,13 @@ export class CreateStatusComponent implements OnInit, OnDestroy {
|
||||||
|
|
||||||
// this.statusStateService.setStatusContent(this.status, this.statusReplyingToWrapper);
|
// this.statusStateService.setStatusContent(this.status, this.statusReplyingToWrapper);
|
||||||
|
|
||||||
|
// Retrieve mentions
|
||||||
|
for(let mention of value.status.mentions){
|
||||||
|
if(this.status){
|
||||||
|
this.status = this.status.replace(`@${mention.username}`, `@${mention.acct}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.setVisibilityFromStatus(value.status);
|
this.setVisibilityFromStatus(value.status);
|
||||||
this.title = value.status.spoiler_text;
|
this.title = value.status.spoiler_text;
|
||||||
this.statusLoaded = true;
|
this.statusLoaded = true;
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
<div class="floating-column__inner--left">
|
<div class="floating-column__inner--left">
|
||||||
<div class="floating-column__header">
|
<div class="floating-column__header">
|
||||||
<a class="close-button" href (click)="closePanel()" title="close">
|
<a class="close-button" href (click)="closePanel()" title="close">
|
||||||
<fa-icon [icon]="faTimes"></fa-icon>
|
<fa-icon class="close-button__icon" [icon]="faTimes"></fa-icon>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -29,9 +29,20 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.close-button {
|
.close-button {
|
||||||
|
// outline: 1px dotted orange;
|
||||||
|
|
||||||
display: block;
|
display: block;
|
||||||
float: right;
|
float: right;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: white;
|
color: white;
|
||||||
margin: 10px 16px 0 0;
|
margin: 5px 5px 0 0;
|
||||||
|
|
||||||
|
width: 40px;
|
||||||
|
height: 34px;
|
||||||
|
|
||||||
|
&__icon {
|
||||||
|
position: relative;
|
||||||
|
top: 6px;
|
||||||
|
left: 17px;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -122,6 +122,17 @@ export class MyAccountComponent implements OnInit, OnDestroy {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
.then(_ => {
|
||||||
|
this.availableLists.sort((a,b) => {
|
||||||
|
if (a.name < b.name) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (a.name > b.name) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
this.notificationService.notifyHttpError(err, this.account.info);
|
this.notificationService.notifyHttpError(err, this.account.info);
|
||||||
});
|
});
|
||||||
|
|
|
@ -70,17 +70,29 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<app-status *ngIf="notification.status && notification.type === 'update'" class="stream__status"
|
<app-status *ngIf="notification.status && notification.type === 'update'" class="stream__status"
|
||||||
[statusWrapper]="notification.status" [notificationAccount]="notification.account"
|
[statusWrapper]="notification.status"
|
||||||
[notificationType]="notification.type" (browseAccountEvent)="browseAccount($event)"
|
[notificationAccount]="notification.account"
|
||||||
(browseHashtagEvent)="browseHashtag($event)" (browseThreadEvent)="browseThread($event)"></app-status>
|
[notificationType]="notification.type"
|
||||||
|
[context]="'notifications'"
|
||||||
|
(browseAccountEvent)="browseAccount($event)"
|
||||||
|
(browseHashtagEvent)="browseHashtag($event)"
|
||||||
|
(browseThreadEvent)="browseThread($event)"></app-status>
|
||||||
|
|
||||||
<app-status *ngIf="notification.status && notification.type === 'mention'" class="stream__status"
|
<app-status *ngIf="notification.status && notification.type === 'mention'" class="stream__status"
|
||||||
[statusWrapper]="notification.status" (browseAccountEvent)="browseAccount($event)"
|
[statusWrapper]="notification.status"
|
||||||
(browseHashtagEvent)="browseHashtag($event)" (browseThreadEvent)="browseThread($event)"></app-status>
|
[context]="'notifications'"
|
||||||
|
(browseAccountEvent)="browseAccount($event)"
|
||||||
|
(browseHashtagEvent)="browseHashtag($event)"
|
||||||
|
(browseThreadEvent)="browseThread($event)"></app-status>
|
||||||
|
|
||||||
<app-status *ngIf="notification.status && notification.type !== 'mention' && notification.type !== 'update'"
|
<app-status *ngIf="notification.status && notification.type !== 'mention' && notification.type !== 'update'"
|
||||||
class="stream__status" [statusWrapper]="notification.status" [notificationAccount]="notification.account"
|
class="stream__status"
|
||||||
[notificationType]="notification.type" (browseAccountEvent)="browseAccount($event)"
|
[statusWrapper]="notification.status"
|
||||||
(browseHashtagEvent)="browseHashtag($event)" (browseThreadEvent)="browseThread($event)"></app-status>
|
[notificationAccount]="notification.account"
|
||||||
|
[notificationType]="notification.type"
|
||||||
|
[context]="'notifications'"
|
||||||
|
(browseAccountEvent)="browseAccount($event)"
|
||||||
|
(browseHashtagEvent)="browseHashtag($event)"
|
||||||
|
(browseThreadEvent)="browseThread($event)"></app-status>
|
||||||
|
|
||||||
</div>
|
</div>
|
|
@ -1,5 +1,8 @@
|
||||||
<div class="image">
|
<div class="image">
|
||||||
<div class="image__alt" *ngIf="displayAltLabel">ALT</div>
|
<div class="image__alt" *ngIf="displayAltLabel && attachment.description" title="{{ attachment.description }}">ALT</div>
|
||||||
|
<a *ngIf="status" href class="image__status" (click)="openStatus()" (auxclick)="openStatus()" title="open status">
|
||||||
|
<fa-icon class="image__status--icon" [icon]="faExternalLinkAlt"></fa-icon>
|
||||||
|
</a>
|
||||||
<a href class="image__link" (click)="openExternal()" (auxclick)="openExternal()" title="open image">
|
<a href class="image__link" (click)="openExternal()" (auxclick)="openExternal()" title="open image">
|
||||||
<fa-icon class="image__link--icon" [icon]="faLink"></fa-icon>
|
<fa-icon class="image__link--icon" [icon]="faLink"></fa-icon>
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -25,11 +25,31 @@
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__status {
|
||||||
|
z-index: 10;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 25px;
|
||||||
|
padding: 5px 5px 8px 8px;
|
||||||
|
transition: all .2s;
|
||||||
|
opacity: 0;
|
||||||
|
color: white;
|
||||||
|
|
||||||
|
&--icon {
|
||||||
|
filter: drop-shadow(0 0 3px rgb(78, 78, 78));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&:hover &__link {
|
&:hover &__link {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&:hover &__status {
|
||||||
|
opacity: 1;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
&__alt {
|
&__alt {
|
||||||
display: inline;
|
display: inline;
|
||||||
color: white;
|
color: white;
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
|
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
|
||||||
import { faLink } from "@fortawesome/free-solid-svg-icons";
|
import { faLink, faExternalLinkAlt } from "@fortawesome/free-solid-svg-icons";
|
||||||
|
|
||||||
import { SettingsService } from '../../../../../services/settings.service';
|
import { SettingsService } from '../../../../../services/settings.service';
|
||||||
import { Attachment } from '../../../../../services/models/mastodon.interfaces';
|
import { Attachment } from '../../../../../services/models/mastodon.interfaces';
|
||||||
|
import { StatusWrapper } from '../../../../../models/common.model';
|
||||||
|
import { OpenThreadEvent } from '../../../../../services/tools.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-attachement-image',
|
selector: 'app-attachement-image',
|
||||||
|
@ -11,10 +13,13 @@ import { Attachment } from '../../../../../services/models/mastodon.interfaces';
|
||||||
})
|
})
|
||||||
export class AttachementImageComponent implements OnInit {
|
export class AttachementImageComponent implements OnInit {
|
||||||
faLink = faLink;
|
faLink = faLink;
|
||||||
|
faExternalLinkAlt = faExternalLinkAlt;
|
||||||
displayAltLabel: boolean;
|
displayAltLabel: boolean;
|
||||||
|
|
||||||
@Input() attachment: Attachment;
|
@Input() attachment: Attachment;
|
||||||
|
@Input() status: StatusWrapper;
|
||||||
@Output() openEvent = new EventEmitter();
|
@Output() openEvent = new EventEmitter();
|
||||||
|
@Output() browseThreadEvent = new EventEmitter<OpenThreadEvent>();
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly settingsService: SettingsService
|
private readonly settingsService: SettingsService
|
||||||
|
@ -34,4 +39,13 @@ export class AttachementImageComponent implements OnInit {
|
||||||
window.open(this.attachment.url, '_blank');
|
window.open(this.attachment.url, '_blank');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
openStatus(): boolean {
|
||||||
|
if(!this.status) return false;
|
||||||
|
|
||||||
|
const openThreadEvent = new OpenThreadEvent(this.status.status, this.status.provider);
|
||||||
|
this.browseThreadEvent.next(openThreadEvent);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -202,7 +202,7 @@ describe('DatabindedTextComponent', () => {
|
||||||
const sample = `<p>Bla <a href="https://ubuntu.social/tags/kubecon" rel="tag">#<span>KubeCon</span></a> Bla</p>`;
|
const sample = `<p>Bla <a href="https://ubuntu.social/tags/kubecon" rel="tag">#<span>KubeCon</span></a> Bla</p>`;
|
||||||
|
|
||||||
component.text = sample;
|
component.text = sample;
|
||||||
expect(component.processedText).toContain('<p>Bla <a href="https://ubuntu.social/tags/kubecon" class="hashtag-KubeCon" title="#KubeCon" target="_blank" rel="noopener noreferrer">#KubeCon</a> Bla</p>');
|
expect(component.processedText).toContain('<p>Bla <a href="https://ubuntu.social/tags/kubecon" class="hashtag-KubeCon" title="#KubeCon" target="_blank" rel="noopener noreferrer">#KubeCon</a> Bla</p>');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should parse link - Pleroma', () => {
|
it('should parse link - Pleroma', () => {
|
||||||
|
|
|
@ -45,7 +45,15 @@ export class PollComponent implements OnInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.options.length = 0;
|
this.options.length = 0;
|
||||||
const maxVotes = Math.max(...this.poll.options.map(x => x.votes_count));
|
|
||||||
|
let maxVotes = Math.max(...this.poll.options.map(x => x.votes_count));
|
||||||
|
|
||||||
|
if(!this.poll.multiple){ //Fix for absurd values in pleroma
|
||||||
|
this.poll.voters_count = this.poll.votes_count;
|
||||||
|
} else if(this.poll.voters_count * this.poll.options.length < this.poll.votes_count){
|
||||||
|
this.poll.voters_count = this.poll.votes_count;
|
||||||
|
}
|
||||||
|
|
||||||
let i = 0;
|
let i = 0;
|
||||||
for (let opt of this.poll.options) {
|
for (let opt of this.poll.options) {
|
||||||
let optWrapper = new PollOptionWrapper(i, opt, this.poll.votes_count, this.poll.voters_count, opt.votes_count === maxVotes);
|
let optWrapper = new PollOptionWrapper(i, opt, this.poll.votes_count, this.poll.voters_count, opt.votes_count === maxVotes);
|
||||||
|
@ -195,7 +203,7 @@ class PollOptionWrapper implements PollOption {
|
||||||
if (totalVotes === 0) {
|
if (totalVotes === 0) {
|
||||||
this.percentage = '0';
|
this.percentage = '0';
|
||||||
} else {
|
} else {
|
||||||
this.percentage = ((this.votes_count / votesDivider) * 100).toFixed(0);
|
this.percentage = ((this.votes_count / votesDivider) * 100).toFixed(0);
|
||||||
}
|
}
|
||||||
this.isMax = isMax;
|
this.isMax = isMax;
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,6 +59,8 @@ export class StatusComponent implements OnInit {
|
||||||
@Input() notificationType: 'mention' | 'reblog' | 'favourite' | 'poll' | 'update';
|
@Input() notificationType: 'mention' | 'reblog' | 'favourite' | 'poll' | 'update';
|
||||||
@Input() notificationAccount: Account;
|
@Input() notificationAccount: Account;
|
||||||
|
|
||||||
|
@Input() context: 'home' | 'notifications' | 'public' | 'thread' | 'account';
|
||||||
|
|
||||||
private _statusWrapper: StatusWrapper;
|
private _statusWrapper: StatusWrapper;
|
||||||
status: Status;
|
status: Status;
|
||||||
|
|
||||||
|
@ -98,6 +100,8 @@ export class StatusComponent implements OnInit {
|
||||||
// this.statusAccountName = this.emojiConverter.applyEmojis(this.displayedStatus.account.emojis, this.displayedStatus.account.display_name, EmojiTypeEnum.small);
|
// this.statusAccountName = this.emojiConverter.applyEmojis(this.displayedStatus.account.emojis, this.displayedStatus.account.display_name, EmojiTypeEnum.small);
|
||||||
let statusContent = this.emojiConverter.applyEmojis(this.displayedStatus.emojis, this.displayedStatus.content, EmojiTypeEnum.medium);
|
let statusContent = this.emojiConverter.applyEmojis(this.displayedStatus.emojis, this.displayedStatus.content, EmojiTypeEnum.medium);
|
||||||
this.statusContent = this.ensureMentionAreDisplayed(statusContent);
|
this.statusContent = this.ensureMentionAreDisplayed(statusContent);
|
||||||
|
|
||||||
|
this.validateFilteringStatus();
|
||||||
}
|
}
|
||||||
get statusWrapper(): StatusWrapper {
|
get statusWrapper(): StatusWrapper {
|
||||||
return this._statusWrapper;
|
return this._statusWrapper;
|
||||||
|
@ -123,6 +127,36 @@ export class StatusComponent implements OnInit {
|
||||||
if (this.statusesStateServiceSub) this.statusesStateServiceSub.unsubscribe();
|
if (this.statusesStateServiceSub) this.statusesStateServiceSub.unsubscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private validateFilteringStatus(){
|
||||||
|
const filterStatus = this.displayedStatus.filtered;
|
||||||
|
|
||||||
|
if(!filterStatus || filterStatus.length === 0) return;
|
||||||
|
|
||||||
|
// if(!this.context){
|
||||||
|
// console.warn('this.context not found');
|
||||||
|
// console.warn(this.context);
|
||||||
|
// }
|
||||||
|
|
||||||
|
for (let filter of filterStatus) {
|
||||||
|
if(this.context && filter.filter.context && filter.filter.context.length > 0){
|
||||||
|
if(!filter.filter.context.includes(this.context)) continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(filter.filter.filter_action === 'warn'){
|
||||||
|
this.isContentWarned = true;
|
||||||
|
|
||||||
|
let filterTxt = `FILTERED:`;
|
||||||
|
for(let w of filter.keyword_matches){
|
||||||
|
filterTxt += ` ${w}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.contentWarningText = filterTxt;
|
||||||
|
} else if (filter.filter.filter_action === 'hide'){
|
||||||
|
this.hideStatus = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
getAvatar(acc: Account): string {
|
getAvatar(acc: Account): string {
|
||||||
if(this.freezeAvatarEnabled){
|
if(this.freezeAvatarEnabled){
|
||||||
return acc.avatar_static;
|
return acc.avatar_static;
|
||||||
|
|
|
@ -122,7 +122,7 @@ export class StreamNotificationsComponent extends BrowseBase {
|
||||||
loadNotifications(): any {
|
loadNotifications(): any {
|
||||||
this.account = this.toolsService.getAccountById(this.streamElement.accountId);
|
this.account = this.toolsService.getAccountById(this.streamElement.accountId);
|
||||||
|
|
||||||
this.mentionsSubscription = this.userNotificationService.userNotifications.subscribe((userNotifications: UserNotification[]) => {
|
this.mentionsSubscription = this.userNotificationService.userNotifications.subscribe((userNotifications: UserNotification[]) => {
|
||||||
this.loadMentions(userNotifications);
|
this.loadMentions(userNotifications);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -130,10 +130,13 @@ export class StreamNotificationsComponent extends BrowseBase {
|
||||||
.then((notifications: Notification[]) => {
|
.then((notifications: Notification[]) => {
|
||||||
this.isNotificationsLoading = false;
|
this.isNotificationsLoading = false;
|
||||||
|
|
||||||
this.notifications = notifications.map(x => {
|
let wrappedNotification= notifications.map(x => {
|
||||||
let cwPolicy = this.toolsService.checkContentWarning(x.status);
|
let cwPolicy = this.toolsService.checkContentWarning(x.status);
|
||||||
return new NotificationWrapper(x, this.account, cwPolicy.applyCw, cwPolicy.hide);
|
return new NotificationWrapper(x, this.account, cwPolicy.applyCw, cwPolicy.hide);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.notifications = wrappedNotification.filter(x => x.type !== 'mention' || (x.type === 'mention' && x.status.status !== null));
|
||||||
|
|
||||||
this.lastNotificationId = this.notifications[this.notifications.length - 1].notification.id;
|
this.lastNotificationId = this.notifications[this.notifications.length - 1].notification.id;
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
|
|
|
@ -1,9 +1,5 @@
|
||||||
<div class="overlay">
|
<div class="overlay">
|
||||||
<div class="overlay__header">
|
<div class="overlay__header">
|
||||||
<a href class="overlay__button overlay-close" title="close" (click)="close()">
|
|
||||||
<fa-icon class="overlay-close__icon" [icon]="faTimes"></fa-icon>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a href class="overlay__button overlay-previous"
|
<a href class="overlay__button overlay-previous"
|
||||||
[ngClass]="{'overlay__button--focus': hasPreviousElements }" title="previous" (click)="previous()">
|
[ngClass]="{'overlay__button--focus': hasPreviousElements }" title="previous" (click)="previous()">
|
||||||
<fa-icon class="overlay-previous__icon" [icon]="faAngleLeft"></fa-icon>
|
<fa-icon class="overlay-previous__icon" [icon]="faAngleLeft"></fa-icon>
|
||||||
|
@ -12,13 +8,17 @@
|
||||||
title="refresh" (click)="refresh()">
|
title="refresh" (click)="refresh()">
|
||||||
<fa-icon class="overlay-refresh__icon" [icon]="faRedoAlt"></fa-icon>
|
<fa-icon class="overlay-refresh__icon" [icon]="faRedoAlt"></fa-icon>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
<a href class="overlay__button overlay-next" [ngClass]="{'overlay__button--focus': hasNextElements }"
|
||||||
|
title="next" (click)="next()">
|
||||||
|
<fa-icon class="overlay-next__icon" [icon]="faAngleRight"></fa-icon>
|
||||||
|
</a>
|
||||||
|
|
||||||
<a href title="return to top" class="overlay-gototop" (click)="goToTop()">
|
<a href title="return to top" class="overlay-gototop" (click)="goToTop()">
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<a href class="overlay__button overlay-next" [ngClass]="{'overlay__button--focus': hasNextElements }"
|
<a href class="overlay__button overlay-close" title="close" (click)="close()">
|
||||||
title="next" (click)="next()">
|
<fa-icon class="overlay-close__icon" [icon]="faTimes"></fa-icon>
|
||||||
<fa-icon class="overlay-next__icon" [icon]="faAngleRight"></fa-icon>
|
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,9 @@ $header-content-height: 40px;
|
||||||
width: calc(100%);
|
width: calc(100%);
|
||||||
height: $header-content-height;
|
height: $header-content-height;
|
||||||
background-color: $column-header-background-color;
|
background-color: $column-header-background-color;
|
||||||
border-bottom: 1px solid #222736;
|
border-bottom: 1px solid #222736;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
}
|
}
|
||||||
&__content-wrapper {
|
&__content-wrapper {
|
||||||
transition: all .2s;
|
transition: all .2s;
|
||||||
|
@ -44,11 +46,17 @@ $header-content-height: 40px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__button {
|
&__button {
|
||||||
|
// outline: 1px dotted orange;
|
||||||
|
|
||||||
width: 25px;
|
width: 25px;
|
||||||
height: 25px;
|
height: 25px;
|
||||||
|
|
||||||
|
width: $header-content-height;
|
||||||
|
height: $header-content-height;
|
||||||
|
|
||||||
color: #354060;
|
color: #354060;
|
||||||
transition: all .2s;
|
transition: all .2s;
|
||||||
margin: 8px 0 0 8px;
|
|
||||||
&:hover {
|
&:hover {
|
||||||
color: #536599;
|
color: #536599;
|
||||||
color: #7a8dc7;
|
color: #7a8dc7;
|
||||||
|
@ -68,19 +76,8 @@ $header-content-height: 40px;
|
||||||
|
|
||||||
&__icon {
|
&__icon {
|
||||||
position: relative;
|
position: relative;
|
||||||
left: 7px;
|
left: 17px;
|
||||||
top: -1px
|
top: 7px
|
||||||
}
|
|
||||||
}
|
|
||||||
&-next {
|
|
||||||
display: block;
|
|
||||||
float: left;
|
|
||||||
font-size: 18px;
|
|
||||||
|
|
||||||
&__icon {
|
|
||||||
position: relative;
|
|
||||||
left: 8px;
|
|
||||||
top: -1px
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&-refresh {
|
&-refresh {
|
||||||
|
@ -90,29 +87,38 @@ $header-content-height: 40px;
|
||||||
|
|
||||||
&__icon {
|
&__icon {
|
||||||
position: relative;
|
position: relative;
|
||||||
left: 5px;
|
left: 13px;
|
||||||
top: 1px
|
top: 9px
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&-next {
|
||||||
|
display: block;
|
||||||
|
float: left;
|
||||||
|
font-size: 18px;
|
||||||
|
|
||||||
|
&__icon {
|
||||||
|
position: relative;
|
||||||
|
left: 13px;
|
||||||
|
top: 7px
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&-gototop {
|
&-gototop {
|
||||||
position: absolute;
|
// outline: 1px dotted orange;
|
||||||
top: 0;
|
|
||||||
left: 110px;
|
flex-grow: 1;
|
||||||
right: 40px;
|
|
||||||
display: block;
|
display: block;
|
||||||
height: $header-content-height;
|
height: $header-content-height;
|
||||||
}
|
}
|
||||||
&-close {
|
&-close {
|
||||||
display: block;
|
display: block;
|
||||||
float: right;
|
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
color: white;
|
color: white;
|
||||||
margin-right: 8px;
|
|
||||||
|
|
||||||
&__icon {
|
&__icon {
|
||||||
position: relative;
|
position: relative;
|
||||||
left: 7px;
|
left: 15px;
|
||||||
top: 1px
|
top: 9px
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,8 +17,11 @@
|
||||||
|
|
||||||
<div class="stream-toots__status" *ngFor="let statusWrapper of statuses" #status>
|
<div class="stream-toots__status" *ngFor="let statusWrapper of statuses" #status>
|
||||||
<app-status
|
<app-status
|
||||||
[statusWrapper]="statusWrapper" [isThreadDisplay]="isThread"
|
[statusWrapper]="statusWrapper"
|
||||||
(browseAccountEvent)="browseAccount($event)" (browseHashtagEvent)="browseHashtag($event)"
|
[isThreadDisplay]="isThread"
|
||||||
|
[context]="context"
|
||||||
|
(browseAccountEvent)="browseAccount($event)"
|
||||||
|
(browseHashtagEvent)="browseHashtag($event)"
|
||||||
(browseThreadEvent)="browseThread($event)"></app-status>
|
(browseThreadEvent)="browseThread($event)"></app-status>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { HttpErrorResponse } from '@angular/common/http';
|
||||||
import { Observable, Subscription } from 'rxjs';
|
import { Observable, Subscription } from 'rxjs';
|
||||||
import { Store } from '@ngxs/store';
|
import { Store } from '@ngxs/store';
|
||||||
|
|
||||||
import { StreamElement } from '../../../states/streams.state';
|
import { StreamElement, StreamTypeEnum } from '../../../states/streams.state';
|
||||||
import { AccountInfo } from '../../../states/accounts.state';
|
import { AccountInfo } from '../../../states/accounts.state';
|
||||||
import { StreamingService, EventEnum, StatusUpdate } from '../../../services/streaming.service';
|
import { StreamingService, EventEnum, StatusUpdate } from '../../../services/streaming.service';
|
||||||
import { Status } from '../../../services/models/mastodon.interfaces';
|
import { Status } from '../../../services/models/mastodon.interfaces';
|
||||||
|
@ -20,9 +20,11 @@ import { SettingsService } from '../../../services/settings.service';
|
||||||
templateUrl: './stream-statuses.component.html',
|
templateUrl: './stream-statuses.component.html',
|
||||||
styleUrls: ['./stream-statuses.component.scss']
|
styleUrls: ['./stream-statuses.component.scss']
|
||||||
})
|
})
|
||||||
export class StreamStatusesComponent extends TimelineBase {
|
export class StreamStatusesComponent extends TimelineBase {
|
||||||
protected _streamElement: StreamElement;
|
protected _streamElement: StreamElement;
|
||||||
|
|
||||||
|
context: 'home' | 'notifications' | 'public' | 'thread' | 'account';
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
set streamElement(streamElement: StreamElement) {
|
set streamElement(streamElement: StreamElement) {
|
||||||
this._streamElement = streamElement;
|
this._streamElement = streamElement;
|
||||||
|
@ -32,6 +34,8 @@ export class StreamStatusesComponent extends TimelineBase {
|
||||||
this.hideReplies = streamElement.hideReplies;
|
this.hideReplies = streamElement.hideReplies;
|
||||||
|
|
||||||
this.load(this._streamElement);
|
this.load(this._streamElement);
|
||||||
|
|
||||||
|
this.setContext(this._streamElement);
|
||||||
}
|
}
|
||||||
get streamElement(): StreamElement {
|
get streamElement(): StreamElement {
|
||||||
return this._streamElement;
|
return this._streamElement;
|
||||||
|
@ -112,6 +116,24 @@ export class StreamStatusesComponent extends TimelineBase {
|
||||||
if (this.deleteStatusSubscription) this.deleteStatusSubscription.unsubscribe();
|
if (this.deleteStatusSubscription) this.deleteStatusSubscription.unsubscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private setContext(streamElement: StreamElement) {
|
||||||
|
switch(streamElement.type){
|
||||||
|
case StreamTypeEnum.global:
|
||||||
|
case StreamTypeEnum.local:
|
||||||
|
case StreamTypeEnum.tag:
|
||||||
|
this.context = 'public';
|
||||||
|
break;
|
||||||
|
case StreamTypeEnum.personnal:
|
||||||
|
case StreamTypeEnum.list:
|
||||||
|
this.context = 'home';
|
||||||
|
break;
|
||||||
|
case StreamTypeEnum.activity:
|
||||||
|
case StreamTypeEnum.directmessages:
|
||||||
|
this.context = 'notifications';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
refresh(): any {
|
refresh(): any {
|
||||||
this.load(this._streamElement);
|
this.load(this._streamElement);
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,8 @@ export class ThreadComponent extends BrowseBase {
|
||||||
hasContentWarnings = false;
|
hasContentWarnings = false;
|
||||||
private remoteStatusFetchingDisabled = false;
|
private remoteStatusFetchingDisabled = false;
|
||||||
|
|
||||||
|
context = 'thread';
|
||||||
|
|
||||||
numNewItems: number; //html compatibility only
|
numNewItems: number; //html compatibility only
|
||||||
bufferStream: Status[] = []; //html compatibility only
|
bufferStream: Status[] = []; //html compatibility only
|
||||||
streamPositionnedAtTop: boolean = true; //html compatibility only
|
streamPositionnedAtTop: boolean = true; //html compatibility only
|
||||||
|
|
|
@ -166,39 +166,27 @@
|
||||||
|
|
||||||
<div class="profile__extra-info profile__extra-info__preparefloating" *ngIf="!isLoading"
|
<div class="profile__extra-info profile__extra-info__preparefloating" *ngIf="!isLoading"
|
||||||
[class.profile__extra-info__floating]="showFloatingStatusMenu">
|
[class.profile__extra-info__floating]="showFloatingStatusMenu">
|
||||||
<div class="profile__extra-info__section">
|
<a href class="profile__extra-info__section profile__extra-info__links" (click)="switchStatusSection('status')" title="Status"
|
||||||
<a href class="profile__extra-info__links" (click)="switchStatusSection('status')" title="Status"
|
|
||||||
[class.profile__extra-info__links--selected]="statusSection === 'status'">Status</a>
|
[class.profile__extra-info__links--selected]="statusSection === 'status'">Status</a>
|
||||||
</div>
|
<a href class="profile__extra-info__section profile__extra-info__links" (click)="switchStatusSection('replies')"
|
||||||
<div class="profile__extra-info__section">
|
|
||||||
<a href class="profile__extra-info__links" (click)="switchStatusSection('replies')"
|
|
||||||
title="Status & Replies"
|
title="Status & Replies"
|
||||||
[class.profile__extra-info__links--selected]="statusSection === 'replies'">Status &
|
[class.profile__extra-info__links--selected]="statusSection === 'replies'">Status &
|
||||||
Replies</a>
|
Replies</a>
|
||||||
</div>
|
<a href class="profile__extra-info__section profile__extra-info__links" (click)="switchStatusSection('media')" title="Media"
|
||||||
<div class="profile__extra-info__section">
|
|
||||||
<a href class="profile__extra-info__links" (click)="switchStatusSection('media')" title="Media"
|
|
||||||
[class.profile__extra-info__links--selected]="statusSection === 'media'">Media</a>
|
[class.profile__extra-info__links--selected]="statusSection === 'media'">Media</a>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="profile-statuses" #profilestatuses>
|
<div class="profile-statuses" #profilestatuses>
|
||||||
<div class="profile__extra-info" *ngIf="!isLoading">
|
<div class="profile__extra-info" *ngIf="!isLoading">
|
||||||
<div class="profile__extra-info__section">
|
<a href class="profile__extra-info__section profile__extra-info__links" (click)="switchStatusSection('status')"
|
||||||
<a href class="profile__extra-info__links" (click)="switchStatusSection('status')"
|
|
||||||
title="Status"
|
title="Status"
|
||||||
[class.profile__extra-info__links--selected]="statusSection === 'status'">Status</a>
|
[class.profile__extra-info__links--selected]="statusSection === 'status'">Status</a>
|
||||||
</div>
|
<a href class="profile__extra-info__section profile__extra-info__links" (click)="switchStatusSection('replies')"
|
||||||
<div class="profile__extra-info__section">
|
|
||||||
<a href class="profile__extra-info__links" (click)="switchStatusSection('replies')"
|
|
||||||
title="Status & Replies"
|
title="Status & Replies"
|
||||||
[class.profile__extra-info__links--selected]="statusSection === 'replies'">Status &
|
[class.profile__extra-info__links--selected]="statusSection === 'replies'">Status &
|
||||||
Replies</a>
|
Replies</a>
|
||||||
</div>
|
<a href class="profile__extra-info__section profile__extra-info__links" (click)="switchStatusSection('media')" title="Media"
|
||||||
<div class="profile__extra-info__section">
|
|
||||||
<a href class="profile__extra-info__links" (click)="switchStatusSection('media')" title="Media"
|
|
||||||
[class.profile__extra-info__links--selected]="statusSection === 'media'">Media</a>
|
[class.profile__extra-info__links--selected]="statusSection === 'media'">Media</a>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div [class.profile__status-switching-section]="isSwitchingSection">
|
<div [class.profile__status-switching-section]="isSwitchingSection">
|
||||||
|
@ -208,21 +196,29 @@
|
||||||
|
|
||||||
<div *ngIf="statusSection === 'status' && !statusLoading">
|
<div *ngIf="statusSection === 'status' && !statusLoading">
|
||||||
<div *ngFor="let statusWrapper of pinnedStatuses">
|
<div *ngFor="let statusWrapper of pinnedStatuses">
|
||||||
<app-status [statusWrapper]="statusWrapper" (browseHashtagEvent)="browseHashtag($event)"
|
<app-status
|
||||||
(browseAccountEvent)="browseAccount($event)" (browseThreadEvent)="browseThread($event)">
|
[statusWrapper]="statusWrapper"
|
||||||
|
[context]="'account'"
|
||||||
|
(browseHashtagEvent)="browseHashtag($event)"
|
||||||
|
(browseAccountEvent)="browseAccount($event)"
|
||||||
|
(browseThreadEvent)="browseThread($event)">
|
||||||
</app-status>
|
</app-status>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div *ngFor="let statusWrapper of statuses">
|
<div *ngFor="let statusWrapper of statuses">
|
||||||
<div *ngIf="statusSection !== 'media'">
|
<div *ngIf="statusSection !== 'media'">
|
||||||
<app-status [statusWrapper]="statusWrapper" (browseHashtagEvent)="browseHashtag($event)"
|
<app-status
|
||||||
(browseAccountEvent)="browseAccount($event)" (browseThreadEvent)="browseThread($event)">
|
[statusWrapper]="statusWrapper"
|
||||||
|
[context]="'account'"
|
||||||
|
(browseHashtagEvent)="browseHashtag($event)"
|
||||||
|
(browseAccountEvent)="browseAccount($event)"
|
||||||
|
(browseThreadEvent)="browseThread($event)">
|
||||||
</app-status>
|
</app-status>
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="statusSection === 'media'" class="status-media">
|
<div *ngIf="statusSection === 'media'" class="status-media">
|
||||||
<div *ngFor="let media of statusWrapper.status.media_attachments">
|
<div *ngFor="let media of statusWrapper.status.media_attachments">
|
||||||
<app-attachement-image *ngIf="media.type === 'image' || media.type === 'gifv'" class="status-media__image" [attachment]="media" (openEvent)="openAttachment(media)"></app-attachement-image>
|
<app-attachement-image *ngIf="media.type === 'image' || media.type === 'gifv'" class="status-media__image" [attachment]="media" [status]="statusWrapper" (openEvent)="openAttachment(media)" (browseThreadEvent)="browseThread($event)"></app-attachement-image>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -275,14 +275,15 @@ $floating-header-height: 60px;
|
||||||
&-follows {
|
&-follows {
|
||||||
width: calc(100%);
|
width: calc(100%);
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
border-bottom: 1px solid #0f111a;;
|
border-bottom: 1px solid #0f111a;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
|
||||||
&__link {
|
&__link {
|
||||||
color: white;
|
color: white;
|
||||||
width: calc(50%);
|
flex-grow: 1;
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
display: inline-block;
|
|
||||||
background-color: #1a1f2e;
|
background-color: #1a1f2e;
|
||||||
transition: all .2s;
|
transition: all .2s;
|
||||||
|
|
||||||
|
@ -311,15 +312,15 @@ $floating-header-height: 60px;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
transition: all .4s;
|
transition: all .4s;
|
||||||
|
|
||||||
&__section {
|
display: flex;
|
||||||
text-align: center;
|
|
||||||
display: inline-block;
|
|
||||||
width: calc(33.333% - 5px);
|
|
||||||
padding: 5px 0 7px 0;
|
|
||||||
|
|
||||||
&:not(:last-child) {
|
&__section {
|
||||||
margin-right: 5px;
|
// outline: 1px dotted orange;
|
||||||
}
|
|
||||||
|
flex-grow: 1;
|
||||||
|
|
||||||
|
text-align: center;
|
||||||
|
padding: 5px 0 7px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__preparefloating {
|
&__preparefloating {
|
||||||
|
|
|
@ -7,7 +7,7 @@ import { Store } from '@ngxs/store';
|
||||||
|
|
||||||
import { Account, Status, Relationship, Attachment } from "../../../services/models/mastodon.interfaces";
|
import { Account, Status, Relationship, Attachment } from "../../../services/models/mastodon.interfaces";
|
||||||
import { MastodonWrapperService } from '../../../services/mastodon-wrapper.service';
|
import { MastodonWrapperService } from '../../../services/mastodon-wrapper.service';
|
||||||
import { ToolsService, OpenThreadEvent } from '../../../services/tools.service';
|
import { ToolsService, OpenThreadEvent, InstanceType } from '../../../services/tools.service';
|
||||||
import { NotificationService } from '../../../services/notification.service';
|
import { NotificationService } from '../../../services/notification.service';
|
||||||
import { AccountInfo } from '../../../states/accounts.state';
|
import { AccountInfo } from '../../../states/accounts.state';
|
||||||
import { StatusWrapper, OpenMediaEvent } from '../../../models/common.model';
|
import { StatusWrapper, OpenMediaEvent } from '../../../models/common.model';
|
||||||
|
@ -286,21 +286,44 @@ export class UserProfileComponent extends BrowseBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
follow(): boolean {
|
follow(): boolean {
|
||||||
|
this.loadingRelationShip = true;
|
||||||
|
|
||||||
const userAccount = this.toolsService.getSelectedAccounts()[0];
|
const userAccount = this.toolsService.getSelectedAccounts()[0];
|
||||||
|
|
||||||
|
let foundAccountToFollow: Account;
|
||||||
this.toolsService.findAccount(userAccount, this.lastAccountName)
|
this.toolsService.findAccount(userAccount, this.lastAccountName)
|
||||||
.then((account: Account) => {
|
.then((account: Account) => {
|
||||||
|
foundAccountToFollow = account;
|
||||||
return this.mastodonService.follow(userAccount, account);
|
return this.mastodonService.follow(userAccount, account);
|
||||||
})
|
})
|
||||||
.then((relationship: Relationship) => {
|
.then((relationship: Relationship) => {
|
||||||
this.relationship = relationship;
|
this.relationship = relationship;
|
||||||
|
})
|
||||||
|
.then(async () => {
|
||||||
|
// Double check for pleroma users
|
||||||
|
const instanceInfo = await this.toolsService.getInstanceInfo(userAccount);
|
||||||
|
if(instanceInfo.type === InstanceType.Pleroma || instanceInfo.type === InstanceType.Akkoma){
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 1000))
|
||||||
|
|
||||||
|
const relationships = await this.mastodonService.getRelationships(userAccount, [foundAccountToFollow]);
|
||||||
|
const relationship = relationships.find(x => x.id === foundAccountToFollow.id);
|
||||||
|
if(relationship){
|
||||||
|
this.relationship = relationship;
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.catch((err: HttpErrorResponse) => {
|
.catch((err: HttpErrorResponse) => {
|
||||||
this.notificationService.notifyHttpError(err, userAccount);
|
this.notificationService.notifyHttpError(err, userAccount);
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
this.loadingRelationShip = false;
|
||||||
});
|
});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
unfollow(): boolean {
|
unfollow(): boolean {
|
||||||
|
this.loadingRelationShip = true;
|
||||||
|
|
||||||
const userAccount = this.toolsService.getSelectedAccounts()[0];
|
const userAccount = this.toolsService.getSelectedAccounts()[0];
|
||||||
this.toolsService.findAccount(userAccount, this.lastAccountName)
|
this.toolsService.findAccount(userAccount, this.lastAccountName)
|
||||||
.then((account: Account) => {
|
.then((account: Account) => {
|
||||||
|
@ -311,6 +334,9 @@ export class UserProfileComponent extends BrowseBase {
|
||||||
})
|
})
|
||||||
.catch((err: HttpErrorResponse) => {
|
.catch((err: HttpErrorResponse) => {
|
||||||
this.notificationService.notifyHttpError(err, userAccount);
|
this.notificationService.notifyHttpError(err, userAccount);
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
this.loadingRelationShip = false;
|
||||||
});
|
});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
import { TestBed } from '@angular/core/testing';
|
import { TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
import { ElectronService } from './electron.service';
|
import { MyElectronService } from './electron.service';
|
||||||
|
|
||||||
describe('ElectronService', () => {
|
xdescribe('MyElectronService', () => {
|
||||||
beforeEach(() => TestBed.configureTestingModule({}));
|
beforeEach(() => TestBed.configureTestingModule({}));
|
||||||
|
|
||||||
it('should be created', () => {
|
it('should be created', () => {
|
||||||
const service: ElectronService = TestBed.get(ElectronService);
|
const service: MyElectronService = TestBed.get(MyElectronService);
|
||||||
expect(service).toBeTruthy();
|
expect(service).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -31,6 +31,8 @@ export class InstancesInfoService {
|
||||||
const instanceV1 = <Instancev1>instance;
|
const instanceV1 = <Instancev1>instance;
|
||||||
if (instanceV1 && instanceV1.max_toot_chars)
|
if (instanceV1 && instanceV1.max_toot_chars)
|
||||||
return instanceV1.max_toot_chars;
|
return instanceV1.max_toot_chars;
|
||||||
|
if(instanceV1 && instanceV1.configuration && instanceV1.configuration.statuses && instanceV1.configuration.statuses.max_characters)
|
||||||
|
return instanceV1.configuration.statuses.max_characters;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.defaultMaxChars;
|
return this.defaultMaxChars;
|
||||||
|
|
|
@ -398,11 +398,13 @@ export class MastodonService {
|
||||||
uploadMediaAttachment(account: AccountInfo, file: File, description: string): Promise<Attachment> {
|
uploadMediaAttachment(account: AccountInfo, file: File, description: string): Promise<Attachment> {
|
||||||
let input = new FormData();
|
let input = new FormData();
|
||||||
input.append('file', file);
|
input.append('file', file);
|
||||||
|
|
||||||
if (description !== null && description !== undefined) {
|
if (description !== null && description !== undefined) {
|
||||||
input.append('description', description);
|
input.append('description', description);
|
||||||
} else {
|
} else {
|
||||||
input.append('description', '');
|
input.append('description', '');
|
||||||
}
|
}
|
||||||
|
|
||||||
const route = `https://${account.instance}${this.apiRoutes.uploadMediaAttachment}`;
|
const route = `https://${account.instance}${this.apiRoutes.uploadMediaAttachment}`;
|
||||||
const headers = new HttpHeaders({ 'Authorization': `Bearer ${account.token.access_token}` });
|
const headers = new HttpHeaders({ 'Authorization': `Bearer ${account.token.access_token}` });
|
||||||
return this.httpClient.post<Attachment>(route, input, { headers: headers }).toPromise();
|
return this.httpClient.post<Attachment>(route, input, { headers: headers }).toPromise();
|
||||||
|
@ -411,7 +413,13 @@ export class MastodonService {
|
||||||
//TODO: add focus support
|
//TODO: add focus support
|
||||||
updateMediaAttachment(account: AccountInfo, mediaId: string, description: string): Promise<Attachment> {
|
updateMediaAttachment(account: AccountInfo, mediaId: string, description: string): Promise<Attachment> {
|
||||||
let input = new FormData();
|
let input = new FormData();
|
||||||
input.append('description', description);
|
|
||||||
|
if (description !== null && description !== undefined) {
|
||||||
|
input.append('description', description);
|
||||||
|
} else {
|
||||||
|
input.append('description', '');
|
||||||
|
}
|
||||||
|
|
||||||
const route = `https://${account.instance}${this.apiRoutes.updateMediaAttachment.replace('{0}', mediaId)}`;
|
const route = `https://${account.instance}${this.apiRoutes.updateMediaAttachment.replace('{0}', mediaId)}`;
|
||||||
const headers = new HttpHeaders({ 'Authorization': `Bearer ${account.token.access_token}` });
|
const headers = new HttpHeaders({ 'Authorization': `Bearer ${account.token.access_token}` });
|
||||||
return this.httpClient.put<Attachment>(route, input, { headers: headers }).toPromise();
|
return this.httpClient.put<Attachment>(route, input, { headers: headers }).toPromise();
|
||||||
|
|
|
@ -124,6 +124,7 @@ export interface Instancev1 extends Instance {
|
||||||
urls: InstanceUrls;
|
urls: InstanceUrls;
|
||||||
contact_account: Account;
|
contact_account: Account;
|
||||||
max_toot_chars: number;
|
max_toot_chars: number;
|
||||||
|
configuration: Instancev2Configuration;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Instancev2 extends Instance {
|
export interface Instancev2 extends Instance {
|
||||||
|
@ -193,6 +194,33 @@ export interface Results {
|
||||||
hashtags: string[];
|
hashtags: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface FilterKeyword {
|
||||||
|
id: string;
|
||||||
|
keyword: string;
|
||||||
|
whole_word: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FilterStatus {
|
||||||
|
id: string;
|
||||||
|
status_id: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Filter {
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
context: string[]; //home notifications public thread account
|
||||||
|
expires_at: string;
|
||||||
|
filter_action: string; //warn hide
|
||||||
|
keywords: FilterKeyword[];
|
||||||
|
statuses: FilterStatus[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FilterResult {
|
||||||
|
filter: Filter;
|
||||||
|
keyword_matches: string[];
|
||||||
|
status_matches: string[];
|
||||||
|
}
|
||||||
|
|
||||||
export interface Status {
|
export interface Status {
|
||||||
id: string;
|
id: string;
|
||||||
uri: string;
|
uri: string;
|
||||||
|
@ -223,6 +251,7 @@ export interface Status {
|
||||||
bookmarked: boolean;
|
bookmarked: boolean;
|
||||||
card: Card;
|
card: Card;
|
||||||
poll: Poll;
|
poll: Poll;
|
||||||
|
filtered: FilterResult[];
|
||||||
|
|
||||||
pleroma: PleromaStatusInfo;
|
pleroma: PleromaStatusInfo;
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,6 +60,8 @@ export class UserNotificationService {
|
||||||
private startFetchingNotifications(account: AccountInfo) {
|
private startFetchingNotifications(account: AccountInfo) {
|
||||||
let getMentionsPromise = this.mastodonService.getNotifications(account, ['favourite', 'follow', 'reblog', 'poll', 'follow_request', 'move', 'update'], null, null, 10)
|
let getMentionsPromise = this.mastodonService.getNotifications(account, ['favourite', 'follow', 'reblog', 'poll', 'follow_request', 'move', 'update'], null, null, 10)
|
||||||
.then((notifications: Notification[]) => {
|
.then((notifications: Notification[]) => {
|
||||||
|
notifications = notifications.filter(x => x.status !== null);
|
||||||
|
|
||||||
this.processMentionsAndNotifications(account, notifications, NotificationTypeEnum.UserMention);
|
this.processMentionsAndNotifications(account, notifications, NotificationTypeEnum.UserMention);
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
|
|
Loading…
Reference in New Issue