Merge pull request #3815 from tsiegleauq/MobileObserveService

ViewportService and new Prefix
This commit is contained in:
Finn Stutzenstein 2018-08-23 17:38:42 +02:00 committed by GitHub
commit cbf8a33b8d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 90 additions and 41 deletions

View File

@ -1,13 +1,13 @@
{ {
"/users/login": { "/apps/users/login": {
"target": "http://localhost:8000", "target": "http://localhost:8000",
"secure": false "secure": false
}, },
"/users/logout": { "/apps/users/logout": {
"target": "http://localhost:8000", "target": "http://localhost:8000",
"secure": false "secure": false
}, },
"/users/whoami": { "/apps/users/whoami": {
"target": "http://localhost:8000", "target": "http://localhost:8000",
"secure": false "secure": false
}, },

View File

@ -12,6 +12,7 @@ import { OperatorService } from './services/operator.service';
import { WebsocketService } from './services/websocket.service'; import { WebsocketService } from './services/websocket.service';
import { AddHeaderInterceptor } from './http-interceptor'; import { AddHeaderInterceptor } from './http-interceptor';
import { DataSendService } from './services/data-send.service'; import { DataSendService } from './services/data-send.service';
import { ViewportService } from './services/viewport.service';
/** Global Core Module. Contains all global (singleton) services /** Global Core Module. Contains all global (singleton) services
* *
@ -26,6 +27,7 @@ import { DataSendService } from './services/data-send.service';
DataStoreService, DataStoreService,
DataSendService, DataSendService,
OperatorService, OperatorService,
ViewportService,
WebsocketService, WebsocketService,
{ {
provide: HTTP_INTERCEPTORS, provide: HTTP_INTERCEPTORS,

View File

@ -5,6 +5,7 @@ import { catchError, tap } from 'rxjs/operators';
import { OperatorService } from 'app/core/services/operator.service'; import { OperatorService } from 'app/core/services/operator.service';
import { OpenSlidesComponent } from '../../openslides.component'; import { OpenSlidesComponent } from '../../openslides.component';
import { environment } from 'environments/environment';
/** /**
* Authenticates an OpenSlides user with username and password * Authenticates an OpenSlides user with username and password
@ -44,7 +45,7 @@ export class AuthService extends OpenSlidesComponent {
username: username, username: username,
password: password password: password
}; };
return this.http.post<any>('/users/login/', user).pipe( return this.http.post<any>(environment.urlPrefix + '/users/login/', user).pipe(
tap(resp => this.operator.storeUser(resp.user)), tap(resp => this.operator.storeUser(resp.user)),
catchError(this.handleError()) catchError(this.handleError())
); );
@ -60,6 +61,6 @@ export class AuthService extends OpenSlidesComponent {
//TODO not yet used //TODO not yet used
logout(): Observable<any> { logout(): Observable<any> {
this.operator.clear(); this.operator.clear();
return this.http.post<any>('/users/logout/', {}); return this.http.post<any>(environment.urlPrefix + '/users/logout/', {});
} }
} }

View File

@ -5,6 +5,7 @@ import { tap, catchError, share } from 'rxjs/operators';
import { OpenSlidesComponent } from 'app/openslides.component'; import { OpenSlidesComponent } from 'app/openslides.component';
import { Group } from 'app/shared/models/users/group'; import { Group } from 'app/shared/models/users/group';
import { User } from '../../shared/models/users/user'; import { User } from '../../shared/models/users/user';
import { environment } from 'environments/environment';
/** /**
* The operator represents the user who is using OpenSlides. * The operator represents the user who is using OpenSlides.
@ -74,10 +75,10 @@ export class OperatorService extends OpenSlidesComponent {
} }
/** /**
* calls `/users/whoami` to find out the real operator * calls `/apps/users/whoami` to find out the real operator
*/ */
public whoAmI(): Observable<any> { public whoAmI(): Observable<any> {
return this.http.get<any>('/users/whoami/').pipe( return this.http.get<any>(environment.urlPrefix + '/users/whoami/').pipe(
tap(whoami => { tap(whoami => {
if (whoami && whoami.user) { if (whoami && whoami.user) {
this.storeUser(whoami.user as User); this.storeUser(whoami.user as User);

View File

@ -0,0 +1,15 @@
import { TestBed, inject } from '@angular/core/testing';
import { ViewportService } from './viewport.service';
describe('ViewportService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
providers: [ViewportService]
});
});
it('should be created', inject([ViewportService], (service: ViewportService) => {
expect(service).toBeTruthy();
}));
});

View File

@ -0,0 +1,30 @@
import { Injectable } from '@angular/core';
import { BreakpointObserver, Breakpoints, BreakpointState } from '@angular/cdk/layout';
@Injectable({
providedIn: 'root'
})
export class ViewportService {
/**
* True if Viewport equals mobile or small resolution.
*/
private _isMobile = false;
constructor(private breakpointObserver: BreakpointObserver) {}
checkForChange() {
this.breakpointObserver
.observe([Breakpoints.Small, Breakpoints.HandsetPortrait])
.subscribe((state: BreakpointState) => {
if (state.matches) {
this._isMobile = true;
} else {
this._isMobile = false;
}
});
}
get isMobile() {
return this._isMobile;
}
}

View File

@ -1,5 +1,5 @@
<mat-sidenav-container autosize class='main-container'> <mat-sidenav-container autosize class='main-container'>
<mat-sidenav #sideNav [mode]="isMobile ? 'push' : 'side'" [opened]='!isMobile' disableClose='!isMobile' class="side-panel"> <mat-sidenav #sideNav [mode]="vp.isMobile ? 'push' : 'side'" [opened]='!vp.isMobile' disableClose='!vp.isMobile' class="side-panel">
<mat-toolbar class='nav-toolbar'> <mat-toolbar class='nav-toolbar'>
<!-- logo --> <!-- logo -->
<mat-toolbar-row class='os-logo-container'> <mat-toolbar-row class='os-logo-container'>
@ -42,39 +42,39 @@
<!-- navigation --> <!-- navigation -->
<mat-nav-list class='main-nav'> <mat-nav-list class='main-nav'>
<a [@navItemAnim] *appOsPerms="['core.can_see_frontpage']" mat-list-item routerLink='/' routerLinkActive='active' [routerLinkActiveOptions]="{exact: true}" <a [@navItemAnim] *appOsPerms="['core.can_see_frontpage']" mat-list-item routerLink='/' routerLinkActive='active' [routerLinkActiveOptions]="{exact: true}"
(click)='isMobile ? sideNav.toggle() : null'> (click)='toggleSideNav()'>
<fa-icon icon='home'></fa-icon> <fa-icon icon='home'></fa-icon>
<span translate>Home</span> <span translate>Home</span>
</a> </a>
<a [@navItemAnim] *appOsPerms="['agenda.can_see']" mat-list-item routerLink='/agenda' routerLinkActive='active' (click)='isMobile ? sideNav.toggle() : null'> <a [@navItemAnim] *appOsPerms="['agenda.can_see']" mat-list-item routerLink='/agenda' routerLinkActive='active' (click)='toggleSideNav()'>
<fa-icon icon='calendar'></fa-icon> <fa-icon icon='calendar'></fa-icon>
<span translate>Agenda</span> <span translate>Agenda</span>
</a> </a>
<a [@navItemAnim] *appOsPerms="['motions.can_see']" mat-list-item routerLink='/motions' routerLinkActive='active' (click)='isMobile ? sideNav.toggle() : null'> <a [@navItemAnim] *appOsPerms="['motions.can_see']" mat-list-item routerLink='/motions' routerLinkActive='active' (click)='toggleSideNav()'>
<fa-icon icon='file-alt'></fa-icon> <fa-icon icon='file-alt'></fa-icon>
<span translate>Motions</span> <span translate>Motions</span>
</a> </a>
<a [@navItemAnim] *appOsPerms="['assignments.can_see']" mat-list-item routerLink='/assignments' routerLinkActive='active' <a [@navItemAnim] *appOsPerms="['assignments.can_see']" mat-list-item routerLink='/assignments' routerLinkActive='active'
(click)='isMobile ? sideNav.toggle() : null'> (click)='vp.isMobile ? sideNav.toggle() : null'>
<fa-icon icon='chart-pie'></fa-icon> <fa-icon icon='chart-pie'></fa-icon>
<span translate>Assignments</span> <span translate>Assignments</span>
</a> </a>
<a [@navItemAnim] *appOsPerms="['users.can_see_name']" mat-list-item routerLink='/users' routerLinkActive='active' (click)='isMobile ? sideNav.toggle() : null'> <a [@navItemAnim] *appOsPerms="['users.can_see_name']" mat-list-item routerLink='/users' routerLinkActive='active' (click)='toggleSideNav()'>
<fa-icon icon='user'></fa-icon> <fa-icon icon='user'></fa-icon>
<span translate>Participants</span> <span translate>Participants</span>
</a> </a>
<a [@navItemAnim] *appOsPerms="['mediafiles.can_see']" mat-list-item routerLink='/mediafiles' routerLinkActive='active' (click)='isMobile ? sideNav.toggle() : null'> <a [@navItemAnim] *appOsPerms="['mediafiles.can_see']" mat-list-item routerLink='/mediafiles' routerLinkActive='active' (click)='toggleSideNav()'>
<fa-icon icon='paperclip'></fa-icon> <fa-icon icon='paperclip'></fa-icon>
<span translate>Files</span> <span translate>Files</span>
</a> </a>
<a [@navItemAnim] *appOsPerms="['core.can_manage_config']" mat-list-item routerLink='/settings' routerLinkActive='active' <a [@navItemAnim] *appOsPerms="['core.can_manage_config']" mat-list-item routerLink='/settings' routerLinkActive='active'
(click)='isMobile ? sideNav.toggle() : null'> (click)='toggleSideNav()'>
<fa-icon icon='cog'></fa-icon> <fa-icon icon='cog'></fa-icon>
<span translate>Settings</span> <span translate>Settings</span>
</a> </a>
<mat-divider></mat-divider> <mat-divider></mat-divider>
<a [@navItemAnim] *appOsPerms="['core.can_see_projector']" mat-list-item routerLink='/projector' routerLinkActive='active' <a [@navItemAnim] *appOsPerms="['core.can_see_projector']" mat-list-item routerLink='/projector' routerLinkActive='active'
(click)='isMobile ? sideNav.toggle() : null'> (click)='toggleSideNav()'>
<fa-icon icon='video'></fa-icon> <fa-icon icon='video'></fa-icon>
<span translate>Projector</span> <span translate>Projector</span>
</a> </a>
@ -83,18 +83,15 @@
<footer> <footer>
<button mat-button> <button mat-button>
<a routerLink='/legalnotice' (click)='isMobile ? sideNav.toggle() : null'> <a routerLink='/legalnotice' (click)='toggleSideNav()'>
<span translate>Legal Notice</span> <span translate>Legal Notice</span>
</a> </a>
</button> </button>
<button mat-button> <button mat-button>
<a routerLink='/privacypolicy' (click)='isMobile ? sideNav.toggle() : null'> <a routerLink='/privacypolicy' (click)='toggleSideNav()'>
<span translate>Privacy Policy</span> <span translate>Privacy Policy</span>
</a> </a>
</button> </button>
<!-- <button mat-button (click)='openPrivacyPolicy()'>
<span translate>Privacy Policy</span>
</button> -->
<br> <br>
<span align="center">© Copyright by <span align="center">© Copyright by
<a href='https://openslides.org/'>OpenSlides</a> <a href='https://openslides.org/'>OpenSlides</a>
@ -107,7 +104,7 @@
<mat-toolbar color='primary'> <mat-toolbar color='primary'>
<!-- show/hide menu button --> <!-- show/hide menu button -->
<button mat-icon-button *ngIf="isMobile" (click)='sideNav.toggle()'> <button mat-icon-button *ngIf="vp.isMobile" (click)='sideNav.toggle()'>
<fa-icon icon='bars'></fa-icon> <fa-icon icon='bars'></fa-icon>
</button> </button>
@ -122,4 +119,4 @@
<main [@pageTransition]="o.isActivated ? o.activatedRoute : ''"> <main [@pageTransition]="o.isActivated ? o.activatedRoute : ''">
<router-outlet #o="outlet"></router-outlet> <router-outlet #o="outlet"></router-outlet>
</main> </main>
</mat-sidenav-container> </mat-sidenav-container>

View File

@ -1,4 +1,4 @@
import { Component, OnInit, HostBinding } from '@angular/core'; import { Component, OnInit, HostBinding, ViewChild } from '@angular/core';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { BreakpointObserver, Breakpoints, BreakpointState } from '@angular/cdk/layout'; import { BreakpointObserver, Breakpoints, BreakpointState } from '@angular/cdk/layout';
@ -9,7 +9,8 @@ import { OperatorService } from 'app/core/services/operator.service';
import { TranslateService } from '@ngx-translate/core'; //showcase import { TranslateService } from '@ngx-translate/core'; //showcase
import { BaseComponent } from 'app/base.component'; import { BaseComponent } from 'app/base.component';
import { pageTransition, navItemAnim } from 'app/shared/animations'; import { pageTransition, navItemAnim } from 'app/shared/animations';
import { MatDialog } from '@angular/material'; import { MatDialog, MatSidenav } from '@angular/material';
import { ViewportService } from '../core/services/viewport.service';
@Component({ @Component({
selector: 'app-site', selector: 'app-site',
@ -18,16 +19,16 @@ import { MatDialog } from '@angular/material';
styleUrls: ['./site.component.scss'] styleUrls: ['./site.component.scss']
}) })
export class SiteComponent extends BaseComponent implements OnInit { export class SiteComponent extends BaseComponent implements OnInit {
/**
* HTML element of the side panel
*/
@ViewChild('sideNav') sideNav: MatSidenav;
/** /**
* Get the username from the operator (should be known already) * Get the username from the operator (should be known already)
*/ */
username = this.operator.username; username = this.operator.username;
/**
* True if Viewport equals mobile or small resolution. Set by breakpointObserver.
*/
isMobile = false;
/** /**
* Constructor * Constructor
* *
@ -44,7 +45,7 @@ export class SiteComponent extends BaseComponent implements OnInit {
private autoupdateService: AutoupdateService, private autoupdateService: AutoupdateService,
private operator: OperatorService, private operator: OperatorService,
private router: Router, private router: Router,
private breakpointObserver: BreakpointObserver, public vp: ViewportService,
public translate: TranslateService, public translate: TranslateService,
public dialog: MatDialog public dialog: MatDialog
) { ) {
@ -55,15 +56,7 @@ export class SiteComponent extends BaseComponent implements OnInit {
* Initialize the site component * Initialize the site component
*/ */
ngOnInit() { ngOnInit() {
this.breakpointObserver this.vp.checkForChange();
.observe([Breakpoints.Small, Breakpoints.HandsetPortrait])
.subscribe((state: BreakpointState) => {
if (state.matches) {
this.isMobile = true;
} else {
this.isMobile = false;
}
});
// get a translation via code: use the translation service // get a translation via code: use the translation service
// this.translate.get('Motions').subscribe((res: string) => { // this.translate.get('Motions').subscribe((res: string) => {
@ -82,6 +75,15 @@ export class SiteComponent extends BaseComponent implements OnInit {
}); });
} }
/**
* Closes the sidenav in mobile view
*/
toggleSideNav() {
if (this.vp.isMobile) {
this.sideNav.toggle();
}
}
/** /**
* Let the user change the language * Let the user change the language
* @param lang the desired language (en, de, fr, ...) * @param lang the desired language (en, de, fr, ...)

View File

@ -3,7 +3,8 @@
// The list of file replacements can be found in `angular.json`. // The list of file replacements can be found in `angular.json`.
export const environment = { export const environment = {
production: false production: false,
urlPrefix: '/apps'
}; };
/* /*