zooming is implemented

This commit is contained in:
Richard Park 2020-01-13 13:55:54 +09:00
parent ff87a0540e
commit d47dd78e4b
25 changed files with 312 additions and 29 deletions

View File

@ -5,7 +5,8 @@ import {
Tray,
Menu,
shell,
dialog
dialog,
webFrame
} from 'electron';
import path from 'path';
import fse from 'fs-extra';

View File

@ -274,14 +274,14 @@
{{ 'profile.open' | translate }}
</button>
</div>
<!-- <div class="setting">
<div class="setting">
<button
mat-menu-item
class="zoom minus-square"
(click)="onClickZoomOut($event)"
>
축소</button
><span class="set-size">100%</span
><span class="set-size" (click)="onClickZoomLabel($event)">{{ zoom }}%</span
><button
mat-menu-item
class="zoom plus-square"
@ -289,7 +289,7 @@
>
확대
</button>
</div> -->
</div>
<div class="setting">
<button mat-menu-item (click)="onClickNotice()">
{{ 'notice.label' | translate }}
@ -334,13 +334,20 @@
<div class="setting">
<button mat-menu-item (click)="onClickStatusBusy($event, 1)">
<span class="presence pcOther"> </span>
{{ loginRes?.statusMessage1 }}
<ucap-inline-edit-input
#statusMessage1
(click)="$event.stopPropagation()"
>
<span ucapInlineEditInput="view">{{ loginRes?.statusMessage1 }}</span>
<span ucapInlineEditInput="edit"><input matInput type="text"/></span>
</ucap-inline-edit-input>
<!-- {{ loginRes?.statusMessage1 }} -->
</button>
<button
<!-- <button
mat-menu-item
class="edit"
(click)="onClickChangeStatusBusy($event, 1)"
></button>
(click)="$event.stopPropagation(); statusMessage1.editMode = true"
></button> -->
</div>
<div class="setting">
<button mat-menu-item (click)="onClickStatusBusy($event, 2)">

View File

@ -20,6 +20,7 @@ import * as ChatStore from '@app/store/messenger/chat';
import * as AuthenticationStore from '@app/store/account/authentication';
import * as SettingsStore from '@app/store/messenger/settings';
import * as UpdateStore from '@app/store/setting/update';
import * as SettingNativeStore from '@app/store/setting/native';
import * as StatusStore from '@app/store/messenger/status';
import { LoginResponse } from '@ucap-webmessenger/protocol-authentication';
@ -60,6 +61,8 @@ import { MatMenu, MatRadioChange } from '@angular/material';
import { StatusCode, StatusType } from '@ucap-webmessenger/core';
import { StatusInfo } from '@ucap-webmessenger/protocol-status';
const zoomFactors = [60, 70, 85, 100, 120, 145, 170, 200];
@Component({
selector: 'app-layout-native-top-bar',
templateUrl: './top-bar.component.html',
@ -81,6 +84,9 @@ export class TopBarComponent implements OnInit, OnDestroy {
myIdleCheckTime: number;
myIdleCheckTimeSubscription: Subscription;
zoom: number;
zoomSubscription: Subscription;
loginInfo: LoginInfo;
weblink: WebLink[] = [];
webLinkBadgeMail = 0;
@ -129,24 +135,35 @@ export class TopBarComponent implements OnInit, OnDestroy {
.subscribe();
this.myStatusSubscription = this.store
.pipe(select(AppStore.MessengerSelector.StatusSelector.selectedMyStatus))
.pipe(select(AppStore.MessengerSelector.StatusSelector.selectMyStatus))
.subscribe(myStatus => {
this.myStatus = myStatus;
});
this.myIdleCheckTimeSubscription = this.store
.pipe(
select(
AppStore.MessengerSelector.StatusSelector.selectedMyIdleCheckTime
)
select(AppStore.MessengerSelector.StatusSelector.selectMyIdleCheckTime)
)
.subscribe(myIdleCheckTime => {
this.myIdleCheckTime = myIdleCheckTime;
});
this.zoomSubscription = this.store
.pipe(select(AppStore.SettingSelector.NativeSelector.selectZoom))
.subscribe(zoom => {
this.zoom = zoom;
});
this.updateInfo$ = this.store.pipe(
select(AppStore.SettingSelector.UpdateSelector.updateInfo)
);
const appUserInfo = this.localStorageService.encGet<AppUserInfo>(
KEY_APP_USER_INFO,
environment.customConfig.appKey
);
this.zoom = appUserInfo.zoom;
}
ngOnDestroy(): void {
@ -361,15 +378,27 @@ export class TopBarComponent implements OnInit, OnDestroy {
}
onClickZoomOut(event: Event) {
event.stopPropagation();
const i = zoomFactors.indexOf(this.zoom);
if (-1 === i || 0 === i) {
return;
}
this.document.body.style.zoom = '80%';
const zoom = zoomFactors[i - 1];
this.store.dispatch(SettingNativeStore.changeZoom({ zoom }));
}
onClickZoomLabel(event: Event) {
this.store.dispatch(SettingNativeStore.changeZoom({ zoom: 100 }));
}
onClickZoomIn(event: Event) {
event.stopPropagation();
const i = zoomFactors.indexOf(this.zoom);
if (-1 === i || zoomFactors.length - 1 === i) {
return;
}
this.document.body.style.zoom = '120%';
const zoom = zoomFactors[i + 1];
this.store.dispatch(SettingNativeStore.changeZoom({ zoom }));
}
onClickRemoteSupport(event: Event) {

View File

@ -137,9 +137,7 @@ export class MainPageComponent implements OnInit, OnDestroy {
this.myIdleCheckTimeSubscription = this.store
.pipe(
select(
AppStore.MessengerSelector.StatusSelector.selectedMyIdleCheckTime
)
select(AppStore.MessengerSelector.StatusSelector.selectMyIdleCheckTime)
)
.subscribe(checkTime => {
this.nativeService.changeLimitOfIdleState(checkTime);

View File

@ -58,6 +58,7 @@ import * as OptionStore from '@app/store/messenger/option';
import * as QueryStore from '@app/store/messenger/query';
import * as SyncStore from '@app/store/messenger/sync';
import * as StatusStore from '@app/store/messenger/status';
import * as SettingInitStore from '@app/store/setting/init';
import { KEY_LOGIN_RES_INFO, KEY_VER_INFO } from '@app/types';
import { environment } from '../../environments/environment';

View File

@ -66,6 +66,7 @@ export class AppAuthenticationService {
if (!appUserInfo) {
appUserInfo = {
idleCheckTime: 10,
zoom: 100,
settings: {
...environment.productConfig.defaultSettings,
chat: {

View File

@ -62,11 +62,8 @@ export function selectors<S>(selector: Selector<any, State>) {
ngeSelectEntitiesStatusBulkInfo,
(_, entities) => (!!entities ? entities[userSeq] : undefined)
),
selectedMyStatus: createSelector(
selector,
(state: State) => state.myStatus
),
selectedMyIdleCheckTime: createSelector(
selectMyStatus: createSelector(selector, (state: State) => state.myStatus),
selectMyIdleCheckTime: createSelector(
selector,
(state: State) => state.myIdleCheckTime
)

View File

@ -5,19 +5,22 @@ import * as CompanyStore from './company';
import * as InitStore from './init';
import * as VersionInfoStore from './version-info';
import * as UpdateStore from './update';
import * as NativeStore from './native';
export interface State {
company: CompanyStore.State;
init: InitStore.State;
versionInfo: VersionInfoStore.State;
update: UpdateStore.State;
native: NativeStore.State;
}
export const effects: Type<any>[] = [
CompanyStore.Effects,
InitStore.Effects,
VersionInfoStore.Effects,
UpdateStore.Effects
UpdateStore.Effects,
NativeStore.Effects
];
export function reducers(state: State | undefined, action: Action) {
@ -25,7 +28,8 @@ export function reducers(state: State | undefined, action: Action) {
company: CompanyStore.reducer,
init: InitStore.reducer,
versionInfo: VersionInfoStore.reducer,
update: UpdateStore.reducer
update: UpdateStore.reducer,
native: NativeStore.reducer
})(state, action);
}
@ -42,6 +46,9 @@ export function selectors<S>(selector: Selector<any, State>) {
),
UpdateSelector: UpdateStore.selectors(
createSelector(selector, (state: State) => state.update)
),
NativeSelector: NativeStore.selectors(
createSelector(selector, (state: State) => state.native)
)
};
}

View File

@ -36,6 +36,7 @@ export class Effects {
),
{ dispatch: false }
);
constructor(
private actions$: Actions,
private translateService: TranslateService,

View File

@ -0,0 +1,17 @@
import { createAction, props } from '@ngrx/store';
export const changeZoom = createAction(
'[Setting::Native] Change Zoom',
props<{ zoom: number }>()
);
export const changeZoomSuccess = createAction(
'[Setting::Native] changeZoom Success',
props<{
zoom: number;
}>()
);
export const changeZoomFailure = createAction(
'[Setting::Native] changeZoom Failure',
props<{ error: any }>()
);

View File

@ -0,0 +1,77 @@
import { Injectable, Inject } from '@angular/core';
import { Actions, createEffect, ofType } from '@ngrx/effects';
import { map, tap } from 'rxjs/operators';
import { TranslateService } from '@ngx-translate/core';
import { TranslateService as UCapTranslateService } from '@ucap-webmessenger/ui';
import { DateService as UCapDateService } from '@ucap-webmessenger/ui';
import * as AuthenticationStore from '@app/store/account/authentication';
import { LocalStorageService } from '@ucap-webmessenger/web-storage';
import { AppUserInfo, KEY_APP_USER_INFO } from '@app/types/app-user-info.type';
import { environment } from '../../../../environments/environment';
import { changeZoom, changeZoomSuccess, changeZoomFailure } from './actions';
import { UCAP_NATIVE_SERVICE, NativeService } from '@ucap-webmessenger/native';
import { Store } from '@ngrx/store';
@Injectable()
export class Effects {
postLogin$ = createEffect(
() =>
this.actions$.pipe(
ofType(AuthenticationStore.postLogin),
map(action => action.loginRes),
tap(async loginRes => {
const appUserInfo = this.localStorageService.encGet<AppUserInfo>(
KEY_APP_USER_INFO,
environment.customConfig.appKey
);
this.store.dispatch(changeZoom({ zoom: appUserInfo.zoom }));
})
),
{ dispatch: false }
);
changeZoom$ = createEffect(
() =>
this.actions$.pipe(
ofType(changeZoom),
map(action => action.zoom),
tap(zoom => {
const appUserInfo = this.localStorageService.encGet<AppUserInfo>(
KEY_APP_USER_INFO,
environment.customConfig.appKey
);
this.nativeService
.zoomTo(zoom)
.then(f => {
this.localStorageService.encSet<AppUserInfo>(
KEY_APP_USER_INFO,
{
...appUserInfo,
zoom
},
environment.customConfig.appKey
);
this.store.dispatch(changeZoomSuccess({ zoom }));
})
.catch(reason => {
this.store.dispatch(changeZoomFailure({ error: reason }));
});
})
),
{ dispatch: false }
);
constructor(
private actions$: Actions,
private store: Store<any>,
@Inject(UCAP_NATIVE_SERVICE) private nativeService: NativeService,
private localStorageService: LocalStorageService
) {}
}

View File

@ -0,0 +1,4 @@
export * from './actions';
export * from './effects';
export * from './reducers';
export * from './state';

View File

@ -0,0 +1,13 @@
import { createReducer, on } from '@ngrx/store';
import { State, initialState } from './state';
import { changeZoomSuccess } from './actions';
export const reducer = createReducer(
initialState,
on(changeZoomSuccess, (state, action) => {
return {
...state,
zoom: action.zoom
} as State;
})
);

View File

@ -0,0 +1,16 @@
import { Selector, createSelector } from '@ngrx/store';
// tslint:disable-next-line: no-empty-interface
export interface State {
zoom: number;
}
export const initialState: State = {
zoom: 100
};
export function selectors<S>(selector: Selector<any, State>) {
return {
selectZoom: createSelector(selector, (state: State) => state.zoom)
};
}

View File

@ -11,6 +11,7 @@ export interface AppUserInfo {
companyGroupType?: string;
localeCode?: LocaleCode;
idleCheckTime?: number;
zoom?: number;
settings?: Settings;
}

View File

@ -215,6 +215,12 @@ export class BrowserNativeService extends NativeService {
windowMaximize(): void {}
zoomTo(factor: number): Promise<number> {
return new Promise<number>((resolve, reject) => {
resolve(-1);
});
}
idleStateChanged(): Observable<WindowIdle> {
return new Observable<WindowIdle>(subscriber => {
try {

View File

@ -1,4 +1,4 @@
import { ipcRenderer, remote, shell } from 'electron';
import { ipcRenderer, remote, shell, webFrame } from 'electron';
import { Observable, Subject } from 'rxjs';
@ -34,6 +34,7 @@ import { StatusCode } from '@ucap-webmessenger/core';
})
export class ElectronNativeService implements NativeService {
private ipcRenderer: typeof ipcRenderer;
private webFrame: typeof webFrame;
private remote: typeof remote;
private shell: typeof shell;
@ -377,6 +378,17 @@ export class ElectronNativeService implements NativeService {
}
}
zoomTo(factor: number): Promise<number> {
return new Promise<number>((resolve, reject) => {
try {
this.webFrame.setZoomFactor(factor / 100);
resolve(this.webFrame.getZoomFactor());
} catch (error) {
reject(error);
}
});
}
idleStateChanged(): Observable<WindowIdle> {
if (!this.idleStateChangedSubject) {
this.idleStateChangedSubject = new Subject<WindowIdle>();
@ -448,6 +460,7 @@ export class ElectronNativeService implements NativeService {
this.ipcRenderer = (window as any).require('electron').ipcRenderer;
this.remote = (window as any).require('electron').remote;
this.shell = (window as any).require('electron').shell;
this.webFrame = (window as any).require('electron').webFrame;
}
}
}

View File

@ -66,6 +66,7 @@ export abstract class NativeService {
abstract windowClose(): void;
abstract windowMinimize(): void;
abstract windowMaximize(): void;
abstract zoomTo(factor: number): Promise<number>;
abstract idleStateChanged(): Observable<WindowIdle>;
abstract changeLimitOfIdleState(limitTime: number): void;

View File

@ -1,6 +1,6 @@
<div>
<mat-list>
<h1 mat-subheader>{{ 'settings.chat.label' | translate }}</h1>
<!-- <h1 mat-subheader>{{ 'settings.chat.label' | translate }}</h1>
<mat-list-item>
<span class="item-title">{{
'settings.chat.fontFamily' | translate
@ -37,7 +37,7 @@
</span>
</mat-list-item>
<mat-divider></mat-divider>
<mat-divider></mat-divider> -->
<h1 mat-subheader *ngIf="_isNodeWebkit">
{{ 'settings.chat.file' | translate }}

View File

@ -0,0 +1,25 @@
<ng-container>
<ng-container #view *ngIf="!editMode">
<ng-content select="[ucapInlineEditInput='view']"></ng-content>
<span class="view-actions">
<button mat-icon-button aria-label="Edit" (click)="onClickEdit($event)">
<span class="mdi mdi-square-edit-outline mdi-20px"></span>
</button>
</span>
</ng-container>
<ng-container #edit *ngIf="editMode">
<ng-content select="[ucapInlineEditInput='edit']"></ng-content>
<span class="edit-actions">
<button mat-icon-button aria-label="Apply" (click)="onClickApply($event)">
<span class="mdi mdi-check mdi-20px"></span>
</button>
<button
mat-icon-button
aria-label="Cacel"
(click)="onClickCancel($event)"
>
<span class="mdi mdi-close mdi-20px"></span>
</button>
</span>
</ng-container>
</ng-container>

View File

@ -0,0 +1,7 @@
.view-actions {
margin-right: 0px;
}
.edit-actions {
margin-right: 0px;
}

View File

@ -0,0 +1,25 @@
/* tslint:disable:no-unused-variable */
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { InlineEditInputComponent } from './inline-edit-input.component';
describe('InlineEditInputComponent', () => {
let component: InlineEditInputComponent;
let fixture: ComponentFixture<InlineEditInputComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [InlineEditInputComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(InlineEditInputComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,33 @@
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'ucap-inline-edit-input',
templateUrl: './inline-edit-input.component.html',
styleUrls: ['./inline-edit-input.component.scss']
})
export class InlineEditInputComponent implements OnInit {
get editMode() {
return this._editMode;
}
set editMode(editMode: boolean) {
this._editMode = editMode;
}
// tslint:disable-next-line: variable-name
_editMode = false;
constructor() {}
ngOnInit() {}
onClickEdit(event: Event) {
this.editMode = true;
}
onClickCancel(event: Event) {
this.editMode = false;
}
onClickApply(event: Event) {
this.editMode = false;
}
}

View File

@ -38,6 +38,7 @@ import { PickDateComponent } from './components/pick-date.component';
import { PickTimeComponent } from './components/pick-time.component';
import { StepInputComponent } from './components/step-input.component';
import { StickerSelectorComponent } from './components/sticker-selector.component';
import { InlineEditInputComponent } from './components/inline-edit-input.component';
import { BinaryViewerComponent } from './components/file-viewer/binary-viewer.component';
import { DocumentViewerComponent } from './components/file-viewer/document-viewer.component';
@ -88,6 +89,7 @@ const COMPONENTS = [
PickTimeComponent,
StepInputComponent,
TranslationSectionComponent,
InlineEditInputComponent,
BinaryViewerComponent,
DocumentViewerComponent,

View File

@ -18,6 +18,7 @@ export * from './lib/components/pick-time.component';
export * from './lib/components/step-input.component';
export * from './lib/components/split-button.component';
export * from './lib/components/sticker-selector.component';
export * from './lib/components/inline-edit-input.component';
export * from './lib/data-source/virtual-scroll-tree-flat.data-source';