Merge pull request #236 from NicolasConstant/topic_simplify-add-account

Topic simplify add account
This commit is contained in:
Nicolas Constant 2020-03-07 19:56:34 -05:00 committed by GitHub
commit c27ed4dc2d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 206 additions and 131 deletions

View File

@ -21,9 +21,9 @@
"test-nowatch": "ng test --watch=false",
"lint": "ng lint",
"e2e": "ng e2e",
"electron": "ng build --prod && electron .",
"electron": "electron .",
"electron-prod": "ng build --prod && electron .",
"electron-debug": "ng build && electron .",
"electron-test": "electron .",
"dist": "npm run build && electron-builder --publish onTagOrDraft",
"travis": "electron-builder --publish onTagOrDraft"
},

View File

@ -1,16 +1,25 @@
import { Component, OnInit, OnDestroy } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { Subscription, Observable, Subject } from 'rxjs';
import { debounceTime, map } from 'rxjs/operators';
import { Select } from '@ngxs/store';
import { Select, Store } from '@ngxs/store';
import { faTimes } from "@fortawesome/free-solid-svg-icons";
// import { ElectronService } from 'ngx-electron';
import { NavigationService, LeftPanelType, OpenLeftPanelEvent } from './services/navigation.service';
import { StreamElement } from './states/streams.state';
import { AccountInfo, AddAccount } from "./states/accounts.state";
import { OpenMediaEvent } from './models/common.model';
import { ToolsService } from './services/tools.service';
import { MediaService } from './services/media.service';
import { ServiceWorkerService } from './services/service-worker.service';
import { AuthService, CurrentAuthProcess } from './services/auth.service';
import { MastodonWrapperService } from './services/mastodon-wrapper.service';
import { TokenData, Account } from './services/models/mastodon.interfaces';
import { NotificationService } from './services/notification.service';
import { AppInfo, RegisteredAppsStateModel } from './states/registered-apps.state';
import { HttpErrorResponse } from '@angular/common/http';
@Component({
selector: 'app-root',
@ -25,15 +34,24 @@ export class AppComponent implements OnInit, OnDestroy {
openedMediaEvent: OpenMediaEvent
updateAvailable: boolean;
private authStorageKey: string = 'tempAuth';
private columnEditorSub: Subscription;
private openMediaSub: Subscription;
private streamSub: Subscription;
private dragoverSub: Subscription;
private updateAvailableSub: Subscription;
private paramsSub: Subscription;
@Select(state => state.streamsstatemodel.streams) streamElements$: Observable<StreamElement[]>;
constructor(
private readonly router: Router,
private readonly notificationService: NotificationService,
private readonly store: Store,
private readonly mastodonService: MastodonWrapperService,
private readonly authService: AuthService,
private readonly activatedRoute: ActivatedRoute,
private readonly serviceWorkerService: ServiceWorkerService,
private readonly toolsService: ToolsService,
private readonly mediaService: MediaService,
@ -41,6 +59,60 @@ export class AppComponent implements OnInit, OnDestroy {
}
ngOnInit(): void {
this.paramsSub = this.activatedRoute.queryParams.subscribe(params => {
const code = params['code'];
if (!code) {
return;
}
const appDataWrapper = <CurrentAuthProcess>JSON.parse(localStorage.getItem(this.authStorageKey));
if (!appDataWrapper) {
this.notificationService.notify('', 400, 'Something when wrong in the authentication process. Please retry.', true);
this.router.navigate(['/']);
return;
}
const appInfo = this.getAllSavedApps().filter(x => x.instance === appDataWrapper.instance)[0];
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){
const nowEpoch = Date.now() / 1000 | 0;
tokenData.created_at = nowEpoch;
}
usedTokenData = tokenData;
return this.mastodonService.retrieveAccountDetails({ 'instance': appDataWrapper.instance, 'id': '', 'username': '', 'order': 0, 'isSelected': true, 'token': tokenData });
})
.then((account: Account) => {
var username = account.username.toLowerCase();
var instance = appDataWrapper.instance.toLowerCase();
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.instance = instance;
accountInfo.token = usedTokenData;
this.store.dispatch([new AddAccount(accountInfo)])
.subscribe(() => {
localStorage.removeItem(this.authStorageKey);
this.router.navigate(['/']);
});
})
.catch((err: HttpErrorResponse) => {
this.notificationService.notifyHttpError(err, null);
this.router.navigate(['/']);
});
});
this.updateAvailableSub = this.serviceWorkerService.newAppVersionIsAvailable.subscribe((updateAvailable) => {
this.updateAvailable = updateAvailable;
});
@ -69,7 +141,6 @@ export class AppComponent implements OnInit, OnDestroy {
}
});
this.dragoverSub = this.dragoverSubject
.pipe(
debounceTime(1500)
@ -85,6 +156,7 @@ export class AppComponent implements OnInit, OnDestroy {
this.openMediaSub.unsubscribe();
this.dragoverSub.unsubscribe();
this.updateAvailableSub.unsubscribe();
this.paramsSub.unsubscribe();
}
closeMedia() {
@ -131,4 +203,19 @@ export class AppComponent implements OnInit, OnDestroy {
this.updateAvailable = false;
return false;
}
private isAccountAlreadyPresent(username: string, instance: string): boolean{
const accounts = <AccountInfo[]>this.store.snapshot().registeredaccounts.accounts;
for (let acc of accounts) {
if(acc.instance === instance && acc.username == username){
return true;
}
}
return false;
}
private getAllSavedApps(): AppInfo[] {
const snapshot = <RegisteredAppsStateModel>this.store.snapshot().registeredapps;
return snapshot.apps;
}
}

View File

@ -23,7 +23,7 @@ import { LeftSideBarComponent } from "./components/left-side-bar/left-side-bar.c
import { StreamsMainDisplayComponent } from "./pages/streams-main-display/streams-main-display.component";
import { StreamComponent } from "./components/stream/stream.component";
import { StreamsSelectionFooterComponent } from "./components/streams-selection-footer/streams-selection-footer.component";
import { RegisterNewAccountComponent } from "./pages/register-new-account/register-new-account.component";
// import { RegisterNewAccountComponent } from "./pages/register-new-account/register-new-account.component";
import { AuthService } from "./services/auth.service";
import { StreamingService } from "./services/streaming.service";
import { RegisteredAppsState } from "./states/registered-apps.state";
@ -84,10 +84,10 @@ import { environment } from '../environments/environment';
const routes: Routes = [
{ path: "", redirectTo: "home", pathMatch: "full" },
{ path: "home", component: StreamsMainDisplayComponent },
{ path: "register", component: RegisterNewAccountComponent },
{ path: "**", redirectTo: "home" }
{ path: "", component: StreamsMainDisplayComponent },
// { path: "home", component: StreamsMainDisplayComponent },
// { path: "register", component: RegisterNewAccountComponent },
{ path: "**", redirectTo: "" }
];
@NgModule({
@ -98,7 +98,7 @@ const routes: Routes = [
StreamComponent,
StreamsSelectionFooterComponent,
StatusComponent,
RegisterNewAccountComponent,
// RegisterNewAccountComponent,
AccountIconComponent,
FloatingColumnComponent,
ManageAccountComponent,

View File

@ -1,124 +1,124 @@
import { Component, OnInit, Input } from "@angular/core";
import { Store, Select } from '@ngxs/store';
import { ActivatedRoute, Router } from "@angular/router";
import { HttpErrorResponse } from "@angular/common/http";
// import { Component, OnInit, Input } from "@angular/core";
// import { Store, Select } from '@ngxs/store';
// import { ActivatedRoute, Router } from "@angular/router";
// import { HttpErrorResponse } from "@angular/common/http";
import { AuthService, CurrentAuthProcess } from "../../services/auth.service";
import { TokenData, Account } from "../../services/models/mastodon.interfaces";
import { RegisteredAppsStateModel, AppInfo } from "../../states/registered-apps.state";
import { AccountInfo, AddAccount, AccountsStateModel } from "../../states/accounts.state";
import { NotificationService } from "../../services/notification.service";
import { MastodonWrapperService } from '../../services/mastodon-wrapper.service';
// import { AuthService, CurrentAuthProcess } from "../../services/auth.service";
// import { TokenData, Account } from "../../services/models/mastodon.interfaces";
// import { RegisteredAppsStateModel, AppInfo } from "../../states/registered-apps.state";
// import { AccountInfo, AddAccount, AccountsStateModel } from "../../states/accounts.state";
// import { NotificationService } from "../../services/notification.service";
// import { MastodonWrapperService } from '../../services/mastodon-wrapper.service';
@Component({
selector: "app-register-new-account",
templateUrl: "./register-new-account.component.html",
styleUrls: ["./register-new-account.component.scss"]
})
export class RegisterNewAccountComponent implements OnInit {
@Input() mastodonFullHandle: string;
// @Component({
// selector: "app-register-new-account",
// templateUrl: "./register-new-account.component.html",
// styleUrls: ["./register-new-account.component.scss"]
// })
// export class RegisterNewAccountComponent implements OnInit {
// // @Input() mastodonFullHandle: string;
hasError: boolean;
errorMessage: string;
// hasError: boolean;
// errorMessage: string;
private authStorageKey: string = 'tempAuth';
// private authStorageKey: string = 'tempAuth';
constructor(
private readonly mastodonService: MastodonWrapperService,
private readonly notificationService: NotificationService,
private readonly authService: AuthService,
private readonly store: Store,
private readonly activatedRoute: ActivatedRoute,
private readonly router: Router) {
// constructor(
// private readonly mastodonService: MastodonWrapperService,
// private readonly notificationService: NotificationService,
// private readonly authService: AuthService,
// private readonly store: Store,
// private readonly activatedRoute: ActivatedRoute,
// private readonly router: Router) {
this.activatedRoute.queryParams.subscribe(params => {
this.hasError = false;
// this.activatedRoute.queryParams.subscribe(params => {
// this.hasError = false;
const code = params['code'];
if (!code) {
this.displayError(RegistrationErrorTypes.CodeNotFound);
return;
}
// const code = params['code'];
// if (!code) {
// this.displayError(RegistrationErrorTypes.CodeNotFound);
// return;
// }
const appDataWrapper = <CurrentAuthProcess>JSON.parse(localStorage.getItem(this.authStorageKey));
if (!appDataWrapper) {
this.displayError(RegistrationErrorTypes.AuthProcessNotFound);
return;
}
// const appDataWrapper = <CurrentAuthProcess>JSON.parse(localStorage.getItem(this.authStorageKey));
// if (!appDataWrapper) {
// this.displayError(RegistrationErrorTypes.AuthProcessNotFound);
// return;
// }
const appInfo = this.getAllSavedApps().filter(x => x.instance === appDataWrapper.instance)[0];
let usedTokenData: TokenData;
this.authService.getToken(appDataWrapper.instance, appInfo.app.client_id, appInfo.app.client_secret, code, appInfo.app.redirect_uri)
.then((tokenData: TokenData) => {
// const appInfo = this.getAllSavedApps().filter(x => x.instance === appDataWrapper.instance)[0];
// 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){
const nowEpoch = Date.now() / 1000 | 0;
tokenData.created_at = nowEpoch;
}
// if(tokenData.refresh_token && !tokenData.created_at){
// const nowEpoch = Date.now() / 1000 | 0;
// tokenData.created_at = nowEpoch;
// }
usedTokenData = tokenData;
// usedTokenData = tokenData;
return this.mastodonService.retrieveAccountDetails({ 'instance': appDataWrapper.instance, 'id': '', 'username': '', 'order': 0, 'isSelected': true, 'token': tokenData });
})
.then((account: Account) => {
var username = account.username.toLowerCase();
var instance = appDataWrapper.instance.toLowerCase();
// return this.mastodonService.retrieveAccountDetails({ 'instance': appDataWrapper.instance, 'id': '', 'username': '', 'order': 0, 'isSelected': true, 'token': tokenData });
// })
// .then((account: Account) => {
// var username = account.username.toLowerCase();
// var instance = appDataWrapper.instance.toLowerCase();
if(this.isAccountAlreadyPresent(username, instance)){
this.notificationService.notify(null, null, `Account @${username}@${instance} is already registered`, true);
this.router.navigate(['/home']);
return;
}
// if(this.isAccountAlreadyPresent(username, instance)){
// this.notificationService.notify(null, null, `Account @${username}@${instance} is already registered`, true);
// this.router.navigate(['/home']);
// return;
// }
const accountInfo = new AccountInfo();
accountInfo.username = username;
accountInfo.instance = instance;
accountInfo.token = usedTokenData;
// const accountInfo = new AccountInfo();
// accountInfo.username = username;
// accountInfo.instance = instance;
// accountInfo.token = usedTokenData;
this.store.dispatch([new AddAccount(accountInfo)])
.subscribe(() => {
localStorage.removeItem(this.authStorageKey);
this.router.navigate(['/home']);
});
})
.catch((err: HttpErrorResponse) => {
this.notificationService.notifyHttpError(err, null);
});
});
}
// this.store.dispatch([new AddAccount(accountInfo)])
// .subscribe(() => {
// localStorage.removeItem(this.authStorageKey);
// this.router.navigate(['/home']);
// });
// })
// .catch((err: HttpErrorResponse) => {
// this.notificationService.notifyHttpError(err, null);
// });
// });
// }
ngOnInit() {
}
// ngOnInit() {
// }
private isAccountAlreadyPresent(username: string, instance: string): boolean{
const accounts = <AccountInfo[]>this.store.snapshot().registeredaccounts.accounts;
for (let acc of accounts) {
if(acc.instance === instance && acc.username == username){
return true;
}
}
return false;
}
// private isAccountAlreadyPresent(username: string, instance: string): boolean{
// const accounts = <AccountInfo[]>this.store.snapshot().registeredaccounts.accounts;
// for (let acc of accounts) {
// if(acc.instance === instance && acc.username == username){
// return true;
// }
// }
// return false;
// }
private displayError(type: RegistrationErrorTypes) {
this.hasError = true;
switch (type) {
case RegistrationErrorTypes.AuthProcessNotFound:
this.errorMessage = 'Something when wrong in the authentication process. Please retry.'
break;
case RegistrationErrorTypes.CodeNotFound:
this.errorMessage = 'No authentication code returned. Please retry.'
break;
}
}
// private displayError(type: RegistrationErrorTypes) {
// this.hasError = true;
// switch (type) {
// case RegistrationErrorTypes.AuthProcessNotFound:
// this.errorMessage = 'Something when wrong in the authentication process. Please retry.'
// break;
// case RegistrationErrorTypes.CodeNotFound:
// this.errorMessage = 'No authentication code returned. Please retry.'
// break;
// }
// }
private getAllSavedApps(): AppInfo[] {
const snapshot = <RegisteredAppsStateModel>this.store.snapshot().registeredapps;
return snapshot.apps;
}
}
// private getAllSavedApps(): AppInfo[] {
// const snapshot = <RegisteredAppsStateModel>this.store.snapshot().registeredapps;
// return snapshot.apps;
// }
// }
enum RegistrationErrorTypes {
CodeNotFound,
AuthProcessNotFound
}
// enum RegistrationErrorTypes {
// CodeNotFound,
// AuthProcessNotFound
// }

View File

@ -1,5 +1,4 @@
import { Component, OnInit, OnDestroy, QueryList, ViewChildren, ElementRef } from "@angular/core";
import { ActivatedRoute, Router } from '@angular/router';
import { Observable, Subscription } from "rxjs";
import { Select } from "@ngxs/store";
import scrollIntoView from "smooth-scroll-into-view-if-needed";
@ -20,20 +19,10 @@ export class StreamsMainDisplayComponent implements OnInit, OnDestroy {
private columnSelectedSub: Subscription;
constructor(
private readonly router: Router,
private readonly activatedRoute: ActivatedRoute,
private readonly navigationService: NavigationService) {
}
ngOnInit() {
this.activatedRoute.queryParams.subscribe(params => {
const code = params['code'];
if (code) {
this.router.navigate(['/register'], { queryParams: { code: code} });
return;
}
});
this.columnSelectedSub = this.navigationService.columnSelectedSubject.subscribe((columnIndex: number) => {
this.focusOnColumn(columnIndex);
});
@ -55,10 +44,6 @@ export class StreamsMainDisplayComponent implements OnInit, OnDestroy {
.then(() => {
this.streamComponents.toArray()[columnIndex].focus();
});
// setTimeout(() => {
// this.streamComponents.toArray()[columnIndex].focus();
// }, 500);
}, 0);

View File

@ -22,6 +22,9 @@ export class MastodonWrapperService {
let isExpired = false;
let storedAccountInfo = this.getStoreAccountInfo(accountInfo.id);
if(!storedAccountInfo || !(storedAccountInfo.token))
return Promise.resolve(accountInfo);
try {
if (storedAccountInfo.token.refresh_token) {
if (!storedAccountInfo.token.created_at || !storedAccountInfo.token.expires_in) {