Merge pull request #2 from NicolasConstant/topic-scss-migration
Topic scss migration
This commit is contained in:
commit
50a4580ad8
10
angular.json
10
angular.json
|
@ -22,9 +22,13 @@
|
|||
],
|
||||
"styles": [
|
||||
"./node_modules/simplebar/dist/simplebar.min.css",
|
||||
"./node_modules/bootstrap/dist/css/bootstrap.min.css",
|
||||
"src/styles.css"
|
||||
"src/sass/styles.scss"
|
||||
],
|
||||
"stylePreprocessorOptions": {
|
||||
"includePaths": [
|
||||
"./node_modules/bootstrap/scss"
|
||||
]
|
||||
},
|
||||
"scripts": [
|
||||
"./node_modules/simplebar/dist/simplebar.min.js"
|
||||
]
|
||||
|
@ -127,7 +131,7 @@
|
|||
"schematics": {
|
||||
"@schematics/angular:component": {
|
||||
"prefix": "app",
|
||||
"styleext": "css"
|
||||
"styleext": "scss"
|
||||
},
|
||||
"@schematics/angular:directive": {
|
||||
"prefix": "app"
|
||||
|
|
141
main.js
141
main.js
|
@ -1,62 +1,121 @@
|
|||
const {app, BrowserWindow, shell} = require('electron')
|
||||
const path = require('path')
|
||||
const url = require('url')
|
||||
const { app, Menu, server, BrowserWindow, shell } = require('electron');
|
||||
const path = require('path');
|
||||
const url = require('url');
|
||||
const http = require('http');
|
||||
const fs = require('fs');
|
||||
|
||||
// Keep a global reference of the window object, if you don't, the window will
|
||||
// be closed automatically when the JavaScript object is garbage collected.
|
||||
let win
|
||||
|
||||
function createWindow () {
|
||||
// Keep a global reference of the window object, if you don't, the window will
|
||||
// be closed automatically when the JavaScript object is garbage collected.
|
||||
let win
|
||||
|
||||
function createWindow() {
|
||||
// Create the browser window.
|
||||
win = new BrowserWindow({ width: 395, height: 800, title: "Sengi", backgroundColor: '#FFF'})
|
||||
win.setMenu(null);
|
||||
win = new BrowserWindow({ width: 395, height: 800, title: "Sengi", backgroundColor: '#FFF' });
|
||||
|
||||
// and load the index.html of the app.
|
||||
win.loadURL(url.format({
|
||||
pathname: path.join(__dirname, 'dist/index.html'),
|
||||
protocol: 'file:',
|
||||
slashes: true
|
||||
}))
|
||||
var server = http.createServer(requestHandler).listen(9527);
|
||||
win.loadURL('http://localhost:9527');
|
||||
|
||||
|
||||
const template = [
|
||||
{
|
||||
label: 'View',
|
||||
submenu: [
|
||||
{ role: 'reload' },
|
||||
{ role: 'forcereload' },
|
||||
{ type: 'separator' },
|
||||
{ role: 'close' }
|
||||
|
||||
]
|
||||
},
|
||||
{
|
||||
role: 'help',
|
||||
submenu: [
|
||||
{ role: 'toggledevtools' },
|
||||
{
|
||||
label: 'Open GitHub project',
|
||||
click() { require('electron').shell.openExternal('https://github.com/NicolasConstant/sengi') }
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
const menu = Menu.buildFromTemplate(template);
|
||||
win.setMenu(menu);
|
||||
|
||||
// Open the DevTools.
|
||||
//win.webContents.openDevTools()
|
||||
// win.webContents.openDevTools()
|
||||
|
||||
//open external links to browser
|
||||
win.webContents.on('new-window', function(event, url){
|
||||
event.preventDefault();
|
||||
shell.openExternal(url);
|
||||
});
|
||||
win.webContents.on('new-window', function (event, url) {
|
||||
event.preventDefault();
|
||||
shell.openExternal(url);
|
||||
});
|
||||
|
||||
// Emitted when the window is closed.
|
||||
win.on('closed', () => {
|
||||
// Dereference the window object, usually you would store windows
|
||||
// in an array if your app supports multi windows, this is the time
|
||||
// when you should delete the corresponding element.
|
||||
win = null
|
||||
// Dereference the window object, usually you would store windows
|
||||
// in an array if your app supports multi windows, this is the time
|
||||
// when you should delete the corresponding element.
|
||||
win = null
|
||||
})
|
||||
}
|
||||
|
||||
// This method will be called when Electron has finished
|
||||
// initialization and is ready to create browser windows.
|
||||
// Some APIs can only be used after this event occurs.
|
||||
app.on('ready', createWindow)
|
||||
|
||||
// Quit when all windows are closed.
|
||||
app.on('window-all-closed', () => {
|
||||
};
|
||||
|
||||
|
||||
function requestHandler(req, res) {
|
||||
var file = req.url == '/' ? '/index.html' : req.url,
|
||||
root = __dirname + '/dist',
|
||||
page404 = root + '/404.html';
|
||||
|
||||
if (file.includes('register') || file.includes('home')) file = '/index.html';
|
||||
|
||||
getFile((root + file), res, page404);
|
||||
};
|
||||
|
||||
function getFile(filePath, res, page404) {
|
||||
console.warn(`filePath: ${filePath}`)
|
||||
fs.exists(filePath, function (exists) {
|
||||
if (exists) {
|
||||
fs.readFile(filePath, function (err, contents) {
|
||||
if (!err) {
|
||||
res.end(contents);
|
||||
} else {
|
||||
console.dir(err);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
fs.readFile(page404, function (err, contents) {
|
||||
if (!err) {
|
||||
res.writeHead(404, { 'Content-Type': 'text/html' });
|
||||
res.end(contents);
|
||||
} else {
|
||||
console.dir(err);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// This method will be called when Electron has finished
|
||||
// initialization and is ready to create browser windows.
|
||||
// Some APIs can only be used after this event occurs.
|
||||
app.on('ready', createWindow)
|
||||
|
||||
// Quit when all windows are closed.
|
||||
app.on('window-all-closed', () => {
|
||||
// On macOS it is common for applications and their menu bar
|
||||
// to stay active until the user quits explicitly with Cmd + Q
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit()
|
||||
app.quit()
|
||||
}
|
||||
})
|
||||
|
||||
app.on('activate', () => {
|
||||
})
|
||||
|
||||
app.on('activate', () => {
|
||||
// On macOS it's common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
if (win === null) {
|
||||
createWindow()
|
||||
createWindow()
|
||||
}
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
// In this file you can include the rest of your app's specific main process
|
||||
// code. You can also put them in separate files and require them here.
|
||||
|
|
|
@ -7124,9 +7124,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"nan": {
|
||||
"version": "2.10.0",
|
||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.10.0.tgz",
|
||||
"integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==",
|
||||
"version": "2.11.0",
|
||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.11.0.tgz",
|
||||
"integrity": "sha512-F4miItu2rGnV2ySkXOQoA8FKz/SR2Q2sWP0sbTxNxz/tuokeC8WxOhPMcwi0qIyGtVn/rrSeLbvVkznqCdwYnw==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
"lint": "ng lint",
|
||||
"e2e": "ng e2e",
|
||||
"electron": "ng build && electron .",
|
||||
"electron-aot": "ng build --prod && electron ."
|
||||
"electron-prod": "ng build --prod && electron ."
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
|
|
|
@ -5,7 +5,7 @@ import { ElectronService } from 'ngx-electron';
|
|||
@Component({
|
||||
selector: 'app-root',
|
||||
templateUrl: './app.component.html',
|
||||
styleUrls: ['./app.component.css']
|
||||
styleUrls: ['./app.component.scss']
|
||||
})
|
||||
export class AppComponent {
|
||||
title = 'app';
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { BrowserModule } from "@angular/platform-browser";
|
||||
import { FormsModule } from "@angular/forms";
|
||||
import { HttpModule } from "@angular/http";
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
import { NgModule, APP_INITIALIZER } from "@angular/core";
|
||||
import { RouterModule, Routes } from "@angular/router";
|
||||
|
||||
|
@ -21,6 +22,8 @@ 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";
|
||||
import { AccountIconComponent } from './components/left-side-bar/presentation/account-icon/account-icon.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{ path: "", redirectTo: "home", pathMatch: "full" },
|
||||
|
@ -37,17 +40,20 @@ const routes: Routes = [
|
|||
StreamComponent,
|
||||
StreamsSelectionFooterComponent,
|
||||
TootComponent,
|
||||
RegisterNewAccountComponent
|
||||
RegisterNewAccountComponent,
|
||||
AccountIconComponent
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
HttpModule,
|
||||
HttpClientModule,
|
||||
FormsModule,
|
||||
NgxElectronModule,
|
||||
RouterModule.forRoot(routes),
|
||||
|
||||
NgxsModule.forRoot([
|
||||
RegisteredAppsState
|
||||
RegisteredAppsState,
|
||||
AccountsState
|
||||
]),
|
||||
NgxsStoragePluginModule.forRoot()
|
||||
],
|
||||
|
|
|
@ -3,10 +3,14 @@
|
|||
<a href title="write toot!" (click)="createNewToot()">Toot!</a>
|
||||
</div>
|
||||
|
||||
<div *ngFor="let account of accounts" class="mam-account-selector">
|
||||
<a href title="{{ account.username }}" (click)="toogleAccount(account.id)"><img src="{{ account.avatar }}" /></a>
|
||||
<div *ngFor="let account of accounts" >
|
||||
<app-account-icon [account]="account" ></app-account-icon>
|
||||
|
||||
<!-- <a href title="{{ account.username }}" (click)="toogleAccount(account.id)"><img src="{{ account.avatar }}" /></a> -->
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div id="mam-account-add">
|
||||
<a href title="add new account" [routerLink]="['/register']">+</a>
|
||||
</div>
|
||||
|
|
|
@ -1,55 +1,62 @@
|
|||
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",
|
||||
templateUrl: "./left-side-bar.component.html",
|
||||
styleUrls: ["./left-side-bar.component.css"]
|
||||
styleUrls: ["./left-side-bar.component.scss"]
|
||||
})
|
||||
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);
|
||||
}
|
||||
|
||||
private currentLoading: number;
|
||||
ngOnInit() {
|
||||
this.sub = this.accountsService.accountsSubject.subscribe((accounts: LocalAccount[]) => {
|
||||
this.accounts.length = 0;
|
||||
this.accounts$.subscribe((accounts: AccountInfo[]) => {
|
||||
console.warn(' this.accounts$.subscribe(');
|
||||
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) {
|
||||
for (let acc of accounts) {
|
||||
|
||||
const accWrapper = new AccountWrapper();
|
||||
accWrapper.username = `${acc.username}@${acc.instance}`;
|
||||
this.accounts.push(accWrapper);
|
||||
|
||||
this.accountsService.retrieveAccountDetails(acc)
|
||||
.then((result: Account) => {
|
||||
console.error(result);
|
||||
const accounts = this.accounts.filter(x => result.url.includes(acc.username) && result.url.includes(acc.instance));
|
||||
for (const account of accounts) {
|
||||
account.avatar = result.avatar;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
//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 {
|
||||
this.sub.unsubscribe();
|
||||
}
|
||||
|
||||
toogleAccount(accountId: number): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
addNewAccount(): boolean {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
<a class="account-icon" href title="{{ account.username }}" (click)="toogleAccount()" (contextmenu)="openMenu()">
|
||||
<img class="account-icon__avatar" src="{{ account.avatar }}" />
|
||||
</a>
|
|
@ -0,0 +1,21 @@
|
|||
.account-icon {
|
||||
display: inline-block;
|
||||
width: 50px;
|
||||
padding-top: 4px;
|
||||
// margin-left: 5px;
|
||||
margin: 0 0 5px 5px;
|
||||
|
||||
&__avatar {
|
||||
border-radius: 50%;
|
||||
width: 40px;
|
||||
}
|
||||
|
||||
// & a {
|
||||
// margin-left: 4px;
|
||||
// /*margin-top: 4px;*/
|
||||
// }
|
||||
// & img {
|
||||
// width: 40px;
|
||||
// border-radius: 50%;
|
||||
// }
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { AccountIconComponent } from './account-icon.component';
|
||||
|
||||
describe('AccountIconComponent', () => {
|
||||
let component: AccountIconComponent;
|
||||
let fixture: ComponentFixture<AccountIconComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ AccountIconComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(AccountIconComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,29 @@
|
|||
import { Component, OnInit, Input } from '@angular/core';
|
||||
import { AccountInfo } from '../../../../states/accounts.state';
|
||||
import { AccountsService } from '../../../../services/accounts.service';
|
||||
import { AccountWrapper } from '../../../../models/account.models';
|
||||
import { Account } from "../../../../services/models/mastodon.interfaces";
|
||||
|
||||
@Component({
|
||||
selector: 'app-account-icon',
|
||||
templateUrl: './account-icon.component.html',
|
||||
styleUrls: ['./account-icon.component.scss']
|
||||
})
|
||||
export class AccountIconComponent implements OnInit {
|
||||
@Input() account: AccountWrapper;
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnInit() {
|
||||
}
|
||||
|
||||
toogleAccount(): boolean {
|
||||
console.warn(`click`);
|
||||
return false;
|
||||
}
|
||||
|
||||
openMenu(event): boolean {
|
||||
console.warn(`openMenu`);
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -5,7 +5,7 @@ import { AccountWrapper } from "../../models/account.models";
|
|||
@Component({
|
||||
selector: "app-stream",
|
||||
templateUrl: "./stream.component.html",
|
||||
styleUrls: ["./stream.component.css"]
|
||||
styleUrls: ["./stream.component.scss"]
|
||||
})
|
||||
export class StreamComponent implements OnInit {
|
||||
private _stream: Stream;
|
||||
|
|
|
@ -3,7 +3,7 @@ import { Component, OnInit } from '@angular/core';
|
|||
@Component({
|
||||
selector: 'app-streams-selection-footer',
|
||||
templateUrl: './streams-selection-footer.component.html',
|
||||
styleUrls: ['./streams-selection-footer.component.css']
|
||||
styleUrls: ['./streams-selection-footer.component.scss']
|
||||
})
|
||||
export class StreamsSelectionFooterComponent implements OnInit {
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ import { TootWrapper } from "../../models/stream.models";
|
|||
@Component({
|
||||
selector: "app-toot",
|
||||
templateUrl: "./toot.component.html",
|
||||
styleUrls: ["./toot.component.css"]
|
||||
styleUrls: ["./toot.component.scss"]
|
||||
})
|
||||
export class TootComponent implements OnInit {
|
||||
@Input() toot: TootWrapper;
|
||||
|
|
|
@ -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);
|
||||
// });
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -1,88 +1,131 @@
|
|||
import { Component, OnInit, Input } from "@angular/core";
|
||||
import { Store, Select } from '@ngxs/store';
|
||||
|
||||
import { AuthService } from "../../services/auth.service";
|
||||
import { TokenData } from "../../services/models/mastodon.interfaces";
|
||||
import { AccountsService } from "../../services/accounts.service";
|
||||
import { AddRegisteredApp, RegisteredAppsState, RegisteredAppsStateModel } from "../../states/registered-apps.state";
|
||||
import { ActivatedRoute } from "@angular/router";
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
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",
|
||||
templateUrl: "./register-new-account.component.html",
|
||||
styleUrls: ["./register-new-account.component.css"]
|
||||
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;
|
||||
// @Input() email: string;
|
||||
// @Input() password: string;
|
||||
result: string;
|
||||
|
||||
//@Select() registeredApps$: Observable<RegisteredAppsStateModel>;
|
||||
registeredApps$: Observable<RegisteredAppsStateModel>;
|
||||
@Input() mastodonFullHandle: string;
|
||||
result: string;
|
||||
// registeredApps$: Observable<RegisteredAppsStateModel>;
|
||||
|
||||
constructor(
|
||||
private readonly authService: AuthService,
|
||||
private readonly accountsService: AccountsService,
|
||||
private readonly store: Store) {
|
||||
private authStorageKey: string = 'tempAuth';
|
||||
|
||||
this.registeredApps$ = this.store.select(state => state.registeredapps.registeredApps);
|
||||
constructor(
|
||||
private readonly authService: AuthService,
|
||||
private readonly accountsService: AccountsService,
|
||||
private readonly store: Store,
|
||||
private readonly activatedRoute: ActivatedRoute) {
|
||||
|
||||
// this.registeredApps$ = this.store.select(state => state.registeredapps.registeredApps);
|
||||
|
||||
this.activatedRoute.queryParams.subscribe(params => {
|
||||
const code = params['code'];
|
||||
if (!code) return;
|
||||
|
||||
const appDataWrapper = <CurrentAuthProcess>JSON.parse(localStorage.getItem(this.authStorageKey));
|
||||
if (!appDataWrapper) return;
|
||||
|
||||
const appInfo = this.getAllSavedApps().filter(x => x.instance === appDataWrapper.instance)[0];
|
||||
console.warn('appInfo');
|
||||
console.warn(appInfo);
|
||||
|
||||
this.authService.getToken(appDataWrapper.instance, appInfo.app.client_id, appInfo.app.client_secret, code, appInfo.app.redirect_uri)
|
||||
.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);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.registeredApps$.subscribe(x => {
|
||||
console.error('registeredApps$')
|
||||
console.warn(x);
|
||||
});
|
||||
ngOnInit() {
|
||||
// this.registeredApps$.subscribe(x => {
|
||||
// console.error('registeredApps$')
|
||||
// console.warn(x);
|
||||
// });
|
||||
}
|
||||
|
||||
}
|
||||
onSubmit(): boolean {
|
||||
let fullHandle = this.mastodonFullHandle.split('@').filter(x => x != null && x !== '');
|
||||
|
||||
onSubmit(): boolean {
|
||||
const username = fullHandle[0];
|
||||
const instance = fullHandle[1];
|
||||
|
||||
this.store
|
||||
.dispatch(new AddRegisteredApp({ name: 'test', id: 15, client_id: 'dsqdqs', client_secret: 'dsqdqs', redirect_uri: 'dsqdqs' }))
|
||||
.subscribe(res => {
|
||||
console.error('dispatch');
|
||||
console.warn(res);
|
||||
});
|
||||
this.checkAndCreateApplication(instance)
|
||||
.then((appData: AppData) => {
|
||||
this.redirectToInstanceAuthPage(username, instance, appData);
|
||||
});
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private checkAndCreateApplication(instance: string): Promise<AppData> {
|
||||
const alreadyRegisteredApps = this.getAllSavedApps();
|
||||
const instanceApps = alreadyRegisteredApps.filter(x => x.instance === instance);
|
||||
|
||||
|
||||
if (instanceApps.length !== 0) {
|
||||
console.log('instance already registered');
|
||||
return Promise.resolve(instanceApps[0].app);
|
||||
} else {
|
||||
console.log('instance not registered');
|
||||
const redirect_uri = this.getLocalHostname() + '/register';
|
||||
return this.authService.createNewApplication(instance, 'Sengi', redirect_uri, 'read write follow', 'https://github.com/NicolasConstant/sengi')
|
||||
.then((appData: AppData) => {
|
||||
return this.saveNewApp(instance, appData)
|
||||
.then(() => { return appData; });
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// let fullHandle = this.mastodonFullHandle.split('@').filter(x => x != null && x !== '');
|
||||
|
||||
// console.log(fullHandle[0]);
|
||||
// console.log(fullHandle[1]);
|
||||
private getLocalHostname(): string {
|
||||
let localHostname = location.protocol + '//' + location.hostname + (location.port ? ':' + location.port : '');
|
||||
return localHostname;
|
||||
}
|
||||
|
||||
// this.result = fullHandle[0] + '*' + fullHandle[1];
|
||||
private getAllSavedApps(): AppInfo[] {
|
||||
const snapshot = <RegisteredAppsStateModel>this.store.snapshot().registeredapps;
|
||||
return snapshot.apps;
|
||||
}
|
||||
|
||||
// window.location.href = "https://google.com";
|
||||
private saveNewApp(instance: string, app: AppData): Promise<any> {
|
||||
const appInfo = new AppInfo();
|
||||
appInfo.instance = instance;
|
||||
appInfo.app = app;
|
||||
|
||||
return this.store.dispatch([
|
||||
new AddRegisteredApp(appInfo)
|
||||
]).toPromise();
|
||||
}
|
||||
|
||||
private redirectToInstanceAuthPage(username: string, instance: string, app: AppData) {
|
||||
const appDataTemp = new CurrentAuthProcess(username, instance);
|
||||
localStorage.setItem('tempAuth', JSON.stringify(appDataTemp));
|
||||
|
||||
let instanceUrl = `https://${instance}/oauth/authorize?scope=${encodeURIComponent('read write follow')}&response_type=code&redirect_uri=${encodeURIComponent(app.redirect_uri)}&client_id=${app.client_id}`;
|
||||
|
||||
|
||||
|
||||
//register app
|
||||
|
||||
//ask for getting token
|
||||
|
||||
// this.authService.getToken(this.mastodonNode, this.email, this.password)
|
||||
// .then((res: TokenData) => {
|
||||
// this.result = res.access_token;
|
||||
|
||||
// this.accountsService.addNewAccount(this.mastodonNode, this.email, res);
|
||||
|
||||
// })
|
||||
// .catch(err => {
|
||||
// this.result = err;
|
||||
// });
|
||||
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
window.location.href = instanceUrl;
|
||||
}
|
||||
}
|
||||
|
||||
class CurrentAuthProcess {
|
||||
constructor(public username: string, public instance: string) { }
|
||||
}
|
|
@ -7,7 +7,7 @@ import { StreamsService } from "../../services/streams.service";
|
|||
@Component({
|
||||
selector: "app-streams-main-display",
|
||||
templateUrl: "./streams-main-display.component.html",
|
||||
styleUrls: ["./streams-main-display.component.css"]
|
||||
styleUrls: ["./streams-main-display.component.scss"]
|
||||
})
|
||||
export class StreamsMainDisplayComponent implements OnInit {
|
||||
streams: Stream[] = [];
|
||||
|
|
|
@ -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(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;
|
||||
}
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { Http, Response, RequestOptions } from '@angular/http';
|
||||
import { ApiRoutes } from './models/api.settings';
|
||||
|
||||
@Injectable()
|
||||
export class AppService {
|
||||
private apiRoutes = new ApiRoutes();
|
||||
|
||||
constructor(private readonly httpService: Http) {
|
||||
}
|
||||
|
||||
createNewApplication(mastodonUrl: string): Promise<> {
|
||||
const url = mastodonUrl + this.apiRoutes.createApp;
|
||||
|
||||
const options = new RequestOptions();
|
||||
const formData = new FormData();
|
||||
|
||||
formData.append('client_name', 'Sengi');
|
||||
formData.append('redirect_uris', '');
|
||||
formData.append('scopes', 'read write follow');
|
||||
formData.append('website', 'https://github.com/NicolasConstant/sengi');
|
||||
|
||||
return this.httpService.post(url, formData, options)
|
||||
.pipe(
|
||||
map((res: Response) => {
|
||||
const result = res.json();
|
||||
return result as TokenData;
|
||||
}))
|
||||
.toPromise()
|
||||
|
||||
|
||||
.then((res: Response) => {
|
||||
const result = res.json();
|
||||
return result as TokenData;
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,48 +1,31 @@
|
|||
import { Injectable } from "@angular/core";
|
||||
import { Http, Response, RequestOptions } from "@angular/http";
|
||||
import { ApiRoutes } from "./models/api.settings";
|
||||
import { TokenData } from "./models/mastodon.interfaces";
|
||||
import { ApiRoutes } from './models/api.settings';
|
||||
import { AppData, TokenData } from "./models/mastodon.interfaces";
|
||||
import { HttpClient } from "@angular/common/http";
|
||||
|
||||
@Injectable()
|
||||
export class AuthService {
|
||||
private apiRoutes = new ApiRoutes();
|
||||
private apiRoutes = new ApiRoutes();
|
||||
|
||||
constructor(
|
||||
private readonly httpService: Http) {
|
||||
}
|
||||
|
||||
getToken(
|
||||
mastodonNode: string, email: string, password: string): Promise<TokenData> {
|
||||
|
||||
//TODO retrieve those via API
|
||||
const clientId = localStorage.getItem("client_id");
|
||||
const clientSecret = localStorage.getItem("client_secret");
|
||||
|
||||
//Retrieve Token
|
||||
const url = this.getHostUrl(mastodonNode) + this.apiRoutes.getToken;
|
||||
|
||||
const options = new RequestOptions();
|
||||
const formData = new FormData();
|
||||
|
||||
formData.append("client_id", clientId);
|
||||
formData.append("client_secret", clientSecret);
|
||||
formData.append("grant_type", "password");
|
||||
formData.append("username", email);
|
||||
formData.append("password", password);
|
||||
formData.append("scope", "read write follow");
|
||||
|
||||
return this.httpService.post(url, formData, options).toPromise()
|
||||
.then((res: Response) => {
|
||||
const result = res.json();
|
||||
return result as TokenData;
|
||||
});
|
||||
}
|
||||
|
||||
private getHostUrl(url: string): string {
|
||||
url = url.replace("http://", "");
|
||||
if (!url.startsWith("https://")) {
|
||||
url = "https://" + url;
|
||||
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)}`;
|
||||
|
||||
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();
|
||||
|
||||
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 url;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,12 @@
|
|||
export interface AppData {
|
||||
client_id: string;
|
||||
client_secret: string;
|
||||
id: string;
|
||||
name: string;
|
||||
redirect_uri: string;
|
||||
website: string;
|
||||
}
|
||||
|
||||
export interface TokenData {
|
||||
access_token: string;
|
||||
token_type: string;
|
||||
|
|
|
@ -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,26 +1,27 @@
|
|||
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) { }
|
||||
}
|
||||
|
||||
export interface RegisteredAppsStateModel {
|
||||
registeredApps: AppInfo[];
|
||||
apps: AppInfo[];
|
||||
}
|
||||
|
||||
@State<RegisteredAppsStateModel>({
|
||||
name: 'registeredapps',
|
||||
defaults: {
|
||||
registeredApps: []
|
||||
}
|
||||
name: 'registeredapps',
|
||||
defaults: {
|
||||
apps: []
|
||||
}
|
||||
})
|
||||
export class RegisteredAppsState {
|
||||
export class RegisteredAppsState {
|
||||
@Action(AddRegisteredApp)
|
||||
AddRegisteredApp(ctx: StateContext<RegisteredAppsStateModel>, action: AddRegisteredApp) {
|
||||
const state = ctx.getState();
|
||||
ctx.patchState({
|
||||
registeredApps: [...state.registeredApps, action.app]
|
||||
apps: [...state.apps, action.app]
|
||||
});
|
||||
|
||||
// ctx.setState({
|
||||
|
@ -30,9 +31,6 @@ export class RegisteredAppsState {
|
|||
}
|
||||
|
||||
export class AppInfo {
|
||||
id: number;
|
||||
name: string;
|
||||
redirect_uri: string;
|
||||
client_id: string;
|
||||
client_secret: string;
|
||||
instance: string;
|
||||
app: AppData;
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
$font-color-primary: #e8eaf3;
|
||||
|
||||
$color-primary: #141824;
|
||||
|
||||
$default-font-size: 15px;
|
|
@ -0,0 +1,43 @@
|
|||
@import './variables';
|
||||
@import './mixins';
|
||||
|
||||
@import "bootstrap";
|
||||
|
||||
*,
|
||||
*::after,
|
||||
*::before {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: inherit;
|
||||
}
|
||||
|
||||
body {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html, body {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
// padding: 0;
|
||||
// margin: 0;
|
||||
|
||||
font-family: 'Roboto', sans-serif;
|
||||
font-size: $default-font-size;
|
||||
|
||||
color: $font-color-primary;
|
||||
background-color: $color-primary;
|
||||
}
|
||||
|
||||
.invisible {
|
||||
display: none;
|
||||
}
|
||||
/* .ellipsis {
|
||||
} */
|
||||
|
||||
#toot-content p {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
#toot-content a {
|
||||
color: #bec3d8;
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
/* You can add global styles to this file, and also import other style files */
|
||||
html, body {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
|
||||
font-family: 'Roboto', sans-serif;
|
||||
/* font-family: "mastodon-font-sans-serif",sans-serif; */
|
||||
font-size: 15px;
|
||||
|
||||
/* color: whitesmoke; */
|
||||
color: #c9cbd4;
|
||||
color: #a8acbc;
|
||||
color: #e8eaf3;
|
||||
background-color: #0f111a;
|
||||
background-color: #0f111a;
|
||||
background-color: #0a0c12;
|
||||
|
||||
background-color: #141824;
|
||||
}
|
||||
|
||||
.invisible {
|
||||
display: none;
|
||||
}
|
||||
/* .ellipsis {
|
||||
} */
|
||||
|
||||
#toot-content p {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
#toot-content a {
|
||||
color: #bec3d8;
|
||||
}
|
Loading…
Reference in New Issue