commit
c3cd6fe79e
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "sengi",
|
"name": "sengi",
|
||||||
"version": "1.2.0",
|
"version": "1.4.0",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "sengi",
|
"name": "sengi",
|
||||||
"version": "1.4.0",
|
"version": "1.5.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",
|
||||||
|
@ -20,8 +20,8 @@
|
||||||
"test": "ng test",
|
"test": "ng test",
|
||||||
"test-nowatch": "ng test --watch=false",
|
"test-nowatch": "ng test --watch=false",
|
||||||
"lint": "ng lint",
|
"lint": "ng lint",
|
||||||
"e2e": "ng e2e",
|
"e2e": "ng e2e",
|
||||||
"dist": "npm run build"
|
"dist": "npm run build"
|
||||||
},
|
},
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|
|
@ -5,8 +5,7 @@ import { HttpModule } from "@angular/http";
|
||||||
import { HttpClientModule } from '@angular/common/http';
|
import { HttpClientModule } from '@angular/common/http';
|
||||||
import { NgModule, APP_INITIALIZER, CUSTOM_ELEMENTS_SCHEMA } from "@angular/core";
|
import { NgModule, APP_INITIALIZER, CUSTOM_ELEMENTS_SCHEMA } from "@angular/core";
|
||||||
import { RouterModule, Routes } from "@angular/router";
|
import { RouterModule, Routes } from "@angular/router";
|
||||||
|
// import { NgxElectronModule } from 'ngx-electron';
|
||||||
// import { NgxElectronModule } from "ngx-electron";
|
|
||||||
|
|
||||||
import { NgxsModule } from '@ngxs/store';
|
import { NgxsModule } from '@ngxs/store';
|
||||||
import { NgxsStoragePluginModule } from '@ngxs/storage-plugin';
|
import { NgxsStoragePluginModule } from '@ngxs/storage-plugin';
|
||||||
|
@ -90,6 +89,7 @@ import { TutorialEnhancedComponent } from './components/tutorial-enhanced/tutori
|
||||||
import { NotificationsTutorialComponent } from './components/tutorial-enhanced/notifications-tutorial/notifications-tutorial.component';
|
import { NotificationsTutorialComponent } from './components/tutorial-enhanced/notifications-tutorial/notifications-tutorial.component';
|
||||||
import { LabelsTutorialComponent } from './components/tutorial-enhanced/labels-tutorial/labels-tutorial.component';
|
import { LabelsTutorialComponent } from './components/tutorial-enhanced/labels-tutorial/labels-tutorial.component';
|
||||||
import { ThankyouTutorialComponent } from './components/tutorial-enhanced/thankyou-tutorial/thankyou-tutorial.component';
|
import { ThankyouTutorialComponent } from './components/tutorial-enhanced/thankyou-tutorial/thankyou-tutorial.component';
|
||||||
|
import { StatusTranslateComponent } from './components/stream/status/status-translate/status-translate.component';
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{ path: "", component: StreamsMainDisplayComponent },
|
{ path: "", component: StreamsMainDisplayComponent },
|
||||||
|
@ -159,7 +159,8 @@ const routes: Routes = [
|
||||||
TutorialEnhancedComponent,
|
TutorialEnhancedComponent,
|
||||||
NotificationsTutorialComponent,
|
NotificationsTutorialComponent,
|
||||||
LabelsTutorialComponent,
|
LabelsTutorialComponent,
|
||||||
ThankyouTutorialComponent
|
ThankyouTutorialComponent,
|
||||||
|
StatusTranslateComponent
|
||||||
],
|
],
|
||||||
entryComponents: [
|
entryComponents: [
|
||||||
EmojiPickerComponent
|
EmojiPickerComponent
|
||||||
|
@ -176,6 +177,7 @@ const routes: Routes = [
|
||||||
OwlDateTimeModule,
|
OwlDateTimeModule,
|
||||||
OwlNativeDateTimeModule,
|
OwlNativeDateTimeModule,
|
||||||
OverlayModule,
|
OverlayModule,
|
||||||
|
// NgxElectronModule,
|
||||||
RouterModule.forRoot(routes),
|
RouterModule.forRoot(routes),
|
||||||
|
|
||||||
NgxsModule.forRoot([
|
NgxsModule.forRoot([
|
||||||
|
|
|
@ -1,16 +1,23 @@
|
||||||
<form class="status-editor" (ngSubmit)="onSubmit()">
|
<form class="status-editor" (ngSubmit)="onSubmit()">
|
||||||
<input [(ngModel)]="title" type="text" class="form-control form-control-sm status-editor__title" name="title"
|
<input #mytitle [(ngModel)]="title" type="text" class="form-control form-control-sm status-editor__title"
|
||||||
autocomplete="off" placeholder="Title, Content Warning (optional)" title="title, content warning (optional)" dir="auto" />
|
name="title" autocomplete="off" placeholder="Title, Content Warning (optional)"
|
||||||
|
title="title, content warning (optional)" dir="auto"
|
||||||
|
(keydown.escape)="mytitle.blur()" />
|
||||||
|
|
||||||
<a class="status-editor__emoji" title="Insert Emoji"
|
<a class="status-editor__emoji" title="Insert Emoji"
|
||||||
#emojiButton href (click)="openEmojiPicker($event)">
|
#emojiButton href (click)="openEmojiPicker($event)">
|
||||||
<img class="status-editor__emoji--image" src="/assets/emoji/72x72/1f636.png">
|
<img class="status-editor__emoji--image" src="/assets/emoji/72x72/1f636.png">
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<textarea #reply [(ngModel)]="status" name="status" class="form-control form-control-sm status-editor__content" (paste)="onPaste($event)"
|
<a class="status-editor__lang" title="Change language" href *ngIf="configuredLanguages && configuredLanguages.length > 1" (click)="onLangContextMenu($event)">
|
||||||
|
{{ selectedLanguage.iso639 }}
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<textarea #reply [(ngModel)]="status" name="status" class="form-control form-control-sm status-editor__content" (paste)="onPaste($event)"
|
||||||
rows="5" required title="content" placeholder="What's on your mind?"
|
rows="5" required title="content" placeholder="What's on your mind?"
|
||||||
(keydown.control.enter)="onCtrlEnter()"
|
(keydown.control.enter)="onCtrlEnter()"
|
||||||
(keydown.meta.enter)="onCtrlEnter()"
|
(keydown.meta.enter)="onCtrlEnter()"
|
||||||
|
(keydown.escape)="reply.blur()"
|
||||||
(keydown)="handleKeyDown($event)" (blur)="statusTextEditorLostFocus()" dir="auto">
|
(keydown)="handleKeyDown($event)" (blur)="statusTextEditorLostFocus()" dir="auto">
|
||||||
</textarea>
|
</textarea>
|
||||||
|
|
||||||
|
@ -23,7 +30,7 @@
|
||||||
(suggestionSelectedEvent)="suggestionSelected($event)" (hasSuggestionsEvent)="suggestionsChanged($event)">
|
(suggestionSelectedEvent)="suggestionSelected($event)" (hasSuggestionsEvent)="suggestionsChanged($event)">
|
||||||
</app-autosuggest>
|
</app-autosuggest>
|
||||||
|
|
||||||
<app-poll-editor *ngIf="instanceSupportsPoll && pollIsActive"></app-poll-editor>
|
<app-poll-editor *ngIf="instanceSupportsPoll && pollIsActive" [oldPoll]="oldPoll"></app-poll-editor>
|
||||||
|
|
||||||
<app-status-scheduler class="scheduler" *ngIf="instanceSupportsScheduling && scheduleIsActive"></app-status-scheduler>
|
<app-status-scheduler class="scheduler" *ngIf="instanceSupportsScheduling && scheduleIsActive"></app-status-scheduler>
|
||||||
|
|
||||||
|
@ -68,6 +75,10 @@
|
||||||
<fa-icon [icon]="faClock"></fa-icon>
|
<fa-icon [icon]="faClock"></fa-icon>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="language-warning" *ngIf="!configuredLanguages || configuredLanguages.length === 0">
|
||||||
|
You haven't set your language(s) yet, please <a href class="language-warning__link" (click)="onNavigateToSettings()">go in the settings</a> to provide it.
|
||||||
|
</div>
|
||||||
|
|
||||||
<context-menu #contextMenu>
|
<context-menu #contextMenu>
|
||||||
<ng-template contextMenuItem (execute)="changePrivacy('Public')">
|
<ng-template contextMenuItem (execute)="changePrivacy('Public')">
|
||||||
|
@ -83,5 +94,12 @@
|
||||||
<fa-icon [icon]="faEnvelope" class="context-menu-icon"></fa-icon> Direct
|
<fa-icon [icon]="faEnvelope" class="context-menu-icon"></fa-icon> Direct
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</context-menu>
|
</context-menu>
|
||||||
|
|
||||||
|
<context-menu #langContextMenu>
|
||||||
|
<ng-template contextMenuItem (execute)="setLanguage(l)" *ngFor="let l of configuredLanguages">
|
||||||
|
{{ l.name }}
|
||||||
|
</ng-template>
|
||||||
|
</context-menu>
|
||||||
|
|
||||||
<app-media></app-media>
|
<app-media></app-media>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -70,6 +70,32 @@ $counter-width: 90px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__lang {
|
||||||
|
position: absolute;
|
||||||
|
top: 64px;
|
||||||
|
right: 12px;
|
||||||
|
|
||||||
|
font-weight: bolder;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #a5a5a5;
|
||||||
|
text-decoration: none;
|
||||||
|
|
||||||
|
display: block;
|
||||||
|
width: 20px;
|
||||||
|
height: 19px;
|
||||||
|
border-radius: 2px;
|
||||||
|
background-color: rgba(255, 255, 255, 0);
|
||||||
|
|
||||||
|
padding: 1px 0 0 2px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
text-decoration: none;
|
||||||
|
color:black;
|
||||||
|
background-color: #e6e6e6;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&__content {
|
&__content {
|
||||||
border-width: 0;
|
border-width: 0;
|
||||||
background-color: $status-editor-background;
|
background-color: $status-editor-background;
|
||||||
|
@ -207,6 +233,20 @@ $counter-width: 90px;
|
||||||
border-bottom: 1px solid whitesmoke;
|
border-bottom: 1px solid whitesmoke;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.language-warning {
|
||||||
|
padding: 5px 10px;
|
||||||
|
color: orange;
|
||||||
|
|
||||||
|
&__link {
|
||||||
|
text-decoration: underline;
|
||||||
|
color: #f0d124;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #d18800;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@import '~@angular/cdk/overlay-prebuilt.css';
|
@import '~@angular/cdk/overlay-prebuilt.css';
|
||||||
// ::ng-deep .cdk-overlay-backdrop {
|
// ::ng-deep .cdk-overlay-backdrop {
|
||||||
// // width: 100%;
|
// // width: 100%;
|
||||||
|
|
|
@ -11,7 +11,7 @@ import { ContextMenuService, ContextMenuComponent } from 'ngx-contextmenu';
|
||||||
|
|
||||||
import { VisibilityEnum, PollParameters } from '../../services/mastodon.service';
|
import { VisibilityEnum, PollParameters } from '../../services/mastodon.service';
|
||||||
import { MastodonWrapperService } from '../../services/mastodon-wrapper.service';
|
import { MastodonWrapperService } from '../../services/mastodon-wrapper.service';
|
||||||
import { Status, Attachment } from '../../services/models/mastodon.interfaces';
|
import { Status, Attachment, Poll } from '../../services/models/mastodon.interfaces';
|
||||||
import { ToolsService, InstanceInfo, InstanceType } from '../../services/tools.service';
|
import { ToolsService, InstanceInfo, InstanceType } from '../../services/tools.service';
|
||||||
import { NotificationService } from '../../services/notification.service';
|
import { NotificationService } from '../../services/notification.service';
|
||||||
import { StatusWrapper } from '../../models/common.model';
|
import { StatusWrapper } from '../../models/common.model';
|
||||||
|
@ -25,6 +25,9 @@ import { StatusSchedulerComponent } from './status-scheduler/status-scheduler.co
|
||||||
import { ScheduledStatusService } from '../../services/scheduled-status.service';
|
import { ScheduledStatusService } from '../../services/scheduled-status.service';
|
||||||
import { StatusesStateService } from '../../services/statuses-state.service';
|
import { StatusesStateService } from '../../services/statuses-state.service';
|
||||||
import { SettingsService } from '../../services/settings.service';
|
import { SettingsService } from '../../services/settings.service';
|
||||||
|
import { LanguageService } from '../../services/language.service';
|
||||||
|
import { ILanguage } from '../../states/settings.state';
|
||||||
|
import { LeftPanelType, NavigationService } from '../../services/navigation.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-create-status',
|
selector: 'app-create-status',
|
||||||
|
@ -140,9 +143,19 @@ export class CreateStatusComponent implements OnInit, OnDestroy {
|
||||||
this.isSending = false;
|
this.isSending = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(value.status.poll){
|
||||||
|
this.pollIsActive = true;
|
||||||
|
this.oldPoll = value.status.poll;
|
||||||
|
// setTimeout(() => {
|
||||||
|
// if(this.pollEditor) this.pollEditor.loadPollParameters(value.status.poll);
|
||||||
|
// }, 250);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
oldPoll: Poll;
|
||||||
|
|
||||||
private maxCharLength: number;
|
private maxCharLength: number;
|
||||||
charCountLeft: number;
|
charCountLeft: number;
|
||||||
postCounts: number = 1;
|
postCounts: number = 1;
|
||||||
|
@ -153,6 +166,8 @@ export class CreateStatusComponent implements OnInit, OnDestroy {
|
||||||
instanceSupportsScheduling = true;
|
instanceSupportsScheduling = true;
|
||||||
isEditing: boolean;
|
isEditing: boolean;
|
||||||
editingStatusId: string;
|
editingStatusId: string;
|
||||||
|
configuredLanguages: ILanguage[] = [];
|
||||||
|
selectedLanguage: ILanguage;
|
||||||
private statusLoaded: boolean;
|
private statusLoaded: boolean;
|
||||||
private hasSuggestions: boolean;
|
private hasSuggestions: boolean;
|
||||||
|
|
||||||
|
@ -162,6 +177,7 @@ export class CreateStatusComponent implements OnInit, OnDestroy {
|
||||||
@ViewChild('fileInput') fileInputElement: ElementRef;
|
@ViewChild('fileInput') fileInputElement: ElementRef;
|
||||||
@ViewChild('footer') footerElement: ElementRef;
|
@ViewChild('footer') footerElement: ElementRef;
|
||||||
@ViewChild(ContextMenuComponent) public contextMenu: ContextMenuComponent;
|
@ViewChild(ContextMenuComponent) public contextMenu: ContextMenuComponent;
|
||||||
|
@ViewChild('langContextMenu') public langContextMenu: ContextMenuComponent;
|
||||||
@ViewChild(PollEditorComponent) pollEditor: PollEditorComponent;
|
@ViewChild(PollEditorComponent) pollEditor: PollEditorComponent;
|
||||||
@ViewChild(StatusSchedulerComponent) statusScheduler: StatusSchedulerComponent;
|
@ViewChild(StatusSchedulerComponent) statusScheduler: StatusSchedulerComponent;
|
||||||
|
|
||||||
|
@ -196,11 +212,15 @@ export class CreateStatusComponent implements OnInit, OnDestroy {
|
||||||
|
|
||||||
private accounts$: Observable<AccountInfo[]>;
|
private accounts$: Observable<AccountInfo[]>;
|
||||||
private accountSub: Subscription;
|
private accountSub: Subscription;
|
||||||
|
private langSub: Subscription;
|
||||||
|
private selectLangSub: Subscription;
|
||||||
private selectedAccount: AccountInfo;
|
private selectedAccount: AccountInfo;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
private readonly navigationService: NavigationService,
|
||||||
|
private readonly languageService: LanguageService,
|
||||||
private readonly settingsService: SettingsService,
|
private readonly settingsService: SettingsService,
|
||||||
private statusStateService: StatusesStateService,
|
private readonly statusStateService: StatusesStateService,
|
||||||
private readonly scheduledStatusService: ScheduledStatusService,
|
private readonly scheduledStatusService: ScheduledStatusService,
|
||||||
private readonly contextMenuService: ContextMenuService,
|
private readonly contextMenuService: ContextMenuService,
|
||||||
private readonly store: Store,
|
private readonly store: Store,
|
||||||
|
@ -216,7 +236,35 @@ export class CreateStatusComponent implements OnInit, OnDestroy {
|
||||||
this.accounts$ = this.store.select(state => state.registeredaccounts.accounts);
|
this.accounts$ = this.store.select(state => state.registeredaccounts.accounts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private initLanguages(){
|
||||||
|
this.configuredLanguages = this.languageService.getConfiguredLanguages();
|
||||||
|
this.selectedLanguage = this.languageService.getSelectedLanguage();
|
||||||
|
this.langSub = this.languageService.configuredLanguagesChanged.subscribe(l => {
|
||||||
|
this.configuredLanguages = l;
|
||||||
|
// if(this.configuredLanguages.length > 0
|
||||||
|
// && this.selectedLanguage
|
||||||
|
// && this.configuredLanguages.findIndex(x => x.iso639 === this.selectedLanguage.iso639)){
|
||||||
|
// this.languageService.setSelectedLanguage(this.configuredLanguages[0]);
|
||||||
|
// }
|
||||||
|
});
|
||||||
|
this.selectLangSub = this.languageService.selectedLanguageChanged.subscribe(l => {
|
||||||
|
this.selectedLanguage = l;
|
||||||
|
});
|
||||||
|
if(!this.selectedLanguage && this.configuredLanguages.length > 0){
|
||||||
|
this.languageService.setSelectedLanguage(this.configuredLanguages[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setLanguage(lang: ILanguage): boolean {
|
||||||
|
if(lang){
|
||||||
|
this.languageService.setSelectedLanguage(lang);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
|
this.initLanguages();
|
||||||
|
|
||||||
if (!this.isRedrafting) {
|
if (!this.isRedrafting) {
|
||||||
this.status = this.statusStateService.getStatusContent(this.statusReplyingToWrapper);
|
this.status = this.statusStateService.getStatusContent(this.statusReplyingToWrapper);
|
||||||
}
|
}
|
||||||
|
@ -263,6 +311,13 @@ export class CreateStatusComponent implements OnInit, OnDestroy {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.accountSub.unsubscribe();
|
this.accountSub.unsubscribe();
|
||||||
|
this.langSub.unsubscribe();
|
||||||
|
this.selectLangSub.unsubscribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
onNavigateToSettings(): boolean {
|
||||||
|
this.navigationService.openPanel(LeftPanelType.Settings);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
onPaste(e: any) {
|
onPaste(e: any) {
|
||||||
|
@ -613,6 +668,14 @@ export class CreateStatusComponent implements OnInit, OnDestroy {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private currentLang(): string {
|
||||||
|
if(this.selectedLanguage){
|
||||||
|
return this.selectedLanguage.iso639;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private sendStatus(account: AccountInfo, status: string, visibility: VisibilityEnum, title: string, previousStatus: Status, attachments: Attachment[], poll: PollParameters, scheduledAt: string, editingStatusId: string): Promise<Status> {
|
private sendStatus(account: AccountInfo, status: string, visibility: VisibilityEnum, title: string, previousStatus: Status, attachments: Attachment[], poll: PollParameters, scheduledAt: string, editingStatusId: string): Promise<Status> {
|
||||||
let parsedStatus = this.parseStatus(status);
|
let parsedStatus = this.parseStatus(status);
|
||||||
let resultPromise = Promise.resolve(previousStatus);
|
let resultPromise = Promise.resolve(previousStatus);
|
||||||
|
@ -630,9 +693,9 @@ export class CreateStatusComponent implements OnInit, OnDestroy {
|
||||||
let postPromise: Promise<Status>;
|
let postPromise: Promise<Status>;
|
||||||
|
|
||||||
if (this.isEditing) {
|
if (this.isEditing) {
|
||||||
postPromise = this.mastodonService.editStatus(account, editingStatusId, s, visibility, title, inReplyToId, attachments, poll, scheduledAt);
|
postPromise = this.mastodonService.editStatus(account, editingStatusId, s, visibility, title, inReplyToId, attachments, poll, scheduledAt, this.currentLang());
|
||||||
} else {
|
} else {
|
||||||
postPromise = this.mastodonService.postNewStatus(account, s, visibility, title, inReplyToId, attachments.map(x => x.id), poll, scheduledAt);
|
postPromise = this.mastodonService.postNewStatus(account, s, visibility, title, inReplyToId, attachments.map(x => x.id), poll, scheduledAt, this.currentLang());
|
||||||
}
|
}
|
||||||
|
|
||||||
return postPromise
|
return postPromise
|
||||||
|
@ -642,9 +705,9 @@ export class CreateStatusComponent implements OnInit, OnDestroy {
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
if (this.isEditing) {
|
if (this.isEditing) {
|
||||||
return this.mastodonService.editStatus(account, editingStatusId, s, visibility, title, inReplyToId, [], null, scheduledAt);
|
return this.mastodonService.editStatus(account, editingStatusId, s, visibility, title, inReplyToId, [], null, scheduledAt, this.currentLang());
|
||||||
} else {
|
} else {
|
||||||
return this.mastodonService.postNewStatus(account, s, visibility, title, inReplyToId, [], null, scheduledAt);
|
return this.mastodonService.postNewStatus(account, s, visibility, title, inReplyToId, [], null, scheduledAt, this.currentLang());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -887,6 +950,17 @@ export class CreateStatusComponent implements OnInit, OnDestroy {
|
||||||
$event.stopPropagation();
|
$event.stopPropagation();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public onLangContextMenu($event: MouseEvent): void {
|
||||||
|
this.contextMenuService.show.next({
|
||||||
|
// Optional - if unspecified, all context menu components will open
|
||||||
|
contextMenu: this.langContextMenu,
|
||||||
|
event: $event,
|
||||||
|
item: null
|
||||||
|
});
|
||||||
|
$event.preventDefault();
|
||||||
|
$event.stopPropagation();
|
||||||
|
}
|
||||||
|
|
||||||
//https://stackblitz.com/edit/overlay-demo
|
//https://stackblitz.com/edit/overlay-demo
|
||||||
@ViewChild('emojiButton') emojiButtonElement: ElementRef;
|
@ViewChild('emojiButton') emojiButtonElement: ElementRef;
|
||||||
overlayRef: OverlayRef;
|
overlayRef: OverlayRef;
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, Input, OnInit, SimpleChanges } from '@angular/core';
|
||||||
import { faPlus } from "@fortawesome/free-solid-svg-icons";
|
import { faPlus } from "@fortawesome/free-solid-svg-icons";
|
||||||
|
|
||||||
import { PollEntry } from './poll-entry/poll-entry.component';
|
import { PollEntry } from './poll-entry/poll-entry.component';
|
||||||
import { PollParameters } from '../../../services/mastodon.service';
|
import { PollParameters } from '../../../services/mastodon.service';
|
||||||
import { retry } from 'rxjs/operators';
|
import { Poll } from '../../../services/models/mastodon.interfaces';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-poll-editor',
|
selector: 'app-poll-editor',
|
||||||
|
@ -19,6 +19,8 @@ export class PollEditorComponent implements OnInit {
|
||||||
selectedId: string;
|
selectedId: string;
|
||||||
private multiSelected: boolean;
|
private multiSelected: boolean;
|
||||||
|
|
||||||
|
@Input() oldPoll: Poll;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.entries.push(new PollEntry(this.getEntryUuid(), this.multiSelected));
|
this.entries.push(new PollEntry(this.getEntryUuid(), this.multiSelected));
|
||||||
this.entries.push(new PollEntry(this.getEntryUuid(), this.multiSelected));
|
this.entries.push(new PollEntry(this.getEntryUuid(), this.multiSelected));
|
||||||
|
@ -40,6 +42,12 @@ export class PollEditorComponent implements OnInit {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ngOnChanges(changes: SimpleChanges): void {
|
||||||
|
if (changes['oldPoll']) {
|
||||||
|
this.loadPollParameters(this.oldPoll);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private getEntryUuid(): number {
|
private getEntryUuid(): number {
|
||||||
this.entryUuid++;
|
this.entryUuid++;
|
||||||
return this.entryUuid;
|
return this.entryUuid;
|
||||||
|
@ -50,7 +58,7 @@ export class PollEditorComponent implements OnInit {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
removeElement(entry: PollEntry){
|
removeElement(entry: PollEntry) {
|
||||||
this.entries = this.entries.filter(x => x.id != entry.id);
|
this.entries = this.entries.filter(x => x.id != entry.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,6 +77,17 @@ export class PollEditorComponent implements OnInit {
|
||||||
params.hide_totals = false;
|
params.hide_totals = false;
|
||||||
return params;
|
return params;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private loadPollParameters(poll: Poll) {
|
||||||
|
const isMulti = poll.multiple;
|
||||||
|
|
||||||
|
this.entries.length = 0;
|
||||||
|
for (let o of poll.options) {
|
||||||
|
const entry = new PollEntry(this.getEntryUuid(), isMulti);
|
||||||
|
entry.label = o.title;
|
||||||
|
this.entries.push(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Delay {
|
class Delay {
|
||||||
|
|
|
@ -60,8 +60,8 @@ export class ManageAccountComponent extends BrowseBase {
|
||||||
private readonly mastodonService: MastodonWrapperService,
|
private readonly mastodonService: MastodonWrapperService,
|
||||||
private readonly notificationService: NotificationService,
|
private readonly notificationService: NotificationService,
|
||||||
private readonly userNotificationService: UserNotificationService) {
|
private readonly userNotificationService: UserNotificationService) {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
}
|
}
|
||||||
|
@ -71,13 +71,9 @@ export class ManageAccountComponent extends BrowseBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
private checkIfBookmarksAreAvailable() {
|
private checkIfBookmarksAreAvailable() {
|
||||||
this.toolsService.getInstanceInfo(this.account.info)
|
this.toolsService.isBookmarksAreAvailable(this.account.info)
|
||||||
.then((instance: InstanceInfo) => {
|
.then((isAvailable: boolean) => {
|
||||||
if (instance.major == 3 && instance.minor >= 1 || instance.major > 3) {
|
this.isBookmarksAvailable = isAvailable;
|
||||||
this.isBookmarksAvailable = true;
|
|
||||||
} else {
|
|
||||||
this.isBookmarksAvailable = false;
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
this.isBookmarksAvailable = false;
|
this.isBookmarksAvailable = false;
|
||||||
|
@ -128,15 +124,15 @@ export class ManageAccountComponent extends BrowseBase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ViewChild('bookmarks') bookmarksComp:BookmarksComponent;
|
@ViewChild('bookmarks') bookmarksComp: BookmarksComponent;
|
||||||
@ViewChild('notifications') notificationsComp:NotificationsComponent;
|
@ViewChild('notifications') notificationsComp: NotificationsComponent;
|
||||||
@ViewChild('mentions') mentionsComp:MentionsComponent;
|
@ViewChild('mentions') mentionsComp: MentionsComponent;
|
||||||
@ViewChild('dm') dmComp:DirectMessagesComponent;
|
@ViewChild('dm') dmComp: DirectMessagesComponent;
|
||||||
@ViewChild('favorites') favoritesComp:FavoritesComponent;
|
@ViewChild('favorites') favoritesComp: FavoritesComponent;
|
||||||
|
|
||||||
loadSubPanel(subpanel: 'account' | 'notifications' | 'mentions' | 'dm' | 'favorites' | 'bookmarks'): boolean {
|
loadSubPanel(subpanel: 'account' | 'notifications' | 'mentions' | 'dm' | 'favorites' | 'bookmarks'): boolean {
|
||||||
if(this.subPanel === subpanel){
|
if (this.subPanel === subpanel) {
|
||||||
switch(subpanel){
|
switch (subpanel) {
|
||||||
case 'bookmarks':
|
case 'bookmarks':
|
||||||
this.bookmarksComp.applyGoToTop();
|
this.bookmarksComp.applyGoToTop();
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
(click)="acceptFollowRequest()">
|
(click)="acceptFollowRequest()">
|
||||||
<fa-icon class="follow_request__icon" [icon]="faCheck"></fa-icon>
|
<fa-icon class="follow_request__icon" [icon]="faCheck"></fa-icon>
|
||||||
</a>
|
</a>
|
||||||
<a href title="Reject" class="follow_request__link follow_request__link--cross"
|
<a href title="Reject" class="follow_request__link follow_request__link--cross"
|
||||||
(click)="refuseFollowRequest()">
|
(click)="refuseFollowRequest()">
|
||||||
<fa-icon class="follow_request__icon" [icon]="faTimes"></fa-icon>
|
<fa-icon class="follow_request__icon" [icon]="faTimes"></fa-icon>
|
||||||
</a>
|
</a>
|
||||||
|
@ -69,12 +69,18 @@
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<app-status *ngIf="notification.status && notification.type !== 'mention'" class="stream__status" [statusWrapper]="notification.status"
|
<app-status *ngIf="notification.status && notification.type === 'update'" class="stream__status"
|
||||||
[notificationAccount]="notification.account" [notificationType]="notification.type"
|
[statusWrapper]="notification.status" [notificationAccount]="notification.account"
|
||||||
(browseAccountEvent)="browseAccount($event)" (browseHashtagEvent)="browseHashtag($event)"
|
[notificationType]="notification.type" (browseAccountEvent)="browseAccount($event)"
|
||||||
(browseThreadEvent)="browseThread($event)"></app-status>
|
(browseHashtagEvent)="browseHashtag($event)" (browseThreadEvent)="browseThread($event)"></app-status>
|
||||||
|
|
||||||
|
<app-status *ngIf="notification.status && notification.type === 'mention'" class="stream__status"
|
||||||
|
[statusWrapper]="notification.status" (browseAccountEvent)="browseAccount($event)"
|
||||||
|
(browseHashtagEvent)="browseHashtag($event)" (browseThreadEvent)="browseThread($event)"></app-status>
|
||||||
|
|
||||||
|
<app-status *ngIf="notification.status && notification.type !== 'mention' && notification.type !== 'update'"
|
||||||
|
class="stream__status" [statusWrapper]="notification.status" [notificationAccount]="notification.account"
|
||||||
|
[notificationType]="notification.type" (browseAccountEvent)="browseAccount($event)"
|
||||||
|
(browseHashtagEvent)="browseHashtag($event)" (browseThreadEvent)="browseThread($event)"></app-status>
|
||||||
|
|
||||||
<app-status *ngIf="notification.status && notification.type === 'mention'" class="stream__status" [statusWrapper]="notification.status"
|
|
||||||
(browseAccountEvent)="browseAccount($event)" (browseHashtagEvent)="browseHashtag($event)"
|
|
||||||
(browseThreadEvent)="browseThread($event)"></app-status>
|
|
||||||
</div>
|
</div>
|
|
@ -101,7 +101,7 @@ export class NotificationsComponent extends BrowseBase {
|
||||||
this.isLoading = true;
|
this.isLoading = true;
|
||||||
this.isProcessingInfiniteScroll = true;
|
this.isProcessingInfiniteScroll = true;
|
||||||
|
|
||||||
this.mastodonService.getNotifications(this.account.info, ['mention', 'update'], this.lastId)
|
this.mastodonService.getNotifications(this.account.info, ['mention'], this.lastId)
|
||||||
.then((notifications: Notification[]) => {
|
.then((notifications: Notification[]) => {
|
||||||
if (notifications.length === 0) {
|
if (notifications.length === 0) {
|
||||||
this.maxReached = true;
|
this.maxReached = true;
|
||||||
|
@ -152,6 +152,7 @@ export class NotificationWrapper {
|
||||||
case 'reblog':
|
case 'reblog':
|
||||||
case 'favourite':
|
case 'favourite':
|
||||||
case 'poll':
|
case 'poll':
|
||||||
|
case 'update':
|
||||||
this.status = new StatusWrapper(notification.status, provider, applyCw, hideStatus);
|
this.status = new StatusWrapper(notification.status, provider, applyCw, hideStatus);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,8 +4,8 @@
|
||||||
<h3 class="panel__title">search</h3>
|
<h3 class="panel__title">search</h3>
|
||||||
|
|
||||||
<form class="form-section" (ngSubmit)="onSubmit()">
|
<form class="form-section" (ngSubmit)="onSubmit()">
|
||||||
<input type="text" class="form-control form-control-sm form-with-button" [(ngModel)]="searchHandle"
|
<input #search type="text" class="form-control form-control-sm form-with-button" [(ngModel)]="searchHandle"
|
||||||
name="searchHandle" placeholder="Search" autocomplete="off" />
|
name="searchHandle" placeholder="Search" autocomplete="off" (keydown.escape)="search.blur()"/>
|
||||||
<button class="form-button" type="submit" title="search">GO</button>
|
<button class="form-button" type="submit" title="search">GO</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
|
import { Component, OnInit, Input, Output, EventEmitter, ViewChild, ElementRef } from '@angular/core';
|
||||||
import { HttpErrorResponse } from '@angular/common/http';
|
import { HttpErrorResponse } from '@angular/common/http';
|
||||||
|
|
||||||
import { MastodonWrapperService } from '../../../services/mastodon-wrapper.service';
|
import { MastodonWrapperService } from '../../../services/mastodon-wrapper.service';
|
||||||
|
@ -26,12 +26,15 @@ export class SearchComponent implements OnInit {
|
||||||
@Output() browseHashtagEvent = new EventEmitter<string>();
|
@Output() browseHashtagEvent = new EventEmitter<string>();
|
||||||
@Output() browseThreadEvent = new EventEmitter<OpenThreadEvent>();
|
@Output() browseThreadEvent = new EventEmitter<OpenThreadEvent>();
|
||||||
|
|
||||||
|
@ViewChild('search') searchElement: ElementRef;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly notificationService: NotificationService,
|
private readonly notificationService: NotificationService,
|
||||||
private readonly toolsService: ToolsService,
|
private readonly toolsService: ToolsService,
|
||||||
private readonly mastodonService: MastodonWrapperService) { }
|
private readonly mastodonService: MastodonWrapperService) { }
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
|
this.searchElement.nativeElement.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
onSubmit(): boolean {
|
onSubmit(): boolean {
|
||||||
|
|
|
@ -35,7 +35,7 @@
|
||||||
</form>
|
</form>
|
||||||
<a href class="form-button sound__play" type="submit" (click)="playNotificationSound()">play</a>
|
<a href class="form-button sound__play" type="submit" (click)="playNotificationSound()">play</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h4 class="panel__subtitle">Shortcuts</h4>
|
<h4 class="panel__subtitle">Shortcuts</h4>
|
||||||
<div class="sub-section">
|
<div class="sub-section">
|
||||||
<span class="sub-section__title">switch column:</span><br />
|
<span class="sub-section__title">switch column:</span><br />
|
||||||
|
@ -51,21 +51,42 @@
|
||||||
<br>
|
<br>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<h4 class="panel__subtitle">Languages</h4>
|
||||||
|
<div class="sub-section">
|
||||||
|
<div class="sub-section__content">
|
||||||
|
<div *ngIf="!configuredLangs || configuredLangs.length === 0" class="language__warning">
|
||||||
|
No language set.
|
||||||
|
</div>
|
||||||
|
<div *ngFor="let l of configuredLangs" class="language__entry">
|
||||||
|
<span class="language__entry__name">{{ l.name }} ({{l.iso639}})</span>
|
||||||
|
<a href (click)="onRemoveLang(l)" class="form-button language__entry__action sound__play">remove</a>
|
||||||
|
</div>
|
||||||
|
<input type="text" (input)="onSearchLang($event.target.value)" [(ngModel)]="searchLang"
|
||||||
|
placeholder="Find Language" autocomplete="off" class="form-control form-control-sm language__search"/>
|
||||||
|
<div *ngFor="let l of searchedLangs" class="language__entry">
|
||||||
|
<span class="language__entry__name">{{ l.name }} ({{l.iso639}})</span>
|
||||||
|
<a href (click)="onAddLang(l)" class="form-button language__entry__action sound__play">add</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<h4 class="panel__subtitle">Twitter Bridge</h4>
|
<h4 class="panel__subtitle">Twitter Bridge</h4>
|
||||||
<div class="sub-section">
|
<div class="sub-section">
|
||||||
<input class="sub-section__checkbox" [(ngModel)]="twitterBridgeEnabled"
|
<input class="sub-section__checkbox" [(ngModel)]="twitterBridgeEnabled"
|
||||||
(change)="onTwitterBridgeEnabledChanged()" type="checkbox" name="onTwitterBridgeEnabled"
|
(change)="onTwitterBridgeEnabledChanged()" type="checkbox" name="onTwitterBridgeEnabled"
|
||||||
value="onTwitterBridgeEnabled" id="onTwitterBridgeEnabled">
|
value="onTwitterBridgeEnabled" id="onTwitterBridgeEnabled">
|
||||||
<label class="noselect sub-section__label" for="onTwitterBridgeEnabled">enable bridge</label>
|
<label class="noselect sub-section__label" for="onTwitterBridgeEnabled">enable bridge</label>
|
||||||
<br>
|
<br>
|
||||||
<div *ngIf="twitterBridgeEnabled">
|
<div *ngIf="twitterBridgeEnabled">
|
||||||
<p>Please provide your bridge instance:
|
<p>Please provide your bridge instance:
|
||||||
<input type="text" class="form-control form-control-sm sub_section__text-input"
|
<input type="text" class="form-control form-control-sm sub_section__text-input"
|
||||||
[(ngModel)]="setTwitterBridgeInstance" placeholder="bridge.tld" />
|
[(ngModel)]="setTwitterBridgeInstance" placeholder="bridge.tld" />
|
||||||
If you don't know any, consider using <a href="https://github.com/NicolasConstant/BirdsiteLive" target="_blank" class="version__link">BirdsiteLIVE</a></p>
|
If you don't know any, consider using <a href="https://github.com/NicolasConstant/BirdsiteLive"
|
||||||
|
target="_blank" class="version__link">BirdsiteLIVE</a>
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<a href="https://github.com/NicolasConstant/sengi/wiki/BirdsiteLIVE-integration" target="_blank" class="version__link">What is this?</a>
|
<a href="https://github.com/NicolasConstant/sengi/wiki/BirdsiteLIVE-integration" target="_blank"
|
||||||
|
class="version__link">What is this?</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -79,7 +100,7 @@
|
||||||
|
|
||||||
<input class="sub-section__checkbox" [checked]="contentWarningPolicy === 2" (change)="onCwPolicyChange(2)"
|
<input class="sub-section__checkbox" [checked]="contentWarningPolicy === 2" (change)="onCwPolicyChange(2)"
|
||||||
type="radio" name="cw-hide-all" value="cw-hide-all" id="cw-hide-all">
|
type="radio" name="cw-hide-all" value="cw-hide-all" id="cw-hide-all">
|
||||||
<label class="noselect sub-section__label" for="cw-hide-all">Hide all CWs</label>
|
<label class="noselect sub-section__label" for="cw-hide-all">Expand all CWs</label>
|
||||||
<br>
|
<br>
|
||||||
<div class="sub-section__cw-settings" *ngIf="contentWarningPolicy === 2">
|
<div class="sub-section__cw-settings" *ngIf="contentWarningPolicy === 2">
|
||||||
<span class="sub-section__title">but add CW on content containing:</span><br />
|
<span class="sub-section__title">but add CW on content containing:</span><br />
|
||||||
|
|
|
@ -31,6 +31,13 @@
|
||||||
padding: 0 5px 15px 5px;
|
padding: 0 5px 15px 5px;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
|
&__content {
|
||||||
|
display: block;
|
||||||
|
padding: 0 0 0 5px;
|
||||||
|
|
||||||
|
// outline: 1px dotted greenyellow;
|
||||||
|
}
|
||||||
|
|
||||||
&__checkbox {
|
&__checkbox {
|
||||||
position: relative;
|
position: relative;
|
||||||
top: 3px;
|
top: 3px;
|
||||||
|
@ -68,6 +75,41 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.language {
|
||||||
|
&__warning {
|
||||||
|
color: orange;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__entry {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
&:not(:last-child){
|
||||||
|
margin-bottom: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__name {
|
||||||
|
display: block;
|
||||||
|
align-items: stretch;
|
||||||
|
padding-left: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__action {
|
||||||
|
align-items: stretch;
|
||||||
|
min-width: 70px;
|
||||||
|
text-align: center;
|
||||||
|
padding: 0 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__search {
|
||||||
|
display: block;
|
||||||
|
margin: 5px 0 5px 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.form-control {
|
.form-control {
|
||||||
border: 1px solid $settings-text-input-border;
|
border: 1px solid $settings-text-input-border;
|
||||||
color: $settings-text-input-foreground;
|
color: $settings-text-input-foreground;
|
||||||
|
|
|
@ -1,15 +1,17 @@
|
||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||||
import { FormBuilder, FormGroup } from '@angular/forms';
|
import { FormBuilder, FormGroup } from '@angular/forms';
|
||||||
import { Howl } from 'howler';
|
import { Howl } from 'howler';
|
||||||
|
import { Subscription } from 'rxjs';
|
||||||
|
|
||||||
import { environment } from '../../../../environments/environment';
|
import { environment } from '../../../../environments/environment';
|
||||||
import { ToolsService, InstanceType } from '../../../services/tools.service';
|
import { ToolsService, InstanceType } from '../../../services/tools.service';
|
||||||
import { UserNotificationService, NotificationSoundDefinition } from '../../../services/user-notification.service';
|
import { UserNotificationService, NotificationSoundDefinition } from '../../../services/user-notification.service';
|
||||||
import { ServiceWorkerService } from '../../../services/service-worker.service';
|
import { ServiceWorkerService } from '../../../services/service-worker.service';
|
||||||
import { ContentWarningPolicy, ContentWarningPolicyEnum, TimeLineModeEnum, TimeLineHeaderEnum } from '../../../states/settings.state';
|
import { ContentWarningPolicy, ContentWarningPolicyEnum, TimeLineModeEnum, TimeLineHeaderEnum, ILanguage } from '../../../states/settings.state';
|
||||||
import { NotificationService } from '../../../services/notification.service';
|
import { NotificationService } from '../../../services/notification.service';
|
||||||
import { NavigationService } from '../../../services/navigation.service';
|
import { NavigationService } from '../../../services/navigation.service';
|
||||||
import { SettingsService } from '../../../services/settings.service';
|
import { SettingsService } from '../../../services/settings.service';
|
||||||
|
import { LanguageService } from '../../../services/language.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-settings',
|
selector: 'app-settings',
|
||||||
|
@ -17,7 +19,7 @@ import { SettingsService } from '../../../services/settings.service';
|
||||||
styleUrls: ['./settings.component.scss']
|
styleUrls: ['./settings.component.scss']
|
||||||
})
|
})
|
||||||
|
|
||||||
export class SettingsComponent implements OnInit {
|
export class SettingsComponent implements OnInit, OnDestroy {
|
||||||
|
|
||||||
notificationSounds: NotificationSoundDefinition[];
|
notificationSounds: NotificationSoundDefinition[];
|
||||||
notificationSoundId: string;
|
notificationSoundId: string;
|
||||||
|
@ -39,6 +41,10 @@ export class SettingsComponent implements OnInit {
|
||||||
timeLineMode: TimeLineModeEnum = TimeLineModeEnum.OnTop;
|
timeLineMode: TimeLineModeEnum = TimeLineModeEnum.OnTop;
|
||||||
contentWarningPolicy: ContentWarningPolicyEnum = ContentWarningPolicyEnum.None;
|
contentWarningPolicy: ContentWarningPolicyEnum = ContentWarningPolicyEnum.None;
|
||||||
|
|
||||||
|
configuredLangs: ILanguage[] = [];
|
||||||
|
searchedLangs: ILanguage[] = [];
|
||||||
|
searchLang: string;
|
||||||
|
|
||||||
private addCwOnContent: string;
|
private addCwOnContent: string;
|
||||||
set setAddCwOnContent(value: string) {
|
set setAddCwOnContent(value: string) {
|
||||||
this.setCwPolicy(null, value, null, null);
|
this.setCwPolicy(null, value, null, null);
|
||||||
|
@ -76,16 +82,25 @@ export class SettingsComponent implements OnInit {
|
||||||
return this.twitterBridgeInstance;
|
return this.twitterBridgeInstance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private languageSub: Subscription;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
private readonly languageService: LanguageService,
|
||||||
private readonly settingsService: SettingsService,
|
private readonly settingsService: SettingsService,
|
||||||
private readonly navigationService: NavigationService,
|
private readonly navigationService: NavigationService,
|
||||||
private formBuilder: FormBuilder,
|
private formBuilder: FormBuilder,
|
||||||
private serviceWorkersService: ServiceWorkerService,
|
private serviceWorkersService: ServiceWorkerService,
|
||||||
private readonly toolsService: ToolsService,
|
private readonly toolsService: ToolsService,
|
||||||
private readonly notificationService: NotificationService,
|
private readonly notificationService: NotificationService,
|
||||||
private readonly userNotificationsService: UserNotificationService) { }
|
private readonly userNotificationsService: UserNotificationService) { }
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
|
this.languageSub = this.languageService.configuredLanguagesChanged.subscribe(l => {
|
||||||
|
if(l){
|
||||||
|
this.configuredLangs = l;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
this.version = environment.VERSION;
|
this.version = environment.VERSION;
|
||||||
|
|
||||||
const settings = this.settingsService.getSettings();
|
const settings = this.settingsService.getSettings();
|
||||||
|
@ -129,6 +144,34 @@ export class SettingsComponent implements OnInit {
|
||||||
|
|
||||||
this.twitterBridgeEnabled = settings.twitterBridgeEnabled;
|
this.twitterBridgeEnabled = settings.twitterBridgeEnabled;
|
||||||
this.twitterBridgeInstance = settings.twitterBridgeInstance;
|
this.twitterBridgeInstance = settings.twitterBridgeInstance;
|
||||||
|
|
||||||
|
this.configuredLangs = this.languageService.getConfiguredLanguages();
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
if(this.languageSub) this.languageSub.unsubscribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
onSearchLang(input: string) {
|
||||||
|
this.searchedLangs = this.languageService.searchLanguage(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
onAddLang(lang: ILanguage): boolean {
|
||||||
|
if(this.configuredLangs.findIndex(x => x.iso639 === lang.iso639) >= 0) return false;
|
||||||
|
|
||||||
|
// this.configuredLangs.push(lang);
|
||||||
|
this.languageService.addLanguage(lang);
|
||||||
|
|
||||||
|
this.searchLang = '';
|
||||||
|
this.searchedLangs.length = 0;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
onRemoveLang(lang: ILanguage): boolean {
|
||||||
|
// this.configuredLangs = this.configuredLangs.filter(x => x.iso639 !== lang.iso639);
|
||||||
|
this.languageService.removeLanguage(lang);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
onShortcutChange(id: ColumnShortcut) {
|
onShortcutChange(id: ColumnShortcut) {
|
||||||
|
|
|
@ -43,7 +43,7 @@ $inner-column-size: 320px;
|
||||||
&__follow-button {
|
&__follow-button {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 7px;
|
top: 7px;
|
||||||
right: 100px;
|
right: 114px;
|
||||||
padding: 0 10px 0 10px;
|
padding: 0 10px 0 10px;
|
||||||
border: 1px solid black;
|
border: 1px solid black;
|
||||||
color: white;
|
color: white;
|
||||||
|
|
|
@ -342,13 +342,9 @@ export class ActionBarComponent implements OnInit, OnDestroy {
|
||||||
}
|
}
|
||||||
|
|
||||||
private checkIfBookmarksAreAvailable(account: AccountInfo) {
|
private checkIfBookmarksAreAvailable(account: AccountInfo) {
|
||||||
this.toolsService.getInstanceInfo(account)
|
this.toolsService.isBookmarksAreAvailable(account)
|
||||||
.then((instance: InstanceInfo) => {
|
.then((isAvailable: boolean) => {
|
||||||
if (instance.major == 3 && instance.minor >= 1 || instance.major > 3) {
|
this.isBookmarksAvailable = isAvailable;
|
||||||
this.isBookmarksAvailable = true;
|
|
||||||
} else {
|
|
||||||
this.isBookmarksAvailable = false;
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
this.isBookmarksAvailable = false;
|
this.isBookmarksAvailable = false;
|
||||||
|
|
|
@ -64,6 +64,7 @@ $expand-color: $column-color;
|
||||||
|
|
||||||
& p {
|
& p {
|
||||||
margin: 0px;
|
margin: 0px;
|
||||||
|
white-space: pre-wrap;
|
||||||
//font-size: .9em;
|
//font-size: .9em;
|
||||||
// font-size: 14px;
|
// font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -97,7 +97,7 @@ export class DatabindedTextComponent implements OnInit {
|
||||||
let extractedUrl = extractedLinkAndNext[0].split('href="')[1].split('"')[0];
|
let extractedUrl = extractedLinkAndNext[0].split('href="')[1].split('"')[0];
|
||||||
|
|
||||||
let classname = this.getClassNameForHastag(extractedHashtag);
|
let classname = this.getClassNameForHastag(extractedHashtag);
|
||||||
this.processedText += ` <a href="${extractedUrl}" class="${classname}" title="#${extractedHashtag}" target="_blank" rel="noopener noreferrer">#${extractedHashtag}</a>`;
|
this.processedText += `<a href="${extractedUrl}" class="${classname}" title="#${extractedHashtag}" target="_blank" rel="noopener noreferrer">#${extractedHashtag}</a>`;
|
||||||
if (extractedLinkAndNext[1]) this.processedText += extractedLinkAndNext[1];
|
if (extractedLinkAndNext[1]) this.processedText += extractedLinkAndNext[1];
|
||||||
this.hashtags.push(extractedHashtag);
|
this.hashtags.push(extractedHashtag);
|
||||||
}
|
}
|
||||||
|
@ -205,6 +205,10 @@ export class DatabindedTextComponent implements OnInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
ngAfterViewInit() {
|
ngAfterViewInit() {
|
||||||
|
this.processEventBindings();
|
||||||
|
}
|
||||||
|
|
||||||
|
processEventBindings(){
|
||||||
for (const hashtag of this.hashtags) {
|
for (const hashtag of this.hashtags) {
|
||||||
let classname = this.getClassNameForHastag(hashtag);
|
let classname = this.getClassNameForHastag(hashtag);
|
||||||
let els = <Element[]>this.contentElement.nativeElement.querySelectorAll(`.${classname}`);
|
let els = <Element[]>this.contentElement.nativeElement.querySelectorAll(`.${classname}`);
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
<div class="translation translation__button-display" *ngIf="isTranslationAvailable && showTranslationButton">
|
||||||
|
<a href class="translation__link translation__button-display__link" (click)="translate()">Translate</a>
|
||||||
|
</div>
|
||||||
|
<div class="translation translation__display" *ngIf="isTranslationAvailable && !showTranslationButton">
|
||||||
|
<span class="translation__by">Translated by {{translatedBy}}</span> <a href (click)="revertTranslation()" class="translation__link translation__display__link">revert</a>
|
||||||
|
</div>
|
|
@ -0,0 +1,44 @@
|
||||||
|
@import "variables";
|
||||||
|
@import "commons";
|
||||||
|
|
||||||
|
$translation-color: #656b8f;
|
||||||
|
$translation-color-hover: #9fa5ca;
|
||||||
|
|
||||||
|
.translation {
|
||||||
|
margin: 0 10px 0 $avatar-column-space;
|
||||||
|
color: $translation-color;
|
||||||
|
font-size: 12px;
|
||||||
|
|
||||||
|
&__button-display {
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
&__link {
|
||||||
|
display: block;
|
||||||
|
padding: 5px 5px 0 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__display {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
&__link {
|
||||||
|
padding: 5px 0 0 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__link {
|
||||||
|
color: $translation-color;
|
||||||
|
transition: all .2s;
|
||||||
|
&:hover {
|
||||||
|
text-decoration: none;
|
||||||
|
color: $translation-color-hover;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__by {
|
||||||
|
display: block;
|
||||||
|
text-align: left;
|
||||||
|
padding: 5px 0 0 0;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { StatusTranslateComponent } from './status-translate.component';
|
||||||
|
|
||||||
|
xdescribe('StatusTranslateComponent', () => {
|
||||||
|
let component: StatusTranslateComponent;
|
||||||
|
let fixture: ComponentFixture<StatusTranslateComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ StatusTranslateComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(StatusTranslateComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,117 @@
|
||||||
|
import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core';
|
||||||
|
import { Subscription } from 'rxjs';
|
||||||
|
|
||||||
|
import { StatusWrapper } from '../../../../models/common.model';
|
||||||
|
import { ILanguage } from '../../../../states/settings.state';
|
||||||
|
import { LanguageService } from '../../../../services/language.service';
|
||||||
|
import { InstancesInfoService } from '../../../../services/instances-info.service';
|
||||||
|
import { MastodonWrapperService } from '../../../../services/mastodon-wrapper.service';
|
||||||
|
import { Translation } from '../../../../services/models/mastodon.interfaces';
|
||||||
|
import { NotificationService } from '../../../../services/notification.service';
|
||||||
|
import { HttpErrorResponse } from '@angular/common/http';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-status-translate',
|
||||||
|
templateUrl: './status-translate.component.html',
|
||||||
|
styleUrls: ['./status-translate.component.scss']
|
||||||
|
})
|
||||||
|
export class StatusTranslateComponent implements OnInit, OnDestroy {
|
||||||
|
|
||||||
|
private languageSub: Subscription;
|
||||||
|
private languagesSub: Subscription;
|
||||||
|
private loadedTranslation: Translation;
|
||||||
|
|
||||||
|
selectedLanguage: ILanguage;
|
||||||
|
configuredLanguages: ILanguage[] = [];
|
||||||
|
|
||||||
|
isTranslationAvailable: boolean;
|
||||||
|
showTranslationButton: boolean = true;
|
||||||
|
translatedBy: string;
|
||||||
|
|
||||||
|
@Input() status: StatusWrapper;
|
||||||
|
@Output() translation = new EventEmitter<Translation>();
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private readonly mastodonWrapperService: MastodonWrapperService,
|
||||||
|
private readonly languageService: LanguageService,
|
||||||
|
private readonly instancesInfoService: InstancesInfoService,
|
||||||
|
private readonly notificationService: NotificationService
|
||||||
|
) { }
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.languageSub = this.languageService.selectedLanguageChanged.subscribe(l => {
|
||||||
|
if (l) {
|
||||||
|
this.selectedLanguage = l;
|
||||||
|
this.analyseAvailability();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.languagesSub = this.languageService.configuredLanguagesChanged.subscribe(l => {
|
||||||
|
if (l) {
|
||||||
|
this.configuredLanguages = l;
|
||||||
|
this.analyseAvailability();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
if (this.languageSub) this.languageSub.unsubscribe();
|
||||||
|
if (this.languagesSub) this.languagesSub.unsubscribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
private analyseAvailability() {
|
||||||
|
this.instancesInfoService.getTranslationAvailability(this.status.provider)
|
||||||
|
.then(canTranslate => {
|
||||||
|
if (canTranslate
|
||||||
|
&& !this.status.isRemote
|
||||||
|
&& this.configuredLanguages.length > 0
|
||||||
|
&& this.configuredLanguages.findIndex(x => x.iso639 === this.status.status.language) === -1) {
|
||||||
|
|
||||||
|
this.isTranslationAvailable = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.isTranslationAvailable = false;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.error(err);
|
||||||
|
this.isTranslationAvailable = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
translate(): boolean {
|
||||||
|
if(this.loadedTranslation){
|
||||||
|
this.translation.next(this.loadedTranslation);
|
||||||
|
this.showTranslationButton = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.mastodonWrapperService.translate(this.status.provider, this.status.status.id, this.selectedLanguage.iso639)
|
||||||
|
.then(x => {
|
||||||
|
this.loadedTranslation = x;
|
||||||
|
this.translation.next(x);
|
||||||
|
this.translatedBy = x.provider;
|
||||||
|
this.showTranslationButton = false;
|
||||||
|
})
|
||||||
|
.catch((err: HttpErrorResponse) => {
|
||||||
|
console.error(err);
|
||||||
|
this.notificationService.notifyHttpError(err, this.status.provider);
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
revertTranslation(): boolean {
|
||||||
|
let revertTranslate: Translation;
|
||||||
|
revertTranslate = {
|
||||||
|
content: this.status.status.content,
|
||||||
|
language: this.loadedTranslation.detected_source_language,
|
||||||
|
detected_source_language: this.loadedTranslation.language,
|
||||||
|
provider: this.loadedTranslation.provider,
|
||||||
|
spoiler_text: this.status.status.spoiler_text
|
||||||
|
};
|
||||||
|
this.translation.next(revertTranslate);
|
||||||
|
|
||||||
|
this.showTranslationButton = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -34,6 +34,17 @@
|
||||||
boosted your status
|
boosted your status
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div *ngIf="notificationType === 'update'">
|
||||||
|
<div class="notification--icon">
|
||||||
|
<fa-icon class="update" [icon]="faEdit"></fa-icon>
|
||||||
|
</div>
|
||||||
|
<div class="notification--label">
|
||||||
|
<a href class="notification--link" title="{{ notificationAccount.acct }}"
|
||||||
|
(click)="openAccount(notificationAccount)"
|
||||||
|
(auxclick)="openUrl(notificationAccount.url)" innerHTML="{{ notificationAccount | accountEmoji }}"></a>
|
||||||
|
edited the status you boosted
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div *ngIf="notificationType === 'poll'">
|
<div *ngIf="notificationType === 'poll'">
|
||||||
<div class="notification--icon">
|
<div class="notification--icon">
|
||||||
<fa-icon class="boost" [icon]="faList"></fa-icon>
|
<fa-icon class="boost" [icon]="faList"></fa-icon>
|
||||||
|
@ -98,10 +109,12 @@
|
||||||
<span class="status__content-warning--title">sensitive content</span>
|
<span class="status__content-warning--title">sensitive content</span>
|
||||||
<span innerHTML="{{ contentWarningText }}"></span>
|
<span innerHTML="{{ contentWarningText }}"></span>
|
||||||
</a>
|
</a>
|
||||||
<app-databinded-text class="status__content" *ngIf="!isContentWarned" [text]="statusContent" [selected]="isSelected"
|
<app-databinded-text #databindedtext class="status__content" *ngIf="!isContentWarned" [text]="statusContent" [selected]="isSelected"
|
||||||
(accountSelected)="accountSelected($event)" (hashtagSelected)="hashtagSelected($event)"
|
(accountSelected)="accountSelected($event)" (hashtagSelected)="hashtagSelected($event)"
|
||||||
(textSelected)="textSelected()"></app-databinded-text>
|
(textSelected)="textSelected()"></app-databinded-text>
|
||||||
|
|
||||||
|
<app-status-translate [status]="displayedStatusWrapper" (translation)="onTranslation($event)"></app-status-translate>
|
||||||
|
|
||||||
<app-poll class="status__poll" *ngIf="!isContentWarned && displayedStatus.poll"
|
<app-poll class="status__poll" *ngIf="!isContentWarned && displayedStatus.poll"
|
||||||
[poll]="displayedStatus.poll" [statusWrapper]="displayedStatusWrapper"></app-poll>
|
[poll]="displayedStatus.poll" [statusWrapper]="displayedStatusWrapper"></app-poll>
|
||||||
|
|
||||||
|
|
|
@ -258,6 +258,10 @@
|
||||||
color: $boost-color;
|
color: $boost-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.update {
|
||||||
|
color: $update-color;
|
||||||
|
}
|
||||||
|
|
||||||
.favorite {
|
.favorite {
|
||||||
color: $favorite-color;
|
color: $favorite-color;
|
||||||
}
|
}
|
||||||
|
@ -272,4 +276,4 @@
|
||||||
&__label{
|
&__label{
|
||||||
color: $status-secondary-color;
|
color: $status-secondary-color;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,15 +1,15 @@
|
||||||
import { Component, OnInit, Input, Output, EventEmitter, ViewChild, ElementRef } from "@angular/core";
|
import { Component, OnInit, Input, Output, EventEmitter, ViewChild, ElementRef } from "@angular/core";
|
||||||
import { faStar, faRetweet, faList, faThumbtack } from "@fortawesome/free-solid-svg-icons";
|
import { faStar, faRetweet, faList, faThumbtack, faEdit } from "@fortawesome/free-solid-svg-icons";
|
||||||
import { Subscription } from "rxjs";
|
import { Subscription } from "rxjs";
|
||||||
|
|
||||||
import { Status, Account } from "../../../services/models/mastodon.interfaces";
|
import { Status, Account, Translation } from "../../../services/models/mastodon.interfaces";
|
||||||
import { OpenThreadEvent, ToolsService } from "../../../services/tools.service";
|
import { OpenThreadEvent, ToolsService } from "../../../services/tools.service";
|
||||||
import { ActionBarComponent } from "./action-bar/action-bar.component";
|
import { ActionBarComponent } from "./action-bar/action-bar.component";
|
||||||
import { StatusWrapper } from '../../../models/common.model';
|
import { StatusWrapper } from '../../../models/common.model';
|
||||||
import { EmojiConverter, EmojiTypeEnum } from '../../../tools/emoji.tools';
|
import { EmojiConverter, EmojiTypeEnum } from '../../../tools/emoji.tools';
|
||||||
import { ContentWarningPolicyEnum } from '../../../states/settings.state';
|
import { ContentWarningPolicyEnum } from '../../../states/settings.state';
|
||||||
import { StatusesStateService, StatusState } from "../../../services/statuses-state.service";
|
import { StatusesStateService, StatusState } from "../../../services/statuses-state.service";
|
||||||
|
import { DatabindedTextComponent } from "./databinded-text/databinded-text.component";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: "app-status",
|
selector: "app-status",
|
||||||
|
@ -23,6 +23,7 @@ export class StatusComponent implements OnInit {
|
||||||
faRetweet = faRetweet;
|
faRetweet = faRetweet;
|
||||||
faList = faList;
|
faList = faList;
|
||||||
faThumbtack = faThumbtack;
|
faThumbtack = faThumbtack;
|
||||||
|
faEdit = faEdit;
|
||||||
|
|
||||||
displayedStatus: Status;
|
displayedStatus: Status;
|
||||||
displayedStatusWrapper: StatusWrapper;
|
displayedStatusWrapper: StatusWrapper;
|
||||||
|
@ -52,7 +53,7 @@ export class StatusComponent implements OnInit {
|
||||||
|
|
||||||
@Input() isThreadDisplay: boolean;
|
@Input() isThreadDisplay: boolean;
|
||||||
|
|
||||||
@Input() notificationType: 'mention' | 'reblog' | 'favourite' | 'poll';
|
@Input() notificationType: 'mention' | 'reblog' | 'favourite' | 'poll' | 'update';
|
||||||
@Input() notificationAccount: Account;
|
@Input() notificationAccount: Account;
|
||||||
|
|
||||||
private _statusWrapper: StatusWrapper;
|
private _statusWrapper: StatusWrapper;
|
||||||
|
@ -106,27 +107,27 @@ export class StatusComponent implements OnInit {
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.statusesStateServiceSub = this.statusesStateService.stateNotification.subscribe(notification => {
|
this.statusesStateServiceSub = this.statusesStateService.stateNotification.subscribe(notification => {
|
||||||
if(this._statusWrapper.status.url === notification.statusId && notification.isEdited) {
|
if (this._statusWrapper.status.url === notification.statusId && notification.isEdited) {
|
||||||
this.statusWrapper = notification.editedStatus;
|
this.statusWrapper = notification.editedStatus;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy(){
|
ngOnDestroy() {
|
||||||
if(this.statusesStateServiceSub) this.statusesStateServiceSub.unsubscribe();
|
if (this.statusesStateServiceSub) this.statusesStateServiceSub.unsubscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
private ensureMentionAreDisplayed(data: string): string {
|
private ensureMentionAreDisplayed(data: string): string {
|
||||||
const mentions = this.displayedStatus.mentions;
|
const mentions = this.displayedStatus.mentions;
|
||||||
if(!mentions || mentions.length === 0) return data;
|
if (!mentions || mentions.length === 0) return data;
|
||||||
|
|
||||||
let textMentions = '';
|
let textMentions = '';
|
||||||
for (const m of mentions) {
|
for (const m of mentions) {
|
||||||
if(!data.includes(m.url)){
|
if (!data.includes(m.url)) {
|
||||||
textMentions += `<span class="h-card"><a class="u-url mention" data-user="${m.id}" href="${m.url}" rel="ugc">@<span>${m.username}</span></a></span> `
|
textMentions += `<span class="h-card"><a class="u-url mention" data-user="${m.id}" href="${m.url}" rel="ugc">@<span>${m.username}</span></a></span> `
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(textMentions !== ''){
|
if (textMentions !== '') {
|
||||||
data = textMentions + data;
|
data = textMentions + data;
|
||||||
}
|
}
|
||||||
return data;
|
return data;
|
||||||
|
@ -156,6 +157,31 @@ export class StatusComponent implements OnInit {
|
||||||
changeCw(cwIsActive: boolean) {
|
changeCw(cwIsActive: boolean) {
|
||||||
this.isContentWarned = cwIsActive;
|
this.isContentWarned = cwIsActive;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ViewChild('databindedtext') public databindedText: DatabindedTextComponent;
|
||||||
|
|
||||||
|
onTranslation(translation: Translation) {
|
||||||
|
let statusContent = translation.content;
|
||||||
|
|
||||||
|
// clean up a bit some issues (not reliable)
|
||||||
|
while (statusContent.includes('<span>@')) {
|
||||||
|
statusContent = statusContent.replace('<span>@', '@<span>');
|
||||||
|
}
|
||||||
|
while (statusContent.includes('h<span class="invisible">')){
|
||||||
|
statusContent = statusContent.replace('h<span class="invisible">', '<span class="invisible">h');
|
||||||
|
}
|
||||||
|
while (statusContent.includes('<span>#')){
|
||||||
|
statusContent = statusContent.replace('<span>#', '#<span>');
|
||||||
|
}
|
||||||
|
|
||||||
|
statusContent = this.emojiConverter.applyEmojis(this.displayedStatus.emojis, statusContent, EmojiTypeEnum.medium);
|
||||||
|
this.statusContent = this.ensureMentionAreDisplayed(statusContent);
|
||||||
|
|
||||||
|
setTimeout(x => {
|
||||||
|
this.databindedText.processEventBindings();
|
||||||
|
}, 500);
|
||||||
|
}
|
||||||
|
|
||||||
private checkLabels(status: Status) {
|
private checkLabels(status: Status) {
|
||||||
//since API is limited with federated status...
|
//since API is limited with federated status...
|
||||||
|
|
|
@ -126,7 +126,7 @@ export class StreamNotificationsComponent extends BrowseBase {
|
||||||
this.loadMentions(userNotifications);
|
this.loadMentions(userNotifications);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.mastodonService.getNotifications(this.account, ['update'], null, null, 10) //FIXME: disable edition update until supported
|
this.mastodonService.getNotifications(this.account, [], null, null, 10)
|
||||||
.then((notifications: Notification[]) => {
|
.then((notifications: Notification[]) => {
|
||||||
this.isNotificationsLoading = false;
|
this.isNotificationsLoading = false;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
import { TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { ElectronService } from './electron.service';
|
||||||
|
|
||||||
|
describe('ElectronService', () => {
|
||||||
|
beforeEach(() => TestBed.configureTestingModule({}));
|
||||||
|
|
||||||
|
it('should be created', () => {
|
||||||
|
const service: ElectronService = TestBed.get(ElectronService);
|
||||||
|
expect(service).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,20 @@
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class MyElectronService {
|
||||||
|
constructor() {
|
||||||
|
}
|
||||||
|
|
||||||
|
setLang(lang: string) {
|
||||||
|
try {
|
||||||
|
if ((<any>window).api) {
|
||||||
|
(<any>window).api.send("changeSpellchecker", lang);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,6 +11,7 @@ import { AccountInfo } from '../states/accounts.state';
|
||||||
export class InstancesInfoService {
|
export class InstancesInfoService {
|
||||||
private defaultMaxChars = 500;
|
private defaultMaxChars = 500;
|
||||||
private cachedMaxInstanceChar: { [id: string]: Promise<number>; } = {};
|
private cachedMaxInstanceChar: { [id: string]: Promise<number>; } = {};
|
||||||
|
private cachedTranslationAvailability: { [id: string]: Promise<boolean>; } = {};
|
||||||
private cachedDefaultPrivacy: { [id: string]: Promise<VisibilityEnum>; } = {};
|
private cachedDefaultPrivacy: { [id: string]: Promise<VisibilityEnum>; } = {};
|
||||||
|
|
||||||
constructor(private mastodonService: MastodonWrapperService) { }
|
constructor(private mastodonService: MastodonWrapperService) { }
|
||||||
|
@ -65,4 +66,30 @@ export class InstancesInfoService {
|
||||||
}
|
}
|
||||||
return this.cachedDefaultPrivacy[instance];
|
return this.cachedDefaultPrivacy[instance];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getTranslationAvailability(account: AccountInfo): Promise<boolean> {
|
||||||
|
const instance = account.instance;
|
||||||
|
if (!this.cachedTranslationAvailability[instance]) {
|
||||||
|
this.cachedTranslationAvailability[instance] = this.mastodonService.getInstance(instance)
|
||||||
|
.then((instance: Instance) => {
|
||||||
|
if (+instance.version.split('.')[0] >= 4) {
|
||||||
|
const instanceV2 = <Instancev2>instance;
|
||||||
|
if (instanceV2
|
||||||
|
&& instanceV2.configuration
|
||||||
|
&& instanceV2.configuration.translation)
|
||||||
|
return instanceV2.configuration.translation.enabled;
|
||||||
|
} else {
|
||||||
|
const instanceV1 = <Instancev1>instance;
|
||||||
|
if (instanceV1 && instanceV1.max_toot_chars)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return this.cachedTranslationAvailability[instance];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
import { TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { LanguageService } from './language.service';
|
||||||
|
|
||||||
|
xdescribe('LanguageService', () => {
|
||||||
|
beforeEach(() => TestBed.configureTestingModule({}));
|
||||||
|
|
||||||
|
it('should be created', () => {
|
||||||
|
const service: LanguageService = TestBed.get(LanguageService);
|
||||||
|
expect(service).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,279 @@
|
||||||
|
import { T } from '@angular/cdk/keycodes';
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { BehaviorSubject } from 'rxjs';
|
||||||
|
|
||||||
|
import { ILanguage } from '../states/settings.state';
|
||||||
|
import { MyElectronService } from './electron.service';
|
||||||
|
import { SettingsService } from './settings.service';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class LanguageService {
|
||||||
|
configuredLanguagesChanged = new BehaviorSubject<ILanguage[]>([]);
|
||||||
|
selectedLanguageChanged = new BehaviorSubject<ILanguage>(null);
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private settingsService: SettingsService,
|
||||||
|
private electronService: MyElectronService
|
||||||
|
) {
|
||||||
|
this.configuredLanguagesChanged.next(this.getConfiguredLanguages());
|
||||||
|
this.selectedLanguageChanged.next(this.getSelectedLanguage());
|
||||||
|
}
|
||||||
|
|
||||||
|
getSelectedLanguage(): ILanguage {
|
||||||
|
const lang = this.settingsService.getSettings().selectedLanguage;
|
||||||
|
return lang;
|
||||||
|
}
|
||||||
|
|
||||||
|
setSelectedLanguage(lang: ILanguage): void {
|
||||||
|
var settings = this.settingsService.getSettings();
|
||||||
|
settings.selectedLanguage = lang;
|
||||||
|
this.settingsService.saveSettings(settings);
|
||||||
|
|
||||||
|
this.selectedLanguageChanged.next(lang);
|
||||||
|
|
||||||
|
if (lang) {
|
||||||
|
this.electronService.setLang(lang.iso639);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getConfiguredLanguages(): ILanguage[] {
|
||||||
|
const langs = this.settingsService.getSettings().configuredLanguages;
|
||||||
|
return langs;
|
||||||
|
}
|
||||||
|
|
||||||
|
addLanguage(lang: ILanguage) {
|
||||||
|
var settings = this.settingsService.getSettings();
|
||||||
|
settings.configuredLanguages.push(lang);
|
||||||
|
settings.configuredLanguages.sort((a, b) => a.name.localeCompare(b.name));
|
||||||
|
this.settingsService.saveSettings(settings);
|
||||||
|
|
||||||
|
this.configuredLanguagesChanged.next(settings.configuredLanguages);
|
||||||
|
|
||||||
|
if (settings.configuredLanguages.length === 1) {
|
||||||
|
this.setSelectedLanguage(lang);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
removeLanguage(lang: ILanguage) {
|
||||||
|
var settings = this.settingsService.getSettings();
|
||||||
|
settings.configuredLanguages = settings.configuredLanguages.filter(x => x.iso639 !== lang.iso639);
|
||||||
|
this.settingsService.saveSettings(settings);
|
||||||
|
|
||||||
|
this.configuredLanguagesChanged.next(settings.configuredLanguages);
|
||||||
|
|
||||||
|
if (this.getSelectedLanguage().iso639 === lang.iso639) {
|
||||||
|
if (settings.configuredLanguages.length > 0) {
|
||||||
|
this.setSelectedLanguage(settings.configuredLanguages[0]);
|
||||||
|
} else {
|
||||||
|
this.setSelectedLanguage(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
searchLanguage(input: string): ILanguage[] {
|
||||||
|
if (!input) return [];
|
||||||
|
|
||||||
|
const avLangs = this.getAllAvaialbleLaguages();
|
||||||
|
let found = avLangs.filter(x => x.name.toLowerCase().includes(input.toLowerCase()) || x.iso639.toLowerCase().includes(input.toLowerCase()));
|
||||||
|
found.sort((a, b) => a.name.localeCompare(b.name));
|
||||||
|
found = found.slice(0, 5);
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
private getAllAvaialbleLaguages(): Language[] {
|
||||||
|
return [
|
||||||
|
new Language("aa", "Afar"),
|
||||||
|
new Language("ab", "Abkhazian"),
|
||||||
|
new Language("af", "Afrikaans"),
|
||||||
|
new Language("ak", "Akan"),
|
||||||
|
new Language("am", "Amharic"),
|
||||||
|
new Language("an", "Aragonese"),
|
||||||
|
new Language("ar", "Arabic"),
|
||||||
|
new Language("as", "Assamese"),
|
||||||
|
new Language("av", "Avar"),
|
||||||
|
new Language("ay", "Aymara"),
|
||||||
|
new Language("az", "Azerbaijani"),
|
||||||
|
new Language("ba", "Bashkir"),
|
||||||
|
new Language("be", "Belarusian"),
|
||||||
|
new Language("bg", "Bulgarian"),
|
||||||
|
new Language("bh", "Bihari"),
|
||||||
|
new Language("bi", "Bislama"),
|
||||||
|
new Language("bm", "Bambara"),
|
||||||
|
new Language("bn", "Bengali"),
|
||||||
|
new Language("bo", "Tibetan"),
|
||||||
|
new Language("br", "Breton"),
|
||||||
|
new Language("bs", "Bosnian"),
|
||||||
|
new Language("ca", "Catalan"),
|
||||||
|
new Language("ce", "Chechen"),
|
||||||
|
new Language("ch", "Chamorro"),
|
||||||
|
new Language("co", "Corsican"),
|
||||||
|
new Language("cr", "Cree"),
|
||||||
|
new Language("cs", "Czech"),
|
||||||
|
new Language("cu", "Old Church Slavonic"),
|
||||||
|
new Language("cv", "Chuvash"),
|
||||||
|
new Language("cy", "Welsh"),
|
||||||
|
new Language("da", "Danish"),
|
||||||
|
new Language("de", "German"),
|
||||||
|
new Language("dv", "Divehi"),
|
||||||
|
new Language("dz", "Dzongkha"),
|
||||||
|
new Language("ee", "Ewe"),
|
||||||
|
new Language("el", "Greek"),
|
||||||
|
new Language("en", "English"),
|
||||||
|
new Language("eo", "Esperanto"),
|
||||||
|
new Language("es", "Spanish"),
|
||||||
|
new Language("et", "Estonian"),
|
||||||
|
new Language("eu", "Basque"),
|
||||||
|
new Language("fa", "Persian"),
|
||||||
|
new Language("ff", "Peul"),
|
||||||
|
new Language("fi", "Finnish"),
|
||||||
|
new Language("fj", "Fijian"),
|
||||||
|
new Language("fo", "Faroese"),
|
||||||
|
new Language("fr", "French"),
|
||||||
|
new Language("fy", "West Frisian"),
|
||||||
|
new Language("ga", "Irish"),
|
||||||
|
new Language("gd", "Scottish Gaelic"),
|
||||||
|
new Language("gl", "Galician"),
|
||||||
|
new Language("gn", "Guarani"),
|
||||||
|
new Language("gu", "Gujarati"),
|
||||||
|
new Language("gv", "Manx"),
|
||||||
|
new Language("ha", "Hausa"),
|
||||||
|
new Language("he", "Hebrew"),
|
||||||
|
new Language("hi", "Hindi"),
|
||||||
|
new Language("ho", "Hiri Motu"),
|
||||||
|
new Language("hr", "Croatian"),
|
||||||
|
new Language("ht", "Haitian"),
|
||||||
|
new Language("hu", "Hungarian"),
|
||||||
|
new Language("hy", "Armenian"),
|
||||||
|
new Language("hz", "Herero"),
|
||||||
|
new Language("ia", "Interlingua"),
|
||||||
|
new Language("id", "Indonesian"),
|
||||||
|
new Language("ie", "Interlingue"),
|
||||||
|
new Language("ig", "Igbo"),
|
||||||
|
new Language("ii", "Sichuan Yi"),
|
||||||
|
new Language("ik", "Inupiak"),
|
||||||
|
new Language("io", "Ido"),
|
||||||
|
new Language("is", "Icelandic"),
|
||||||
|
new Language("it", "Italian"),
|
||||||
|
new Language("iu", "Inuktitut"),
|
||||||
|
new Language("ja", "Japanese"),
|
||||||
|
new Language("jv", "Javanese"),
|
||||||
|
new Language("ka", "Georgian"),
|
||||||
|
new Language("kg", "Kongo"),
|
||||||
|
new Language("ki", "Kikuyu"),
|
||||||
|
new Language("kj", "Kuanyama"),
|
||||||
|
new Language("kk", "Kazakh"),
|
||||||
|
new Language("kl", "Greenlandic"),
|
||||||
|
new Language("km", "Cambodian"),
|
||||||
|
new Language("kn", "Kannada"),
|
||||||
|
new Language("ko", "Korean"),
|
||||||
|
new Language("kr", "Kanuri"),
|
||||||
|
new Language("ks", "Kashmiri"),
|
||||||
|
new Language("ku", "Kurdish"),
|
||||||
|
new Language("kv", "Komi"),
|
||||||
|
new Language("kw", "Cornish"),
|
||||||
|
new Language("ky", "Kirghiz"),
|
||||||
|
new Language("la", "Latin"),
|
||||||
|
new Language("lb", "Luxembourgish"),
|
||||||
|
new Language("lg", "Ganda"),
|
||||||
|
new Language("li", "Limburgian"),
|
||||||
|
new Language("ln", "Lingala"),
|
||||||
|
new Language("lo", "Laotian"),
|
||||||
|
new Language("lt", "Lithuanian"),
|
||||||
|
new Language("lu", "Luba-Katanga"),
|
||||||
|
new Language("lv", "Latvian"),
|
||||||
|
new Language("mg", "Malagasy"),
|
||||||
|
new Language("mh", "Marshallese"),
|
||||||
|
new Language("mi", "Maori"),
|
||||||
|
new Language("mk", "Macedonian"),
|
||||||
|
new Language("ml", "Malayalam"),
|
||||||
|
new Language("mn", "Mongolian"),
|
||||||
|
new Language("mo", "Moldovan"),
|
||||||
|
new Language("mr", "Marathi"),
|
||||||
|
new Language("ms", "Malay"),
|
||||||
|
new Language("mt", "Maltese"),
|
||||||
|
new Language("my", "Burmese"),
|
||||||
|
new Language("na", "Nauruan"),
|
||||||
|
new Language("nb", "Norwegian Bokmål"),
|
||||||
|
new Language("nd", "North Ndebele"),
|
||||||
|
new Language("ne", "Nepali"),
|
||||||
|
new Language("ng", "Ndonga"),
|
||||||
|
new Language("nl", "Dutch"),
|
||||||
|
new Language("nn", "Norwegian Nynorsk"),
|
||||||
|
new Language("no", "Norwegian"),
|
||||||
|
new Language("nr", "South Ndebele"),
|
||||||
|
new Language("nv", "Navajo"),
|
||||||
|
new Language("ny", "Chichewa"),
|
||||||
|
new Language("oc", "Occitan"),
|
||||||
|
new Language("oj", "Ojibwa"),
|
||||||
|
new Language("om", "Oromo"),
|
||||||
|
new Language("or", "Oriya"),
|
||||||
|
new Language("os", "Ossetian"),
|
||||||
|
new Language("pa", "Panjabi"),
|
||||||
|
new Language("pi", "Pali"),
|
||||||
|
new Language("pl", "Polish"),
|
||||||
|
new Language("ps", "Pashto"),
|
||||||
|
new Language("pt", "Portuguese"),
|
||||||
|
new Language("qu", "Quechua"),
|
||||||
|
new Language("rm", "Raeto Romance"),
|
||||||
|
new Language("rn", "Kirundi"),
|
||||||
|
new Language("ro", "Romanian"),
|
||||||
|
new Language("ru", "Russian"),
|
||||||
|
new Language("rw", "Rwandi"),
|
||||||
|
new Language("sa", "Sanskrit"),
|
||||||
|
new Language("sc", "Sardinian"),
|
||||||
|
new Language("sd", "Sindhi"),
|
||||||
|
new Language("se", "Northern Sami"),
|
||||||
|
new Language("sg", "Sango"),
|
||||||
|
new Language("sh", "Serbo-Croatian"),
|
||||||
|
new Language("si", "Sinhalese"),
|
||||||
|
new Language("sk", "Slovak"),
|
||||||
|
new Language("sl", "Slovenian"),
|
||||||
|
new Language("sm", "Samoan"),
|
||||||
|
new Language("sn", "Shona"),
|
||||||
|
new Language("so", "Somalia"),
|
||||||
|
new Language("sq", "Albanian"),
|
||||||
|
new Language("sr", "Serbian"),
|
||||||
|
new Language("ss", "Swati"),
|
||||||
|
new Language("st", "Southern Sotho"),
|
||||||
|
new Language("su", "Sundanese"),
|
||||||
|
new Language("sv", "Swedish"),
|
||||||
|
new Language("sw", "Swahili"),
|
||||||
|
new Language("ta", "Tamil"),
|
||||||
|
new Language("te", "Telugu"),
|
||||||
|
new Language("tg", "Tajik"),
|
||||||
|
new Language("th", "Thai"),
|
||||||
|
new Language("ti", "Tigrinya"),
|
||||||
|
new Language("tk", "Turkmen"),
|
||||||
|
new Language("tl", "Tagalog"),
|
||||||
|
new Language("tn", "Tswana"),
|
||||||
|
new Language("to", "Tonga"),
|
||||||
|
new Language("tr", "Turkish"),
|
||||||
|
new Language("ts", "Tsonga"),
|
||||||
|
new Language("tt", "Tatar"),
|
||||||
|
new Language("tw", "Twi"),
|
||||||
|
new Language("ty", "Tahitian"),
|
||||||
|
new Language("ug", "Uyghur"),
|
||||||
|
new Language("uk", "Ukrainian"),
|
||||||
|
new Language("ur", "Urdu"),
|
||||||
|
new Language("uz", "Uzbek"),
|
||||||
|
new Language("ve", "Venda"),
|
||||||
|
new Language("vi", "Vietnamese"),
|
||||||
|
new Language("vo", "Volapük"),
|
||||||
|
new Language("wa", "Walloon"),
|
||||||
|
new Language("wo", "Wolof"),
|
||||||
|
new Language("xh", "Xhosa"),
|
||||||
|
new Language("yi", "Yiddish"),
|
||||||
|
new Language("yo", "Yoruba"),
|
||||||
|
new Language("za", "Zhuang"),
|
||||||
|
new Language("zh", "Chinese"),
|
||||||
|
new Language("zu", "Zulu"),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Language {
|
||||||
|
constructor(public iso639: string, public name: string) {
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { Store } from '@ngxs/store';
|
import { Store } from '@ngxs/store';
|
||||||
|
|
||||||
import { Account, Status, Results, Context, Relationship, Instance, Attachment, Notification, List, Poll, Emoji, Conversation, ScheduledStatus, TokenData, Tag } from "./models/mastodon.interfaces";
|
import { Account, Status, Results, Context, Relationship, Instance, Attachment, Notification, List, Poll, Emoji, Conversation, ScheduledStatus, TokenData, Tag, Translation } from "./models/mastodon.interfaces";
|
||||||
import { AccountInfo, UpdateAccount } from '../states/accounts.state';
|
import { AccountInfo, UpdateAccount } from '../states/accounts.state';
|
||||||
import { StreamTypeEnum, StreamElement } from '../states/streams.state';
|
import { StreamTypeEnum, StreamElement } from '../states/streams.state';
|
||||||
import { FavoriteResult, VisibilityEnum, PollParameters, MastodonService, BookmarkResult, FollowingResult } from './mastodon.service';
|
import { FavoriteResult, VisibilityEnum, PollParameters, MastodonService, BookmarkResult, FollowingResult } from './mastodon.service';
|
||||||
|
@ -96,6 +96,13 @@ export class MastodonWrapperService {
|
||||||
return this.mastodonService.getInstance(instance);
|
return this.mastodonService.getInstance(instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
translate(account: AccountInfo, statusId: string, lang: string): Promise<Translation>{
|
||||||
|
return this.refreshAccountIfNeeded(account)
|
||||||
|
.then((refreshedAccount: AccountInfo) => {
|
||||||
|
return this.mastodonService.translate(refreshedAccount, statusId, lang);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
retrieveAccountDetails(account: AccountInfo): Promise<Account> {
|
retrieveAccountDetails(account: AccountInfo): Promise<Account> {
|
||||||
return this.refreshAccountIfNeeded(account)
|
return this.refreshAccountIfNeeded(account)
|
||||||
.then((refreshedAccount: AccountInfo) => {
|
.then((refreshedAccount: AccountInfo) => {
|
||||||
|
@ -117,17 +124,17 @@ export class MastodonWrapperService {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
postNewStatus(account: AccountInfo, status: string, visibility: VisibilityEnum, spoiler: string = null, in_reply_to_id: string = null, mediaIds: string[], poll: PollParameters = null, scheduled_at: string = null): Promise<Status> {
|
postNewStatus(account: AccountInfo, status: string, visibility: VisibilityEnum, spoiler: string = null, in_reply_to_id: string = null, mediaIds: string[], poll: PollParameters = null, scheduled_at: string = null, lang: string = null): Promise<Status> {
|
||||||
return this.refreshAccountIfNeeded(account)
|
return this.refreshAccountIfNeeded(account)
|
||||||
.then((refreshedAccount: AccountInfo) => {
|
.then((refreshedAccount: AccountInfo) => {
|
||||||
return this.mastodonService.postNewStatus(refreshedAccount, status, visibility, spoiler, in_reply_to_id, mediaIds, poll, scheduled_at);
|
return this.mastodonService.postNewStatus(refreshedAccount, status, visibility, spoiler, in_reply_to_id, mediaIds, poll, scheduled_at, lang);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
editStatus(account: AccountInfo, statusId: string, status: string, visibility: VisibilityEnum, spoiler: string = null, in_reply_to_id: string = null, attachements: Attachment[], poll: PollParameters = null, scheduled_at: string = null): Promise<Status> {
|
editStatus(account: AccountInfo, statusId: string, status: string, visibility: VisibilityEnum, spoiler: string = null, in_reply_to_id: string = null, attachements: Attachment[], poll: PollParameters = null, scheduled_at: string = null, lang: string = null): Promise<Status> {
|
||||||
return this.refreshAccountIfNeeded(account)
|
return this.refreshAccountIfNeeded(account)
|
||||||
.then((refreshedAccount: AccountInfo) => {
|
.then((refreshedAccount: AccountInfo) => {
|
||||||
return this.mastodonService.editStatus(refreshedAccount, statusId, status, visibility, spoiler, in_reply_to_id, attachements, poll, scheduled_at);
|
return this.mastodonService.editStatus(refreshedAccount, statusId, status, visibility, spoiler, in_reply_to_id, attachements, poll, scheduled_at, lang);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { Injectable } from '@angular/core';
|
||||||
import { HttpHeaders, HttpClient, HttpResponse } from '@angular/common/http';
|
import { HttpHeaders, HttpClient, HttpResponse } from '@angular/common/http';
|
||||||
|
|
||||||
import { ApiRoutes } from './models/api.settings';
|
import { ApiRoutes } from './models/api.settings';
|
||||||
import { Account, Status, Results, Context, Relationship, Instance, Attachment, Notification, List, Poll, Emoji, Conversation, ScheduledStatus, Tag, Instancev2, Instancev1 } from "./models/mastodon.interfaces";
|
import { Account, Status, Results, Context, Relationship, Instance, Attachment, Notification, List, Poll, Emoji, Conversation, ScheduledStatus, Tag, Instancev2, Instancev1, Translation } from "./models/mastodon.interfaces";
|
||||||
import { AccountInfo } from '../states/accounts.state';
|
import { AccountInfo } from '../states/accounts.state';
|
||||||
import { StreamTypeEnum, StreamElement } from '../states/streams.state';
|
import { StreamTypeEnum, StreamElement } from '../states/streams.state';
|
||||||
|
|
||||||
|
@ -21,6 +21,13 @@ export class MastodonService {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
translate(account: AccountInfo, statusId: string, lang: string): Promise<Translation>{
|
||||||
|
const headers = new HttpHeaders({ 'Authorization': `Bearer ${account.token.access_token}` });
|
||||||
|
let route = `https://${account.instance}${this.apiRoutes.translate.replace('{0}', statusId)}`;
|
||||||
|
|
||||||
|
return this.httpClient.post<Translation>(route, { 'lang': lang }, { headers: headers }).toPromise();
|
||||||
|
}
|
||||||
|
|
||||||
retrieveAccountDetails(account: AccountInfo): Promise<Account> {
|
retrieveAccountDetails(account: AccountInfo): Promise<Account> {
|
||||||
const headers = new HttpHeaders({ 'Authorization': `Bearer ${account.token.access_token}` });
|
const headers = new HttpHeaders({ 'Authorization': `Bearer ${account.token.access_token}` });
|
||||||
return this.httpClient.get<Account>('https://' + account.instance + this.apiRoutes.getCurrentAccount, { headers: headers }).toPromise();
|
return this.httpClient.get<Account>('https://' + account.instance + this.apiRoutes.getCurrentAccount, { headers: headers }).toPromise();
|
||||||
|
@ -88,7 +95,7 @@ export class MastodonService {
|
||||||
return origString.replace(regEx, "");
|
return origString.replace(regEx, "");
|
||||||
};
|
};
|
||||||
|
|
||||||
postNewStatus(account: AccountInfo, status: string, visibility: VisibilityEnum, spoiler: string = null, in_reply_to_id: string = null, mediaIds: string[], poll: PollParameters = null, scheduled_at: string = null): Promise<Status> {
|
postNewStatus(account: AccountInfo, status: string, visibility: VisibilityEnum, spoiler: string = null, in_reply_to_id: string = null, mediaIds: string[], poll: PollParameters = null, scheduled_at: string = null, lang: string = null): Promise<Status> {
|
||||||
const url = `https://${account.instance}${this.apiRoutes.postNewStatus}`;
|
const url = `https://${account.instance}${this.apiRoutes.postNewStatus}`;
|
||||||
|
|
||||||
const statusData = new StatusData();
|
const statusData = new StatusData();
|
||||||
|
@ -106,10 +113,16 @@ export class MastodonService {
|
||||||
if (in_reply_to_id) {
|
if (in_reply_to_id) {
|
||||||
statusData.in_reply_to_id = in_reply_to_id;
|
statusData.in_reply_to_id = in_reply_to_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (spoiler) {
|
if (spoiler) {
|
||||||
statusData.sensitive = true;
|
statusData.sensitive = true;
|
||||||
statusData.spoiler_text = spoiler;
|
statusData.spoiler_text = spoiler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(lang) {
|
||||||
|
statusData.language = lang;
|
||||||
|
}
|
||||||
|
|
||||||
switch (visibility) {
|
switch (visibility) {
|
||||||
case VisibilityEnum.Public:
|
case VisibilityEnum.Public:
|
||||||
statusData.visibility = 'public';
|
statusData.visibility = 'public';
|
||||||
|
@ -132,7 +145,7 @@ export class MastodonService {
|
||||||
return this.httpClient.post<Status>(url, statusData, { headers: headers }).toPromise();
|
return this.httpClient.post<Status>(url, statusData, { headers: headers }).toPromise();
|
||||||
}
|
}
|
||||||
|
|
||||||
editStatus(account: AccountInfo, statusId: string, status: string, visibility: VisibilityEnum, spoiler: string = null, in_reply_to_id: string = null, attachements: Attachment[], poll: PollParameters = null, scheduled_at: string = null): Promise<Status> {
|
editStatus(account: AccountInfo, statusId: string, status: string, visibility: VisibilityEnum, spoiler: string = null, in_reply_to_id: string = null, attachements: Attachment[], poll: PollParameters = null, scheduled_at: string = null, lang: string = null): Promise<Status> {
|
||||||
const url = `https://${account.instance}${this.apiRoutes.editStatus.replace('{0}', statusId)}`;
|
const url = `https://${account.instance}${this.apiRoutes.editStatus.replace('{0}', statusId)}`;
|
||||||
|
|
||||||
const statusData = new StatusData();
|
const statusData = new StatusData();
|
||||||
|
@ -151,10 +164,16 @@ export class MastodonService {
|
||||||
if (in_reply_to_id) {
|
if (in_reply_to_id) {
|
||||||
statusData.in_reply_to_id = in_reply_to_id;
|
statusData.in_reply_to_id = in_reply_to_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (spoiler) {
|
if (spoiler) {
|
||||||
statusData.sensitive = true;
|
statusData.sensitive = true;
|
||||||
statusData.spoiler_text = spoiler;
|
statusData.spoiler_text = spoiler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(lang) {
|
||||||
|
statusData.language = lang;
|
||||||
|
}
|
||||||
|
|
||||||
switch (visibility) {
|
switch (visibility) {
|
||||||
case VisibilityEnum.Public:
|
case VisibilityEnum.Public:
|
||||||
statusData.visibility = 'public';
|
statusData.visibility = 'public';
|
||||||
|
@ -651,6 +670,8 @@ class StatusData {
|
||||||
spoiler_text: string;
|
spoiler_text: string;
|
||||||
visibility: string;
|
visibility: string;
|
||||||
// scheduled_at: string;
|
// scheduled_at: string;
|
||||||
|
|
||||||
|
language: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
class MediaAttributes {
|
class MediaAttributes {
|
||||||
|
|
|
@ -80,4 +80,5 @@ export class ApiRoutes {
|
||||||
followHashtag = '/api/v1/tags/{0}/follow';
|
followHashtag = '/api/v1/tags/{0}/follow';
|
||||||
unfollowHashtag = '/api/v1/tags/{0}/unfollow';
|
unfollowHashtag = '/api/v1/tags/{0}/unfollow';
|
||||||
getHashtag = '/api/v1/tags/{0}';
|
getHashtag = '/api/v1/tags/{0}';
|
||||||
|
translate = '/api/v1/statuses/{0}/translate';
|
||||||
}
|
}
|
||||||
|
|
|
@ -132,7 +132,8 @@ export interface Instancev2 extends Instance {
|
||||||
|
|
||||||
export interface Instancev2Configuration {
|
export interface Instancev2Configuration {
|
||||||
urls: Instancev2Urls;
|
urls: Instancev2Urls;
|
||||||
statuses: Instancev2Statuses
|
statuses: Instancev2Statuses;
|
||||||
|
translation: Instancev2Translation;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface InstanceUrls {
|
export interface InstanceUrls {
|
||||||
|
@ -147,6 +148,10 @@ export interface Instancev2Statuses {
|
||||||
max_characters: number;
|
max_characters: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface Instancev2Translation {
|
||||||
|
enabled: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export interface Mention {
|
export interface Mention {
|
||||||
url: string;
|
url: string;
|
||||||
username: string;
|
username: string;
|
||||||
|
@ -284,4 +289,12 @@ export interface Tag {
|
||||||
url: string;
|
url: string;
|
||||||
history: TagHistory[];
|
history: TagHistory[];
|
||||||
following: boolean;
|
following: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Translation {
|
||||||
|
content: string;
|
||||||
|
language: string;
|
||||||
|
detected_source_language: string;
|
||||||
|
provider: string;
|
||||||
|
spoiler_text: string;
|
||||||
}
|
}
|
|
@ -33,6 +33,11 @@ export class SettingsService {
|
||||||
this.saveSettings(settings);
|
this.saveSettings(settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(!settings.configuredLanguages){
|
||||||
|
settings.configuredLanguages = [];
|
||||||
|
this.saveSettings(settings);
|
||||||
|
}
|
||||||
|
|
||||||
return settings;
|
return settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -96,7 +96,7 @@ export class StreamingWrapper {
|
||||||
}
|
}
|
||||||
|
|
||||||
private pullNewNotifications() {
|
private pullNewNotifications() {
|
||||||
this.mastodonService.getNotifications(this.account, ['update'], null, this.since_id_notifications, 10)
|
this.mastodonService.getNotifications(this.account, [], null, this.since_id_notifications, 10)
|
||||||
.then((notifications: Notification[]) => {
|
.then((notifications: Notification[]) => {
|
||||||
//notifications = notifications.sort((a, b) => a.id.localeCompare(b.id));
|
//notifications = notifications.sort((a, b) => a.id.localeCompare(b.id));
|
||||||
let soundMuted = !this.since_id_notifications;
|
let soundMuted = !this.since_id_notifications;
|
||||||
|
@ -168,9 +168,6 @@ export class StreamingWrapper {
|
||||||
newUpdate.type = EventEnum.unknow;
|
newUpdate.type = EventEnum.unknow;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(newUpdate.notification && newUpdate.notification.type === 'update') { //FIXME: disabling edition update until supported
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.statusUpdateSubjet.next(newUpdate);
|
this.statusUpdateSubjet.next(newUpdate);
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,21 +77,47 @@ export class ToolsService {
|
||||||
return Promise.resolve(this.instanceInfos[acc.instance]);
|
return Promise.resolve(this.instanceInfos[acc.instance]);
|
||||||
} else {
|
} else {
|
||||||
return this.mastodonService.getInstance(acc.instance)
|
return this.mastodonService.getInstance(acc.instance)
|
||||||
.then(instance => {
|
.then(instance => {
|
||||||
let type = InstanceType.Mastodon;
|
|
||||||
if (instance.version.toLowerCase().includes('pleroma')) {
|
|
||||||
type = InstanceType.Pleroma;
|
|
||||||
} else if (instance.version.toLowerCase().includes('+glitch')) {
|
|
||||||
type = InstanceType.GlitchSoc;
|
|
||||||
} else if (instance.version.toLowerCase().includes('+florence')) {
|
|
||||||
type = InstanceType.Florence;
|
|
||||||
} else if (instance.version.toLowerCase().includes('pixelfed')) {
|
|
||||||
type = InstanceType.Pixelfed;
|
|
||||||
}
|
|
||||||
|
|
||||||
const splittedVersion = instance.version.split('.');
|
const splittedVersion = instance.version.split('.');
|
||||||
const major = +splittedVersion[0];
|
let major = +splittedVersion[0];
|
||||||
const minor = +splittedVersion[1];
|
let minor = +splittedVersion[1];
|
||||||
|
|
||||||
|
let altMajor = 0;
|
||||||
|
let altMinor = 0;
|
||||||
|
|
||||||
|
let type = InstanceType.Mastodon;
|
||||||
|
|
||||||
|
const version = instance.version.toLowerCase();
|
||||||
|
|
||||||
|
if (version.includes('pleroma')) {
|
||||||
|
type = InstanceType.Pleroma;
|
||||||
|
|
||||||
|
const pleromaVersion = version.split('pleroma ')[1].split('.');
|
||||||
|
altMajor = +pleromaVersion[0];
|
||||||
|
altMinor = +pleromaVersion[1];
|
||||||
|
|
||||||
|
} else if (version.includes('+glitch')) {
|
||||||
|
type = InstanceType.GlitchSoc;
|
||||||
|
} else if (version.includes('+florence')) {
|
||||||
|
type = InstanceType.Florence;
|
||||||
|
} else if (version.includes('pixelfed')) {
|
||||||
|
type = InstanceType.Pixelfed;
|
||||||
|
} 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
|
||||||
|
|
||||||
|
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];
|
||||||
|
}
|
||||||
|
|
||||||
let streamingApi = "";
|
let streamingApi = "";
|
||||||
|
|
||||||
|
@ -108,7 +134,7 @@ export class ToolsService {
|
||||||
streamingApi = instanceV1.urls.streaming_api;
|
streamingApi = instanceV1.urls.streaming_api;
|
||||||
}
|
}
|
||||||
|
|
||||||
let instanceInfo = new InstanceInfo(type, major, minor, streamingApi);
|
let instanceInfo = new InstanceInfo(type, major, minor, streamingApi, altMajor, altMinor);
|
||||||
this.instanceInfos[acc.instance] = instanceInfo;
|
this.instanceInfos[acc.instance] = instanceInfo;
|
||||||
|
|
||||||
return instanceInfo;
|
return instanceInfo;
|
||||||
|
@ -116,6 +142,25 @@ export class ToolsService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
getAvatar(acc: AccountInfo): Promise<string> {
|
getAvatar(acc: AccountInfo): Promise<string> {
|
||||||
if (this.accountAvatar[acc.id]) {
|
if (this.accountAvatar[acc.id]) {
|
||||||
return Promise.resolve(this.accountAvatar[acc.id]);
|
return Promise.resolve(this.accountAvatar[acc.id]);
|
||||||
|
@ -247,16 +292,20 @@ export class InstanceInfo {
|
||||||
public readonly type: InstanceType,
|
public readonly type: InstanceType,
|
||||||
public readonly major: number,
|
public readonly major: number,
|
||||||
public readonly minor: number,
|
public readonly minor: number,
|
||||||
public readonly streamingApi: string) {
|
public readonly streamingApi: string,
|
||||||
|
public readonly altMajor: number,
|
||||||
|
public readonly altMinor: number) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum InstanceType {
|
export enum InstanceType {
|
||||||
Mastodon = 1,
|
Mastodon = 1,
|
||||||
Pleroma = 2,
|
Pleroma = 2, // "2.7.2 (compatible; Pleroma 2.5.1)"
|
||||||
GlitchSoc = 3,
|
GlitchSoc = 3, // "4.1.5+glitch_0801_3b49b5a"
|
||||||
Florence = 4,
|
Florence = 4,
|
||||||
Pixelfed = 5
|
Pixelfed = 5,
|
||||||
|
Takahe = 6, // "takahe/0.9.0"
|
||||||
|
Akkoma = 7, // "2.7.2 (compatible; Akkoma 3.9.2-develop)"
|
||||||
}
|
}
|
||||||
|
|
||||||
export class StatusWithCwPolicyResult {
|
export class StatusWithCwPolicyResult {
|
||||||
|
|
|
@ -66,7 +66,7 @@ export class UserNotificationService {
|
||||||
this.notificationService.notifyHttpError(err, account);
|
this.notificationService.notifyHttpError(err, account);
|
||||||
});
|
});
|
||||||
|
|
||||||
let getNotificationPromise = this.mastodonService.getNotifications(account, ['mention', 'update'], null, null, 10)
|
let getNotificationPromise = this.mastodonService.getNotifications(account, ['mention'], null, null, 10)
|
||||||
.then((notifications: Notification[]) => {
|
.then((notifications: Notification[]) => {
|
||||||
this.processMentionsAndNotifications(account, notifications, NotificationTypeEnum.UserNotification);
|
this.processMentionsAndNotifications(account, notifications, NotificationTypeEnum.UserNotification);
|
||||||
})
|
})
|
||||||
|
|
|
@ -80,7 +80,15 @@ export class GlobalSettings {
|
||||||
|
|
||||||
columnSwitchingWinAlt = false;
|
columnSwitchingWinAlt = false;
|
||||||
|
|
||||||
accountSettings: AccountSettings[] = [];
|
accountSettings: AccountSettings[] = [];
|
||||||
|
|
||||||
|
configuredLanguages: ILanguage[] = [];
|
||||||
|
selectedLanguage: ILanguage;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ILanguage {
|
||||||
|
iso639: string;
|
||||||
|
name: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SettingsStateModel {
|
export interface SettingsStateModel {
|
||||||
|
@ -171,6 +179,8 @@ export class SettingsState {
|
||||||
newSettings.autoFollowOnListEnabled = oldSettings.autoFollowOnListEnabled;
|
newSettings.autoFollowOnListEnabled = oldSettings.autoFollowOnListEnabled;
|
||||||
newSettings.twitterBridgeEnabled = oldSettings.twitterBridgeEnabled;
|
newSettings.twitterBridgeEnabled = oldSettings.twitterBridgeEnabled;
|
||||||
newSettings.twitterBridgeInstance = oldSettings.twitterBridgeInstance;
|
newSettings.twitterBridgeInstance = oldSettings.twitterBridgeInstance;
|
||||||
|
newSettings.configuredLanguages = oldSettings.configuredLanguages;
|
||||||
|
newSettings.selectedLanguage = oldSettings.selectedLanguage;
|
||||||
|
|
||||||
return newSettings;
|
return newSettings;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,27 @@
|
||||||
@import "variables";
|
@import "variables";
|
||||||
|
|
||||||
::ng-deep .ngx-contextmenu {
|
::ng-deep .ngx-contextmenu {
|
||||||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);
|
$shadow: 0.4;
|
||||||
-moz-box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);
|
box-shadow: 0 0 10px rgba(0, 0, 0, $shadow);
|
||||||
-webkit-box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);
|
-moz-box-shadow: 0 0 10px rgba(0, 0, 0, $shadow);
|
||||||
-o-box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);
|
-webkit-box-shadow: 0 0 10px rgba(0, 0, 0, $shadow);
|
||||||
|
-o-box-shadow: 0 0 10px rgba(0, 0, 0, $shadow);
|
||||||
|
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
border-radius: 7px;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
& .dropdown-menu {
|
& .dropdown-menu {
|
||||||
//border: solid 1px $context-menu-border-color;
|
//border: solid 1px $context-menu-border-color;
|
||||||
border: none;
|
border: none;
|
||||||
background-color: $context-menu-background;
|
background-color: $context-menu-background;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
border-radius: 0px;
|
border-radius: 0px;
|
||||||
|
|
||||||
|
border-radius: 7px;
|
||||||
|
overflow: hidden;
|
||||||
// padding: 2px 0;
|
// padding: 2px 0;
|
||||||
// border-radius: 2px;
|
// border-radius: 2px;
|
||||||
//border: solid 2px $context-menu-border-color;
|
//border: solid 2px $context-menu-border-color;
|
||||||
|
@ -44,6 +54,6 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
& .divider {
|
& .divider {
|
||||||
border-top: solid 2px $context-menu-border-color;
|
border-top: solid 1px $context-menu-border-color;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -21,6 +21,7 @@ $status-primary-color: #fff;
|
||||||
$status-secondary-color: #4e5572;
|
$status-secondary-color: #4e5572;
|
||||||
$status-links-color: #d9e1e8;
|
$status-links-color: #d9e1e8;
|
||||||
$boost-color : #5098eb;
|
$boost-color : #5098eb;
|
||||||
|
$update-color : #95e470;
|
||||||
$favorite-color: #ffc16f;
|
$favorite-color: #ffc16f;
|
||||||
$bookmarked-color: #ff5050;
|
$bookmarked-color: #ff5050;
|
||||||
|
|
||||||
|
@ -52,9 +53,12 @@ $column-background: #0f111a;
|
||||||
$card-border-color: #2b344d;
|
$card-border-color: #2b344d;
|
||||||
|
|
||||||
$context-menu-background: #d9e1e8;
|
$context-menu-background: #d9e1e8;
|
||||||
|
$context-menu-background: #ffffff;
|
||||||
$context-menu-background-hover: #a9c9e6;
|
$context-menu-background-hover: #a9c9e6;
|
||||||
|
$context-menu-background-hover: #d7dfeb;
|
||||||
$context-menu-font-color: #000000;
|
$context-menu-font-color: #000000;
|
||||||
$context-menu-border-color: #c0cdd9;
|
$context-menu-border-color: #c0cdd9;
|
||||||
|
$context-menu-border-color: #cbd3df;
|
||||||
|
|
||||||
$direct-message-background: #090a0f;
|
$direct-message-background: #090a0f;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue