Merge pull request #231 from NicolasConstant/topic_auto-update

Topic auto update
This commit is contained in:
Nicolas Constant 2020-02-27 23:38:54 -05:00 committed by GitHub
commit 6f0c949a91
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 555 additions and 405 deletions

View File

@ -1,145 +1,149 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"sengi": {
"root": "",
"sourceRoot": "src",
"projectType": "application",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist",
"index": "src/index.html",
"main": "src/main.ts",
"tsConfig": "src/tsconfig.app.json",
"polyfills": "src/polyfills.ts",
"assets": [
"src/assets",
"src/favicon.ico"
],
"styles": [
"src/sass/styles.scss",
"node_modules/@ctrl/ngx-emoji-mart/picker.css"
],
"stylePreprocessorOptions": {
"includePaths": [
"./src/sass",
"./node_modules/bootstrap/scss"
]
},
"scripts": []
},
"configurations": {
"production": {
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"extractCss": true,
"namedChunks": false,
"aot": true,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true,
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
]
}
}
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "sengi:build"
},
"configurations": {
"production": {
"browserTarget": "sengi:build:production"
}
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "sengi:build"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "src/test.ts",
"karmaConfig": "./karma.conf.js",
"polyfills": "src/polyfills.ts",
"tsConfig": "src/tsconfig.spec.json",
"scripts": [],
"styles": [
"src/sass/styles.scss"
],
"assets": [
"src/assets",
"src/favicon.ico"
],
"stylePreprocessorOptions": {
"includePaths": [
"./src/sass",
"./node_modules/bootstrap/scss"
]
}
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"src/tsconfig.app.json",
"src/tsconfig.spec.json"
],
"exclude": [
"**/node_modules/**"
]
}
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"sengi": {
"root": "",
"sourceRoot": "src",
"projectType": "application",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist",
"index": "src/index.html",
"main": "src/main.ts",
"tsConfig": "src/tsconfig.app.json",
"polyfills": "src/polyfills.ts",
"assets": [
"src/assets",
"src/favicon.ico",
"src/manifest.json"
],
"styles": [
"src/sass/styles.scss",
"node_modules/@ctrl/ngx-emoji-mart/picker.css"
],
"stylePreprocessorOptions": {
"includePaths": [
"./src/sass",
"./node_modules/bootstrap/scss"
]
},
"scripts": []
},
"configurations": {
"production": {
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"extractCss": true,
"namedChunks": false,
"aot": true,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true,
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
],
"serviceWorker": true,
"ngswConfigPath": "src/ngsw-config.json"
}
}
},
"sengi-e2e": {
"root": "e2e",
"sourceRoot": "e2e",
"projectType": "application",
"architect": {
"e2e": {
"builder": "@angular-devkit/build-angular:protractor",
"options": {
"protractorConfig": "./protractor.conf.js",
"devServerTarget": "sengi:serve"
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"e2e/tsconfig.e2e.json"
],
"exclude": [
"**/node_modules/**"
]
}
}
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "sengi:build"
},
"configurations": {
"production": {
"browserTarget": "sengi:build:production"
}
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "sengi:build"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "src/test.ts",
"karmaConfig": "./karma.conf.js",
"polyfills": "src/polyfills.ts",
"tsConfig": "src/tsconfig.spec.json",
"scripts": [],
"styles": [
"src/sass/styles.scss"
],
"assets": [
"src/assets",
"src/favicon.ico",
"src/manifest.json"
],
"stylePreprocessorOptions": {
"includePaths": [
"./src/sass",
"./node_modules/bootstrap/scss"
]
}
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"src/tsconfig.app.json",
"src/tsconfig.spec.json"
],
"exclude": [
"**/node_modules/**"
]
}
}
}
},
"defaultProject": "sengi",
"schematics": {
"@schematics/angular:component": {
"prefix": "app",
"styleext": "scss"
"sengi-e2e": {
"root": "e2e",
"sourceRoot": "e2e",
"projectType": "application",
"architect": {
"e2e": {
"builder": "@angular-devkit/build-angular:protractor",
"options": {
"protractorConfig": "./protractor.conf.js",
"devServerTarget": "sengi:serve"
}
},
"@schematics/angular:directive": {
"prefix": "app"
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"e2e/tsconfig.e2e.json"
],
"exclude": [
"**/node_modules/**"
]
}
}
}
}
},
"defaultProject": "sengi",
"schematics": {
"@schematics/angular:component": {
"prefix": "app",
"styleext": "scss"
},
"@schematics/angular:directive": {
"prefix": "app"
}
}
}

425
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -36,7 +36,9 @@
"@angular/http": "^7.2.7",
"@angular/platform-browser": "^7.2.7",
"@angular/platform-browser-dynamic": "^7.2.7",
"@angular/pwa": "^0.12.4",
"@angular/router": "^7.2.7",
"@angular/service-worker": "^7.2.7",
"@ctrl/ngx-emoji-mart": "^0.17.0",
"@fortawesome/angular-fontawesome": "^0.3.0",
"@fortawesome/fontawesome-svg-core": "^1.2.13",
@ -89,6 +91,7 @@
"productName": "Sengi",
"appId": "org.sengi.desktop",
"artifactName": "${productName}-${version}-${os}.${ext}",
"npmRebuild": false,
"directories": {
"output": "release"
},

View File

@ -1,4 +1,4 @@
<div class="drag-and-drop" *ngIf="drag" (dragover)="dragover($event)" (drop)="drop($event)"
<div class="drag-and-drop" *ngIf="drag" (dragover)="dragover($event)" (drop)="drop($event)"
[ngClass]="{'drag-and-drop__on-drag': drag2 === true }">
<!-- (dragleave)="dragleave($event)" -->
<div class="drag-and-drop__card">
@ -10,6 +10,12 @@
</div>
</div>
<div class="auto-update" [class.auto-update__activated]="updateAvailable">
<div class="auto-update__display">
<div class="auto-update__display--text">A new version is available!</div> <a href class="auto-update__display--reload" (click)="loadNewVersion()">reload</a> <a href class="auto-update__display--close" (click)="closeAutoUpdate()"><fa-icon [icon]="faTimes"></fa-icon></a>
</div>
</div>
<app-media-viewer id="media-viewer" *ngIf="openedMediaEvent" [openedMediaEvent]="openedMediaEvent"
(closeSubject)="closeMedia()" (dragenter)="dragenter($event)"></app-media-viewer>

View File

@ -97,4 +97,75 @@ app-streams-selection-footer {
right: 0;
bottom: 0;
left: 50px;
}
.auto-update {
transition: all .2s;
transition-timing-function: ease-in;
position: absolute;
height: 70px;
left: 0;
right: 0;
bottom: -80px;
z-index: 999999999;
&__activated {
// opacity: 1;
transition: all .25s;
transition-timing-function: ease-out;
bottom: 0px;
}
&__display {
position: relative;
// height: 30px;
width: 300px;
// margin: 0 auto 30px auto;
margin: auto;
border-radius: 2px;
color: rgba(rgb(0, 4, 24), 1);
background: #ffffff;
box-shadow: 0px 0px 10px rgb(0, 0, 0);
&--text {
display: inline-block;
padding: 5px 10px;
}
&--reload {
transition: all .2s;
position: absolute;
right: 30px;
padding: 5px 10px;
text-decoration: none;
color: #ffffff;
background-color: #3e455f;
&:hover {
background-color: #1d202c;
}
}
&--close {
transition: all .2s;
position: absolute;
right: 0;
display: inline-block;
padding: 5px 10px;
text-decoration: none;
border-top-right-radius: 2px;
border-bottom-right-radius: 2px;
color: #ffffff;
background-color: #3e455f;
&:hover {
background-color: #1d202c;
}
}
}
}

View File

@ -2,6 +2,7 @@ import { Component, OnInit, OnDestroy } from '@angular/core';
import { Subscription, Observable, Subject } from 'rxjs';
import { debounceTime, map } from 'rxjs/operators';
import { Select } from '@ngxs/store';
import { faTimes } from "@fortawesome/free-solid-svg-icons";
// import { ElectronService } from 'ngx-electron';
import { NavigationService, LeftPanelType, OpenLeftPanelEvent } from './services/navigation.service';
@ -9,6 +10,7 @@ import { StreamElement } from './states/streams.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';
@Component({
selector: 'app-root',
@ -16,26 +18,33 @@ import { MediaService } from './services/media.service';
styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit, OnDestroy {
faTimes = faTimes;
title = 'Sengi';
floatingColumnActive: boolean;
tutorialActive: boolean;
// mediaViewerActive: boolean = false;
openedMediaEvent: OpenMediaEvent
updateAvailable: boolean;
private columnEditorSub: Subscription;
private openMediaSub: Subscription;
private streamSub: Subscription;
private dragoverSub: Subscription;
private updateAvailableSub: Subscription;
@Select(state => state.streamsstatemodel.streams) streamElements$: Observable<StreamElement[]>;
constructor(
private readonly serviceWorkerService: ServiceWorkerService,
private readonly toolsService: ToolsService,
private readonly mediaService: MediaService,
private readonly navigationService: NavigationService) {
}
ngOnInit(): void {
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;
@ -65,7 +74,7 @@ export class AppComponent implements OnInit, OnDestroy {
.pipe(
debounceTime(1500)
)
.subscribe(() => {
.subscribe(() => {
this.drag = false;
})
}
@ -75,6 +84,7 @@ export class AppComponent implements OnInit, OnDestroy {
this.columnEditorSub.unsubscribe();
this.openMediaSub.unsubscribe();
this.dragoverSub.unsubscribe();
this.updateAvailableSub.unsubscribe();
}
closeMedia() {
@ -96,7 +106,6 @@ export class AppComponent implements OnInit, OnDestroy {
return false;
}
dragover(event): boolean {
// console.warn('dragover');
event.stopPropagation();
event.preventDefault();
this.dragoverSubject.next(true);
@ -112,4 +121,14 @@ export class AppComponent implements OnInit, OnDestroy {
this.mediaService.uploadMedia(selectedAccount, files);
return false;
}
loadNewVersion(): boolean {
this.serviceWorkerService.loadNewAppVersion();
return false;
}
closeAutoUpdate(): boolean {
this.updateAvailable = false;
return false;
}
}

View File

@ -79,6 +79,8 @@ import { ScheduledStatusesComponent } from './components/floating-column/schedul
import { ScheduledStatusComponent } from './components/floating-column/scheduled-statuses/scheduled-status/scheduled-status.component';
import { StreamNotificationsComponent } from './components/stream/stream-notifications/stream-notifications.component';
import { NotificationComponent } from './components/floating-column/manage-account/notifications/notification/notification.component';
import { ServiceWorkerModule } from '@angular/service-worker';
import { environment } from '../environments/environment';
const routes: Routes = [
@ -167,7 +169,8 @@ const routes: Routes = [
]),
NgxsStoragePluginModule.forRoot(),
ContextMenuModule.forRoot(),
HotkeyModule.forRoot()
HotkeyModule.forRoot(),
ServiceWorkerModule.register('ngsw-worker.js', { enabled: environment.production })
],
providers: [AuthService, NavigationService, NotificationService, MastodonWrapperService, MastodonService, StreamingService],
bootstrap: [AppComponent],

View File

@ -0,0 +1,12 @@
import { TestBed } from '@angular/core/testing';
import { ServiceWorkerService } from './service-worker.service';
xdescribe('ServiceWorkerService', () => {
beforeEach(() => TestBed.configureTestingModule({}));
it('should be created', () => {
const service: ServiceWorkerService = TestBed.get(ServiceWorkerService);
expect(service).toBeTruthy();
});
});

View File

@ -0,0 +1,49 @@
import { Injectable, ApplicationRef } from '@angular/core';
import { SwUpdate } from '@angular/service-worker';
import { first } from 'rxjs/operators';
import { interval, concat, BehaviorSubject } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class ServiceWorkerService {
newAppVersionIsAvailable = new BehaviorSubject<boolean>(false);
private isListening = false;
constructor(appRef: ApplicationRef, updates: SwUpdate) {
//https://angular.io/guide/service-worker-communications
updates.available.subscribe(event => {
console.log('current version is', event.current);
console.log('available version is', event.available);
this.newAppVersionIsAvailable.next(true);
});
// Allow the app to stabilize first, before starting polling for updates with `interval()`.
// const updateCheckTimer$ = interval(10 * 1000);
// const appIsStable$ = appRef.isStable; //.pipe(first(isStable => isStable === true));
// const everySixHoursOnceAppIsStable$ = concat(appIsStable$, updateCheckTimer$);
// everySixHoursOnceAppIsStable$.subscribe(() => {
// updates.checkForUpdate();
// });
const updateCheckTimer$ = interval(6 * 60 * 60 * 1000);
appRef.isStable.subscribe(() => {
if (this.isListening) return;
this.isListening = true;
updateCheckTimer$.subscribe(() => {
updates.checkForUpdate();
});
});
}
loadNewAppVersion() {
document.location.reload();
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 792 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 958 B

View File

@ -50,6 +50,8 @@
}
}
</style>
<link rel="manifest" href="manifest.json">
<meta name="theme-color" content="#1976d2">
</head>
<body ondragstart="return false;" ondrop="return false;">
@ -59,5 +61,6 @@
<div></div>
</div>
</app-root>
<noscript>Please enable JavaScript to continue using this application.</noscript>
</body>
</html>

View File

@ -5,8 +5,13 @@ import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
if (environment.production) {
enableProdMode();
enableProdMode();
}
platformBrowserDynamic().bootstrapModule(AppModule)
.catch(err => console.log(err));
.then(() => {
if ('serviceWorker' in navigator && environment.production) {
navigator.serviceWorker.register('./ngsw-worker.js');
}
})
.catch(err => console.log(err));

51
src/manifest.json Normal file
View File

@ -0,0 +1,51 @@
{
"name": "sengi",
"short_name": "sengi",
"theme_color": "#1976d2",
"background_color": "#fafafa",
"display": "standalone",
"scope": "/",
"start_url": "/",
"icons": [
{
"src": "assets/icons/icon-72x72.png",
"sizes": "72x72",
"type": "image/png"
},
{
"src": "assets/icons/icon-96x96.png",
"sizes": "96x96",
"type": "image/png"
},
{
"src": "assets/icons/icon-128x128.png",
"sizes": "128x128",
"type": "image/png"
},
{
"src": "assets/icons/icon-144x144.png",
"sizes": "144x144",
"type": "image/png"
},
{
"src": "assets/icons/icon-152x152.png",
"sizes": "152x152",
"type": "image/png"
},
{
"src": "assets/icons/icon-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "assets/icons/icon-384x384.png",
"sizes": "384x384",
"type": "image/png"
},
{
"src": "assets/icons/icon-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
]
}

27
src/ngsw-config.json Normal file
View File

@ -0,0 +1,27 @@
{
"index": "/index.html",
"assetGroups": [
{
"name": "app",
"installMode": "prefetch",
"resources": {
"files": [
"/favicon.ico",
"/index.html",
"/*.css",
"/*.js"
]
}
}, {
"name": "assets",
"installMode": "lazy",
"updateMode": "prefetch",
"resources": {
"files": [
"/assets/**",
"/*.(eot|svg|cur|jpg|png|webp|gif|otf|ttf|woff|woff2|ani)"
]
}
}
]
}