This commit is contained in:
병준 박 2019-12-27 14:07:14 +09:00
commit a7fb5b2c4b
17 changed files with 744 additions and 183 deletions

View File

@ -51,7 +51,7 @@
</ng-template>
<ucap-message-list-item
*ngFor="let message of messageList"
*ngFor="let message of messageRetrieveList$ | async"
[message]="message"
(click)="onClickDetail(message)"
class="message-item"
@ -63,7 +63,7 @@
</ng-template>
<ucap-message-list-item
*ngFor="let message of messageList"
*ngFor="let message of messageSendList$ | async"
[message]="message"
(click)="onClickDetail(message)"
class="message-item"
@ -75,7 +75,7 @@
</ng-template>
<ucap-message-list-item
*ngFor="let message of messageList"
*ngFor="let message of messageReservationList$ | async"
[message]="message"
(click)="onClickDetail(message)"
class="message-item"
@ -116,7 +116,7 @@
</div>
<div>
<ucap-message-list-item
*ngFor="let message of messageList"
*ngFor="let message of messageSearchList$ | async"
[message]="message"
(click)="onClickDetail(message)"
class="message-item"

View File

@ -8,8 +8,8 @@ import {
Input,
AfterViewChecked
} from '@angular/core';
import { Subscription, of } from 'rxjs';
import { Store } from '@ngrx/store';
import { of, Observable } from 'rxjs';
import { Store, select } from '@ngrx/store';
import { map, catchError, take } from 'rxjs/operators';
import { NGXLogger } from 'ngx-logger';
@ -21,9 +21,7 @@ import { KEY_LOGIN_RES_INFO } from '@app/types/login-res-info.type';
import {
MessageApiService,
MessageType,
RetrieveRequest,
MessageList,
RetrieveSearchRequest,
MessageSearchType,
DetailRequest,
MessageDetailInfo,
@ -50,6 +48,9 @@ import {
KEY_VER_INFO
} from '@app/types';
import * as AppStore from '@app/store';
import * as MessageStore from '@app/store/messenger/message';
@Component({
selector: 'app-layout-chat-left-sidenav-message',
templateUrl: './message.component.html',
@ -73,19 +74,13 @@ export class MessageBoxComponent
sessionVerinfo: VersionInfo2Response;
environmentsInfo: EnvironmentsInfo;
messageList: MessageList[] = [];
messageRecieveListSubscription: Subscription;
messageSendListSubscription: Subscription;
messageReservationListSubscription: Subscription;
messageSearchListSubscription: Subscription;
messageRetrieveList$: Observable<MessageList[]>;
messageSendList$: Observable<MessageList[]>;
messageReservationList$: Observable<MessageList[]>;
messageSearchList$: Observable<MessageList[]>;
currentTabIndex = 0;
defaultPageSize = 1000; // default
recieveCurrentPage = 0; // start index is 0.
sendCurrentPage = 0; // start index is 0.
reservationCurrentPage = 0; // start index is 0.
searchCurrentPage = 0; // start index is 0.
currentTotalCount = 0;
currentPage = 0;
@ -124,8 +119,23 @@ export class MessageBoxComponent
searchMessageSearchType: [MessageSearchType.Name]
});
this.messageRetrieveList$ = this.store.pipe(
select(AppStore.MessengerSelector.MessageSelector.selectAllReceiveList)
);
this.messageSendList$ = this.store.pipe(
select(AppStore.MessengerSelector.MessageSelector.selectAllSendList)
);
this.messageReservationList$ = this.store.pipe(
select(
AppStore.MessengerSelector.MessageSelector.selectAllReservationList
)
);
this.messageSearchList$ = this.store.pipe(
select(AppStore.MessengerSelector.MessageSelector.selectAllSearchList)
);
// 초기 검색은 수신함.
this.getRetrieveMessage(MessageType.Receive, this.recieveCurrentPage);
this.getRetrieveMessage(MessageType.Receive, 0);
if (!!this.tabs) {
this.tabs.realignInkBar();
@ -139,51 +149,34 @@ export class MessageBoxComponent
}
}
ngOnDestroy(): void {
if (!!this.messageRecieveListSubscription) {
this.messageRecieveListSubscription.unsubscribe();
}
if (!!this.messageSendListSubscription) {
this.messageSendListSubscription.unsubscribe();
}
if (!!this.messageReservationListSubscription) {
this.messageReservationListSubscription.unsubscribe();
}
if (!!this.messageSearchListSubscription) {
this.messageSearchListSubscription.unsubscribe();
}
}
ngOnDestroy(): void {}
onSelectedIndexTab(value: number) {
this.tabs.selectedIndex = value;
}
onSelectedIndexChange(value: number) {
this.currentTabIndex = value;
let type: MessageType;
let messageType: MessageType;
switch (value) {
case 0:
// Recieve
type = MessageType.Receive;
this.recieveCurrentPage = 0;
messageType = MessageType.Receive;
break;
case 1:
// Send
type = MessageType.Send;
this.sendCurrentPage = 0;
messageType = MessageType.Send;
break;
case 2:
// Reservation
type = MessageType.Reservation;
this.reservationCurrentPage = 0;
messageType = MessageType.Reservation;
break;
}
this.getRetrieveMessage(type, 0);
this.getRetrieveMessage(messageType, 0);
}
/** 쪽지 검색 관련 */
onChangeSelection(event: MatSelectChange) {
this.searchCurrentPage = 0;
this.getSearchMessage(
event.value,
this.fgSearchType.get('searchMessageSearchType').value,
@ -191,7 +184,6 @@ export class MessageBoxComponent
);
}
onChangeSearchType(event: MatRadioChange) {
this.searchCurrentPage = 0;
this.getSearchMessage(
this.fgSearchType.get('searchMessageType').value,
event.value,
@ -202,8 +194,6 @@ export class MessageBoxComponent
event.preventDefault();
event.stopPropagation();
this.searchCurrentPage = 0;
this.getSearchMessage(
MessageType.All,
MessageSearchType.Name,
@ -216,125 +206,27 @@ export class MessageBoxComponent
searchStr: string
) {
this.isSearch = true;
this.messageSendListSubscription = this.messageApiService
.retrieveSearchMessage({
userSeq: this.loginRes.userSeq,
deviceType: DeviceType.PC,
tokenKey: this.loginRes.tokenString,
type: messageType,
pageSize: this.defaultPageSize,
pageCount: this.searchCurrentPage,
searchTitle: searchType === MessageSearchType.Title ? searchStr : '',
searchName: searchType === MessageSearchType.Name ? searchStr : '',
searchContent:
searchType === MessageSearchType.Contents ? searchStr : ''
} as RetrieveSearchRequest)
.pipe(
take(1),
map(res => {
if (res.responseCode === MessageStatusCode.Success) {
this.currentTotalCount = res.totalCount;
this.currentPage = res.pageCount;
this.searchCurrentPage = res.pageCount;
this.messageList = res.messageList;
} else {
}
}),
catchError(error => of(this.logger.error(error)))
)
.subscribe();
this.store.dispatch(
MessageStore.searchMessage({
messageType,
searchType,
searchStr
})
);
}
onClickSearchCancel() {
this.isSearch = false;
this.getRetrieveMessage(MessageType.Receive, this.recieveCurrentPage);
this.getRetrieveMessage(MessageType.Receive, 0);
}
/** 쪽지 타입별 조회 */
getRetrieveMessage(type: MessageType, trgtPageIndex: number) {
switch (type) {
case MessageType.Receive:
{
this.messageSendListSubscription = this.messageApiService
.retrieveReceiveMessage({
userSeq: this.loginRes.userSeq,
deviceType: DeviceType.PC,
tokenKey: this.loginRes.tokenString,
type: MessageType.Receive,
pageSize: this.defaultPageSize,
pageCount: this.recieveCurrentPage
} as RetrieveRequest)
.pipe(
take(1),
map(res => {
if (res.responseCode === MessageStatusCode.Success) {
this.currentTotalCount = res.totalCount;
this.currentPage = res.pageCount;
this.recieveCurrentPage = res.pageCount;
this.messageList = res.messageList;
} else {
}
}),
catchError(error => of(this.logger.error(error)))
)
.subscribe();
}
break;
case MessageType.Send:
{
this.messageSendListSubscription = this.messageApiService
.retrieveSendMessage({
userSeq: this.loginRes.userSeq,
deviceType: DeviceType.PC,
tokenKey: this.loginRes.tokenString,
type: MessageType.Send,
pageSize: this.defaultPageSize,
pageCount: this.sendCurrentPage
} as RetrieveRequest)
.pipe(
take(1),
map(res => {
if (res.responseCode === MessageStatusCode.Success) {
this.currentTotalCount = res.totalCount;
this.currentPage = res.pageCount;
this.sendCurrentPage = res.pageCount;
this.messageList = res.messageList;
} else {
}
}),
catchError(error => of(this.logger.error(error)))
)
.subscribe();
}
break;
case MessageType.Reservation:
{
this.messageSendListSubscription = this.messageApiService
.retrieveReservationMessage({
userSeq: this.loginRes.userSeq,
deviceType: DeviceType.PC,
tokenKey: this.loginRes.tokenString,
type: MessageType.Reservation,
pageSize: this.defaultPageSize,
pageCount: this.reservationCurrentPage
} as RetrieveRequest)
.pipe(
take(1),
map(res => {
if (res.responseCode === MessageStatusCode.Success) {
this.currentTotalCount = res.totalCount;
this.currentPage = res.pageCount;
this.reservationCurrentPage = res.pageCount;
this.messageList = res.messageList;
} else {
}
}),
catchError(error => of(this.logger.error(error)))
)
.subscribe();
}
break;
}
getRetrieveMessage(messageType: MessageType, trgtPageIndex: number) {
this.store.dispatch(
MessageStore.retrieveMessage({
messageType
})
);
}
/** 쪽지 상세보기 */

View File

@ -151,27 +151,6 @@
}
}
::ng-deep .mat-paginator {
.mat-paginator-container {
justify-content: center;
}
.mat-paginator-navigation-first {
order: 1;
}
.mat-paginator-navigation-previous {
order: 2;
}
// override material paginator page switch
.mat-paginator-range-label {
order: 3;
}
.mat-paginator-navigation-next {
order: 4;
}
.mat-paginator-navigation-last {
order: 5;
}
}
::ng-deep .mat-form-field-appearance-legacy {
.mat-form-field-infix {
padding: 6px;

View File

@ -66,6 +66,11 @@
box-sizing: border-box;
display: flex;
border-top: 1px solid #dddddd;
.mat-paginator {
.mat-paginator-container {
justify-content: center;
}
}
.btn-box {
height: 50px;
padding-bottom: 10px;
@ -77,7 +82,7 @@
}
}
.mat-paginator {
/*::ng-deep .mat-paginator {
.mat-paginator-container {
justify-content: center;
}
@ -97,7 +102,7 @@
.mat-paginator-navigation-last {
order: 5;
}
}
}*/
.mat-form-field-appearance-legacy {
.mat-form-field-infix {
padding: 6px;

View File

@ -3,6 +3,7 @@ import { Action, combineReducers, Selector, createSelector } from '@ngrx/store';
import * as ChatStore from './chat';
import * as EventStore from './event';
import * as MessageStore from './message';
import * as OptionStore from './option';
import * as QueryStore from './query';
import * as RoomStore from './room';
@ -13,6 +14,7 @@ import * as SettingsStore from './settings';
export interface State {
chat: ChatStore.State;
event: EventStore.State;
message: MessageStore.State;
option: OptionStore.State;
query: QueryStore.State;
room: RoomStore.State;
@ -24,6 +26,7 @@ export interface State {
export const effects: Type<any>[] = [
ChatStore.Effects,
EventStore.Effects,
MessageStore.Effects,
OptionStore.Effects,
QueryStore.Effects,
RoomStore.Effects,
@ -36,6 +39,7 @@ export function reducers(state: State | undefined, action: Action) {
return combineReducers({
chat: ChatStore.reducer,
event: EventStore.reducer,
message: MessageStore.reducer,
option: OptionStore.reducer,
query: QueryStore.reducer,
room: RoomStore.reducer,
@ -53,6 +57,9 @@ export function selectors<S>(selector: Selector<any, State>) {
EventSelector: EventStore.selectors(
createSelector(selector, (state: State) => state.event)
),
MessageSelector: MessageStore.selectors(
createSelector(selector, (state: State) => state.message)
),
OptionSelector: OptionStore.selectors(
createSelector(selector, (state: State) => state.option)
),

View File

@ -0,0 +1,42 @@
import { createAction, props } from '@ngrx/store';
import {
MessageType,
RetrieveResponse,
MessageSearchType
} from '@ucap-webmessenger/api-message';
export const retrieveMessage = createAction(
'[Messenger::Message] RetrieveMessage',
props<{
messageType: MessageType;
pageSize?: number;
pageCount?: number;
}>()
);
export const retrieveMessageSuccess = createAction(
'[Messenger::Message] RetrieveMessage Success',
props<{ res: RetrieveResponse; messageType: MessageType }>()
);
export const retrieveMessageFailure = createAction(
'[Messenger::Message] RetrieveMessage Failure',
props<{ error: any }>()
);
export const searchMessage = createAction(
'[Messenger::Message] searchMessage',
props<{
messageType: MessageType;
pageSize?: number;
pageCount?: number;
searchType?: MessageSearchType;
searchStr?: string;
}>()
);
export const searchMessageSuccess = createAction(
'[Messenger::Message] searchMessage Success',
props<{ res: RetrieveResponse; messageType: MessageType }>()
);
export const searchMessageFailure = createAction(
'[Messenger::Message] searchMessage Failure',
props<{ error: any }>()
);

View File

@ -0,0 +1,210 @@
import { Injectable } from '@angular/core';
import { Actions, createEffect, ofType } from '@ngrx/effects';
import { Store, select } from '@ngrx/store';
import { NGXLogger } from 'ngx-logger';
import { of } from 'rxjs';
import {
tap,
switchMap,
map,
catchError,
exhaustMap,
withLatestFrom,
concatMap,
take
} from 'rxjs/operators';
import moment from 'moment';
import * as ChatStore from '@app/store/messenger/chat';
import * as RoomStore from '@app/store/messenger/room';
import * as SyncStore from '@app/store/messenger/sync';
import { SessionStorageService } from '@ucap-webmessenger/web-storage';
import { KEY_ENVIRONMENTS_INFO } from './../../../types/environment.type';
import { LoginInfo, KEY_LOGIN_INFO, EnvironmentsInfo } from '@app/types';
import { Dictionary } from '@ngrx/entity';
import { openSuccess, openFailure } from '../room';
import { LoginResponse } from '@ucap-webmessenger/protocol-authentication';
import { KEY_LOGIN_RES_INFO } from '@app/types/login-res-info.type';
import { StatusCode, MessageStatusCode } from '@ucap-webmessenger/api';
import { environment } from '../../../../environments/environment';
import {
AlertDialogComponent,
AlertDialogResult,
AlertDialogData,
DialogService
} from '@ucap-webmessenger/ui';
import {
retrieveMessage,
retrieveMessageFailure,
retrieveMessageSuccess,
searchMessage,
searchMessageSuccess,
searchMessageFailure
} from './actions';
import {
MessageApiService,
RetrieveRequest,
MessageType,
RetrieveSearchRequest,
MessageSearchType
} from '@ucap-webmessenger/api-message';
@Injectable()
export class Effects {
retrieveMessage$ = createEffect(
() => {
// const loginResInfo: LoginResponse = this.sessionStorageService.get<
// LoginResponse
// >(KEY_LOGIN_RES_INFO);
const environmentsInfo = this.sessionStorageService.get<EnvironmentsInfo>(
KEY_ENVIRONMENTS_INFO
);
return this.actions$.pipe(
ofType(retrieveMessage),
withLatestFrom(
this.store.pipe(
select(
(state: any) =>
state.account.authentication.loginRes as LoginResponse
)
)
),
switchMap(([req, loginResInfo]) => {
const request: RetrieveRequest = {
userSeq: loginResInfo.userSeq,
deviceType: environmentsInfo.deviceType,
tokenKey: loginResInfo.tokenString,
type: req.messageType,
pageSize: !!req.pageSize ? req.pageSize : 1000,
pageCount: !!req.pageCount ? req.pageCount : 0
};
switch (req.messageType) {
case MessageType.Receive: {
return this.messageApiService
.retrieveReceiveMessage(request)
.pipe(
map(res => {
if (res.responseCode === MessageStatusCode.Success) {
this.store.dispatch(
retrieveMessageSuccess({
res,
messageType: req.messageType
})
);
}
}),
catchError(error => of(retrieveMessageFailure({ error })))
);
}
case MessageType.Send: {
return this.messageApiService.retrieveSendMessage(request).pipe(
map(res => {
if (res.responseCode === MessageStatusCode.Success) {
this.store.dispatch(
retrieveMessageSuccess({
res,
messageType: req.messageType
})
);
}
}),
catchError(error => of(retrieveMessageFailure({ error })))
);
}
case MessageType.Reservation: {
return this.messageApiService
.retrieveReservationMessage(request)
.pipe(
map(res => {
if (res.responseCode === MessageStatusCode.Success) {
this.store.dispatch(
retrieveMessageSuccess({
res,
messageType: req.messageType
})
);
}
}),
catchError(error => of(retrieveMessageFailure({ error })))
);
}
}
})
);
},
{ dispatch: false }
);
searchMessage$ = createEffect(
() => {
const environmentsInfo = this.sessionStorageService.get<EnvironmentsInfo>(
KEY_ENVIRONMENTS_INFO
);
return this.actions$.pipe(
ofType(searchMessage),
withLatestFrom(
this.store.pipe(
select(
(state: any) =>
state.account.authentication.loginRes as LoginResponse
)
)
),
switchMap(([req, loginResInfo]) => {
const request: RetrieveRequest = {
userSeq: loginResInfo.userSeq,
deviceType: environmentsInfo.deviceType,
tokenKey: loginResInfo.tokenString,
type: req.messageType,
pageSize: !!req.pageSize ? req.pageSize : 1000,
pageCount: !!req.pageCount ? req.pageCount : 0
};
return this.messageApiService
.retrieveSearchMessage({
...request,
searchTitle:
req.searchType === MessageSearchType.Title ? req.searchStr : '',
searchName:
req.searchType === MessageSearchType.Name ? req.searchStr : '',
searchContent:
req.searchType === MessageSearchType.Contents
? req.searchStr
: ''
} as RetrieveSearchRequest)
.pipe(
map(res => {
if (res.responseCode === MessageStatusCode.Success) {
this.store.dispatch(
searchMessageSuccess({
res,
messageType: req.messageType
})
);
}
}),
catchError(error => of(searchMessageFailure({ error })))
);
})
);
},
{ dispatch: false }
);
constructor(
private actions$: Actions,
private store: Store<any>,
private messageApiService: MessageApiService,
private sessionStorageService: SessionStorageService,
private dialogService: DialogService,
private logger: NGXLogger
) {}
}

View File

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

View File

@ -0,0 +1,100 @@
import { createReducer, on } from '@ngrx/store';
import {
initialState,
adapterReceiveList,
adapterSendList,
adapterReservationList,
adapterSearchList
} from './state';
import * as AuthenticationStore from '@app/store/account/authentication';
import {
retrieveMessageSuccess,
retrieveMessage,
searchMessage,
searchMessageSuccess
} from './actions';
import { MessageType } from '@ucap-webmessenger/api-message';
export const reducer = createReducer(
initialState,
on(retrieveMessage, (state, action) => {
switch (action.messageType) {
case MessageType.Receive: {
return {
...state,
receiveListProcessing: true
};
}
case MessageType.Send: {
return {
...state,
sendListProcessing: true
};
}
case MessageType.Reservation: {
return {
...state,
reservationListProcessing: true
};
}
}
}),
on(retrieveMessageSuccess, (state, action) => {
switch (action.messageType) {
case MessageType.Receive: {
return {
...state,
receiveList: adapterReceiveList.upsertMany(action.res.messageList, {
...state.receiveList
}),
receiveListProcessing: false
};
}
case MessageType.Send: {
return {
...state,
sendList: adapterSendList.upsertMany(action.res.messageList, {
...state.sendList
}),
sendListProcessing: false
};
}
case MessageType.Reservation: {
return {
...state,
reservationList: adapterReservationList.upsertMany(
action.res.messageList,
{
...state.reservationList
}
),
reservationListProcessing: false
};
}
}
}),
on(searchMessage, (state, action) => {
return {
...state,
searchList: adapterSearchList.removeAll(state.searchList),
searchListProcessing: true
};
}),
on(searchMessageSuccess, (state, action) => {
return {
...state,
searchList: adapterSearchList.upsertMany(action.res.messageList, {
...state.searchList
}),
searchListProcessing: false
};
}),
on(AuthenticationStore.logoutInitialize, (state, action) => {
return {
...initialState
};
})
);

View File

@ -0,0 +1,218 @@
import { Selector, createSelector } from '@ngrx/store';
import { EntityState, createEntityAdapter } from '@ngrx/entity';
import { MessageList } from '@ucap-webmessenger/api-message';
import {
InfoResponse,
Info,
EventJson
} from '@ucap-webmessenger/protocol-event';
import { FileInfo, FileDownloadInfo } from '@ucap-webmessenger/protocol-file';
export interface ReceiveListState extends EntityState<MessageList> {}
export interface SendListState extends EntityState<MessageList> {}
export interface ReservationListState extends EntityState<MessageList> {}
export interface SearchListState extends EntityState<MessageList> {}
export interface InfoListState extends EntityState<Info<EventJson>> {}
export interface FileInfoListState extends EntityState<FileInfo> {}
export interface FileInfoCheckListState extends EntityState<FileDownloadInfo> {}
export interface State {
receiveListProcessing: boolean;
receiveList: ReceiveListState;
receiveTotalCount: number;
receivePage: number;
sendListProcessing: boolean;
sendList: SendListState;
sendTotalCount: number;
sendPage: number;
reservationListProcessing: boolean;
reservationList: ReservationListState;
reservationTotalCount: number;
reservationPage: number;
searchListProcessing: boolean;
searchList: SearchListState;
searchTotalCount: number;
searchPage: number;
}
export const adapterReceiveList = createEntityAdapter<MessageList>({
selectId: info => info.msgId,
sortComparer: (a, b) => {
return b.msgId - a.msgId;
}
});
export const adapterSendList = createEntityAdapter<MessageList>({
selectId: info => info.msgId,
sortComparer: (a, b) => {
return b.msgId - a.msgId;
}
});
export const adapterReservationList = createEntityAdapter<MessageList>({
selectId: info => info.msgId,
sortComparer: (a, b) => {
return b.msgId - a.msgId;
}
});
export const adapterSearchList = createEntityAdapter<MessageList>({
selectId: info => info.msgId,
sortComparer: (a, b) => {
return b.msgId - a.msgId;
}
});
const receiveListInitialState: ReceiveListState = adapterReceiveList.getInitialState(
{}
);
const sendListInitialState: SendListState = adapterSendList.getInitialState({});
const reservationListInitialState: ReservationListState = adapterReservationList.getInitialState(
{}
);
const searchListInitialState: SearchListState = adapterSearchList.getInitialState(
{}
);
export const initialState: State = {
receiveListProcessing: false,
receiveList: receiveListInitialState,
receiveTotalCount: 0,
receivePage: 0,
sendListProcessing: false,
sendList: sendListInitialState,
sendTotalCount: 0,
sendPage: 0,
reservationListProcessing: false,
reservationList: reservationListInitialState,
reservationTotalCount: 0,
reservationPage: 0,
searchListProcessing: false,
searchList: searchListInitialState,
searchTotalCount: 0,
searchPage: 0
};
const {
selectAll: ngeSelectAllReceiveList,
selectEntities: ngeSelectEntitiesReceiveList,
selectIds: ngeSelectIdsReceiveList,
selectTotal: ngeSelectTotalReceiveList
} = adapterReceiveList.getSelectors();
const {
selectAll: ngeSelectAllSendList,
selectEntities: ngeSelectEntitiesSendList,
selectIds: ngeSelectIdsSendList,
selectTotal: ngeSelectTotalSendList
} = adapterSendList.getSelectors();
const {
selectAll: ngeSelectAllReservationList,
selectEntities: ngeSelectEntitiesReservationList,
selectIds: ngeSelectIdsReservationList,
selectTotal: ngeSelectTotalReservationList
} = adapterReservationList.getSelectors();
const {
selectAll: ngeSelectAllSearchList,
selectEntities: ngeSelectEntitiesSearchList,
selectIds: ngeSelectIdsSearchList,
selectTotal: ngeSelectTotalSearchList
} = adapterSearchList.getSelectors();
export function selectors<S>(selector: Selector<any, State>) {
const selectReceiveList = createSelector(
selector,
(state: State) => state.receiveList
);
const selectSendList = createSelector(
selector,
(state: State) => state.sendList
);
const selectReservationList = createSelector(
selector,
(state: State) => state.reservationList
);
const selectSearchList = createSelector(
selector,
(state: State) => state.searchList
);
return {
receiveListProcessing: createSelector(
selector,
(state: State) => state.receiveListProcessing
),
sendListProcessing: createSelector(
selector,
(state: State) => state.sendListProcessing
),
reservationListProcessing: createSelector(
selector,
(state: State) => state.reservationListProcessing
),
searchListProcessing: createSelector(
selector,
(state: State) => state.searchListProcessing
),
selectAllReceiveList: createSelector(
selectReceiveList,
ngeSelectAllReceiveList
),
selectEntitiesReceiveList: createSelector(
selectReceiveList,
ngeSelectEntitiesReceiveList
),
selectReceiveList: (seq: number) =>
createSelector(
selectReceiveList,
ngeSelectEntitiesReceiveList,
(_, entities) => (!!entities ? entities[seq] : undefined)
),
selectAllSendList: createSelector(selectSendList, ngeSelectAllSendList),
selectEntitiesSendList: createSelector(
selectSendList,
ngeSelectEntitiesSendList
),
selectSendList: (seq: number) =>
createSelector(selectSendList, ngeSelectEntitiesSendList, (_, entities) =>
!!entities ? entities[seq] : undefined
),
selectAllReservationList: createSelector(
selectReservationList,
ngeSelectAllReservationList
),
selectEntitiesReservationList: createSelector(
selectReservationList,
ngeSelectEntitiesReservationList
),
selectReservationList: (seq: number) =>
createSelector(
selectReservationList,
ngeSelectEntitiesReservationList,
(_, entities) => (!!entities ? entities[seq] : undefined)
),
selectAllSearchList: createSelector(
selectSearchList,
ngeSelectAllSearchList
),
selectEntitiesSearchList: createSelector(
selectSearchList,
ngeSelectEntitiesSearchList
),
selectSearchList: (seq: number) =>
createSelector(
selectSearchList,
ngeSelectEntitiesSearchList,
(_, entities) => (!!entities ? entities[seq] : undefined)
)
};
}

View File

@ -2,6 +2,7 @@
display: flex;
align-content: center;
flex-flow: row;
align-items: flex-end;
.line {
height: 1px;
background-color: #cccccc;

View File

@ -7,4 +7,5 @@
color: #ffffff;
border-radius: 100px;
justify-content: center;
margin: 10px 0 20px;
}

View File

@ -1,4 +1,4 @@
<div class="chat-messages" #scrollMe>
<!--<div class="chat-messages" #scrollMe>
<div class="message-row" *ngIf="eventRemain && messages.length > 0">
<button mat-button (click)="onClickMore($event)">
이전 대화 보기
@ -14,7 +14,45 @@
}})개
</span>
</button>
</div>-->
<div class="chat-messages" #scrollMe>
<div class="unRead-count" *ngIf="
!!roomInfo &&
!!firstEventSeq &&
roomInfo.lastReadEventSeq < firstEventSeq
">
<span class="line"></span>
<span class="count">
안읽은 메시지가 <span class="text-warn-color">({{ firstEventSeq - (roomInfo.lastReadEventSeq + 1) }})</span>
더 있습니다.
</span>
<span class="line"></span>
</div>
<div
*ngIf="eventRemain && messages.length > 0"
class="message-row view-previous bg-accent-dark"
>
<button mat-button (click)="onClickMore($event)">
<svg
xmlns="http://www.w3.org/2000/svg"
width="20"
height="20"
viewBox="0 0 20 20"
stroke="currentColor"
fill="none"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<circle class="st0" cx="10" cy="10" r="7.3" />
<polyline class="st0" points="12.9,10 10,7.1 7.1,10 " />
<line class="st0" x1="10" y1="12.9" x2="10" y2="7.1" />
</svg>
이전 대화 보기
</button>
<span class="line"></span>
</div>
<!-- MESSAGE -->
<div
*ngFor="let message of messages; let i = index"

View File

@ -158,3 +158,43 @@ $meBox-bg: #ffffff;
white-space: pre-wrap;
word-break: break-word;
}
.view-previous {
display: flex;
align-content: center;
flex-flow: row;
background-color: rgba(0, 0, 0, 0.4);
padding: 4px 10px;
color: #ffffff;
border-radius: 100px;
justify-content: center;
margin: 10px 0 20px;
button {
height: 24px;
line-height: 24px;
width: 100%;
svg {
stroke: #ffffff;
align-self: center;
}
}
}
.unRead-count {
display: flex;
justify-items: self-end;
flex-flow: row;
align-items: flex-end;
.line {
height: 1px;
background-color: #cccccc;
width: 40%;
flex: 1 1 auto;
margin-bottom: 10px;
display: inline-flex;
}
.count {
width: 260px;
font-size: 13px;
text-align: center;
font-weight: 600;
}
}

View File

@ -12,7 +12,8 @@
@import 'partials/icons';
@import 'partials/normalize';
@import 'partials/scrollbars';
@import 'partials/presence.scss';
@import 'partials/paginator';
@import 'partials/presence';
@import 'partials/list-item';
@import 'partials/dialogs';

View File

@ -0,0 +1,23 @@
@charset 'utf-8';
.mat-paginator {
.mat-paginator-container {
justify-content: center;
}
.mat-paginator-navigation-first {
order: 1;
}
.mat-paginator-navigation-previous {
order: 2;
}
// override material paginator page switch
.mat-paginator-range-label {
order: 3;
}
.mat-paginator-navigation-next {
order: 4;
}
.mat-paginator-navigation-last {
order: 5;
}
}