This commit is contained in:
leejinho 2020-01-14 08:28:02 +09:00
commit 20110f2c2a
39 changed files with 685 additions and 74 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';
@ -396,6 +397,13 @@ ipcMain.on(
}
);
ipcMain.on(
MessengerChannel.GetVersionInfo,
(event: IpcMainEvent, ...args: any[]) => {
event.returnValue = app.getVersion();
}
);
ipcMain.on(
MessengerChannel.ChangeAutoLaunch,
(event: IpcMainEvent, ...args: any[]) => {

View File

@ -1,6 +1,6 @@
{
"name": "ucap-webmessenger",
"version": "0.0.2",
"version": "0.0.3",
"author": {
"name": "LG CNS",
"email": "lgucap@lgcns.com"

View File

@ -168,7 +168,7 @@
(click)="onClickProfileContextMenu('REGISTER_FAVORITE', userInfo)"
>
{{
(userInfo.isFavorit ? 'group.unfavorite' : 'group.favorite') | translate
(userInfo.isFavorit ? 'group.unfavorite' : 'group.favorit') | translate
}}
</button>
<button mat-menu-item (click)="onClickProfileContextMenu('CHAT', userInfo)">

View File

@ -130,7 +130,7 @@
'common.file.errors.expired' | translate
}}</span>
<button
mat-stroked-button
mat-flat-button
class="mat-primary"
(click)="downloadAttachFileAll()"
>
@ -143,7 +143,8 @@
<div>
<span class="mdi" [ngClass]="getFileStatusIcon(file)"></span>
<span>{{ file.resContent }}</span>
<span>{{ file.resSize | ucapBytes }}</span>
</div>
<span class="file-size">{{ file.resSize | ucapBytes }}</span>
<a>
<span
class="mdi mdi-download"
@ -151,7 +152,6 @@
(click)="downloadAttachFileSingle(file)"
></span>
</a>
</div>
</li>
</ul>
</div>

View File

@ -109,5 +109,42 @@
.ps {
align-items: flex-start;
}
.attachFile {
border-top: 1px solid #dddddd;
li {
display: flex;
flex-flow: row;
line-height: 2em;
flex: 1 1 auto;
.file-name {
display: inline-flex;
flex-flow: row;
flex: 1 1 auto;
border: 1px solid red;
white-space: nowrap;
word-wrap: normal;
overflow: hidden;
margin-right: 10px;
span:last-child {
overflow: hidden;
text-overflow: ellipsis;
display: block;
width: calc(100% - 40px);
}
}
.file-size {
display: flex;
margin-left: auto;
align-self: flex-end;
flex: 0 0 auto;
}
a {
display: inline-block;
width: 20px;
height: 100%;
margin-left: 10px;
}
}
}
}
}

View File

@ -281,7 +281,7 @@
(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"
@ -306,6 +306,12 @@
</button>
</div>
<mat-divider></mat-divider>
<div class="setting">
<button mat-menu-item [matMenuTriggerFor]="informationMenu">
{{ 'information.label' | translate }}
</button>
</div>
<mat-divider></mat-divider>
<div class="setting">
<button mat-menu-item (click)="onClickLogout()">
{{ 'accounts.logout' | translate }}
@ -334,35 +340,71 @@
<div class="setting">
<button mat-menu-item (click)="onClickStatusBusy($event, 1)">
<span class="presence pcOther"> </span>
{{ loginRes?.statusMessage1 }}
<ucap-inline-edit-input
(apply)="
$event.stopPropagation();
onApplyStatusMessage(1, statusMessage1.value)
"
(edit)="$event.stopPropagation()"
(cancel)="$event.stopPropagation()"
>
<span ucapInlineEditInput="view">{{ loginRes?.statusMessage1 }}</span>
<span ucapInlineEditInput="edit"
><input
matInput
#statusMessage1
type="text"
[value]="loginRes?.statusMessage1"
(click)="$event.stopPropagation()"
/></span>
</ucap-inline-edit-input>
</button>
<button
mat-menu-item
class="edit"
(click)="onClickChangeStatusBusy($event, 1)"
></button>
</div>
<div class="setting">
<button mat-menu-item (click)="onClickStatusBusy($event, 2)">
<span class="presence pcOther"> </span>
{{ loginRes?.statusMessage2 }}
<ucap-inline-edit-input
(apply)="
$event.stopPropagation();
onApplyStatusMessage(2, statusMessage2.value)
"
(edit)="$event.stopPropagation()"
(cancel)="$event.stopPropagation()"
>
<span ucapInlineEditInput="view">{{ loginRes?.statusMessage2 }}</span>
<span ucapInlineEditInput="edit"
><input
matInput
#statusMessage2
type="text"
[value]="loginRes?.statusMessage2"
(click)="$event.stopPropagation()"
/></span>
</ucap-inline-edit-input>
</button>
<button
mat-menu-item
class="edit"
(click)="onClickChangeStatusBusy($event, 2)"
></button>
</div>
<div class="setting">
<button mat-menu-item (click)="onClickStatusBusy($event, 3)">
<span class="presence pcOther"> </span>
{{ loginRes?.statusMessage3 }}
<ucap-inline-edit-input
(apply)="
$event.stopPropagation();
onApplyStatusMessage(3, statusMessage3.value)
"
(edit)="$event.stopPropagation()"
(cancel)="$event.stopPropagation()"
>
<span ucapInlineEditInput="view">{{ loginRes?.statusMessage3 }}</span>
<span ucapInlineEditInput="edit"
><input
matInput
#statusMessage3
type="text"
[value]="loginRes?.statusMessage3"
(click)="$event.stopPropagation()"
/></span>
</ucap-inline-edit-input>
</button>
<button
mat-menu-item
class="edit"
(click)="onClickChangeStatusBusy($event, 3)"
></button>
</div>
</mat-menu>
@ -370,28 +412,17 @@
<div mat-menu-item (click)="$event.stopPropagation()">
{{ 'presence.settingOfAwayTime' | translate }}
</div>
<mat-radio-group aria-label="Select an option" [value]="myIdleCheckTime">
<div mat-menu-item *ngFor="let awayTime of awayTimeList">
<mat-radio-button [value]="awayTime" (change)="onChangeAwayTime($event)">
{{ awayTime }}{{ 'common.units.minute' | translate }}</mat-radio-button
>
</div>
</mat-radio-group>
</mat-menu>
<mat-menu #informationMenu="matMenu">
<div mat-menu-item>
<mat-radio-button
value="10"
[checked]="myIdleCheckTime === 10"
(change)="onChangeAwayTime($event)"
>10{{ 'common.units.minute' | translate }}</mat-radio-button
>
</div>
<div mat-menu-item>
<mat-radio-button
value="20"
[checked]="myIdleCheckTime === 20"
(change)="onChangeAwayTime($event)"
>20{{ 'common.units.minute' | translate }}</mat-radio-button
>
</div>
<div mat-menu-item>
<mat-radio-button
value="30"
[checked]="myIdleCheckTime === 30"
(change)="onChangeAwayTime($event)"
>30{{ 'common.units.minute' | translate }}</mat-radio-button
>
{{ 'information.version' | translate }}: {{ appVersion }}
</div>
</mat-menu>

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';
@ -58,6 +59,9 @@ import { DOCUMENT } from '@angular/common';
import { MatMenu, MatRadioChange } from '@angular/material';
import { StatusCode, StatusType } from '@ucap-webmessenger/core';
import { StatusInfo } from '@ucap-webmessenger/protocol-status';
import { UserInfoUpdateType } from '@ucap-webmessenger/protocol-info';
const zoomFactors = [60, 70, 85, 100, 120, 145, 170, 200];
@Component({
selector: 'app-layout-native-top-bar',
@ -80,14 +84,21 @@ export class TopBarComponent implements OnInit, OnDestroy {
myIdleCheckTime: number;
myIdleCheckTimeSubscription: Subscription;
zoom: number;
zoomSubscription: Subscription;
loginInfo: LoginInfo;
weblink: WebLink[] = [];
webLinkBadgeMail = 0;
webLinkBadgePayment = 0;
appVersion: string;
WebLinkType = WebLinkType;
StatusCode = StatusCode;
readonly awayTimeList = [10, 20, 30];
@ViewChild('profileMenu', { static: true })
profileMenu: MatMenu;
@ -127,35 +138,50 @@ 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)
);
this.nativeService.getVersionInfo().then(ver => {
this.appVersion = ver;
});
}
ngOnDestroy(): void {
if (!!this.loginResSubscription) {
this.loginResSubscription.unsubscribe();
this.loginResSubscription = undefined;
}
if (!!this.myStatusSubscription) {
this.myStatusSubscription.unsubscribe();
this.myStatusSubscription = undefined;
}
if (!!this.myIdleCheckTimeSubscription) {
this.myIdleCheckTimeSubscription.unsubscribe();
this.myIdleCheckTimeSubscription = undefined;
}
if (!!this.zoomSubscription) {
this.zoomSubscription.unsubscribe();
this.zoomSubscription = undefined;
}
}
@ -357,15 +383,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) {
@ -419,6 +457,32 @@ export class TopBarComponent implements OnInit, OnDestroy {
);
}
onApplyStatusMessage(index: number, statusMessage: string) {
this.logger.debug('StatusMessage', index, statusMessage);
let updateType: UserInfoUpdateType;
switch (index) {
case 1:
updateType = UserInfoUpdateType.StatusMessage1;
break;
case 2:
updateType = UserInfoUpdateType.StatusMessage2;
break;
case 3:
updateType = UserInfoUpdateType.StatusMessage3;
break;
default:
return;
}
this.store.dispatch(
AuthenticationStore.infoUser({
req: { type: updateType, info: statusMessage }
})
);
}
onClickChangeStatusBusy(event: Event, index: number) {
event.stopPropagation();
}

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

@ -8,6 +8,7 @@ import {
UserPasswordSetRequest,
UserPasswordSetResponse
} from '@ucap-webmessenger/protocol-service';
import { UserRequest, UserResponse } from '@ucap-webmessenger/protocol-info';
export const webLogin = createAction(
'[Account::Authentication] Web Login',
@ -122,3 +123,20 @@ export const updateLoginRes = createAction(
loginRes: LoginResponse;
}>()
);
export const infoUser = createAction(
'[Account::Authentication] Info User',
props<{ req: UserRequest }>()
);
export const infoUserSuccess = createAction(
'[Account::Authentication] Info User Success',
props<{
res: UserResponse;
}>()
);
export const infoUserFailure = createAction(
'[Account::Authentication] Info User Failure',
props<{ error: any }>()
);

View File

@ -41,7 +41,10 @@ import {
logoutInitialize,
userPasswordSet,
userPasswordSetSuccess,
userPasswordSetFailure
userPasswordSetFailure,
infoUser,
infoUserSuccess,
infoUserFailure
} from './actions';
import {
LoginInfo,
@ -77,6 +80,10 @@ import { DaesangUrlInfoResponse } from '@ucap-webmessenger/api-external';
import { AppUserInfo, KEY_APP_USER_INFO } from '@app/types/app-user-info.type';
import { DaesangCipherService, WebLinkType } from '@ucap-webmessenger/daesang';
import { TranslateService } from '@ngx-translate/core';
import {
InfoProtocolService,
UserResponse
} from '@ucap-webmessenger/protocol-info';
@Injectable()
export class Effects {
@ -482,6 +489,23 @@ export class Effects {
{ dispatch: false }
);
infoUser$ = createEffect(() =>
this.actions$.pipe(
ofType(infoUser),
map(action => action.req),
exhaustMap(req =>
this.infoProtocolService.user(req).pipe(
map((res: UserResponse) => {
return infoUserSuccess({
res
});
}),
catchError(error => of(infoUserFailure({ error })))
)
)
)
);
constructor(
private actions$: Actions,
private ngZone: NgZone,
@ -493,6 +517,7 @@ export class Effects {
private appAuthenticationService: AppAuthenticationService,
private protocolService: ProtocolService,
private authenticationProtocolService: AuthenticationProtocolService,
private infoProtocolService: InfoProtocolService,
private serviceProtocolService: ServiceProtocolService,
private translateService: TranslateService,
private dialogService: DialogService,

View File

@ -6,8 +6,10 @@ import {
initialLoginFailCount,
logout,
logoutInitialize,
updateLoginRes
updateLoginRes,
infoUserSuccess
} from './actions';
import { UserInfoUpdateType } from '@ucap-webmessenger/protocol-info';
export const reducer = createReducer(
initialState,
@ -39,6 +41,57 @@ export const reducer = createReducer(
};
}),
on(infoUserSuccess, (state, action) => {
let loginRes = {
...state.loginRes
};
switch (action.res.type) {
case UserInfoUpdateType.Image:
loginRes = {
...loginRes
};
break;
case UserInfoUpdateType.Intro:
loginRes = {
...loginRes
};
break;
case UserInfoUpdateType.TelephoneVisible:
loginRes = {
...loginRes
};
break;
case UserInfoUpdateType.StatusMessage1:
loginRes = {
...loginRes,
statusMessage1: action.res.info
};
break;
case UserInfoUpdateType.StatusMessage2:
loginRes = {
...loginRes,
statusMessage2: action.res.info
};
break;
case UserInfoUpdateType.StatusMessage3:
loginRes = {
...loginRes,
statusMessage3: action.res.info
};
break;
default:
break;
}
return {
...state,
loginRes: {
...loginRes
}
};
}),
on(logoutInitialize, (state, action) => {
return {
...initialState

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

@ -67,6 +67,10 @@
"failToChangeProfileImage": "Failed to change profile image."
}
},
"information": {
"label": "Information",
"version": "Version"
},
"settings": {
"label": "Settings",
"typeGenernal": "Genernal",

View File

@ -67,6 +67,10 @@
"failToChangeProfileImage": "프로필 이미지 변경에 실패 하였습니다."
}
},
"information": {
"label": "정보",
"version": "버전"
},
"settings": {
"label": "설정",
"typeGenernal": "일반",

View File

@ -78,6 +78,12 @@ export class BrowserNativeService extends NativeService {
});
}
getVersionInfo(): Promise<string> {
return new Promise<any>((resolve, reject) => {
resolve('');
});
}
changeAutoLaunch(autoLaunch: boolean): Promise<boolean> {
return new Promise<boolean>((resolve, reject) => {
resolve(true);
@ -215,6 +221,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;
@ -89,6 +90,16 @@ export class ElectronNativeService implements NativeService {
});
}
getVersionInfo(): Promise<string> {
return new Promise<string>((resolve, reject) => {
try {
resolve(this.ipcRenderer.sendSync(MessengerChannel.GetVersionInfo));
} catch (error) {
reject(error);
}
});
}
changeStatus(): Observable<StatusCode> {
if (!this.changeStatusSubject) {
this.changeStatusSubject = new Subject<StatusCode>();
@ -377,6 +388,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 +470,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

@ -5,7 +5,8 @@ export enum MessengerChannel {
ChangeAutoLaunch = 'UCAP::messenger::changeAutoLaunch',
ChangeStartupHideWindow = 'UCAP::messenger::changeStartupHideWindow',
ChangeDownloadPath = 'UCAP::messenger::changeDownloadPath',
GetNetworkInfo = 'UCAP::messenger::getNetworkInfo'
GetNetworkInfo = 'UCAP::messenger::getNetworkInfo',
GetVersionInfo = 'UCAP::messenger::getVersionInfo'
}
export enum ChatChannel {

View File

@ -25,6 +25,7 @@ export abstract class NativeService {
abstract showSetting(): Observable<void>;
abstract getNetworkInfo(): Promise<any>;
abstract getVersionInfo(): Promise<string>;
abstract changeAutoLaunch(autoLaunch: boolean): Promise<boolean>;
abstract changeStartupHideWindow(
@ -66,6 +67,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

@ -4,7 +4,13 @@ export enum UserInfoUpdateType {
// R: 인트로소개
Intro = 'R',
// T: 전화보이기여부
TelephoneVisible = 'T'
TelephoneVisible = 'T',
/** StatusMessage1 */
StatusMessage1 = 'S1',
/** StatusMessage2 */
StatusMessage2 = 'S2',
/** StatusMessage3 */
StatusMessage3 = 'S3'
}
export enum TelephoneVisibleType {

View File

@ -1,5 +1,5 @@
<!--체크박스 보여줄때는 <div class="list-item checkbox" matRipple> 클래스에 checkbox만 추가-->
<div class="list-item" *ngIf="message" matRipple>
<!--<div class="list-item" *ngIf="message" matRipple>
<dl class="item-default">
<dt>
<mat-icon
@ -34,4 +34,39 @@
</div>
</dd>
</dl>
</div>-->
<div class="list-item message-list" *ngIf="message" matRipple>
<dl class="item-default">
<dt>
<span class="name">
{{ message.userName }}
<span *ngIf="message.userCount > 1" class="text-accent-color number">
{{
'message.andOthers' | translate: { count: message.userCount - 1 }
}}
</span>
</span>
</dt>
<dd>
<mat-icon
*ngIf="!!message.resType && message.resType === ContentType.Image"
>image</mat-icon
>
<mat-icon
*ngIf="!!message.resType && message.resType === ContentType.AttachFile"
>attach_file</mat-icon
>
{{ message.title }}
</dd>
</dl>
<div class="date">
<span>{{ message.regDate | ucapDate: 'MM:DD' }}</span>
<span
*ngIf="message.type === MessageType.Receive && !message.readYn"
class="badge-new bg-warn-darkest"
>
N
</span>
</div>
</div>

View File

@ -0,0 +1,52 @@
.message-list {
&.list-item {
dl {
display: flex;
flex-flow: column;
flex: 1 1 auto;
width: calc(100% - 80px);
dt {
.name {
font-size: 16px;
overflow: hidden;
text-overflow: ellipsis;
display: block;
white-space: nowrap;
word-wrap: normal;
font-weight: 600;
span {
font-size: 13px;
}
}
}
dd {
color: #777777;
margin-top: 4px;
white-space: nowrap;
word-wrap: normal;
text-overflow: ellipsis;
display: block;
width: 100%;
position: relative;
overflow: hidden;
}
}
.date {
display: flex;
flex-flow: column;
align-self: flex-start;
.badge-new {
border-radius: 50%;
color: #ffffff;
padding: 0;
text-align: center;
align-self: flex-end;
width: 20px;
height: 20px;
font-size: 12px;
margin-top: 4px;
}
}
}
}

View File

@ -1,5 +1,5 @@
<!--체크박스 보여줄때는 <div class="list-item checkbox" matRipple> 클래스에 checkbox만 추가-->
<div class="list-item" *ngIf="userInfo" matRipple>
<div class="list-item checkbox" *ngIf="userInfo" matRipple>
<!--pcOn , pcOut pcOff , pcOther-->
<span class="presence" [ngClass]="getPresence(PresenceType.PC)"> </span>
<dl class="item-default">

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,43 @@
import { Component, OnInit, Output, EventEmitter } 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 {
@Output()
edit = new EventEmitter<Event>();
@Output()
cancel = new EventEmitter<Event>();
@Output()
apply = new EventEmitter<Event>();
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;
this.edit.emit(event);
}
onClickCancel(event: Event) {
this.editMode = false;
this.cancel.emit(event);
}
onClickApply(event: Event) {
this.editMode = false;
this.apply.emit(event);
}
}

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';