diff --git a/package.json b/package.json
index 5405b6f7..1756e0d7 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "sengi",
- "version": "0.27.1",
+ "version": "0.28.0",
"license": "AGPL-3.0-or-later",
"main": "main-electron.js",
"description": "A multi-account desktop client for Mastodon and Pleroma",
diff --git a/src/app/app.component.html b/src/app/app.component.html
index 9a1e319a..fd202f74 100644
--- a/src/app/app.component.html
+++ b/src/app/app.component.html
@@ -10,9 +10,9 @@
-
+
-
A new version is available!
reload
+
{{restartNotificationLabel}}
reload
diff --git a/src/app/app.component.scss b/src/app/app.component.scss
index c13b810d..aef18c8b 100644
--- a/src/app/app.component.scss
+++ b/src/app/app.component.scss
@@ -104,17 +104,20 @@ app-streams-selection-footer {
transition-timing-function: ease-in;
position: absolute;
- height: 70px;
+ height: 50px;
left: 0;
right: 0;
- bottom: -80px;
+ bottom: 0;
+ //bottom: -80px;
+ opacity: 0;
z-index: 999999999;
&__activated {
// opacity: 1;
transition: all .25s;
transition-timing-function: ease-out;
- bottom: 0px;
+ opacity: 1;
+ height: 70px;
}
&__display {
diff --git a/src/app/app.component.ts b/src/app/app.component.ts
index e0a03b45..dd3e8bad 100644
--- a/src/app/app.component.ts
+++ b/src/app/app.component.ts
@@ -32,7 +32,10 @@ export class AppComponent implements OnInit, OnDestroy {
floatingColumnActive: boolean;
tutorialActive: boolean;
openedMediaEvent: OpenMediaEvent
- updateAvailable: boolean;
+
+ restartNotificationLabel: string;
+ restartNotificationAvailable: boolean;
+ showRestartNotification: boolean;
private authStorageKey: string = 'tempAuth';
@@ -40,9 +43,9 @@ export class AppComponent implements OnInit, OnDestroy {
private openMediaSub: Subscription;
private streamSub: Subscription;
private dragoverSub: Subscription;
- private updateAvailableSub: Subscription;
private paramsSub: Subscription;
-
+ private restartNotificationSub: Subscription;
+
@Select(state => state.streamsstatemodel.streams) streamElements$: Observable
;
constructor(
@@ -52,14 +55,17 @@ export class AppComponent implements OnInit, OnDestroy {
private readonly mastodonService: MastodonWrapperService,
private readonly authService: AuthService,
private readonly activatedRoute: ActivatedRoute,
- private readonly serviceWorkerService: ServiceWorkerService,
+ private readonly serviceWorkerService: ServiceWorkerService, // Ensure update checks
private readonly toolsService: ToolsService,
private readonly mediaService: MediaService,
private readonly navigationService: NavigationService) {
}
ngOnInit(): void {
- this.paramsSub = this.activatedRoute.queryParams.subscribe(params => {
+ // disable tutorial for future update
+ localStorage.setItem('tutorial', JSON.stringify(true));
+
+ this.paramsSub = this.activatedRoute.queryParams.subscribe(params => {
const code = params['code'];
if (!code) {
return;
@@ -76,10 +82,10 @@ export class AppComponent implements OnInit, OnDestroy {
let usedTokenData: TokenData;
this.authService.getToken(appDataWrapper.instance, appInfo.app.client_id, appInfo.app.client_secret, code, appInfo.app.redirect_uri)
.then((tokenData: TokenData) => {
-
- if(tokenData.refresh_token && !tokenData.created_at){
+
+ if (tokenData.refresh_token && !tokenData.created_at) {
const nowEpoch = Date.now() / 1000 | 0;
- tokenData.created_at = nowEpoch;
+ tokenData.created_at = nowEpoch;
}
usedTokenData = tokenData;
@@ -87,17 +93,17 @@ export class AppComponent implements OnInit, OnDestroy {
return this.mastodonService.retrieveAccountDetails({ 'instance': appDataWrapper.instance, 'id': '', 'username': '', 'order': 0, 'isSelected': true, 'token': tokenData });
})
.then((account: Account) => {
- var username = account.username.toLowerCase();
+ var username = account.username.toLowerCase();
var instance = appDataWrapper.instance.toLowerCase();
- if(this.isAccountAlreadyPresent(username, instance)){
+ if (this.isAccountAlreadyPresent(username, instance)) {
this.notificationService.notify(null, null, `Account @${username}@${instance} is already registered`, true);
this.router.navigate(['/']);
return;
}
const accountInfo = new AccountInfo();
- accountInfo.username = username;
+ accountInfo.username = username;
accountInfo.instance = instance;
accountInfo.token = usedTokenData;
@@ -113,10 +119,6 @@ export class AppComponent implements OnInit, OnDestroy {
});
});
- this.updateAvailableSub = this.serviceWorkerService.newAppVersionIsAvailable.subscribe((updateAvailable) => {
- this.updateAvailable = updateAvailable;
- });
-
this.streamSub = this.streamElements$.subscribe((streams: StreamElement[]) => {
if (streams && streams.length === 0) {
this.tutorialActive = true;
@@ -147,7 +149,13 @@ export class AppComponent implements OnInit, OnDestroy {
)
.subscribe(() => {
this.drag = false;
- })
+ });
+
+ this.restartNotificationSub = this.notificationService.restartNotificationStream.subscribe((label: string) => {
+ if (label) {
+ this.displayRestartNotification(label);
+ }
+ });
}
ngOnDestroy(): void {
@@ -155,8 +163,8 @@ export class AppComponent implements OnInit, OnDestroy {
this.columnEditorSub.unsubscribe();
this.openMediaSub.unsubscribe();
this.dragoverSub.unsubscribe();
- this.updateAvailableSub.unsubscribe();
this.paramsSub.unsubscribe();
+ this.restartNotificationSub.unsubscribe();
}
closeMedia() {
@@ -195,19 +203,34 @@ export class AppComponent implements OnInit, OnDestroy {
}
loadNewVersion(): boolean {
- this.serviceWorkerService.loadNewAppVersion();
+ document.location.reload();
+ // this.serviceWorkerService.loadNewAppVersion();
return false;
}
- closeAutoUpdate(): boolean {
- this.updateAvailable = false;
+ displayRestartNotification(label: string): boolean {
+ this.restartNotificationLabel = label;
+ this.showRestartNotification = true;
+ setTimeout(() => {
+ this.restartNotificationAvailable = true;
+ }, 200);
+
return false;
}
- private isAccountAlreadyPresent(username: string, instance: string): boolean{
+ closeRestartNotification(): boolean {
+ this.restartNotificationAvailable = false;
+ setTimeout(() => {
+ this.showRestartNotification = false;
+ }, 250);
+
+ return false;
+ }
+
+ private isAccountAlreadyPresent(username: string, instance: string): boolean {
const accounts = this.store.snapshot().registeredaccounts.accounts;
for (let acc of accounts) {
- if(acc.instance === instance && acc.username == username){
+ if (acc.instance === instance && acc.username == username) {
return true;
}
}
diff --git a/src/app/components/create-status/create-status.component.html b/src/app/components/create-status/create-status.component.html
index df0480d4..d7f3538e 100644
--- a/src/app/components/create-status/create-status.component.html
+++ b/src/app/components/create-status/create-status.component.html
@@ -8,7 +8,7 @@
diff --git a/src/app/components/create-status/create-status.component.ts b/src/app/components/create-status/create-status.component.ts
index a926199b..ec26f747 100644
--- a/src/app/components/create-status/create-status.component.ts
+++ b/src/app/components/create-status/create-status.component.ts
@@ -735,7 +735,11 @@ export class CreateStatusComponent implements OnInit, OnDestroy {
if (isVisible) {
setTimeout(() => {
- this.footerElement.nativeElement.scrollIntoViewIfNeeded({ behavior: 'instant', block: 'end', inline: 'start' });
+ try{
+ this.footerElement.nativeElement.scrollIntoViewIfNeeded({ behavior: 'instant', block: 'end', inline: 'start' });
+ }catch(err) {
+ this.footerElement.nativeElement.scrollIntoView({ behavior: 'instant', block: 'end', inline: 'start' });
+ }
}, 0);
}
}
diff --git a/src/app/components/floating-column/manage-account/bookmarks/bookmarks.component.ts b/src/app/components/floating-column/manage-account/bookmarks/bookmarks.component.ts
index 42e3010d..0b148cb7 100644
--- a/src/app/components/floating-column/manage-account/bookmarks/bookmarks.component.ts
+++ b/src/app/components/floating-column/manage-account/bookmarks/bookmarks.component.ts
@@ -127,4 +127,15 @@ export class BookmarksComponent implements OnInit {
browseThread(openThreadEvent: OpenThreadEvent): void {
this.browseThreadEvent.next(openThreadEvent);
}
+
+ applyGoToTop(): boolean {
+ const stream = this.statustream.nativeElement as HTMLElement;
+ setTimeout(() => {
+ stream.scrollTo({
+ top: 0,
+ behavior: 'smooth'
+ });
+ }, 0);
+ return false;
+ }
}
diff --git a/src/app/components/floating-column/manage-account/direct-messages/direct-messages.component.ts b/src/app/components/floating-column/manage-account/direct-messages/direct-messages.component.ts
index da6d00f2..c03de70f 100644
--- a/src/app/components/floating-column/manage-account/direct-messages/direct-messages.component.ts
+++ b/src/app/components/floating-column/manage-account/direct-messages/direct-messages.component.ts
@@ -121,6 +121,17 @@ export class DirectMessagesComponent implements OnInit {
browseThread(openThreadEvent: OpenThreadEvent): void {
this.browseThreadEvent.next(openThreadEvent);
}
+
+ applyGoToTop(): boolean {
+ const stream = this.statustream.nativeElement as HTMLElement;
+ setTimeout(() => {
+ stream.scrollTo({
+ top: 0,
+ behavior: 'smooth'
+ });
+ }, 0);
+ return false;
+ }
}
class ConversationWrapper {
diff --git a/src/app/components/floating-column/manage-account/favorites/favorites.component.ts b/src/app/components/floating-column/manage-account/favorites/favorites.component.ts
index 6f5dfd83..89e8e4af 100644
--- a/src/app/components/floating-column/manage-account/favorites/favorites.component.ts
+++ b/src/app/components/floating-column/manage-account/favorites/favorites.component.ts
@@ -128,4 +128,15 @@ export class FavoritesComponent implements OnInit {
browseThread(openThreadEvent: OpenThreadEvent): void {
this.browseThreadEvent.next(openThreadEvent);
}
+
+ applyGoToTop(): boolean {
+ const stream = this.statustream.nativeElement as HTMLElement;
+ setTimeout(() => {
+ stream.scrollTo({
+ top: 0,
+ behavior: 'smooth'
+ });
+ }, 0);
+ return false;
+ }
}
diff --git a/src/app/components/floating-column/manage-account/manage-account.component.html b/src/app/components/floating-column/manage-account/manage-account.component.html
index 4ad23889..f1916c76 100644
--- a/src/app/components/floating-column/manage-account/manage-account.component.html
+++ b/src/app/components/floating-column/manage-account/manage-account.component.html
@@ -35,20 +35,20 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/src/app/components/floating-column/manage-account/manage-account.component.ts b/src/app/components/floating-column/manage-account/manage-account.component.ts
index c6fdfe69..1f314a68 100644
--- a/src/app/components/floating-column/manage-account/manage-account.component.ts
+++ b/src/app/components/floating-column/manage-account/manage-account.component.ts
@@ -1,4 +1,4 @@
-import { Component, OnInit, OnDestroy, Input, Output, EventEmitter } from '@angular/core';
+import { Component, OnInit, OnDestroy, Input, Output, EventEmitter, ViewChild } from '@angular/core';
import { faAt, faUserPlus } from "@fortawesome/free-solid-svg-icons";
import { faBell, faEnvelope, faUser, faStar, faBookmark } from "@fortawesome/free-regular-svg-icons";
import { Subscription } from 'rxjs';
@@ -10,6 +10,11 @@ import { MastodonWrapperService } from '../../../services/mastodon-wrapper.servi
import { Account } from "../../../services/models/mastodon.interfaces";
import { NotificationService } from '../../../services/notification.service';
import { AccountInfo } from '../../../states/accounts.state';
+import { BookmarksComponent } from './bookmarks/bookmarks.component';
+import { NotificationsComponent } from './notifications/notifications.component';
+import { MentionsComponent } from './mentions/mentions.component';
+import { DirectMessagesComponent } from './direct-messages/direct-messages.component';
+import { FavoritesComponent } from './favorites/favorites.component';
@Component({
@@ -122,8 +127,35 @@ export class ManageAccountComponent implements OnInit, OnDestroy {
}
}
- loadSubPanel(subpanel: 'account' | 'notifications' | 'mentions' | 'dm' | 'favorites'): boolean {
+ @ViewChild('bookmarks') bookmarksComp:BookmarksComponent;
+ @ViewChild('notifications') notificationsComp:NotificationsComponent;
+ @ViewChild('mentions') mentionsComp:MentionsComponent;
+ @ViewChild('dm') dmComp:DirectMessagesComponent;
+ @ViewChild('favorites') favoritesComp:FavoritesComponent;
+
+ loadSubPanel(subpanel: 'account' | 'notifications' | 'mentions' | 'dm' | 'favorites' | 'bookmarks'): boolean {
+ if(this.subPanel === subpanel){
+ switch(subpanel){
+ case 'bookmarks':
+ this.bookmarksComp.applyGoToTop();
+ break;
+ case 'notifications':
+ this.notificationsComp.applyGoToTop();
+ break;
+ case 'mentions':
+ this.mentionsComp.applyGoToTop();
+ break;
+ case 'dm':
+ this.dmComp.applyGoToTop();
+ break;
+ case 'favorites':
+ this.favoritesComp.applyGoToTop();
+ break;
+ }
+ }
+
this.subPanel = subpanel;
+
return false;
}
diff --git a/src/app/components/floating-column/manage-account/mentions/mentions.component.ts b/src/app/components/floating-column/manage-account/mentions/mentions.component.ts
index b90a56a2..cee550d4 100644
--- a/src/app/components/floating-column/manage-account/mentions/mentions.component.ts
+++ b/src/app/components/floating-column/manage-account/mentions/mentions.component.ts
@@ -146,4 +146,15 @@ export class MentionsComponent implements OnInit, OnDestroy {
browseThread(openThreadEvent: OpenThreadEvent): void {
this.browseThreadEvent.next(openThreadEvent);
}
+
+ applyGoToTop(): boolean {
+ const stream = this.statustream.nativeElement as HTMLElement;
+ setTimeout(() => {
+ stream.scrollTo({
+ top: 0,
+ behavior: 'smooth'
+ });
+ }, 0);
+ return false;
+ }
}
diff --git a/src/app/components/floating-column/manage-account/my-account/my-account.component.html b/src/app/components/floating-column/manage-account/my-account/my-account.component.html
index b18a8fea..adcb39fc 100644
--- a/src/app/components/floating-column/manage-account/my-account/my-account.component.html
+++ b/src/app/components/floating-column/manage-account/my-account/my-account.component.html
@@ -1,11 +1,16 @@
add timeline:
-
- {{ stream.name }}
-
-
+
+
manage list:
@@ -26,11 +31,12 @@
*ngIf="list.confirmDeletion">
-
+
+
+
- {{ list.name }}
-
+ title="{{ list.isAdded ? '' : 'add list'}}" [class.my-account__list--disabled]="list.isAdded">
+ {{ list.name }}
@@ -44,7 +50,9 @@
advanced settings:
disable avatar notifications
+ (change)="onDisableAvatarNotificationChanged()" type="checkbox" name="avatarNotification"
+ value="avatarNotification" id="avatarNotification">
disable avatar notifications
custom char
diff --git a/src/app/components/floating-column/manage-account/my-account/my-account.component.scss b/src/app/components/floating-column/manage-account/my-account/my-account.component.scss
index 8574e7c2..29eef8d2 100644
--- a/src/app/components/floating-column/manage-account/my-account/my-account.component.scss
+++ b/src/app/components/floating-column/manage-account/my-account/my-account.component.scss
@@ -1,6 +1,10 @@
@import "variables";
@import "commons";
+
+$list-width: 60px;
+$button-width: $list-width/2;
+
.my-account {
transition: all .2s;
@@ -50,10 +54,19 @@
float: right;
}
+ &--remove {
+ position: relative;
+ top: 1px;
+ right: 1px;
+ }
+
&--disabled {
cursor: default;
background-color: darken($color-primary, 4);
+ width: calc(100% - #{$button-width} - 1px);
+ //outline: 1px solid greenyellow;
+
&:hover {
background-color: darken($color-primary, 4);
}
@@ -67,9 +80,27 @@
}
&__list {
- $list-width: 60px;
+
width: calc(100% - #{$list-width} - 2px);
+ &--remove {
+ position: relative;
+ top: 0px;
+ right: 1px;
+ }
+
+ &--disabled {
+ cursor: default;
+ background-color: darken($color-primary, 4);
+
+ width: calc(100% - #{$button-width} * 3 - 3px);
+ //outline: 1px solid greenyellow;
+
+ &:hover {
+ background-color: darken($color-primary, 4);
+ }
+ }
+
&--button {
margin-left: 1px;
width: calc(#{$list-width}/2);
diff --git a/src/app/components/floating-column/manage-account/my-account/my-account.component.ts b/src/app/components/floating-column/manage-account/my-account/my-account.component.ts
index 43a54251..c888fb90 100644
--- a/src/app/components/floating-column/manage-account/my-account/my-account.component.ts
+++ b/src/app/components/floating-column/manage-account/my-account/my-account.component.ts
@@ -104,18 +104,22 @@ export class MyAccountComponent implements OnInit, OnDestroy {
}
});
- this.availableLists.length = 0;
+ // this.availableLists.length = 0;
this.mastodonService.getLists(account.info)
.then((streams: StreamElement[]) => {
- this.availableLists.length = 0;
+ // this.availableLists.length = 0;
for (let stream of streams) {
- let wrappedStream = new StreamWrapper(stream);
+ let wrappedStream = this.availableLists.find(x => x.id === stream.id);
+ if(!wrappedStream){
+ wrappedStream = new StreamWrapper(stream);
+ this.availableLists.push(wrappedStream);
+ }
+
if(loadedStreams.find(x => x.id == stream.id)){
wrappedStream.isAdded = true;
} else {
wrappedStream.isAdded = false;
- }
- this.availableLists.push(wrappedStream);
+ }
}
})
.catch(err => {
@@ -133,6 +137,16 @@ export class MyAccountComponent implements OnInit, OnDestroy {
return false;
}
+ removeStream(stream: StreamWrapper): boolean {
+ if (stream && stream.isAdded) {
+ this.store.dispatch([new RemoveStream(stream.id)]).toPromise()
+ .then(() => {
+ stream.isAdded = false;
+ });
+ }
+ return false;
+ }
+
removeAccount(): boolean {
const accountId = this.account.info.id;
this.store.dispatch([new RemoveAllStreams(accountId), new RemoveAccount(accountId)]);
diff --git a/src/app/components/floating-column/manage-account/notifications/notifications.component.ts b/src/app/components/floating-column/manage-account/notifications/notifications.component.ts
index 35f4fe12..0753858b 100644
--- a/src/app/components/floating-column/manage-account/notifications/notifications.component.ts
+++ b/src/app/components/floating-column/manage-account/notifications/notifications.component.ts
@@ -135,6 +135,17 @@ export class NotificationsComponent implements OnInit, OnDestroy {
browseThread(openThreadEvent: OpenThreadEvent): void {
this.browseThreadEvent.next(openThreadEvent);
}
+
+ applyGoToTop(): boolean {
+ const stream = this.statustream.nativeElement as HTMLElement;
+ setTimeout(() => {
+ stream.scrollTo({
+ top: 0,
+ behavior: 'smooth'
+ });
+ }, 0);
+ return false;
+ }
}
export class NotificationWrapper {
diff --git a/src/app/components/floating-column/settings/settings.component.html b/src/app/components/floating-column/settings/settings.component.html
index 039ab411..8977c6a0 100644
--- a/src/app/components/floating-column/settings/settings.component.html
+++ b/src/app/components/floating-column/settings/settings.component.html
@@ -48,9 +48,6 @@
type="radio" name="colmun-win" value="colmun-win" id="colmun-win">
Win + Alt + Left | Win + Alt + Right
-
- this settings needs a reload to be effective.
Content-Warning Policies
@@ -90,9 +87,6 @@
-
-
this settings needs a reload to be effective.
Timelines
@@ -123,9 +117,6 @@
Title
- this settings needs a reload to be effective.
-
loading behavior:
Slow mode (manual loading)
-
- this settings needs a reload to be effective.
Other
diff --git a/src/app/components/floating-column/settings/settings.component.ts b/src/app/components/floating-column/settings/settings.component.ts
index b8aacd77..190a0c56 100644
--- a/src/app/components/floating-column/settings/settings.component.ts
+++ b/src/app/components/floating-column/settings/settings.component.ts
@@ -7,6 +7,7 @@ import { ToolsService } from '../../../services/tools.service';
import { UserNotificationService, NotificationSoundDefinition } from '../../../services/user-notification.service';
import { ServiceWorkerService } from '../../../services/service-worker.service';
import { ContentWarningPolicy, ContentWarningPolicyEnum, TimeLineModeEnum, TimeLineHeaderEnum } from '../../../states/settings.state';
+import { NotificationService } from '../../../services/notification.service';
@Component({
selector: 'app-settings',
@@ -27,16 +28,9 @@ export class SettingsComponent implements OnInit {
version: string;
columnShortcutEnabled: ColumnShortcut = ColumnShortcut.Ctrl;
- columnShortcutChanged = false;
-
timeLineHeader: TimeLineHeaderEnum = TimeLineHeaderEnum.Title_DomainName;
- timeLineHeaderChanged = false;
-
timeLineMode: TimeLineModeEnum = TimeLineModeEnum.OnTop;
- timeLineModeChanged = false;
-
contentWarningPolicy: ContentWarningPolicyEnum = ContentWarningPolicyEnum.None;
- contentWarningPolicyChanged = false;
private addCwOnContent: string;
set setAddCwOnContent(value: string) {
@@ -69,6 +63,7 @@ export class SettingsComponent implements OnInit {
private formBuilder: FormBuilder,
private serviceWorkersService: ServiceWorkerService,
private readonly toolsService: ToolsService,
+ private readonly notificationService: NotificationService,
private readonly userNotificationsService: UserNotificationService) { }
ngOnInit() {
@@ -104,7 +99,7 @@ export class SettingsComponent implements OnInit {
onShortcutChange(id: ColumnShortcut) {
this.columnShortcutEnabled = id;
- this.columnShortcutChanged = true;
+ this.notifyRestartNeeded();
let settings = this.toolsService.getSettings();
settings.columnSwitchingWinAlt = id === ColumnShortcut.Win;
@@ -113,7 +108,7 @@ export class SettingsComponent implements OnInit {
onTimeLineHeaderChange(id: TimeLineHeaderEnum){
this.timeLineHeader = id;
- this.timeLineHeaderChanged = true;
+ this.notifyRestartNeeded();
let settings = this.toolsService.getSettings();
settings.timelineHeader = id;
@@ -122,7 +117,7 @@ export class SettingsComponent implements OnInit {
onTimeLineModeChange(id: TimeLineModeEnum){
this.timeLineMode = id;
- this.timeLineModeChanged = true;
+ this.notifyRestartNeeded();
let settings = this.toolsService.getSettings();
settings.timelineMode = id;
@@ -131,13 +126,13 @@ export class SettingsComponent implements OnInit {
onCwPolicyChange(id: ContentWarningPolicyEnum) {
this.contentWarningPolicy = id;
- this.contentWarningPolicyChanged = true;
+ this.notifyRestartNeeded();
this.setCwPolicy(id);
}
private setCwPolicy(id: ContentWarningPolicyEnum = null, addCw: string = null, removeCw: string = null, hide: string = null){
- this.contentWarningPolicyChanged = true;
+ this.notifyRestartNeeded();
let settings = this.toolsService.getSettings();
let cwPolicySettings = new ContentWarningPolicy();
@@ -172,10 +167,10 @@ export class SettingsComponent implements OnInit {
return data.split(';').map(x => x.trim().toLowerCase()).filter((value, index, self) => self.indexOf(value) === index).filter(y => y !== '');
}
- reload(): boolean {
- window.location.reload();
- return false;
- }
+ // reload(): boolean {
+ // window.location.reload();
+ // return false;
+ // }
onChange(soundId: string) {
this.notificationSoundId = soundId;
@@ -196,18 +191,21 @@ export class SettingsComponent implements OnInit {
}
onDisableAutofocusChanged() {
+ this.notifyRestartNeeded();
let settings = this.toolsService.getSettings();
settings.disableAutofocus = this.disableAutofocusEnabled;
this.toolsService.saveSettings(settings);
}
onDisableRemoteStatusFetchingChanged() {
+ this.notifyRestartNeeded();
let settings = this.toolsService.getSettings();
settings.disableRemoteStatusFetching = this.disableRemoteStatusFetchingEnabled;
this.toolsService.saveSettings(settings);
}
onDisableAvatarNotificationsChanged() {
+ this.notifyRestartNeeded();
let settings = this.toolsService.getSettings();
settings.disableAvatarNotifications = this.disableAvatarNotificationsEnabled;
this.toolsService.saveSettings(settings);
@@ -248,6 +246,10 @@ export class SettingsComponent implements OnInit {
});
return false;
}
+
+ notifyRestartNeeded(){
+ this.notificationService.notifyRestartNotification('Reload to apply changes');
+ }
}
enum ColumnShortcut {
diff --git a/src/app/components/stream/status/databinded-text/databinded-text.component.spec.ts b/src/app/components/stream/status/databinded-text/databinded-text.component.spec.ts
index 20fe2a3b..f1f43893 100644
--- a/src/app/components/stream/status/databinded-text/databinded-text.component.spec.ts
+++ b/src/app/components/stream/status/databinded-text/databinded-text.component.spec.ts
@@ -31,6 +31,12 @@ describe('DatabindedTextComponent', () => {
expect(component.processedText).toContain(sample);
});
+ it('should parse href text', () => {
+ const sample = 'href
';
+ component.text = sample;
+ expect(component.processedText).toBe(sample);
+ });
+
it('should parse hashtag', () => {
const hashtag = 'programmers';
const url = 'https://test.social/tags/programmers';
diff --git a/src/app/components/stream/status/databinded-text/databinded-text.component.ts b/src/app/components/stream/status/databinded-text/databinded-text.component.ts
index e8c3f6e1..efdc1160 100644
--- a/src/app/components/stream/status/databinded-text/databinded-text.component.ts
+++ b/src/app/components/stream/status/databinded-text/databinded-text.component.ts
@@ -28,7 +28,7 @@ export class DatabindedTextComponent implements OnInit {
@Input('text')
set text(value: string) {
- //console.warn(value);
+ // console.warn(value);
let parser = new DOMParser();
var dom = parser.parseFromString(value, 'text/html')
@@ -133,6 +133,11 @@ export class DatabindedTextComponent implements OnInit {
}
private processLink(section: string) {
+ if(!section.includes('')){
+ this.processedText += section;
+ return;
+ }
+
let extractedLinkAndNext = section.split('')
let extractedUrl = extractedLinkAndNext[0].split('"')[1];
diff --git a/src/app/components/stream/user-profile/user-profile.component.ts b/src/app/components/stream/user-profile/user-profile.component.ts
index c9c1b586..f89809ce 100644
--- a/src/app/components/stream/user-profile/user-profile.component.ts
+++ b/src/app/components/stream/user-profile/user-profile.component.ts
@@ -266,6 +266,7 @@ export class UserProfileComponent implements OnInit {
refresh(): any {
this.showFloatingHeader = false;
+ this.showFloatingStatusMenu = false;
this.load(this.lastAccountName);
}
diff --git a/src/app/pipes/account-emoji.pipe.ts b/src/app/pipes/account-emoji.pipe.ts
index 31480535..50cb6a96 100644
--- a/src/app/pipes/account-emoji.pipe.ts
+++ b/src/app/pipes/account-emoji.pipe.ts
@@ -4,19 +4,22 @@ import { EmojiConverter, EmojiTypeEnum } from '../tools/emoji.tools';
import { Account } from '../services/models/mastodon.interfaces';
@Pipe({
- name: "accountEmoji"
+ name: "accountEmoji"
})
export class AccountEmojiPipe implements PipeTransform {
- private emojiConverter = new EmojiConverter();
+ private emojiConverter = new EmojiConverter();
- transform(value: Account, text?: string): any {
+ transform(value: Account, text?: string): any {
+ try {
+ let textToTransform = text;
+ if (!text) {
+ if (value.display_name) textToTransform = value.display_name;
+ else textToTransform = value.acct.split('@')[0];
+ }
- let textToTransform = text;
- if(!text){
- if(value.display_name) textToTransform = value.display_name;
- else textToTransform = value.acct.split('@')[0];
- }
-
- return this.emojiConverter.applyEmojis(value.emojis, textToTransform, EmojiTypeEnum.small)
- }
+ return this.emojiConverter.applyEmojis(value.emojis, textToTransform, EmojiTypeEnum.small);
+ } catch (err){
+ return '';
+ }
+ }
}
diff --git a/src/app/services/mastodon-wrapper.service.ts b/src/app/services/mastodon-wrapper.service.ts
index 694a73b6..db2cad4e 100644
--- a/src/app/services/mastodon-wrapper.service.ts
+++ b/src/app/services/mastodon-wrapper.service.ts
@@ -12,6 +12,7 @@ import { AppInfo, RegisteredAppsStateModel } from '../states/registered-apps.sta
providedIn: 'root'
})
export class MastodonWrapperService {
+ private refreshingToken: { [id: string]: Promise } = {};
constructor(
private readonly store: Store,
@@ -19,6 +20,10 @@ export class MastodonWrapperService {
private readonly mastodonService: MastodonService) { }
refreshAccountIfNeeded(accountInfo: AccountInfo): Promise {
+ if(this.refreshingToken[accountInfo.id]){
+ return this.refreshingToken[accountInfo.id];
+ }
+
let isExpired = false;
let storedAccountInfo = this.getStoreAccountInfo(accountInfo.id);
@@ -47,11 +52,8 @@ export class MastodonWrapperService {
}
if (storedAccountInfo.token.refresh_token && isExpired) {
- console.log('>>> MARTY!! ------------');
- console.log('>>> RENEW TOKEN FFS ----');
-
const app = this.getAllSavedApps().find(x => x.instance === storedAccountInfo.instance);
- return this.authService.refreshToken(storedAccountInfo.instance, app.app.client_id, app.app.client_secret, storedAccountInfo.token.refresh_token)
+ let p = this.authService.refreshToken(storedAccountInfo.instance, app.app.client_id, app.app.client_secret, storedAccountInfo.token.refresh_token)
.then((tokenData: TokenData) => {
if (tokenData.refresh_token && !tokenData.created_at) {
const nowEpoch = Date.now() / 1000 | 0;
@@ -66,6 +68,13 @@ export class MastodonWrapperService {
.catch(err => {
return Promise.resolve(storedAccountInfo);
});
+
+ p.then(() => {
+ this.refreshingToken[accountInfo.id] = null;
+ });
+
+ this.refreshingToken[accountInfo.id] = p;
+ return p;
} else {
return Promise.resolve(storedAccountInfo);
}
diff --git a/src/app/services/notification.service.ts b/src/app/services/notification.service.ts
index a8406bce..80696dad 100644
--- a/src/app/services/notification.service.ts
+++ b/src/app/services/notification.service.ts
@@ -9,6 +9,7 @@ import { ToolsService } from './tools.service';
@Injectable()
export class NotificationService {
+ public restartNotificationStream = new Subject();
public notifactionStream = new Subject();
public newRespondPostedStream = new Subject();
public hideAccountUrlStream = new Subject();
@@ -60,6 +61,10 @@ export class NotificationService {
public deleteStatus(status: StatusWrapper) {
this.deletedStatusStream.next(status);
}
+
+ public notifyRestartNotification(label: string){
+ this.restartNotificationStream.next(label);
+ }
}
export class NotificatioData {
diff --git a/src/app/services/service-worker.service.ts b/src/app/services/service-worker.service.ts
index f5dfc761..6b477517 100644
--- a/src/app/services/service-worker.service.ts
+++ b/src/app/services/service-worker.service.ts
@@ -1,17 +1,19 @@
import { Injectable, ApplicationRef } from '@angular/core';
import { SwUpdate } from '@angular/service-worker';
-import { first } from 'rxjs/operators';
import { interval, concat, BehaviorSubject } from 'rxjs';
+import { NotificationService } from './notification.service';
+
@Injectable({
providedIn: 'root'
})
export class ServiceWorkerService {
- newAppVersionIsAvailable = new BehaviorSubject(false);
-
private isListening = false;
- constructor(appRef: ApplicationRef, private updates: SwUpdate) {
+ constructor(
+ appRef: ApplicationRef,
+ private updates: SwUpdate,
+ private notificationService: NotificationService) {
//https://angular.io/guide/service-worker-communications
@@ -19,7 +21,7 @@ export class ServiceWorkerService {
console.log('current version is', event.current);
console.log('available version is', event.available);
- this.newAppVersionIsAvailable.next(true);
+ this.notificationService.notifyRestartNotification('A new version is available!');
});
// Allow the app to stabilize first, before starting polling for updates with `interval()`.
@@ -47,6 +49,6 @@ export class ServiceWorkerService {
}
checkForUpdates(): Promise {
- return this.updates.checkForUpdate();
+ return this.updates.checkForUpdate();
}
}
\ No newline at end of file
diff --git a/src/app/services/user-notification.service.ts b/src/app/services/user-notification.service.ts
index 7c0413c5..f2545b53 100644
--- a/src/app/services/user-notification.service.ts
+++ b/src/app/services/user-notification.service.ts
@@ -25,82 +25,94 @@ export class UserNotificationService {
private soundJustPlayed = false;
private soundFileId: string;
+ private accountSub: Subscription;
+ private loadedAccounts: AccountInfo[] = [];
+
constructor(
private readonly streamingService: StreamingService,
private readonly toolsService: ToolsService,
private readonly notificationService: NotificationService,
private readonly mastodonService: MastodonWrapperService,
private readonly store: Store) {
-
+
this.fetchNotifications();
- }
+ }
private fetchNotifications() {
let accounts = this.store.snapshot().registeredaccounts.accounts;
- // let promises: Promise[] = [];
accounts.forEach((account: AccountInfo) => {
- // let sinceId = null;
- // if (this.sinceIds[account.id]) {
- // sinceId = this.sinceIds[account.id];
- // }
-
- let getMentionsPromise = this.mastodonService.getNotifications(account, ['favourite', 'follow', 'reblog', 'poll'], null, null, 10)
- .then((notifications: Notification[]) => {
- this.processMentionsAndNotifications(account, notifications, NotificationTypeEnum.UserMention);
- })
- .catch(err => {
- this.notificationService.notifyHttpError(err, account);
- });
-
- let getNotificationPromise = this.mastodonService.getNotifications(account, ['mention'], null, null, 10)
- .then((notifications: Notification[]) => {
- this.processMentionsAndNotifications(account, notifications, NotificationTypeEnum.UserNotification);
- })
- .catch(err => {
- this.notificationService.notifyHttpError(err, account);
- });
-
- Promise.all([getMentionsPromise, getNotificationPromise])
- .then(() => {
- let streamElement = new StreamElement(StreamTypeEnum.personnal, 'activity', account.id, null, null, null, account.instance);
-
- let streaming = this.streamingService.getStreaming(account, streamElement);
- streaming.statusUpdateSubjet.subscribe((notification: StatusUpdate) => {
- if (notification && notification.type === EventEnum.notification) {
- this.processNewUpdate(account, notification);
- }
- });
- })
- .catch(err => { });
+ this.loadedAccounts.push(account);
+ this.startFetchingNotifications(account);
});
+
+ this.accountSub = this.store.select(state => state.registeredaccounts.accounts)
+ .subscribe((accounts: AccountInfo[]) => {
+ accounts.forEach(a => {
+ if(!this.loadedAccounts.find(x => x.id === a.id)){
+ this.loadedAccounts.push(a);
+ this.startFetchingNotifications(a);
+ }
+ });
+ });
+ }
+
+ private startFetchingNotifications(account: AccountInfo) {
+ let getMentionsPromise = this.mastodonService.getNotifications(account, ['favourite', 'follow', 'reblog', 'poll'], null, null, 10)
+ .then((notifications: Notification[]) => {
+ this.processMentionsAndNotifications(account, notifications, NotificationTypeEnum.UserMention);
+ })
+ .catch(err => {
+ this.notificationService.notifyHttpError(err, account);
+ });
+
+ let getNotificationPromise = this.mastodonService.getNotifications(account, ['mention'], null, null, 10)
+ .then((notifications: Notification[]) => {
+ this.processMentionsAndNotifications(account, notifications, NotificationTypeEnum.UserNotification);
+ })
+ .catch(err => {
+ this.notificationService.notifyHttpError(err, account);
+ });
+
+ Promise.all([getMentionsPromise, getNotificationPromise])
+ .then(() => {
+ let streamElement = new StreamElement(StreamTypeEnum.personnal, 'activity', account.id, null, null, null, account.instance);
+
+ let streaming = this.streamingService.getStreaming(account, streamElement);
+ streaming.statusUpdateSubjet.subscribe((notification: StatusUpdate) => {
+ if (notification && notification.type === EventEnum.notification) {
+ this.processNewUpdate(account, notification);
+ }
+ });
+ })
+ .catch(err => { });
}
private playSoundNotification() {
const settings = this.toolsService.getSettings();
- if(settings.disableSounds) return;
- if(this.soundJustPlayed) return;
+ if (settings.disableSounds) return;
+ if (this.soundJustPlayed) return;
this.soundJustPlayed = true;
-
+
this.setNotificationSound();
this.sound.play();
- setTimeout(() => {
+ setTimeout(() => {
this.soundJustPlayed = false;
}, 2000);
}
private setNotificationSound() {
let settings = this.toolsService.getSettings();
- let soundId = settings.notificationSoundFileId;
-
- if(!soundId){
+ let soundId = settings.notificationSoundFileId;
+
+ if (!soundId) {
soundId = '0';
settings.notificationSoundFileId = '0';
this.toolsService.saveSettings(settings);
}
- if(this.soundFileId === soundId) return;
+ if (this.soundFileId === soundId) return;
var sound = this.getAllNotificationSounds().find(x => x.id === soundId);
this.sound = new Howl({
@@ -110,9 +122,9 @@ export class UserNotificationService {
}
private processNewUpdate(account: AccountInfo, notification: StatusUpdate) {
- if(!notification && !notification.notification) return;
+ if (!notification && !notification.notification) return;
- if(!notification.muteSound){
+ if (!notification.muteSound) {
this.playSoundNotification();
}
@@ -129,15 +141,15 @@ export class UserNotificationService {
}
let currentNotifications = this.userNotifications.value;
- let currentAccountNotifications = currentNotifications.find(x => x.account.id === account.id);
+ let currentAccountNotifications = currentNotifications.find(x => x.account.id === account.id);
if (currentAccountNotifications) {
currentAccountNotifications = this.analyseNotifications(account, currentAccountNotifications, notifications, type);
//if (currentAccountNotifications.hasNewMentions || currentAccountNotifications.hasNewNotifications) {
- currentNotifications = currentNotifications.filter(x => x.account.id !== account.id);
- currentNotifications.push(currentAccountNotifications);
- this.userNotifications.next(currentNotifications);
+ currentNotifications = currentNotifications.filter(x => x.account.id !== account.id);
+ currentNotifications.push(currentAccountNotifications);
+ this.userNotifications.next(currentNotifications);
//}
} else {
let newNotifications = new UserNotification();
@@ -230,7 +242,7 @@ export class UserNotificationService {
new NotificationSoundDefinition('0', 'assets/audio/all-eyes-on-me.mp3', 'All eyes on me'),
new NotificationSoundDefinition('1', 'assets/audio/exquisite.mp3', 'Exquisite'),
new NotificationSoundDefinition('2', 'assets/audio/appointed.mp3', 'Appointed'),
- new NotificationSoundDefinition('3', 'assets/audio/boop.mp3', 'Mastodon boop'),
+ new NotificationSoundDefinition('3', 'assets/audio/boop.mp3', 'Mastodon boop'),
];
return defs;
}
@@ -261,5 +273,5 @@ export class NotificationSoundDefinition {
constructor(
public readonly id: string,
public readonly path: string,
- public readonly name: string) {}
+ public readonly name: string) { }
}
\ No newline at end of file