oauth workflow working
This commit is contained in:
parent
89ee5fadf9
commit
0325b5bfbe
13
main.js
13
main.js
|
@ -8,15 +8,16 @@ const url = require('url')
|
|||
|
||||
function createWindow () {
|
||||
// Create the browser window.
|
||||
win = new BrowserWindow({ width: 395, height: 800, title: "Sengi", backgroundColor: '#FFF'})
|
||||
win = new BrowserWindow({ width: 395, height: 800, title: "Sengi", backgroundColor: '#FFF'});
|
||||
win.loadURL("http://localhost:4200");
|
||||
win.setMenu(null);
|
||||
|
||||
// and load the index.html of the app.
|
||||
win.loadURL(url.format({
|
||||
pathname: path.join(__dirname, 'dist/index.html'),
|
||||
protocol: 'file:',
|
||||
slashes: true
|
||||
}))
|
||||
// win.loadURL(url.format({
|
||||
// pathname: path.join(__dirname, 'dist/index.html'),
|
||||
// protocol: 'file:',
|
||||
// slashes: true
|
||||
// }))
|
||||
|
||||
// Open the DevTools.
|
||||
win.webContents.openDevTools()
|
||||
|
|
|
@ -22,6 +22,7 @@ import { AccountsService } from "./services/accounts.service";
|
|||
import { StreamsService } from "./services/streams.service";
|
||||
import { StreamingService } from "./services/streaming.service";
|
||||
import { RegisteredAppsState } from "./states/registered-apps.state";
|
||||
import { AccountsState } from "./states/accounts.state";
|
||||
|
||||
const routes: Routes = [
|
||||
{ path: "", redirectTo: "home", pathMatch: "full" },
|
||||
|
@ -49,7 +50,8 @@ const routes: Routes = [
|
|||
RouterModule.forRoot(routes),
|
||||
|
||||
NgxsModule.forRoot([
|
||||
RegisteredAppsState
|
||||
RegisteredAppsState,
|
||||
AccountsState
|
||||
]),
|
||||
NgxsStoragePluginModule.forRoot()
|
||||
],
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
import { Component, OnInit, OnDestroy } from "@angular/core";
|
||||
import { Subscription, BehaviorSubject } from "rxjs";
|
||||
import { Subscription, BehaviorSubject, Observable } from "rxjs";
|
||||
import { Store } from "@ngxs/store";
|
||||
|
||||
import { Account } from "../../services/models/mastodon.interfaces";
|
||||
import { AccountWrapper } from "../../models/account.models";
|
||||
import { AccountsService, LocalAccount } from "../../services/accounts.service";
|
||||
import { AccountsService } from "../../services/accounts.service";
|
||||
import { AccountsStateModel, AccountInfo } from "../../states/accounts.state";
|
||||
|
||||
|
||||
@Component({
|
||||
selector: "app-left-side-bar",
|
||||
|
@ -11,35 +15,35 @@ import { AccountsService, LocalAccount } from "../../services/accounts.service";
|
|||
})
|
||||
export class LeftSideBarComponent implements OnInit, OnDestroy {
|
||||
accounts: AccountWrapper[] = [];
|
||||
accounts$: Observable<AccountInfo[]>;
|
||||
|
||||
private sub: Subscription;
|
||||
|
||||
constructor(
|
||||
private readonly accountsService: AccountsService) { }
|
||||
private readonly accountsService: AccountsService,
|
||||
private readonly store: Store) {
|
||||
|
||||
this.accounts$ = this.store.select(state => state.registeredaccounts.accounts);
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.sub = this.accountsService.accountsSubject.subscribe((accounts: LocalAccount[]) => {
|
||||
this.accounts.length = 0;
|
||||
this.accounts$.subscribe((accounts: AccountInfo[]) => {
|
||||
console.warn(accounts);
|
||||
|
||||
for (let acc of accounts) {
|
||||
const accWrapper = new AccountWrapper();
|
||||
accWrapper.username = `${acc.mastodonAccount.username}@${acc.mastodonInstance.replace("https://", "")}`;
|
||||
accWrapper.avatar = acc.mastodonAccount.avatar;
|
||||
this.accounts.push(accWrapper);
|
||||
if (accounts) {
|
||||
this.accounts.length = 0;
|
||||
|
||||
for (let acc of accounts) {
|
||||
this.accountsService.retrieveAccountDetails(acc)
|
||||
.then((result: Account) => {
|
||||
const accWrapper = new AccountWrapper();
|
||||
accWrapper.username = `${acc.username}@${acc.instance}`;
|
||||
accWrapper.avatar = result.avatar;
|
||||
this.accounts.push(accWrapper);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
//const acc1 = new AccountWrapper();
|
||||
//acc1.username = "@mastodon.social@Gargron";
|
||||
//acc1.avatar = "https://files.mastodon.social/accounts/avatars/000/000/001/original/4df197532c6b768c.png";
|
||||
//this.accounts.push(acc1);
|
||||
|
||||
//const acc2 = new AccountWrapper();
|
||||
//acc2.username = "@mastodon.art@DearMsDearn";
|
||||
//acc2.avatar = "https://curate.mastodon.art/gallery/accounts/avatars/000/015/092/original/3a112863f2dd22a27764179912dc8984.gif";
|
||||
//this.accounts.push(acc2);
|
||||
|
||||
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
|
|
|
@ -2,7 +2,7 @@ import { Http, Headers, Response } from "@angular/http";
|
|||
import { BehaviorSubject } from "rxjs";
|
||||
|
||||
import { AccountWrapper } from "./account.models";
|
||||
import { LocalAccount } from "../services/accounts.service";
|
||||
// import { LocalAccount } from "../services/accounts.service";
|
||||
import { ApiRoutes } from "../services/models/api.settings";
|
||||
import { Account, Status } from "../services/models/mastodon.interfaces";
|
||||
import { StreamingService, StreamingWrapper } from "../services/streaming.service";
|
||||
|
@ -15,8 +15,7 @@ export class Stream {
|
|||
constructor(
|
||||
private readonly httpService: Http,
|
||||
public streamName: string,
|
||||
private readonly type: StreamTypeEnum,
|
||||
private readonly account: LocalAccount) {
|
||||
private readonly type: StreamTypeEnum) {
|
||||
|
||||
this.retrieveToots(); //TODO change this for WebSockets
|
||||
}
|
||||
|
@ -31,17 +30,17 @@ export class Stream {
|
|||
const route = this.getTimelineRoute();
|
||||
|
||||
const header = new Headers();
|
||||
header.append("Authorization", `Bearer ${this.account.tokenData.access_token}`);
|
||||
// header.append("Authorization", `Bearer ${this.account.tokenData.access_token}`);
|
||||
|
||||
this.httpService.get(this.account.mastodonInstance + route, { headers: header }).toPromise()
|
||||
.then((res: Response) => {
|
||||
const statuses = (res.json() as Status[])
|
||||
.map((status: Status) => {
|
||||
return new TootWrapper(status);
|
||||
});
|
||||
// this.httpService.get(this.account.mastodonInstance + route, { headers: header }).toPromise()
|
||||
// .then((res: Response) => {
|
||||
// const statuses = (res.json() as Status[])
|
||||
// .map((status: Status) => {
|
||||
// return new TootWrapper(status);
|
||||
// });
|
||||
|
||||
this.statuses.next(statuses);
|
||||
});
|
||||
// this.statuses.next(statuses);
|
||||
// });
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ import { AuthService } from "../../services/auth.service";
|
|||
import { TokenData, AppData } from "../../services/models/mastodon.interfaces";
|
||||
import { AccountsService } from "../../services/accounts.service";
|
||||
import { AddRegisteredApp, RegisteredAppsState, RegisteredAppsStateModel, AppInfo } from "../../states/registered-apps.state";
|
||||
import { AccountInfo, AddAccount } from "../../states/accounts.state";
|
||||
|
||||
@Component({
|
||||
selector: "app-register-new-account",
|
||||
|
@ -40,15 +41,16 @@ export class RegisterNewAccountComponent implements OnInit {
|
|||
console.warn(appInfo);
|
||||
|
||||
this.authService.getToken(appDataWrapper.instance, appInfo.app.client_id, appInfo.app.client_secret, code, appInfo.app.redirect_uri)
|
||||
.then(tokenData => {
|
||||
console.warn('Got token data!');
|
||||
console.warn(tokenData);
|
||||
|
||||
localStorage.removeItem(this.authStorageKey);
|
||||
|
||||
//TODO review all this
|
||||
// this.accountsService.addNewAccount(appDataWrapper.instance, appDataWrapper.username, tokenData);
|
||||
.then((tokenData: TokenData) => {
|
||||
const accountInfo = new AccountInfo();
|
||||
accountInfo.username = appDataWrapper.username;
|
||||
accountInfo.instance = appDataWrapper.instance;
|
||||
accountInfo.token= tokenData;
|
||||
|
||||
this.store.dispatch([new AddAccount(accountInfo)])
|
||||
.subscribe(() => {
|
||||
localStorage.removeItem(this.authStorageKey);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
|
|
@ -4,97 +4,23 @@ import { Subject, BehaviorSubject } from "rxjs";
|
|||
|
||||
import { TokenData, Account } from "./models/mastodon.interfaces";
|
||||
import { ApiRoutes } from "./models/api.settings";
|
||||
import { AccountInfo } from "../states/accounts.state";
|
||||
import { HttpClient, HttpHeaders } from "@angular/common/http";
|
||||
|
||||
|
||||
@Injectable()
|
||||
export class AccountsService {
|
||||
private localAccountKey = "localAccounts";
|
||||
|
||||
private apiRoutes = new ApiRoutes();
|
||||
|
||||
accountsSubject: BehaviorSubject<LocalAccount[]>;
|
||||
constructor(private readonly httpClient: HttpClient) {}
|
||||
|
||||
constructor(private readonly httpService: Http) {}
|
||||
|
||||
load(): Promise<boolean> {
|
||||
return this.getAllLocalAccount()
|
||||
.then((accounts) => {
|
||||
this.accountsSubject = new BehaviorSubject<LocalAccount[]>(accounts);
|
||||
return true;
|
||||
});
|
||||
retrieveAccountDetails(account: AccountInfo): Promise<Account> {
|
||||
const headers = new HttpHeaders({'Authorization':`Bearer ${account.token.access_token}`});
|
||||
// const headers = new HttpHeaders({'Bearer':`${account.token}`});
|
||||
return this.httpClient.get<Account>('https://' + account.instance + this.apiRoutes.getCurrentAccount, {headers: headers}).toPromise();
|
||||
}
|
||||
|
||||
addNewAccount(mastodonInstance: string, email: string, token: TokenData) {
|
||||
const newAccount = new LocalAccount();
|
||||
newAccount.mastodonInstance = mastodonInstance;
|
||||
newAccount.email = email;
|
||||
newAccount.tokenData = token;
|
||||
|
||||
this.getAllLocalAccount().then((allAccounts) => {
|
||||
allAccounts.push(newAccount);
|
||||
this.saveAccounts(allAccounts);
|
||||
|
||||
this.accountsSubject.next(allAccounts);
|
||||
});
|
||||
load(): any {
|
||||
}
|
||||
|
||||
private getAllLocalAccount(): Promise<LocalAccount[]> {
|
||||
const allSavedAccounts = this.retrieveSavedAccounts();
|
||||
const allAccounts: LocalAccount[] = [];
|
||||
const allTasks: Promise<any>[] = [];
|
||||
|
||||
for (let savedAcc of allSavedAccounts) {
|
||||
const promise = this.retrieveMastodonDetails(savedAcc)
|
||||
.then((acc) => {
|
||||
allAccounts.push(acc);
|
||||
})
|
||||
.catch(err => console.error(err));
|
||||
|
||||
allTasks.push(promise);
|
||||
}
|
||||
|
||||
return Promise.all(allTasks).then(() => {
|
||||
return allAccounts;
|
||||
});
|
||||
}
|
||||
|
||||
private retrieveMastodonDetails(account: SavedLocalAccount): Promise<LocalAccount> {
|
||||
const localAccount = new LocalAccount();
|
||||
localAccount.mastodonInstance = account.mastodonInstance;
|
||||
localAccount.email = account.email;
|
||||
localAccount.tokenData = account.tokenData;
|
||||
|
||||
const header = new Headers();
|
||||
header.append("Authorization", `Bearer ${localAccount.tokenData.access_token}`);
|
||||
|
||||
return this.httpService.get('https://' + localAccount.mastodonInstance + this.apiRoutes.getCurrentAccount, { headers: header }).toPromise()
|
||||
.then((res: Response) => {
|
||||
const mastodonAccount = res.json() as Account;
|
||||
localAccount.mastodonAccount = mastodonAccount;
|
||||
return localAccount;
|
||||
});
|
||||
}
|
||||
|
||||
private retrieveSavedAccounts(): SavedLocalAccount[] {
|
||||
const savedData = <SavedLocalAccount[]>JSON.parse(localStorage.getItem(this.localAccountKey));
|
||||
if (savedData) {
|
||||
return savedData;
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
private saveAccounts(accounts: SavedLocalAccount[]) {
|
||||
localStorage.setItem(this.localAccountKey, JSON.stringify(accounts));
|
||||
}
|
||||
}
|
||||
|
||||
class SavedLocalAccount {
|
||||
mastodonInstance: string;
|
||||
email: string;
|
||||
tokenData: TokenData;
|
||||
}
|
||||
|
||||
export class LocalAccount implements SavedLocalAccount {
|
||||
mastodonAccount: Account;
|
||||
mastodonInstance: string;
|
||||
email: string;
|
||||
tokenData: TokenData;
|
||||
}
|
||||
}
|
|
@ -5,27 +5,27 @@ import { HttpClient } from "@angular/common/http";
|
|||
|
||||
@Injectable()
|
||||
export class AuthService {
|
||||
private apiRoutes = new ApiRoutes();
|
||||
private apiRoutes = new ApiRoutes();
|
||||
|
||||
constructor(
|
||||
private readonly httpClient: HttpClient) {
|
||||
}
|
||||
constructor(
|
||||
private readonly httpClient: HttpClient) {
|
||||
}
|
||||
|
||||
getToken(instance: string, client_id: string, client_secret: string, code: string, redirect_uri: string): Promise<TokenData> {
|
||||
const url = `https://${instance}/oauth/token?client_id=${client_id}&client_secret=${client_secret}&grant_type=authorization_code&code=${code}&redirect_uri=${encodeURIComponent(redirect_uri)}`;
|
||||
getToken(instance: string, client_id: string, client_secret: string, code: string, redirect_uri: string): Promise<TokenData> {
|
||||
const url = `https://${instance}/oauth/token?client_id=${client_id}&client_secret=${client_secret}&grant_type=authorization_code&code=${code}&redirect_uri=${encodeURIComponent(redirect_uri)}`;
|
||||
|
||||
return this.httpClient.post<TokenData>(url, null).toPromise();
|
||||
}
|
||||
return this.httpClient.post<TokenData>(url, null).toPromise();
|
||||
}
|
||||
|
||||
createNewApplication(instance: string, appName: string, redirectUrl: string, scopes: string, website: string): Promise<AppData> {
|
||||
const url = 'https://' + instance + this.apiRoutes.createApp;
|
||||
const formData = new FormData();
|
||||
createNewApplication(instance: string, appName: string, redirectUrl: string, scopes: string, website: string): Promise<AppData> {
|
||||
const url = 'https://' + instance + this.apiRoutes.createApp;
|
||||
const formData = new FormData();
|
||||
|
||||
formData.append('client_name', appName);
|
||||
formData.append('redirect_uris', redirectUrl);
|
||||
formData.append('scopes', scopes);
|
||||
formData.append('website', website);
|
||||
formData.append('client_name', appName);
|
||||
formData.append('redirect_uris', redirectUrl);
|
||||
formData.append('scopes', scopes);
|
||||
formData.append('website', website);
|
||||
|
||||
return this.httpClient.post<AppData>(url, formData).toPromise();
|
||||
}
|
||||
return this.httpClient.post<AppData>(url, formData).toPromise();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ import { Http } from "@angular/http";
|
|||
import { BehaviorSubject } from "rxjs";
|
||||
|
||||
import { Stream, StreamTypeEnum } from "../models/stream.models";
|
||||
import { AccountsService, LocalAccount } from "./accounts.service";
|
||||
import { AccountsService } from "./accounts.service";
|
||||
|
||||
@Injectable()
|
||||
export class StreamsService {
|
||||
|
@ -11,23 +11,24 @@ export class StreamsService {
|
|||
|
||||
constructor(
|
||||
private readonly httpService: Http,
|
||||
private readonly accountsService: AccountsService) {
|
||||
// private readonly accountsService: AccountsService
|
||||
) {
|
||||
|
||||
// Return home/local/public of all accounts
|
||||
this.accountsService.accountsSubject
|
||||
.subscribe((accounts: LocalAccount[]) => {
|
||||
const streams: Stream[] = [];
|
||||
for (let acc of accounts) {
|
||||
const homeStream = new Stream(this.httpService, "Home", StreamTypeEnum.Home, acc);
|
||||
const localStream = new Stream(this.httpService, "Local", StreamTypeEnum.Local, acc);
|
||||
const publicStream = new Stream(this.httpService, "Public", StreamTypeEnum.Public, acc);
|
||||
// this.accountsService.accountsSubject
|
||||
// .subscribe((accounts: LocalAccount[]) => {
|
||||
// const streams: Stream[] = [];
|
||||
// for (let acc of accounts) {
|
||||
// const homeStream = new Stream(this.httpService, "Home", StreamTypeEnum.Home, acc);
|
||||
// const localStream = new Stream(this.httpService, "Local", StreamTypeEnum.Local, acc);
|
||||
// const publicStream = new Stream(this.httpService, "Public", StreamTypeEnum.Public, acc);
|
||||
|
||||
streams.push(homeStream);
|
||||
streams.push(localStream);
|
||||
streams.push(publicStream);
|
||||
}
|
||||
this.streamsSubject.next(streams);
|
||||
});
|
||||
// streams.push(homeStream);
|
||||
// streams.push(localStream);
|
||||
// streams.push(publicStream);
|
||||
// }
|
||||
// this.streamsSubject.next(streams);
|
||||
// });
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
import { State, Action, StateContext } from '@ngxs/store';
|
||||
import { TokenData } from '../services/models/mastodon.interfaces';
|
||||
|
||||
export class AddAccount {
|
||||
static readonly type = '[Accounts] Add account';
|
||||
constructor(public account: AccountInfo) {}
|
||||
}
|
||||
|
||||
export interface AccountsStateModel {
|
||||
accounts: AccountInfo[];
|
||||
}
|
||||
|
||||
|
||||
@State<AccountsStateModel>({
|
||||
name: 'registeredaccounts',
|
||||
defaults: {
|
||||
accounts: []
|
||||
}
|
||||
})
|
||||
export class AccountsState {
|
||||
@Action(AddAccount)
|
||||
AddRegisteredApp(ctx: StateContext<AccountsStateModel>, action: AddAccount) {
|
||||
const state = ctx.getState();
|
||||
ctx.patchState({
|
||||
accounts: [...state.accounts, action.account]
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class AccountInfo {
|
||||
username: string;
|
||||
instance: string;
|
||||
token: TokenData;
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
import { State, Action, StateContext } from '@ngxs/store';
|
||||
import { AppData } from '../services/models/mastodon.interfaces';
|
||||
|
||||
|
||||
export class AddRegisteredApp {
|
||||
static readonly type = '[RegisteredApps] Add app';
|
||||
constructor(public app: AppInfo) { }
|
||||
|
@ -11,12 +11,12 @@ export interface RegisteredAppsStateModel {
|
|||
}
|
||||
|
||||
@State<RegisteredAppsStateModel>({
|
||||
name: 'registeredapps',
|
||||
defaults: {
|
||||
apps: []
|
||||
}
|
||||
name: 'registeredapps',
|
||||
defaults: {
|
||||
apps: []
|
||||
}
|
||||
})
|
||||
export class RegisteredAppsState {
|
||||
export class RegisteredAppsState {
|
||||
@Action(AddRegisteredApp)
|
||||
AddRegisteredApp(ctx: StateContext<RegisteredAppsStateModel>, action: AddRegisteredApp) {
|
||||
const state = ctx.getState();
|
||||
|
|
Loading…
Reference in New Issue