From 1810bbbd29262c900c11ffc6a832ea5f0562c0bb Mon Sep 17 00:00:00 2001 From: Richard Park Date: Fri, 29 Nov 2019 06:45:47 +0900 Subject: [PATCH] update --- .prettierrc | 5 + .vscode/settings.json | 4 +- angular.json | 394 +- ...ck.config.js => angular.webpack.config.js} | 12 +- config/enviroment.ts | 3 +- config/main.webpack.config.ts | 74 +- docker/docker-compose.yml | 10 + docker/nginx/nginx.conf | 40 + electron-builder.json | 22 +- .../ucap-webmessenger-electron-core/README.md | 0 .../karma.conf.js | 34 + .../package.json | 5 + .../src/lib/types/channel.type.ts | 0 .../src/public-api.ts | 0 .../src/test.ts | 0 .../tsconfig.lib.json | 13 + .../tsconfig.spec.json | 9 + .../tslint.json | 0 .../README.md | 0 .../karma.conf.js | 34 + .../package.json | 0 .../models/electron-notification-options.ts | 2 +- .../src/lib/models/electron-notification.ts | 2 +- .../services/electron-notification.service.ts | 135 +- .../src/lib/types/channel.type.ts | 0 .../src/lib/types/event.type.ts | 0 .../src/lib/utils/animation-queue.ts | 0 .../src/public-api.ts | 0 .../src/test.ts | 0 .../tsconfig.lib.json | 12 + .../tsconfig.spec.json | 0 .../tslint.json | 0 .../ucap-webmessenger-electron/README.md | 24 + .../ucap-webmessenger-electron/karma.conf.js | 34 + .../ucap-webmessenger-electron/package.json | 5 + .../resources/image/128_128.png | Bin 0 -> 3385 bytes .../resources/image/16_16.ico | Bin 0 -> 106583 bytes .../resources/image/16_16.png | Bin 0 -> 503 bytes .../resources/image/256_256.png | Bin 0 -> 6722 bytes .../resources/image/32_32.png | Bin 0 -> 886 bytes .../resources/image/48_48.png | Bin 0 -> 1289 bytes .../resources/image/64_64.png | Bin 0 -> 1688 bytes .../resources/image/ico_64_64.png | Bin 0 -> 999 bytes .../resources/image/ico_64x64.ico | Bin 0 -> 109473 bytes .../resources/image/ico_64x64_r.png | Bin 0 -> 2082 bytes .../resources/image/lgRed/128_128.png | Bin 0 -> 4613 bytes .../resources/image/lgRed/16_16.png | Bin 0 -> 551 bytes .../resources/image/lgRed/256_256.png | Bin 0 -> 10149 bytes .../resources/image/lgRed/32_32.png | Bin 0 -> 1106 bytes .../resources/image/lgRed/48_48.png | Bin 0 -> 1512 bytes .../resources/image/lgRed/64_64.png | Bin 0 -> 2082 bytes .../resources/image/lgRed/ico_64_64.png | Bin 0 -> 999 bytes .../resources/image/lgRed/ico_64x64.ico | Bin 0 -> 109473 bytes .../resources/image/lgRed/ico_64x64_r.png | Bin 0 -> 2082 bytes .../resources/installer/woori.icns | Bin .../resources/installer/woori.ico | Bin .../resources/installer/woori_256x256.ico | Bin .../resources/installer/woori_256x256.png | Bin .../resources/linuxicon/256x256.png | Bin .../notification/image/btn_call_message.png | Bin .../notification/image/btn_call_receive.png | Bin .../notification/image/btn_call_refuse.png | Bin .../notification/image/btn_call_transfer.png | Bin .../notification/image/btn_close.png | Bin .../notification/image/btn_close_gray.png | Bin .../notification/image/btn_noti_call.png | Bin .../notification/image/img_nophoto_50.png | Bin .../resources/notification/preload.js | 0 .../notification/sound/messageAlarm.mp3 | Bin .../notification/styles/noti_messege.css | 0 .../resources/notification/template.html | 0 .../src/app/AppWindow.ts | 22 +- .../src/crash/CrashWindow.ts | 0 .../src/crash/show-uncaught-exception.ts | 0 .../src/global.d.ts | 0 .../ucap-webmessenger-electron}/src/index.ts | 81 +- .../src/lib/default-folder.ts | 0 .../src/lib/file-util.ts | 0 .../src/lib/idle-checker.ts | 3 - .../src/lib/storage.ts | 0 .../src/lib/window-state.ts | 0 .../src/util/now.ts | 0 .../src/util/root.ts | 0 .../tsconfig.electron.json | 19 +- .../tsconfig.spec.json | 0 .../ucap-webmessenger-electron/tslint.json | 7 + main/resources/image/128_128.png | Bin 3840 -> 0 bytes main/resources/image/16_16.png | Bin 504 -> 0 bytes main/resources/image/256_256.png | Bin 7420 -> 0 bytes main/resources/image/32_32.png | Bin 897 -> 0 bytes main/resources/image/48_48.png | Bin 1392 -> 0 bytes main/resources/image/64_64.png | Bin 1966 -> 0 bytes main/resources/image/ico_64x64.ico | Bin 119658 -> 0 bytes main/resources/image/ico_64x64.png | Bin 3170 -> 0 bytes main/resources/image/ico_64x64_r.ico | Bin 107599 -> 0 bytes main/resources/image/ico_64x64_r.png | Bin 1966 -> 0 bytes package-lock.json | 896 +- package.json | 43 +- .../src/lib/config/module-config.ts | 7 + .../src/lib/{types => config}/token.ts | 0 .../src/lib/config/urls.ts | 12 + .../src/lib/services/common-api.service.ts | 63 +- .../src/lib/types/module-config.ts | 15 - .../src/lib/ucap-common-api.module.ts | 9 +- .../src/public-api.ts | 5 +- .../src/lib/config/module-config.ts | 5 + .../src/lib/{types => config}/token.ts | 0 .../src/lib/config/urls.ts | 6 + .../src/lib/services/external-api.service.ts | 42 +- .../src/lib/types/module-config.ts | 8 - .../src/lib/ucap-external-api.module.ts | 9 +- .../src/public-api.ts | 5 +- .../ucap-webmessenger-api-message/README.md | 24 + .../karma.conf.js | 2 +- .../ng-package.json | 2 +- .../package.json | 2 +- .../src/lib/apis/del.ts | 31 + .../src/lib/apis/detail.ts | 182 + .../src/lib/apis/edit-reservation-ex.ts | 42 + .../src/lib/apis/my-message.ts | 104 + .../src/lib/apis/retrieve.ts | 181 + .../src/lib/apis/send-copy.ts | 41 + .../src/lib/apis/send.ts | 41 + .../src/lib/apis/unread-count.ts | 33 + .../src/lib/config/module-config.ts | 5 + .../src/lib/config/token.ts | 5 + .../src/lib/config/urls.ts | 47 + .../src/lib/models/message-list.ts | 34 + .../lib/services/message-api.service.spec.ts | 12 + .../src/lib/services/message-api.service.ts | 293 + .../src/lib/types/category.type.ts | 6 + .../src/lib/types/content.type.ts | 8 + .../src/lib/types/message.search.type.ts | 5 + .../src/lib/types/message.type.ts | 6 + .../src/lib/ucap-message-api.module.ts | 24 + .../src/public-api.ts | 24 + .../ucap-webmessenger-api-message/src/test.ts | 21 + .../tsconfig.lib.json | 0 .../tsconfig.spec.json | 17 + .../ucap-webmessenger-api-message/tslint.json | 17 + .../src/lib/apis/update-info.ts | 6 +- .../src/lib/config/module-config.ts | 5 + .../src/lib/{types => config}/token.ts | 0 .../src/lib/config/urls.ts | 4 + .../src/lib/services/public-api.service.ts | 30 +- .../src/lib/types/module-config.ts | 6 - .../src/lib/ucap-public-api.module.ts | 9 +- .../src/public-api.ts | 4 +- .../ucap-webmessenger-api/src/lib/apis/api.ts | 8 + .../src/lib/types/message-status-code.type.ts | 28 + .../src/lib/types/status-code.type.ts | 2 +- .../ucap-webmessenger-api/src/public-api.ts | 1 + .../src/app/app-provider.module.ts | 90 +- .../src/app/app-routing.module.ts | 10 +- .../src/app/app-translate.module.ts | 11 +- .../src/app/app.component.ts | 3 +- .../src/app/app.module.ts | 224 +- .../src/app/app.theme.scss | 49 +- .../dialogs/file-viewer.dialog.component.html | 7 +- .../dialogs/file-viewer.dialog.component.ts | 36 +- .../app/layouts/messenger/components/index.ts | 6 +- .../components/left-side.component.html | 197 +- .../components/left-side.component.scss | 58 +- .../components/left-side.component.ts | 92 +- .../left-sidenav/chat.component.html | 41 +- .../left-sidenav/chat.component.scss | 18 +- .../components/left-sidenav/chat.component.ts | 50 +- .../left-sidenav/group.component.html | 183 +- .../left-sidenav/group.component.scss | 34 +- .../left-sidenav/group.component.ts | 178 +- .../components/left-sidenav/index.ts | 4 +- .../left-sidenav/message.component.html | 198 + .../left-sidenav/message.component.scss | 67 + .../left-sidenav/message.component.spec.ts | 24 + .../left-sidenav/message.component.ts | 317 + .../left-sidenav/organization.component.html | 119 +- .../left-sidenav/organization.component.scss | 167 +- .../left-sidenav/organization.component.ts | 155 +- .../components/messages.component.html | 98 +- .../components/messages.component.scss | 31 +- .../components/messages.component.ts | 284 +- .../components/right-drawer.component.html | 32 + .../components/right-drawer.component.scss | 19 + .../components/right-drawer.component.spec.ts | 25 + .../components/right-drawer.component.ts | 39 + .../right-drawer/album-box.component.html | 154 + .../right-drawer/album-box.component.scss | 123 + .../right-drawer/album-box.component.spec.ts | 25 + .../right-drawer/album-box.component.ts | 198 + .../right-drawer/file-box.component.html | 204 + .../right-drawer/file-box.component.scss | 171 + .../right-drawer/file-box.component.spec.ts | 25 + .../right-drawer/file-box.component.ts | 215 + .../components/right-drawer/index.ts | 9 + .../room-user-list.component.html | 25 + .../room-user-list.component.scss | 16 + .../room-user-list.component.spec.ts | 25 + .../right-drawer/room-user-list.component.ts | 172 + .../chat/create-chat.dialog.component.html | 76 +- .../chat/create-chat.dialog.component.scss | 61 +- .../chat/create-chat.dialog.component.ts | 86 +- .../chat/edit-chat-room.dialog.component.html | 2 +- .../layouts/messenger/dialogs/chat/index.ts | 7 +- .../dialogs/chat/mass-detail.component.html | 15 + .../dialogs/chat/mass-detail.component.scss | 25 + .../chat/mass-detail.component.spec.ts | 25 + .../dialogs/chat/mass-detail.component.ts | 58 + .../group/edit-group.dialog.component.html | 2 +- .../group/edit-group.dialog.component.scss | 3 +- .../group/select-group.dialog.component.html | 23 +- .../app/layouts/messenger/dialogs/index.ts | 9 +- .../messenger/dialogs/profile/index.ts | 3 + .../profile/profile.dialog.component.html | 11 + .../profile/profile.dialog.component.scss | 0 .../profile/profile.dialog.component.spec.ts | 27 + .../profile/profile.dialog.component.ts | 163 + .../messenger/dialogs/settings/index.ts | 3 + .../messenger-settings.dialog.component.html | 110 + .../messenger-settings.dialog.component.scss | 112 + ...essenger-settings.dialog.component.spec.ts | 27 + .../messenger-settings.dialog.component.ts | 57 + .../messenger/messenger.layout.module.ts | 16 +- .../native/components/top-bar.component.html | 123 +- .../native/components/top-bar.component.scss | 60 +- .../native/components/top-bar.component.ts | 108 +- .../components/login.page.component.html | 2 + .../components/login.page.component.scss | 2 +- .../components/login.page.component.ts | 107 +- .../components/main.page.component.html | 77 +- .../components/main.page.component.scss | 36 +- .../components/main.page.component.ts | 97 +- .../pages/messenger/messenger.page.module.ts | 8 +- .../src/app/services/app.service.ts | 18 +- .../app/services/authentication.service.ts | 4 + .../src/app/services/index.ts | 4 +- .../src/app/services/native.service.ts | 28 + .../src/app/services/notification.service.ts | 2 +- .../store/account/authentication/actions.ts | 13 + .../store/account/authentication/effects.ts | 116 +- .../store/account/authentication/reducers.ts | 28 +- .../app/store/account/authentication/state.ts | 9 +- .../src/app/store/messenger/chat/actions.ts | 10 + .../src/app/store/messenger/chat/effects.ts | 31 +- .../src/app/store/messenger/chat/reducers.ts | 25 +- .../src/app/store/messenger/chat/state.ts | 9 +- .../src/app/store/messenger/event/actions.ts | 33 +- .../src/app/store/messenger/event/effects.ts | 136 +- .../src/app/store/messenger/event/reducers.ts | 37 +- .../src/app/store/messenger/event/state.ts | 98 +- .../src/app/store/messenger/index.ts | 46 +- .../app/store/messenger/option/reducers.ts | 2 +- .../src/app/store/messenger/query/reducers.ts | 2 +- .../src/app/store/messenger/room/reducers.ts | 9 +- .../app/store/messenger/settings/actions.ts | 3 + .../app/store/messenger/settings/effects.ts | 59 + .../src/app/store/messenger/settings/index.ts | 4 + .../app/store/messenger/settings/reducers.ts | 4 + .../src/app/store/messenger/settings/state.ts | 11 + .../app/store/messenger/status/reducers.ts | 2 +- .../src/app/store/messenger/sync/actions.ts | 30 + .../src/app/store/messenger/sync/effects.ts | 116 +- .../src/app/store/messenger/sync/reducers.ts | 8 + .../src/app/store/messenger/sync/state.ts | 78 +- .../src/app/store/setting/company/reducers.ts | 9 + .../store/setting/version-info/reducers.ts | 8 + .../src/app/types/index.ts | 2 + .../src/app/types/right-drawer.type.ts | 8 + .../src/app/types/sticker-info.type.ts | 1 + .../css/materialdesignicons.css | 18654 ++++++++++++++++ .../css/materialdesignicons.css.map | 16 + .../css/materialdesignicons.min.css | 3 + .../css/materialdesignicons.min.css.map | 16 + .../fonts/materialdesignicons-webfont.eot | Bin 0 -> 772648 bytes .../fonts/materialdesignicons-webfont.ttf | Bin 0 -> 772428 bytes .../fonts/materialdesignicons-webfont.woff | Bin 0 -> 373304 bytes .../fonts/materialdesignicons-webfont.woff2 | Bin 0 -> 262440 bytes .../materialdesignicons/scss/_animated.scss | 27 + .../fonts/materialdesignicons/scss/_core.scss | 10 + .../materialdesignicons/scss/_extras.scss | 65 + .../materialdesignicons/scss/_functions.scss | 19 + .../materialdesignicons/scss/_icons.scss | 10 + .../fonts/materialdesignicons/scss/_path.scss | 14 + .../materialdesignicons/scss/_variables.scss | 4606 ++++ .../scss/materialdesignicons.scss | 8 + .../src/assets/images/logo/64_64.png | Bin 0 -> 1688 bytes .../src/assets/images/logo/ico_64x64.png | Bin 3170 -> 999 bytes .../assets/images/logo/ico_64x64.png.woori | Bin .../src/assets/images/theme/theme-default.png | Bin 0 -> 9813 bytes .../src/assets/images/theme/theme-lgRed.png | Bin 0 -> 9412 bytes .../src/assets/scss/global/_default.scss | 222 +- .../src/assets/scss/partials/splash.css | 48 + .../environments/environment-browser.dev.ts | 7 + .../environments/environment-browser.prod.ts | 7 + .../environments/environment-renderer.dev.ts | 7 + .../environments/environment-renderer.prod.ts | 7 + .../src/environments/environment.dev.ts | 79 + .../src/environments/environment.prod.ts | 82 +- .../src/environments/environment.ts | 7 +- .../src/environments/environment.type.ts | 1169 +- projects/ucap-webmessenger-app/src/index.html | 110 +- .../src/polyfills-es5.ts | 92 + .../ucap-webmessenger-app/src/polyfills.ts | 126 +- .../ucap-webmessenger-app/src/styles.scss | 4 + .../tsconfig-es5.app.json | 6 + .../src/lib/config/host.config.ts | 7 + .../src/lib/config/module.config.ts | 6 + .../src/lib/config/url.config.ts | 27 + .../src/lib/utils/file.util.ts | 107 + .../src/lib/utils/sticker.util.ts | 172 + .../src/lib/utils/string.util.ts | 19 +- .../ucap-webmessenger-core/src/public-api.ts | 5 + .../lib/notification/notification.service.ts | 44 + .../lib/services/browser-native.service.ts | 113 +- .../src/lib/translate/browser-loader.ts | 23 +- .../src/public-api.ts | 2 + .../lib/services/electron-native.service.ts | 167 +- .../src/lib/translate/electron-loader.ts | 22 +- .../src/lib/types/channel.type.ts | 10 + .../src/lib/config/module-config.ts | 4 + .../src/lib/services/native.service.ts | 46 +- .../src/lib/ucap-native.module.ts | 8 - .../src/public-api.ts | 5 +- .../src/lib/config/module-config.ts | 5 + .../src/lib/{types => config}/token.ts | 2 +- .../module-config.ts => config/urls.ts} | 6 +- .../src/lib/services/pi.service.ts | 36 +- .../src/lib/ucap-pi.module.ts | 9 +- .../ucap-webmessenger-pi/src/public-api.ts | 5 +- .../src/lib/types/file.type.ts | 12 +- .../src/lib/config/module-config.ts | 13 + .../src/lib/{types => config}/token.ts | 0 .../src/lib/config/urls.ts | 3 + .../src/lib/services/protocol.service.ts | 39 +- .../src/lib/types/module-config.ts | 12 - .../src/lib/ucap-protocol.module.ts | 8 +- .../src/public-api.ts | 4 +- .../src/lib/components/login.component.html | 21 +- .../src/lib/components/login.component.scss | 17 +- .../src/lib/components/login.component.ts | 33 +- .../src/lib/components/form.component.html | 21 +- .../src/lib/components/form.component.ts | 151 +- .../message-box/date-splitter.component.ts | 5 +- .../components/message-box/file.component.ts | 2 +- .../message-box/image.component.scss | 4 + .../message-box/mass.component.html | 4 +- .../components/message-box/mass.component.ts | 5 +- .../message-box/sticker.component.html | 5 +- .../message-box/sticker.component.ts | 41 +- .../message-box/text.component.html | 2 +- .../message-box/text.component.scss | 8 +- .../components/message-box/text.component.ts | 36 +- .../message-box/video.component.html | 4 +- .../message-box/video.component.scss | 34 +- .../lib/components/messages.component.html | 9 +- .../lib/components/messages.component.scss | 7 +- .../src/lib/components/messages.component.ts | 32 +- .../components/expansion-panel.component.html | 179 +- .../components/expansion-panel.component.scss | 148 +- .../components/expansion-panel.component.ts | 253 +- .../src/lib/ucap-ui-group.module.ts | 16 +- .../components/tenant-search.component.html | 2 +- .../src/lib/components/tree.component.html | 124 +- .../src/lib/components/tree.component.scss | 130 +- .../src/lib/components/tree.component.ts | 251 +- .../src/lib/ucap-ui-organization.module.ts | 12 +- .../my-profile-widget.component.html | 12 + .../my-profile-widget.component.scss | 26 + .../my-profile-widget.component.spec.ts | 25 + .../components/my-profile-widget.component.ts | 38 + .../src/lib/components/profile.component.html | 262 + .../src/lib/components/profile.component.scss | 104 + .../lib/components/profile.component.spec.ts | 28 + .../src/lib/components/profile.component.ts | 76 + .../components/user-list-item.component.html | 12 +- .../components/user-list-item.component.ts | 24 +- .../src/lib/ucap-ui-profile.module.ts | 13 +- .../lib/components/list-item.component.html | 11 +- .../lib/components/list-item.component.scss | 25 +- .../ucap-webmessenger-ui-settings/README.md | 24 + .../karma.conf.js | 2 +- .../ng-package.json | 2 +- .../package.json | 8 + .../src/assets/timezone/en.json | 1247 ++ .../src/assets/timezone/ko.json | 1247 ++ .../src/lib/components/call.component.html | 63 + .../src/lib/components/call.component.scss | 7 + .../src/lib/components/call.component.spec.ts | 24 + .../src/lib/components/call.component.ts | 20 + .../src/lib/components/device.component.html | 63 + .../src/lib/components/device.component.scss | 7 + .../lib/components/device.component.spec.ts | 24 + .../src/lib/components/device.component.ts | 20 + .../src/lib/components/general.component.html | 89 + .../src/lib/components/general.component.scss | 72 + .../lib/components/general.component.spec.ts | 24 + .../src/lib/components/general.component.ts | 46 + .../components/notification.component.html | 81 + .../components/notification.component.scss | 7 + .../components/notification.component.spec.ts | 24 + .../lib/components/notification.component.ts | 20 + .../lib/components/permission.component.html | 27 + .../lib/components/permission.component.scss | 7 + .../components/permission.component.spec.ts | 24 + .../lib/components/permission.component.ts | 20 + .../src/lib/components/privacy.component.html | 15 + .../src/lib/components/privacy.component.scss | 0 .../lib/components/privacy.component.spec.ts | 24 + .../src/lib/components/privacy.component.ts | 20 + .../src/lib/ucap-ui-settings.module.ts | 53 + .../src/public-api.ts | 7 + .../ucap-webmessenger-ui-settings/src/test.ts | 21 + .../tsconfig.lib.json | 0 .../tsconfig.spec.json | 17 + .../ucap-webmessenger-ui-settings/tslint.json | 17 + .../src/assets/scss/partials/_general.scss | 144 +- .../src/assets/scss/partials/_presence.scss | 4 +- .../src/lib/animations/index.ts | 30 +- .../components/expansion-panel.component.html | 1 + .../components/expansion-panel.component.scss | 4 + .../expansion-panel.component.spec.ts | 27 + .../components/expansion-panel.component.ts | 12 + .../file-upload-queue.component.html | 34 +- .../file-upload-queue.component.scss | 37 + .../lib/components/file-viewer.component.html | 10 +- .../lib/components/file-viewer.component.ts | 9 +- .../file-viewer/binary-viewer.component.html | 78 +- .../file-viewer/binary-viewer.component.scss | 40 +- .../file-viewer/binary-viewer.component.ts | 10 +- .../document-viewer.component.html | 13 +- .../file-viewer/document-viewer.component.ts | 12 +- .../file-viewer/image-viewer.component.html | 125 +- .../file-viewer/image-viewer.component.scss | 23 +- .../file-viewer/image-viewer.component.ts | 14 +- .../file-viewer/sound-viewer.component.html | 112 +- .../file-viewer/sound-viewer.component.scss | 88 +- .../file-viewer/sound-viewer.component.ts | 11 +- .../file-viewer/video-viewer.component.html | 152 +- .../file-viewer/video-viewer.component.scss | 66 +- .../file-viewer/video-viewer.component.ts | 11 +- .../float-action-button.component.html | 2 +- .../float-action-button.component.scss | 2 +- .../sticker-selector.component.html | 39 + .../sticker-selector.component.scss | 8 + .../sticker-selector.component.spec.ts | 25 + .../components/sticker-selector.component.ts | 91 + .../virtual-scroll-tree-flat.data-source.ts | 132 + .../lib/dialogs/alert.dialog.component.html | 2 +- ...virtual-scroll-viewport-patch.directive.ts | 30 + .../directives/file-upload-for.directive.ts | 44 +- .../src/lib/pipes/dates.pipe.spec.ts | 11 - .../src/lib/pipes/dates.pipe.ts | 20 +- .../src/lib/pipes/linefeed.pipe.spec.ts | 11 - .../src/lib/pipes/linky.pipe.ts | 22 + .../services/splash-screen.service.spec.ts | 12 + .../src/lib/services/splash-screen.service.ts | 88 + .../src/lib/ucap-ui.module.ts | 24 +- .../src/lib/utils/string.util.ts | 74 +- .../ucap-webmessenger-ui/src/public-api.ts | 3 + tsconfig.json | 17 +- 459 files changed, 40550 insertions(+), 3335 deletions(-) create mode 100644 .prettierrc rename config/{renderer.webpack.config.js => angular.webpack.config.js} (58%) create mode 100644 docker/docker-compose.yml create mode 100644 docker/nginx/nginx.conf rename {projects => electron-projects}/ucap-webmessenger-electron-core/README.md (100%) create mode 100644 electron-projects/ucap-webmessenger-electron-core/karma.conf.js create mode 100644 electron-projects/ucap-webmessenger-electron-core/package.json rename {projects => electron-projects}/ucap-webmessenger-electron-core/src/lib/types/channel.type.ts (100%) rename {projects => electron-projects}/ucap-webmessenger-electron-core/src/public-api.ts (100%) rename {projects => electron-projects}/ucap-webmessenger-electron-core/src/test.ts (100%) create mode 100644 electron-projects/ucap-webmessenger-electron-core/tsconfig.lib.json create mode 100644 electron-projects/ucap-webmessenger-electron-core/tsconfig.spec.json rename {projects => electron-projects}/ucap-webmessenger-electron-core/tslint.json (100%) rename {projects => electron-projects}/ucap-webmessenger-electron-notification/README.md (100%) create mode 100644 electron-projects/ucap-webmessenger-electron-notification/karma.conf.js rename {projects => electron-projects}/ucap-webmessenger-electron-notification/package.json (100%) rename {projects => electron-projects}/ucap-webmessenger-electron-notification/src/lib/models/electron-notification-options.ts (94%) rename {projects => electron-projects}/ucap-webmessenger-electron-notification/src/lib/models/electron-notification.ts (90%) rename {projects => electron-projects}/ucap-webmessenger-electron-notification/src/lib/services/electron-notification.service.ts (75%) rename {projects => electron-projects}/ucap-webmessenger-electron-notification/src/lib/types/channel.type.ts (100%) rename {projects => electron-projects}/ucap-webmessenger-electron-notification/src/lib/types/event.type.ts (100%) rename {projects => electron-projects}/ucap-webmessenger-electron-notification/src/lib/utils/animation-queue.ts (100%) rename {projects => electron-projects}/ucap-webmessenger-electron-notification/src/public-api.ts (100%) rename {projects => electron-projects}/ucap-webmessenger-electron-notification/src/test.ts (100%) create mode 100644 electron-projects/ucap-webmessenger-electron-notification/tsconfig.lib.json rename {projects/ucap-webmessenger-electron-core => electron-projects/ucap-webmessenger-electron-notification}/tsconfig.spec.json (100%) rename {projects => electron-projects}/ucap-webmessenger-electron-notification/tslint.json (100%) create mode 100644 electron-projects/ucap-webmessenger-electron/README.md create mode 100644 electron-projects/ucap-webmessenger-electron/karma.conf.js create mode 100644 electron-projects/ucap-webmessenger-electron/package.json create mode 100644 electron-projects/ucap-webmessenger-electron/resources/image/128_128.png create mode 100644 electron-projects/ucap-webmessenger-electron/resources/image/16_16.ico create mode 100644 electron-projects/ucap-webmessenger-electron/resources/image/16_16.png create mode 100644 electron-projects/ucap-webmessenger-electron/resources/image/256_256.png create mode 100644 electron-projects/ucap-webmessenger-electron/resources/image/32_32.png create mode 100644 electron-projects/ucap-webmessenger-electron/resources/image/48_48.png create mode 100644 electron-projects/ucap-webmessenger-electron/resources/image/64_64.png create mode 100644 electron-projects/ucap-webmessenger-electron/resources/image/ico_64_64.png create mode 100644 electron-projects/ucap-webmessenger-electron/resources/image/ico_64x64.ico create mode 100644 electron-projects/ucap-webmessenger-electron/resources/image/ico_64x64_r.png create mode 100644 electron-projects/ucap-webmessenger-electron/resources/image/lgRed/128_128.png create mode 100644 electron-projects/ucap-webmessenger-electron/resources/image/lgRed/16_16.png create mode 100644 electron-projects/ucap-webmessenger-electron/resources/image/lgRed/256_256.png create mode 100644 electron-projects/ucap-webmessenger-electron/resources/image/lgRed/32_32.png create mode 100644 electron-projects/ucap-webmessenger-electron/resources/image/lgRed/48_48.png create mode 100644 electron-projects/ucap-webmessenger-electron/resources/image/lgRed/64_64.png create mode 100644 electron-projects/ucap-webmessenger-electron/resources/image/lgRed/ico_64_64.png create mode 100644 electron-projects/ucap-webmessenger-electron/resources/image/lgRed/ico_64x64.ico create mode 100644 electron-projects/ucap-webmessenger-electron/resources/image/lgRed/ico_64x64_r.png rename {main => electron-projects/ucap-webmessenger-electron}/resources/installer/woori.icns (100%) rename {main => electron-projects/ucap-webmessenger-electron}/resources/installer/woori.ico (100%) rename {main => electron-projects/ucap-webmessenger-electron}/resources/installer/woori_256x256.ico (100%) rename {main => electron-projects/ucap-webmessenger-electron}/resources/installer/woori_256x256.png (100%) rename {main => electron-projects/ucap-webmessenger-electron}/resources/linuxicon/256x256.png (100%) rename {main => electron-projects/ucap-webmessenger-electron}/resources/notification/image/btn_call_message.png (100%) rename {main => electron-projects/ucap-webmessenger-electron}/resources/notification/image/btn_call_receive.png (100%) rename {main => electron-projects/ucap-webmessenger-electron}/resources/notification/image/btn_call_refuse.png (100%) rename {main => electron-projects/ucap-webmessenger-electron}/resources/notification/image/btn_call_transfer.png (100%) rename {main => electron-projects/ucap-webmessenger-electron}/resources/notification/image/btn_close.png (100%) rename {main => electron-projects/ucap-webmessenger-electron}/resources/notification/image/btn_close_gray.png (100%) rename {main => electron-projects/ucap-webmessenger-electron}/resources/notification/image/btn_noti_call.png (100%) rename {main => electron-projects/ucap-webmessenger-electron}/resources/notification/image/img_nophoto_50.png (100%) rename {main => electron-projects/ucap-webmessenger-electron}/resources/notification/preload.js (100%) rename {main => electron-projects/ucap-webmessenger-electron}/resources/notification/sound/messageAlarm.mp3 (100%) rename {main => electron-projects/ucap-webmessenger-electron}/resources/notification/styles/noti_messege.css (100%) rename {main => electron-projects/ucap-webmessenger-electron}/resources/notification/template.html (100%) rename {main => electron-projects/ucap-webmessenger-electron}/src/app/AppWindow.ts (89%) rename {main => electron-projects/ucap-webmessenger-electron}/src/crash/CrashWindow.ts (100%) rename {main => electron-projects/ucap-webmessenger-electron}/src/crash/show-uncaught-exception.ts (100%) rename {main => electron-projects/ucap-webmessenger-electron}/src/global.d.ts (100%) rename {main => electron-projects/ucap-webmessenger-electron}/src/index.ts (77%) rename {main => electron-projects/ucap-webmessenger-electron}/src/lib/default-folder.ts (100%) rename {main => electron-projects/ucap-webmessenger-electron}/src/lib/file-util.ts (100%) rename {main => electron-projects/ucap-webmessenger-electron}/src/lib/idle-checker.ts (89%) rename {main => electron-projects/ucap-webmessenger-electron}/src/lib/storage.ts (100%) rename {main => electron-projects/ucap-webmessenger-electron}/src/lib/window-state.ts (100%) rename {main => electron-projects/ucap-webmessenger-electron}/src/util/now.ts (100%) rename {main => electron-projects/ucap-webmessenger-electron}/src/util/root.ts (100%) rename main/tsconfig.main.json => electron-projects/ucap-webmessenger-electron/tsconfig.electron.json (50%) rename {projects/ucap-webmessenger-electron-notification => electron-projects/ucap-webmessenger-electron}/tsconfig.spec.json (100%) create mode 100644 electron-projects/ucap-webmessenger-electron/tslint.json delete mode 100644 main/resources/image/128_128.png delete mode 100644 main/resources/image/16_16.png delete mode 100644 main/resources/image/256_256.png delete mode 100644 main/resources/image/32_32.png delete mode 100644 main/resources/image/48_48.png delete mode 100644 main/resources/image/64_64.png delete mode 100644 main/resources/image/ico_64x64.ico delete mode 100644 main/resources/image/ico_64x64.png delete mode 100644 main/resources/image/ico_64x64_r.ico delete mode 100644 main/resources/image/ico_64x64_r.png create mode 100644 projects/ucap-webmessenger-api-common/src/lib/config/module-config.ts rename projects/ucap-webmessenger-api-common/src/lib/{types => config}/token.ts (100%) create mode 100644 projects/ucap-webmessenger-api-common/src/lib/config/urls.ts delete mode 100644 projects/ucap-webmessenger-api-common/src/lib/types/module-config.ts create mode 100644 projects/ucap-webmessenger-api-external/src/lib/config/module-config.ts rename projects/ucap-webmessenger-api-external/src/lib/{types => config}/token.ts (100%) create mode 100644 projects/ucap-webmessenger-api-external/src/lib/config/urls.ts delete mode 100644 projects/ucap-webmessenger-api-external/src/lib/types/module-config.ts create mode 100644 projects/ucap-webmessenger-api-message/README.md rename projects/{ucap-webmessenger-electron-core => ucap-webmessenger-api-message}/karma.conf.js (97%) rename projects/{ucap-webmessenger-electron-core => ucap-webmessenger-api-message}/ng-package.json (68%) rename projects/{ucap-webmessenger-electron-core => ucap-webmessenger-api-message}/package.json (72%) create mode 100644 projects/ucap-webmessenger-api-message/src/lib/apis/del.ts create mode 100644 projects/ucap-webmessenger-api-message/src/lib/apis/detail.ts create mode 100644 projects/ucap-webmessenger-api-message/src/lib/apis/edit-reservation-ex.ts create mode 100644 projects/ucap-webmessenger-api-message/src/lib/apis/my-message.ts create mode 100644 projects/ucap-webmessenger-api-message/src/lib/apis/retrieve.ts create mode 100644 projects/ucap-webmessenger-api-message/src/lib/apis/send-copy.ts create mode 100644 projects/ucap-webmessenger-api-message/src/lib/apis/send.ts create mode 100644 projects/ucap-webmessenger-api-message/src/lib/apis/unread-count.ts create mode 100644 projects/ucap-webmessenger-api-message/src/lib/config/module-config.ts create mode 100644 projects/ucap-webmessenger-api-message/src/lib/config/token.ts create mode 100644 projects/ucap-webmessenger-api-message/src/lib/config/urls.ts create mode 100644 projects/ucap-webmessenger-api-message/src/lib/models/message-list.ts create mode 100644 projects/ucap-webmessenger-api-message/src/lib/services/message-api.service.spec.ts create mode 100644 projects/ucap-webmessenger-api-message/src/lib/services/message-api.service.ts create mode 100644 projects/ucap-webmessenger-api-message/src/lib/types/category.type.ts create mode 100644 projects/ucap-webmessenger-api-message/src/lib/types/content.type.ts create mode 100644 projects/ucap-webmessenger-api-message/src/lib/types/message.search.type.ts create mode 100644 projects/ucap-webmessenger-api-message/src/lib/types/message.type.ts create mode 100644 projects/ucap-webmessenger-api-message/src/lib/ucap-message-api.module.ts create mode 100644 projects/ucap-webmessenger-api-message/src/public-api.ts create mode 100644 projects/ucap-webmessenger-api-message/src/test.ts rename projects/{ucap-webmessenger-electron-core => ucap-webmessenger-api-message}/tsconfig.lib.json (100%) create mode 100644 projects/ucap-webmessenger-api-message/tsconfig.spec.json create mode 100644 projects/ucap-webmessenger-api-message/tslint.json create mode 100644 projects/ucap-webmessenger-api-public/src/lib/config/module-config.ts rename projects/ucap-webmessenger-api-public/src/lib/{types => config}/token.ts (100%) create mode 100644 projects/ucap-webmessenger-api-public/src/lib/config/urls.ts delete mode 100644 projects/ucap-webmessenger-api-public/src/lib/types/module-config.ts create mode 100644 projects/ucap-webmessenger-api/src/lib/types/message-status-code.type.ts create mode 100644 projects/ucap-webmessenger-app/src/app/layouts/messenger/components/left-sidenav/message.component.html create mode 100644 projects/ucap-webmessenger-app/src/app/layouts/messenger/components/left-sidenav/message.component.scss create mode 100644 projects/ucap-webmessenger-app/src/app/layouts/messenger/components/left-sidenav/message.component.spec.ts create mode 100644 projects/ucap-webmessenger-app/src/app/layouts/messenger/components/left-sidenav/message.component.ts create mode 100644 projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer.component.html create mode 100644 projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer.component.scss create mode 100644 projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer.component.spec.ts create mode 100644 projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer.component.ts create mode 100644 projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer/album-box.component.html create mode 100644 projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer/album-box.component.scss create mode 100644 projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer/album-box.component.spec.ts create mode 100644 projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer/album-box.component.ts create mode 100644 projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer/file-box.component.html create mode 100644 projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer/file-box.component.scss create mode 100644 projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer/file-box.component.spec.ts create mode 100644 projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer/file-box.component.ts create mode 100644 projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer/index.ts create mode 100644 projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer/room-user-list.component.html create mode 100644 projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer/room-user-list.component.scss create mode 100644 projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer/room-user-list.component.spec.ts create mode 100644 projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer/room-user-list.component.ts create mode 100644 projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/chat/mass-detail.component.html create mode 100644 projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/chat/mass-detail.component.scss create mode 100644 projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/chat/mass-detail.component.spec.ts create mode 100644 projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/chat/mass-detail.component.ts create mode 100644 projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/profile/index.ts create mode 100644 projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/profile/profile.dialog.component.html create mode 100644 projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/profile/profile.dialog.component.scss create mode 100644 projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/profile/profile.dialog.component.spec.ts create mode 100644 projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/profile/profile.dialog.component.ts create mode 100644 projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/settings/index.ts create mode 100644 projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/settings/messenger-settings.dialog.component.html create mode 100644 projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/settings/messenger-settings.dialog.component.scss create mode 100644 projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/settings/messenger-settings.dialog.component.spec.ts create mode 100644 projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/settings/messenger-settings.dialog.component.ts create mode 100644 projects/ucap-webmessenger-app/src/app/services/native.service.ts create mode 100644 projects/ucap-webmessenger-app/src/app/store/messenger/settings/actions.ts create mode 100644 projects/ucap-webmessenger-app/src/app/store/messenger/settings/effects.ts create mode 100644 projects/ucap-webmessenger-app/src/app/store/messenger/settings/index.ts create mode 100644 projects/ucap-webmessenger-app/src/app/store/messenger/settings/reducers.ts create mode 100644 projects/ucap-webmessenger-app/src/app/store/messenger/settings/state.ts create mode 100644 projects/ucap-webmessenger-app/src/app/types/right-drawer.type.ts create mode 100644 projects/ucap-webmessenger-app/src/app/types/sticker-info.type.ts create mode 100644 projects/ucap-webmessenger-app/src/assets/fonts/materialdesignicons/css/materialdesignicons.css create mode 100644 projects/ucap-webmessenger-app/src/assets/fonts/materialdesignicons/css/materialdesignicons.css.map create mode 100644 projects/ucap-webmessenger-app/src/assets/fonts/materialdesignicons/css/materialdesignicons.min.css create mode 100644 projects/ucap-webmessenger-app/src/assets/fonts/materialdesignicons/css/materialdesignicons.min.css.map create mode 100644 projects/ucap-webmessenger-app/src/assets/fonts/materialdesignicons/fonts/materialdesignicons-webfont.eot create mode 100644 projects/ucap-webmessenger-app/src/assets/fonts/materialdesignicons/fonts/materialdesignicons-webfont.ttf create mode 100644 projects/ucap-webmessenger-app/src/assets/fonts/materialdesignicons/fonts/materialdesignicons-webfont.woff create mode 100644 projects/ucap-webmessenger-app/src/assets/fonts/materialdesignicons/fonts/materialdesignicons-webfont.woff2 create mode 100644 projects/ucap-webmessenger-app/src/assets/fonts/materialdesignicons/scss/_animated.scss create mode 100644 projects/ucap-webmessenger-app/src/assets/fonts/materialdesignicons/scss/_core.scss create mode 100644 projects/ucap-webmessenger-app/src/assets/fonts/materialdesignicons/scss/_extras.scss create mode 100644 projects/ucap-webmessenger-app/src/assets/fonts/materialdesignicons/scss/_functions.scss create mode 100644 projects/ucap-webmessenger-app/src/assets/fonts/materialdesignicons/scss/_icons.scss create mode 100644 projects/ucap-webmessenger-app/src/assets/fonts/materialdesignicons/scss/_path.scss create mode 100644 projects/ucap-webmessenger-app/src/assets/fonts/materialdesignicons/scss/_variables.scss create mode 100644 projects/ucap-webmessenger-app/src/assets/fonts/materialdesignicons/scss/materialdesignicons.scss create mode 100644 projects/ucap-webmessenger-app/src/assets/images/logo/64_64.png rename main/resources/image/ico_64_64.png => projects/ucap-webmessenger-app/src/assets/images/logo/ico_64x64.png.woori (100%) create mode 100644 projects/ucap-webmessenger-app/src/assets/images/theme/theme-default.png create mode 100644 projects/ucap-webmessenger-app/src/assets/images/theme/theme-lgRed.png create mode 100644 projects/ucap-webmessenger-app/src/assets/scss/partials/splash.css create mode 100644 projects/ucap-webmessenger-app/src/environments/environment-browser.dev.ts create mode 100644 projects/ucap-webmessenger-app/src/environments/environment-browser.prod.ts create mode 100644 projects/ucap-webmessenger-app/src/environments/environment-renderer.dev.ts create mode 100644 projects/ucap-webmessenger-app/src/environments/environment-renderer.prod.ts create mode 100644 projects/ucap-webmessenger-app/src/environments/environment.dev.ts create mode 100644 projects/ucap-webmessenger-app/src/polyfills-es5.ts create mode 100644 projects/ucap-webmessenger-app/tsconfig-es5.app.json create mode 100644 projects/ucap-webmessenger-core/src/lib/config/host.config.ts create mode 100644 projects/ucap-webmessenger-core/src/lib/config/module.config.ts create mode 100644 projects/ucap-webmessenger-core/src/lib/config/url.config.ts create mode 100644 projects/ucap-webmessenger-core/src/lib/utils/sticker.util.ts create mode 100644 projects/ucap-webmessenger-native-browser/src/lib/notification/notification.service.ts create mode 100644 projects/ucap-webmessenger-native/src/lib/config/module-config.ts delete mode 100644 projects/ucap-webmessenger-native/src/lib/ucap-native.module.ts create mode 100644 projects/ucap-webmessenger-pi/src/lib/config/module-config.ts rename projects/ucap-webmessenger-pi/src/lib/{types => config}/token.ts (64%) rename projects/ucap-webmessenger-pi/src/lib/{types/module-config.ts => config/urls.ts} (82%) create mode 100644 projects/ucap-webmessenger-protocol/src/lib/config/module-config.ts rename projects/ucap-webmessenger-protocol/src/lib/{types => config}/token.ts (100%) create mode 100644 projects/ucap-webmessenger-protocol/src/lib/config/urls.ts delete mode 100644 projects/ucap-webmessenger-protocol/src/lib/types/module-config.ts create mode 100644 projects/ucap-webmessenger-ui-profile/src/lib/components/my-profile-widget.component.html create mode 100644 projects/ucap-webmessenger-ui-profile/src/lib/components/my-profile-widget.component.scss create mode 100644 projects/ucap-webmessenger-ui-profile/src/lib/components/my-profile-widget.component.spec.ts create mode 100644 projects/ucap-webmessenger-ui-profile/src/lib/components/my-profile-widget.component.ts create mode 100644 projects/ucap-webmessenger-ui-profile/src/lib/components/profile.component.html create mode 100644 projects/ucap-webmessenger-ui-profile/src/lib/components/profile.component.scss create mode 100644 projects/ucap-webmessenger-ui-profile/src/lib/components/profile.component.spec.ts create mode 100644 projects/ucap-webmessenger-ui-profile/src/lib/components/profile.component.ts create mode 100644 projects/ucap-webmessenger-ui-settings/README.md rename projects/{ucap-webmessenger-electron-notification => ucap-webmessenger-ui-settings}/karma.conf.js (96%) rename projects/{ucap-webmessenger-electron-notification => ucap-webmessenger-ui-settings}/ng-package.json (65%) create mode 100644 projects/ucap-webmessenger-ui-settings/package.json create mode 100644 projects/ucap-webmessenger-ui-settings/src/assets/timezone/en.json create mode 100644 projects/ucap-webmessenger-ui-settings/src/assets/timezone/ko.json create mode 100644 projects/ucap-webmessenger-ui-settings/src/lib/components/call.component.html create mode 100644 projects/ucap-webmessenger-ui-settings/src/lib/components/call.component.scss create mode 100644 projects/ucap-webmessenger-ui-settings/src/lib/components/call.component.spec.ts create mode 100644 projects/ucap-webmessenger-ui-settings/src/lib/components/call.component.ts create mode 100644 projects/ucap-webmessenger-ui-settings/src/lib/components/device.component.html create mode 100644 projects/ucap-webmessenger-ui-settings/src/lib/components/device.component.scss create mode 100644 projects/ucap-webmessenger-ui-settings/src/lib/components/device.component.spec.ts create mode 100644 projects/ucap-webmessenger-ui-settings/src/lib/components/device.component.ts create mode 100644 projects/ucap-webmessenger-ui-settings/src/lib/components/general.component.html create mode 100644 projects/ucap-webmessenger-ui-settings/src/lib/components/general.component.scss create mode 100644 projects/ucap-webmessenger-ui-settings/src/lib/components/general.component.spec.ts create mode 100644 projects/ucap-webmessenger-ui-settings/src/lib/components/general.component.ts create mode 100644 projects/ucap-webmessenger-ui-settings/src/lib/components/notification.component.html create mode 100644 projects/ucap-webmessenger-ui-settings/src/lib/components/notification.component.scss create mode 100644 projects/ucap-webmessenger-ui-settings/src/lib/components/notification.component.spec.ts create mode 100644 projects/ucap-webmessenger-ui-settings/src/lib/components/notification.component.ts create mode 100644 projects/ucap-webmessenger-ui-settings/src/lib/components/permission.component.html create mode 100644 projects/ucap-webmessenger-ui-settings/src/lib/components/permission.component.scss create mode 100644 projects/ucap-webmessenger-ui-settings/src/lib/components/permission.component.spec.ts create mode 100644 projects/ucap-webmessenger-ui-settings/src/lib/components/permission.component.ts create mode 100644 projects/ucap-webmessenger-ui-settings/src/lib/components/privacy.component.html create mode 100644 projects/ucap-webmessenger-ui-settings/src/lib/components/privacy.component.scss create mode 100644 projects/ucap-webmessenger-ui-settings/src/lib/components/privacy.component.spec.ts create mode 100644 projects/ucap-webmessenger-ui-settings/src/lib/components/privacy.component.ts create mode 100644 projects/ucap-webmessenger-ui-settings/src/lib/ucap-ui-settings.module.ts create mode 100644 projects/ucap-webmessenger-ui-settings/src/public-api.ts create mode 100644 projects/ucap-webmessenger-ui-settings/src/test.ts rename projects/{ucap-webmessenger-electron-notification => ucap-webmessenger-ui-settings}/tsconfig.lib.json (100%) create mode 100644 projects/ucap-webmessenger-ui-settings/tsconfig.spec.json create mode 100644 projects/ucap-webmessenger-ui-settings/tslint.json create mode 100644 projects/ucap-webmessenger-ui/src/lib/components/expansion-panel.component.html create mode 100644 projects/ucap-webmessenger-ui/src/lib/components/expansion-panel.component.scss create mode 100644 projects/ucap-webmessenger-ui/src/lib/components/expansion-panel.component.spec.ts create mode 100644 projects/ucap-webmessenger-ui/src/lib/components/expansion-panel.component.ts create mode 100644 projects/ucap-webmessenger-ui/src/lib/components/sticker-selector.component.html create mode 100644 projects/ucap-webmessenger-ui/src/lib/components/sticker-selector.component.scss create mode 100644 projects/ucap-webmessenger-ui/src/lib/components/sticker-selector.component.spec.ts create mode 100644 projects/ucap-webmessenger-ui/src/lib/components/sticker-selector.component.ts create mode 100644 projects/ucap-webmessenger-ui/src/lib/data-source/virtual-scroll-tree-flat.data-source.ts create mode 100644 projects/ucap-webmessenger-ui/src/lib/directives/cdk-virtual-scroll-viewport-patch.directive.ts delete mode 100644 projects/ucap-webmessenger-ui/src/lib/pipes/dates.pipe.spec.ts delete mode 100644 projects/ucap-webmessenger-ui/src/lib/pipes/linefeed.pipe.spec.ts create mode 100644 projects/ucap-webmessenger-ui/src/lib/pipes/linky.pipe.ts create mode 100644 projects/ucap-webmessenger-ui/src/lib/services/splash-screen.service.spec.ts create mode 100644 projects/ucap-webmessenger-ui/src/lib/services/splash-screen.service.ts diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..212426a --- /dev/null +++ b/.prettierrc @@ -0,0 +1,5 @@ +{ + "trailingComma": "none", + "tabWidth": 2, + "singleQuote": true +} diff --git a/.vscode/settings.json b/.vscode/settings.json index df8e24a..2cdeaf4 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -7,8 +7,10 @@ "editor.trimAutoWhitespace": true, "files.trimTrailingWhitespace": true, "files.trimFinalNewlines": true, + "files.watcherExclude": { + "**/dist/electron/win-unpacked/**": true + }, "go.testFlags": ["-v"], "go.testTimeout": "100s", - "prettier.singleQuote": true, "debug.node.autoAttach": "on" } diff --git a/angular.json b/angular.json index e493b62..5f3b5fa 100644 --- a/angular.json +++ b/angular.json @@ -30,18 +30,52 @@ "styles": ["projects/ucap-webmessenger-app/src/styles.scss"], "scripts": [], "customWebpackConfig": { - "path": "./config/renderer.webpack.config.js", - "mergeStrategies": { "externals": "replace" } + "path": "./config/angular.webpack.config.js" } }, "configurations": { - "production": { + "browser-development": { "fileReplacements": [ { "replace": "projects/ucap-webmessenger-app/src/environments/environment.ts", - "with": "projects/ucap-webmessenger-app/src/environments/environment.prod.ts" + "with": "projects/ucap-webmessenger-app/src/environments/environment-browser.dev.ts" } ], + "outputPath": "dist/ucap-webmessenger-app-browser", + "polyfills": "projects/ucap-webmessenger-app/src/polyfills-es5.ts", + "tsConfig": "projects/ucap-webmessenger-app/tsconfig-es5.app.json", + "optimization": false, + "outputHashing": "all", + "sourceMap": true, + "extractCss": true, + "namedChunks": false, + "aot": false, + "extractLicenses": true, + "vendorChunk": false, + "buildOptimizer": false, + "budgets": [ + { + "type": "initial", + "maximumWarning": "2mb", + "maximumError": "5mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "6kb", + "maximumError": "10kb" + } + ] + }, + "browser-production": { + "fileReplacements": [ + { + "replace": "projects/ucap-webmessenger-app/src/environments/environment.ts", + "with": "projects/ucap-webmessenger-app/src/environments/environment-browser.prod.ts" + } + ], + "outputPath": "dist/ucap-webmessenger-app-browser", + "polyfills": "projects/ucap-webmessenger-app/src/polyfills-es5.ts", + "tsConfig": "projects/ucap-webmessenger-app/tsconfig-es5.app.json", "optimization": true, "outputHashing": "all", "sourceMap": false, @@ -51,6 +85,36 @@ "extractLicenses": true, "vendorChunk": false, "buildOptimizer": true, + "budgets": [ + { + "type": "initial", + "maximumWarning": "4mb", + "maximumError": "5mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "6kb", + "maximumError": "10kb" + } + ] + }, + "renderer-development": { + "fileReplacements": [ + { + "replace": "projects/ucap-webmessenger-app/src/environments/environment.ts", + "with": "projects/ucap-webmessenger-app/src/environments/environment-renderer.dev.ts" + } + ], + "tsConfig": "projects/ucap-webmessenger-app/tsconfig-es5.app.json", + "optimization": false, + "outputHashing": "all", + "sourceMap": true, + "extractCss": true, + "namedChunks": false, + "aot": false, + "extractLicenses": true, + "vendorChunk": false, + "buildOptimizer": false, "budgets": [ { "type": "initial", @@ -63,17 +127,57 @@ "maximumError": "10kb" } ] + }, + "renderer-production": { + "fileReplacements": [ + { + "replace": "projects/ucap-webmessenger-app/src/environments/environment.ts", + "with": "projects/ucap-webmessenger-app/src/environments/environment-renderer.prod.ts" + } + ], + "tsConfig": "projects/ucap-webmessenger-app/tsconfig-es5.app.json", + "optimization": true, + "outputHashing": "all", + "sourceMap": false, + "extractCss": true, + "namedChunks": false, + "aot": true, + "extractLicenses": true, + "vendorChunk": false, + "buildOptimizer": true, + "budgets": [ + { + "type": "initial", + "maximumWarning": "4mb", + "maximumError": "5mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "6kb", + "maximumError": "10kb" + } + ] } } }, "serve": { "builder": "@angular-builders/custom-webpack:dev-server", "options": { - "browserTarget": "ucap-webmessenger-app:build" + "browserTarget": "ucap-webmessenger-app:build", + "disableHostCheck": true }, "configurations": { - "production": { - "browserTarget": "ucap-webmessenger-app:build:production" + "browser-development": { + "browserTarget": "ucap-webmessenger-app:build:browser-development" + }, + "browser-production": { + "browserTarget": "ucap-webmessenger-app:build:browser-production" + }, + "renderer-development": { + "browserTarget": "ucap-webmessenger-app:build:renderer-development" + }, + "renderer-production": { + "browserTarget": "ucap-webmessenger-app:build:renderer-production" } } }, @@ -84,7 +188,7 @@ } }, "test": { - "builder": "@angular-devkit/build-angular:karma", + "builder": "@angular-builders/custom-webpack:karma", "options": { "main": "projects/ucap-webmessenger-app/src/test.ts", "polyfills": "projects/ucap-webmessenger-app/src/polyfills.ts", @@ -95,7 +199,10 @@ "projects/ucap-webmessenger-app/src/assets" ], "styles": ["projects/ucap-webmessenger-app/src/styles.scss"], - "scripts": [] + "scripts": [], + "customWebpackConfig": { + "path": "./config/angular.webpack.config.js" + } } }, "lint": { @@ -256,6 +363,40 @@ } } }, + "ucap-webmessenger-api-message": { + "projectType": "library", + "root": "projects/ucap-webmessenger-api-message", + "sourceRoot": "projects/ucap-webmessenger-api-message/src", + "prefix": "ucap-api-message", + "architect": { + "build": { + "builder": "@angular-devkit/build-ng-packagr:build", + "options": { + "tsConfig": "projects/ucap-webmessenger-api-message/tsconfig.lib.json", + "project": "projects/ucap-webmessenger-api-message/ng-package.json" + } + }, + "test": { + "builder": "@angular-devkit/build-angular:karma", + "options": { + "main": "projects/ucap-webmessenger-api-message/src/test.ts", + "tsConfig": "projects/ucap-webmessenger-api-message/tsconfig.spec.json", + "karmaConfig": "projects/ucap-webmessenger-api-message/karma.conf.js" + } + }, + "lint": { + "builder": "@angular-devkit/build-angular:tslint", + "options": { + "tsConfig": [ + "projects/ucap-webmessenger-api-message/tsconfig.lib.json", + "projects/ucap-webmessenger-api-message/tsconfig.spec.json" + ], + "exclude": ["**/node_modules/**"] + } + } + } + }, + "ucap-webmessenger-api": { "projectType": "library", "root": "projects/ucap-webmessenger-api", @@ -588,6 +729,39 @@ } } }, + "ucap-webmessenger-ui-settings": { + "projectType": "library", + "root": "projects/ucap-webmessenger-ui-settings", + "sourceRoot": "projects/ucap-webmessenger-ui-settings/src", + "prefix": "ucap-settings", + "architect": { + "build": { + "builder": "@angular-devkit/build-ng-packagr:build", + "options": { + "tsConfig": "projects/ucap-webmessenger-ui-settings/tsconfig.lib.json", + "project": "projects/ucap-webmessenger-ui-settings/ng-package.json" + } + }, + "test": { + "builder": "@angular-devkit/build-angular:karma", + "options": { + "main": "projects/ucap-webmessenger-ui-settings/src/test.ts", + "tsConfig": "projects/ucap-webmessenger-ui-settings/tsconfig.spec.json", + "karmaConfig": "projects/ucap-webmessenger-ui-settings/karma.conf.js" + } + }, + "lint": { + "builder": "@angular-devkit/build-angular:tslint", + "options": { + "tsConfig": [ + "projects/ucap-webmessenger-ui-settings/tsconfig.lib.json", + "projects/ucap-webmessenger-ui-settings/tsconfig.spec.json" + ], + "exclude": ["**/node_modules/**"] + } + } + } + }, "ucap-webmessenger-protocol": { "projectType": "library", "root": "projects/ucap-webmessenger-protocol", @@ -1182,72 +1356,6 @@ } } }, - "ucap-webmessenger-native": { - "projectType": "library", - "root": "projects/ucap-webmessenger-native", - "sourceRoot": "projects/ucap-webmessenger-native/src", - "prefix": "ucap-native", - "architect": { - "build": { - "builder": "@angular-devkit/build-ng-packagr:build", - "options": { - "tsConfig": "projects/ucap-webmessenger-native/tsconfig.lib.json", - "project": "projects/ucap-webmessenger-native/ng-package.json" - } - }, - "test": { - "builder": "@angular-devkit/build-angular:karma", - "options": { - "main": "projects/ucap-webmessenger-native/src/test.ts", - "tsConfig": "projects/ucap-webmessenger-native/tsconfig.spec.json", - "karmaConfig": "projects/ucap-webmessenger-native/karma.conf.js" - } - }, - "lint": { - "builder": "@angular-devkit/build-angular:tslint", - "options": { - "tsConfig": [ - "projects/ucap-webmessenger-native/tsconfig.lib.json", - "projects/ucap-webmessenger-native/tsconfig.spec.json" - ], - "exclude": ["**/node_modules/**"] - } - } - } - }, - "ucap-webmessenger-native-electron": { - "projectType": "library", - "root": "projects/ucap-webmessenger-native-electron", - "sourceRoot": "projects/ucap-webmessenger-native-electron/src", - "prefix": "ucap-native-electron", - "architect": { - "build": { - "builder": "@angular-devkit/build-ng-packagr:build", - "options": { - "tsConfig": "projects/ucap-webmessenger-native-electron/tsconfig.lib.json", - "project": "projects/ucap-webmessenger-native-electron/ng-package.json" - } - }, - "test": { - "builder": "@angular-devkit/build-angular:karma", - "options": { - "main": "projects/ucap-webmessenger-native-electron/src/test.ts", - "tsConfig": "projects/ucap-webmessenger-native-electron/tsconfig.spec.json", - "karmaConfig": "projects/ucap-webmessenger-native-electron/karma.conf.js" - } - }, - "lint": { - "builder": "@angular-devkit/build-angular:tslint", - "options": { - "tsConfig": [ - "projects/ucap-webmessenger-native-electron/tsconfig.lib.json", - "projects/ucap-webmessenger-native-electron/tsconfig.spec.json" - ], - "exclude": ["**/node_modules/**"] - } - } - } - }, "ucap-webmessenger-web-socket": { "projectType": "library", "root": "projects/ucap-webmessenger-web-socket", @@ -1413,6 +1521,72 @@ } } }, + "ucap-webmessenger-native": { + "projectType": "library", + "root": "projects/ucap-webmessenger-native", + "sourceRoot": "projects/ucap-webmessenger-native/src", + "prefix": "ucap-native", + "architect": { + "build": { + "builder": "@angular-devkit/build-ng-packagr:build", + "options": { + "tsConfig": "projects/ucap-webmessenger-native/tsconfig.lib.json", + "project": "projects/ucap-webmessenger-native/ng-package.json" + } + }, + "test": { + "builder": "@angular-devkit/build-angular:karma", + "options": { + "main": "projects/ucap-webmessenger-native/src/test.ts", + "tsConfig": "projects/ucap-webmessenger-native/tsconfig.spec.json", + "karmaConfig": "projects/ucap-webmessenger-native/karma.conf.js" + } + }, + "lint": { + "builder": "@angular-devkit/build-angular:tslint", + "options": { + "tsConfig": [ + "projects/ucap-webmessenger-native/tsconfig.lib.json", + "projects/ucap-webmessenger-native/tsconfig.spec.json" + ], + "exclude": ["**/node_modules/**"] + } + } + } + }, + "ucap-webmessenger-native-electron": { + "projectType": "library", + "root": "projects/ucap-webmessenger-native-electron", + "sourceRoot": "projects/ucap-webmessenger-native-electron/src", + "prefix": "ucap-native-electron", + "architect": { + "build": { + "builder": "@angular-devkit/build-ng-packagr:build", + "options": { + "tsConfig": "projects/ucap-webmessenger-native-electron/tsconfig.lib.json", + "project": "projects/ucap-webmessenger-native-electron/ng-package.json" + } + }, + "test": { + "builder": "@angular-devkit/build-angular:karma", + "options": { + "main": "projects/ucap-webmessenger-native-electron/src/test.ts", + "tsConfig": "projects/ucap-webmessenger-native-electron/tsconfig.spec.json", + "karmaConfig": "projects/ucap-webmessenger-native-electron/karma.conf.js" + } + }, + "lint": { + "builder": "@angular-devkit/build-angular:tslint", + "options": { + "tsConfig": [ + "projects/ucap-webmessenger-native-electron/tsconfig.lib.json", + "projects/ucap-webmessenger-native-electron/tsconfig.spec.json" + ], + "exclude": ["**/node_modules/**"] + } + } + } + }, "ucap-webmessenger-native-browser": { "projectType": "library", "root": "projects/ucap-webmessenger-native-browser", @@ -1445,76 +1619,6 @@ } } } - }, - "ucap-webmessenger-electron-notification": { - "projectType": "library", - "root": "projects/ucap-webmessenger-electron-notification", - "sourceRoot": "projects/ucap-webmessenger-electron-notification/src", - "prefix": "ucap-electron-notification", - "architect": { - "build": { - "builder": "@angular-devkit/build-ng-packagr:build", - "options": { - "tsConfig": "projects/ucap-webmessenger-electron-notification/tsconfig.lib.json", - "project": "projects/ucap-webmessenger-electron-notification/ng-package.json" - } - }, - "test": { - "builder": "@angular-devkit/build-angular:karma", - "options": { - "main": "projects/ucap-webmessenger-electron-notification/src/test.ts", - "tsConfig": "projects/ucap-webmessenger-electron-notification/tsconfig.spec.json", - "karmaConfig": "projects/ucap-webmessenger-electron-notification/karma.conf.js" - } - }, - "lint": { - "builder": "@angular-devkit/build-angular:tslint", - "options": { - "tsConfig": [ - "projects/ucap-webmessenger-electron-notification/tsconfig.lib.json", - "projects/ucap-webmessenger-electron-notification/tsconfig.spec.json" - ], - "exclude": [ - "**/node_modules/**" - ] - } - } - } - }, - "ucap-webmessenger-electron-core": { - "projectType": "library", - "root": "projects/ucap-webmessenger-electron-core", - "sourceRoot": "projects/ucap-webmessenger-electron-core/src", - "prefix": "ucap-electron-core", - "architect": { - "build": { - "builder": "@angular-devkit/build-ng-packagr:build", - "options": { - "tsConfig": "projects/ucap-webmessenger-electron-core/tsconfig.lib.json", - "project": "projects/ucap-webmessenger-electron-core/ng-package.json" - } - }, - "test": { - "builder": "@angular-devkit/build-angular:karma", - "options": { - "main": "projects/ucap-webmessenger-electron-core/src/test.ts", - "tsConfig": "projects/ucap-webmessenger-electron-core/tsconfig.spec.json", - "karmaConfig": "projects/ucap-webmessenger-electron-core/karma.conf.js" - } - }, - "lint": { - "builder": "@angular-devkit/build-angular:tslint", - "options": { - "tsConfig": [ - "projects/ucap-webmessenger-electron-core/tsconfig.lib.json", - "projects/ucap-webmessenger-electron-core/tsconfig.spec.json" - ], - "exclude": [ - "**/node_modules/**" - ] - } - } - } } }, "defaultProject": "ucap-webmessenger-app" diff --git a/config/renderer.webpack.config.js b/config/angular.webpack.config.js similarity index 58% rename from config/renderer.webpack.config.js rename to config/angular.webpack.config.js index 7d76c60..71303bb 100644 --- a/config/renderer.webpack.config.js +++ b/config/angular.webpack.config.js @@ -2,8 +2,16 @@ const path = require('path'); module.exports = (config, options) => { const PRODUCTION = process.env.NODE_ENV === 'production'; + const BROWSER = process.env.UCAP_ENV_RUNTIME === 'BROWSER'; - config.target = 'electron-renderer'; + if (!BROWSER) { + config.target = 'electron-renderer'; + } else { + config.target = 'web'; + config.node = { + fs: 'empty', + }; + } config.resolve.alias = { ...config.resolve.alias, @@ -11,7 +19,7 @@ module.exports = (config, options) => { __dirname, '..', 'projects/ucap-webmessenger-ui/src/assets/scss' - ) + ), }; return config; diff --git a/config/enviroment.ts b/config/enviroment.ts index 6ce87ed..70f97e5 100644 --- a/config/enviroment.ts +++ b/config/enviroment.ts @@ -1,4 +1,3 @@ -import * as fs from 'fs'; import * as Path from 'path'; const projectRoot = Path.dirname(__dirname); @@ -14,6 +13,6 @@ export function getEnviroments() { __DEV__: channel === 'development', 'process.platform': s(process.platform), 'process.env.NODE_ENV': s(process.env.NODE_ENV || 'development'), - 'process.env.TEST_ENV': s(process.env.TEST_ENV) + 'process.env.TEST_ENV': s(process.env.TEST_ENV), }; } diff --git a/config/main.webpack.config.ts b/config/main.webpack.config.ts index aad0c9b..1d8a5ea 100644 --- a/config/main.webpack.config.ts +++ b/config/main.webpack.config.ts @@ -12,28 +12,39 @@ export const externals = [nodeExternals()]; // externals.push('devtron'); // } -const outputDir = 'dist/main'; +const outputDir = 'dist/ucap-webmessenger-electron'; const mainConfig: webpack.Configuration = { - entry: { main: path.resolve(__dirname, '..', 'main/src/index') }, + entry: { + main: path.resolve( + __dirname, + '..', + 'electron-projects/ucap-webmessenger-electron/src/index' + ), + }, target: 'electron-main', mode: enviroments.__DEV__ ? 'development' : 'production', devtool: 'source-map', optimization: { - noEmitOnErrors: true + noEmitOnErrors: true, }, externals, output: { filename: '[name].js', - path: path.resolve(__dirname, '..', outputDir) + path: path.resolve(__dirname, '..', outputDir), }, module: { rules: [ { test: /\.tsx?$/, include: [ - path.resolve(__dirname, '..', 'main/src'), - path.resolve(__dirname, '..', 'projects') + path.resolve( + __dirname, + '..', + 'electron-projects/ucap-webmessenger-electron/src' + ), + path.resolve(__dirname, '..', 'electron-projects'), + path.resolve(__dirname, '..', 'projects'), ], use: [ { @@ -43,21 +54,21 @@ const mainConfig: webpack.Configuration = { configFileName: path.resolve( __dirname, '..', - 'main/tsconfig.main.json' - ) - } - } + 'electron-projects/ucap-webmessenger-electron/tsconfig.electron.json' + ), + }, + }, ], - exclude: /node_modules/ + exclude: /node_modules/, }, { test: /\.node$/, loader: 'awesome-node-loader', options: { - name: '[name].[ext]' - } - } - ] + name: '[name].[ext]', + }, + }, + ], }, plugins: [ new CleanWebpackPlugin({ verbose: false }), @@ -66,15 +77,16 @@ const mainConfig: webpack.Configuration = { new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/), new webpack.DefinePlugin( Object.assign({}, enviroments, { - __PROCESS_KIND__: JSON.stringify('main') + __PROCESS_KIND__: JSON.stringify('main'), }) ), new CopyWebpackPlugin([ { - from: 'main/resources/**/*', - to: path.resolve(__dirname, '..', 'dist') - } - ]) + from: 'ucap-webmessenger-electron/resources/**/*', + to: path.resolve(__dirname, '..', 'dist'), + context: 'electron-projects', + }, + ]), ], resolve: { extensions: ['.js', '.ts'], @@ -82,12 +94,17 @@ const mainConfig: webpack.Configuration = { '@ucap-webmessenger/electron-core': path.resolve( __dirname, '..', - 'projects/ucap-webmessenger-electron-core/src/public-api' + 'electron-projects/ucap-webmessenger-electron-core/src/public-api' ), '@ucap-webmessenger/electron-notification': path.resolve( __dirname, '..', - 'projects/ucap-webmessenger-electron-notification/src/public-api' + 'electron-projects/ucap-webmessenger-electron-notification/src/public-api' + ), + '@ucap-webmessenger/core': path.resolve( + __dirname, + '..', + 'projects/ucap-webmessenger-core/src/public-api' ), '@ucap-webmessenger/native': path.resolve( __dirname, @@ -98,14 +115,19 @@ const mainConfig: webpack.Configuration = { __dirname, '..', 'projects/ucap-webmessenger-native-electron/src/public-api' - ) + ), + '@ucap-webmessenger/electron': path.resolve( + __dirname, + '..', + 'electron-projects/ucap-webmessenger-electron/src/public-api' + ), }, - modules: [path.resolve(__dirname, '..', 'node_modules/')] + modules: [path.resolve(__dirname, '..', 'node_modules/')], }, node: { __dirname: false, - __filename: false - } + __filename: false, + }, }; export default [mainConfig]; diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml new file mode 100644 index 0000000..a5445db --- /dev/null +++ b/docker/docker-compose.yml @@ -0,0 +1,10 @@ +version: '3.1' + +services: + nginx: + image: nginx:1.17.5-alpine + volumes: + - ../dist/web:/usr/share/nginx/html:ro + - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro + ports: + - 8099:80 diff --git a/docker/nginx/nginx.conf b/docker/nginx/nginx.conf new file mode 100644 index 0000000..ea65e87 --- /dev/null +++ b/docker/nginx/nginx.conf @@ -0,0 +1,40 @@ +worker_processes 4; + +events { worker_connections 1024; } + +http { + ssl_session_cache shared:SSL:10m; + ssl_session_timeout 30m; + + #See http://blog.argteam.com/coding/hardening-node-js-for-production-part-2-using-nginx-to-avoid-node-js-load + proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=one:8m max_size=3000m inactive=600m; + proxy_temp_path /var/tmp; + include mime.types; + default_type application/octet-stream; + sendfile on; + keepalive_timeout 65; + + gzip on; + gzip_comp_level 6; + gzip_vary on; + gzip_min_length 1000; + gzip_proxied any; + gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript; + gzip_buffers 16 8k; + + server { + listen 80; + server_name localhost; + + location / { + root /usr/share/nginx/html; + index index.html; + expires -1; + add_header Pragma "no-cache"; + add_header Cache-Control "no-store, no-cache, must-revalidate, post-check=0, pre-check=0"; + try_files $uri$args $uri$args/ $uri $uri/ /index.html =404; + } + } + + +} \ No newline at end of file diff --git a/electron-builder.json b/electron-builder.json index d2cced2..fa84f18 100644 --- a/electron-builder.json +++ b/electron-builder.json @@ -1,10 +1,10 @@ { - "productName": "WooriTalk", - "appId": "lgcns.ucap.messenger", + "productName": "UCapMessenger", + "appId": "com.lgucap.messenger", "asar": true, "protocols": { - "name": "WooriTalk", - "schemes": ["WooriTalk"] + "name": "UCapMessenger", + "schemes": ["UCapMessenger"] }, "publish": { "provider": "generic", @@ -12,19 +12,19 @@ }, "mac": { "target": ["default"], - "icon": "./resources/installer/woori.icns" + "icon": "./dist/ucap-webmessenger-electron/resources/installer/woori.icns" }, "dmg": { - "title": "WooriTalk", - "icon": "./resources/installer/woori.icns" + "title": "UCapMessenger", + "icon": "./dist/ucap-webmessenger-electron/resources/installer/woori.icns" }, "win": { "target": ["zip", "nsis"], - "icon": "./resources/installer/woori_256x256.ico" + "icon": "./dist/ucap-webmessenger-electron/resources/image/16_16.ico" }, "linux": { "target": ["AppImage", "deb", "rpm", "zip", "tar.gz"], - "icon": "./resources/linuxicon" + "icon": "./dist/ucap-webmessenger-electron/resources/linuxicon" }, "nsis": { "oneClick": false, @@ -33,8 +33,8 @@ "differentialPackage": true }, "directories": { - "buildResources": "resources/installer/", - "output": "dist-electron/", + "buildResources": "./dist/ucap-webmessenger-electron/resources/installer/", + "output": "./dist/electron/", "app": "." } } diff --git a/projects/ucap-webmessenger-electron-core/README.md b/electron-projects/ucap-webmessenger-electron-core/README.md similarity index 100% rename from projects/ucap-webmessenger-electron-core/README.md rename to electron-projects/ucap-webmessenger-electron-core/README.md diff --git a/electron-projects/ucap-webmessenger-electron-core/karma.conf.js b/electron-projects/ucap-webmessenger-electron-core/karma.conf.js new file mode 100644 index 0000000..ddd5108 --- /dev/null +++ b/electron-projects/ucap-webmessenger-electron-core/karma.conf.js @@ -0,0 +1,34 @@ +// Karma configuration file, see link for more information +// https://karma-runner.github.io/1.0/config/configuration-file.html + +module.exports = function(config) { + config.set({ + basePath: '', + frameworks: ['jasmine'], + plugins: [ + require('karma-jasmine'), + require('karma-chrome-launcher'), + require('karma-jasmine-html-reporter'), + require('karma-coverage-istanbul-reporter') + ], + client: { + clearContext: false // leave Jasmine Spec Runner output visible in browser + }, + coverageIstanbulReporter: { + dir: require('path').join( + __dirname, + '../../coverage/ucap-webmessenger-electron-core' + ), + reports: ['html', 'lcovonly', 'text-summary'], + fixWebpackSourcePaths: true + }, + reporters: ['progress', 'kjhtml'], + port: 9876, + colors: true, + logLevel: config.LOG_INFO, + autoWatch: true, + browsers: ['Chrome'], + singleRun: false, + restartOnFileChange: true + }); +}; diff --git a/electron-projects/ucap-webmessenger-electron-core/package.json b/electron-projects/ucap-webmessenger-electron-core/package.json new file mode 100644 index 0000000..1285c0f --- /dev/null +++ b/electron-projects/ucap-webmessenger-electron-core/package.json @@ -0,0 +1,5 @@ +{ + "name": "@ucap-webmessenger/electron-core", + "version": "0.0.1", + "peerDependencies": {} +} diff --git a/projects/ucap-webmessenger-electron-core/src/lib/types/channel.type.ts b/electron-projects/ucap-webmessenger-electron-core/src/lib/types/channel.type.ts similarity index 100% rename from projects/ucap-webmessenger-electron-core/src/lib/types/channel.type.ts rename to electron-projects/ucap-webmessenger-electron-core/src/lib/types/channel.type.ts diff --git a/projects/ucap-webmessenger-electron-core/src/public-api.ts b/electron-projects/ucap-webmessenger-electron-core/src/public-api.ts similarity index 100% rename from projects/ucap-webmessenger-electron-core/src/public-api.ts rename to electron-projects/ucap-webmessenger-electron-core/src/public-api.ts diff --git a/projects/ucap-webmessenger-electron-core/src/test.ts b/electron-projects/ucap-webmessenger-electron-core/src/test.ts similarity index 100% rename from projects/ucap-webmessenger-electron-core/src/test.ts rename to electron-projects/ucap-webmessenger-electron-core/src/test.ts diff --git a/electron-projects/ucap-webmessenger-electron-core/tsconfig.lib.json b/electron-projects/ucap-webmessenger-electron-core/tsconfig.lib.json new file mode 100644 index 0000000..a4240e9 --- /dev/null +++ b/electron-projects/ucap-webmessenger-electron-core/tsconfig.lib.json @@ -0,0 +1,13 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "../../out-tsc/lib", + "target": "es2015", + "declaration": true, + "inlineSources": true, + "types": [], + "lib": ["dom", "es2018"] + }, + + "exclude": ["src/test.ts", "**/*.spec.ts"] +} diff --git a/electron-projects/ucap-webmessenger-electron-core/tsconfig.spec.json b/electron-projects/ucap-webmessenger-electron-core/tsconfig.spec.json new file mode 100644 index 0000000..ec3528a --- /dev/null +++ b/electron-projects/ucap-webmessenger-electron-core/tsconfig.spec.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "../../out-tsc/spec", + "types": ["jasmine", "node"] + }, + "files": ["src/test.ts"], + "include": ["**/*.spec.ts", "**/*.d.ts"] +} diff --git a/projects/ucap-webmessenger-electron-core/tslint.json b/electron-projects/ucap-webmessenger-electron-core/tslint.json similarity index 100% rename from projects/ucap-webmessenger-electron-core/tslint.json rename to electron-projects/ucap-webmessenger-electron-core/tslint.json diff --git a/projects/ucap-webmessenger-electron-notification/README.md b/electron-projects/ucap-webmessenger-electron-notification/README.md similarity index 100% rename from projects/ucap-webmessenger-electron-notification/README.md rename to electron-projects/ucap-webmessenger-electron-notification/README.md diff --git a/electron-projects/ucap-webmessenger-electron-notification/karma.conf.js b/electron-projects/ucap-webmessenger-electron-notification/karma.conf.js new file mode 100644 index 0000000..24d5a09 --- /dev/null +++ b/electron-projects/ucap-webmessenger-electron-notification/karma.conf.js @@ -0,0 +1,34 @@ +// Karma configuration file, see link for more information +// https://karma-runner.github.io/1.0/config/configuration-file.html + +module.exports = function(config) { + config.set({ + basePath: '', + frameworks: ['jasmine'], + plugins: [ + require('karma-jasmine'), + require('karma-chrome-launcher'), + require('karma-jasmine-html-reporter'), + require('karma-coverage-istanbul-reporter') + ], + client: { + clearContext: false // leave Jasmine Spec Runner output visible in browser + }, + coverageIstanbulReporter: { + dir: require('path').join( + __dirname, + '../../coverage/ucap-webmessenger-electron-notification' + ), + reports: ['html', 'lcovonly', 'text-summary'], + fixWebpackSourcePaths: true + }, + reporters: ['progress', 'kjhtml'], + port: 9876, + colors: true, + logLevel: config.LOG_INFO, + autoWatch: true, + browsers: ['Chrome'], + singleRun: false, + restartOnFileChange: true + }); +}; diff --git a/projects/ucap-webmessenger-electron-notification/package.json b/electron-projects/ucap-webmessenger-electron-notification/package.json similarity index 100% rename from projects/ucap-webmessenger-electron-notification/package.json rename to electron-projects/ucap-webmessenger-electron-notification/package.json diff --git a/projects/ucap-webmessenger-electron-notification/src/lib/models/electron-notification-options.ts b/electron-projects/ucap-webmessenger-electron-notification/src/lib/models/electron-notification-options.ts similarity index 94% rename from projects/ucap-webmessenger-electron-notification/src/lib/models/electron-notification-options.ts rename to electron-projects/ucap-webmessenger-electron-notification/src/lib/models/electron-notification-options.ts index 6cbec62..faafe33 100644 --- a/projects/ucap-webmessenger-electron-notification/src/lib/models/electron-notification-options.ts +++ b/electron-projects/ucap-webmessenger-electron-notification/src/lib/models/electron-notification-options.ts @@ -40,7 +40,7 @@ export const DefaultElectronNotificationOptions: ElectronNotificationOptions = { borderRadius: 5, displayTime: 5000, animationSteps: 5, - animationStepMs: 5, + animationStepMs: 20, appIcon: null, pathToModule: '', logging: true, diff --git a/projects/ucap-webmessenger-electron-notification/src/lib/models/electron-notification.ts b/electron-projects/ucap-webmessenger-electron-notification/src/lib/models/electron-notification.ts similarity index 90% rename from projects/ucap-webmessenger-electron-notification/src/lib/models/electron-notification.ts rename to electron-projects/ucap-webmessenger-electron-notification/src/lib/models/electron-notification.ts index 133bed8..fd7eea6 100644 --- a/projects/ucap-webmessenger-electron-notification/src/lib/models/electron-notification.ts +++ b/electron-projects/ucap-webmessenger-electron-notification/src/lib/models/electron-notification.ts @@ -3,7 +3,7 @@ import { ElectronNotificationEventType } from '../types/event.type'; export interface ElectronNotificationEvent { type: ElectronNotificationEventType; id: number; - close?: (reason: any) => void; + close?: (reason?: any) => void; } export interface ElectronNotification { diff --git a/projects/ucap-webmessenger-electron-notification/src/lib/services/electron-notification.service.ts b/electron-projects/ucap-webmessenger-electron-notification/src/lib/services/electron-notification.service.ts similarity index 75% rename from projects/ucap-webmessenger-electron-notification/src/lib/services/electron-notification.service.ts rename to electron-projects/ucap-webmessenger-electron-notification/src/lib/services/electron-notification.service.ts index 5f949e3..6095fb2 100644 --- a/projects/ucap-webmessenger-electron-notification/src/lib/services/electron-notification.service.ts +++ b/electron-projects/ucap-webmessenger-electron-notification/src/lib/services/electron-notification.service.ts @@ -1,10 +1,11 @@ import * as path from 'path'; import * as url from 'url'; +import * as fse from 'fs-extra'; import { AnimationQueue } from '../utils/animation-queue'; import { ElectronNotificationOptions, - DefaultElectronNotificationOptions + DefaultElectronNotificationOptions, } from '../models/electron-notification-options'; import { screen, BrowserWindow, ipcMain, IpcMainEvent, shell } from 'electron'; import { ElectronNotification } from '../models/electron-notification'; @@ -42,12 +43,12 @@ export class ElectronNotificationService { constructor(options?: ElectronNotificationOptions) { this.customOptions = { - ...DefaultElectronNotificationOptions + ...DefaultElectronNotificationOptions, }; if (!!options) { this.customOptions = { ...this.customOptions, - ...options + ...options, }; } @@ -59,7 +60,7 @@ export class ElectronNotificationService { if (!!options) { this.customOptions = { ...this.customOptions, - ...options + ...options, }; } this.calcDimensions(); @@ -88,7 +89,7 @@ export class ElectronNotificationService { this.animationQueue.push({ context: this, func: this.showNotification, - args: [notification] + args: [notification], }); return notification.id; } @@ -123,7 +124,7 @@ export class ElectronNotificationService { this.lowerRightCornerPosition = { x: display.bounds.x + display.workArea.x + display.workAreaSize.width, - y: display.bounds.y + display.workArea.y + display.workAreaSize.height + y: display.bounds.y + display.workArea.y + display.workAreaSize.height, }; this.calcDimensions(); @@ -176,7 +177,7 @@ export class ElectronNotificationService { notificationWindow[onClickElectronNotification]({ type: ElectronNotificationEventType.Click, id: notification.id, - close: self.buildCloseNotificationSafely(onClose) + close: self.buildCloseNotificationSafely(onClose), }); delete notificationWindow[onClickElectronNotification]; } @@ -187,17 +188,17 @@ export class ElectronNotificationService { private calcDimensions() { this.totalDimension = { width: this.customOptions.width + this.customOptions.padding, - height: this.customOptions.height + this.customOptions.padding + height: this.customOptions.height + this.customOptions.padding, }; this.firstPosition = { - x: this.lowerRightCornerPosition.x = this.totalDimension.width, - y: this.lowerRightCornerPosition.y = this.totalDimension.height + x: this.lowerRightCornerPosition.x - this.totalDimension.width, + y: this.lowerRightCornerPosition.y - this.totalDimension.height, }; this.nextInsertPosition = { x: this.firstPosition.x, - y: this.firstPosition.y + y: this.firstPosition.y, }; } @@ -211,19 +212,13 @@ export class ElectronNotificationService { private updateTemplatePath() { try { - import('fs') - .then(fs => { - fs.statSync(this.customOptions.templatePath).isFile(); + fse.statSync(this.customOptions.templatePath).isFile(); - this.templateUrl = url.format({ - pathname: this.customOptions.templatePath, - protocol: 'file:', - slashes: true - }); - }) - .catch(reason => { - throw reason; - }); + this.templateUrl = url.format({ + pathname: this.customOptions.templatePath, + protocol: 'file:', + slashes: true, + }); } catch (e) { console.log( 'electron-notify: Could not find template ("' + @@ -237,26 +232,27 @@ export class ElectronNotificationService { } private showNotification(notification: ElectronNotification): Promise { + const self = this; return new Promise((resolve, reject) => { if (this.activeNotifications.length < this.maxVisibleNotifications) { - this.getWindow().then(notificationWindow => { - this.calcInsertPosition(); + self.getWindow().then(notificationWindow => { + self.calcInsertPosition(); notificationWindow.setPosition( - this.nextInsertPosition.x, - this.nextInsertPosition.y + self.nextInsertPosition.x, + self.nextInsertPosition.y ); - this.activeNotifications.push(notificationWindow); + self.activeNotifications.push(notificationWindow); const displayTime = !!notification.displayTime ? notification.displayTime - : this.customOptions.displayTime; + : self.customOptions.displayTime; let timeoutId: any; - const onClose = this.buildCloseNotification( + const onClose = self.buildCloseNotification( notificationWindow, notification, () => timeoutId ); - const onCloseNotificationSafely = this.buildCloseNotificationSafely( + const onCloseNotificationSafely = self.buildCloseNotificationSafely( onClose ); timeoutId = setTimeout(() => { @@ -270,11 +266,11 @@ export class ElectronNotificationService { notification.onShow({ type: ElectronNotificationEventType.Show, id: notification.id, - close: onCloseNotificationSafely + close: onCloseNotificationSafely, }); } - if (!!notification.onClose) { + if (!!notification.onClick) { notificationWindow[onClickElectronNotification] = notification.onClick; } else { @@ -296,7 +292,7 @@ export class ElectronNotificationService { resolve(notificationWindow); }); } else { - this.notificationQueue.push(notification); + self.notificationQueue.push(notification); resolve(); } }); @@ -307,24 +303,25 @@ export class ElectronNotificationService { notification: ElectronNotification, timeoutIdFunc?: () => number ) { + const self = this; return (e: ElectronNotificationEventType): Promise => { if (notificationWindow.isDestroyed()) { return; } - if (this.closedNotifications.has(notification.id)) { - this.closedNotifications.delete(notification.id); + if (self.closedNotifications.has(notification.id)) { + self.closedNotifications.delete(notification.id); return new Promise(resolve => { resolve(); }); } else { - this.closedNotifications.set(notification.id, true); + self.closedNotifications.set(notification.id, true); } if (!!notificationWindow[onCloseElectronNotification]) { notificationWindow[onCloseElectronNotification]({ type: e, - id: notification.id + id: notification.id, }); delete notificationWindow[onCloseElectronNotification]; } @@ -334,29 +331,30 @@ export class ElectronNotificationService { if (!!timeoutIdFunc) { clearTimeout(timeoutIdFunc()); } - const i = this.activeNotifications.indexOf(notificationWindow); - this.activeNotifications.splice(i, 1); - this.inactiveWindows.push(notificationWindow); + const i = self.activeNotifications.indexOf(notificationWindow); + self.activeNotifications.splice(i, 1); + self.inactiveWindows.push(notificationWindow); notificationWindow.hide(); - this.checkForQueuedNotifications(); + self.checkForQueuedNotifications(); - return this.moveOneDown(i); + return self.moveOneDown(i); }; } private buildCloseNotificationSafely( onClose: (e: ElectronNotificationEventType) => any ) { + const self = this; return (reason: any) => { if (!reason) { reason = 'closedByAPI'; - this.animationQueue.push({ - context: this, - func: onClose, - args: [reason] - }); } + self.animationQueue.push({ + context: self, + func: onClose, + args: [reason], + }); }; } @@ -368,30 +366,31 @@ export class ElectronNotificationService { this.animationQueue.push({ context: this, func: this.showNotification, - args: [this.notificationQueue.shift()] + args: [this.notificationQueue.shift()], }); } } private getWindow(): Promise { + const slef = this; return new Promise((resolve, reject) => { - if (0 < this.inactiveWindows.length) { - resolve(this.inactiveWindows.pop()); + if (0 < slef.inactiveWindows.length) { + resolve(slef.inactiveWindows.pop()); } else { - const windowProperties = this.customOptions.defaultWindow; - windowProperties.width = this.customOptions.width; - windowProperties.height = this.customOptions.height; + const windowProperties = slef.customOptions.defaultWindow; + windowProperties.width = slef.customOptions.width; + windowProperties.height = slef.customOptions.height; const notificationWindow = new BrowserWindow(windowProperties); notificationWindow.setVisibleOnAllWorkspaces(true); - notificationWindow.loadURL(this.templatePath); + notificationWindow.loadURL(slef.templatePath); notificationWindow.webContents.on( ElectronWebContentsChannel.DidFinishLoad, () => { // Done notificationWindow.webContents.send( Channel.loadConfig, - this.customOptions + slef.customOptions ); resolve(notificationWindow); } @@ -407,20 +406,21 @@ export class ElectronNotificationService { } private moveOneDown(startPos: number): Promise { + const self = this; return new Promise(async (resolve, reject) => { - if (startPos >= this.activeNotifications.length || -1 === startPos) { + if (startPos >= self.activeNotifications.length || -1 === startPos) { resolve(); return; } const aryNotificationPos: number[] = []; - for (let i = startPos; i < this.activeNotifications.length; i++) { + for (let i = startPos; i < self.activeNotifications.length; i++) { aryNotificationPos.push(i); } await Promise.all( aryNotificationPos.map(async index => { - await this.moveNotificationAnimation(index); + await self.moveNotificationAnimation(index); }) ); resolve(); @@ -428,28 +428,29 @@ export class ElectronNotificationService { } private moveNotificationAnimation(index: number): Promise { + const self = this; return new Promise((resolve, reject) => { - const notificationWindow = this.activeNotifications[index]; + const notificationWindow = self.activeNotifications[index]; const newY = - this.lowerRightCornerPosition.y - - this.totalDimension.height * (index + 1); + self.lowerRightCornerPosition.y - + self.totalDimension.height * (index + 1); const startY = notificationWindow.getPosition()[1]; - const step = (newY - startY) / this.customOptions.animationSteps; + const step = (newY - startY) / self.customOptions.animationSteps; let curStep = 1; const animationInterval = setInterval(() => { // Abort condition - if (curStep === this.customOptions.animationSteps) { - notificationWindow.setPosition(this.firstPosition.x, newY); + if (curStep === self.customOptions.animationSteps) { + notificationWindow.setPosition(self.firstPosition.x, newY); clearInterval(animationInterval); return resolve(); } // Move one step down notificationWindow.setPosition( - this.firstPosition.x, + self.firstPosition.x, Math.trunc(startY + curStep * step) ); curStep++; - }, this.customOptions.animationStepMs); + }, self.customOptions.animationStepMs); }); } } diff --git a/projects/ucap-webmessenger-electron-notification/src/lib/types/channel.type.ts b/electron-projects/ucap-webmessenger-electron-notification/src/lib/types/channel.type.ts similarity index 100% rename from projects/ucap-webmessenger-electron-notification/src/lib/types/channel.type.ts rename to electron-projects/ucap-webmessenger-electron-notification/src/lib/types/channel.type.ts diff --git a/projects/ucap-webmessenger-electron-notification/src/lib/types/event.type.ts b/electron-projects/ucap-webmessenger-electron-notification/src/lib/types/event.type.ts similarity index 100% rename from projects/ucap-webmessenger-electron-notification/src/lib/types/event.type.ts rename to electron-projects/ucap-webmessenger-electron-notification/src/lib/types/event.type.ts diff --git a/projects/ucap-webmessenger-electron-notification/src/lib/utils/animation-queue.ts b/electron-projects/ucap-webmessenger-electron-notification/src/lib/utils/animation-queue.ts similarity index 100% rename from projects/ucap-webmessenger-electron-notification/src/lib/utils/animation-queue.ts rename to electron-projects/ucap-webmessenger-electron-notification/src/lib/utils/animation-queue.ts diff --git a/projects/ucap-webmessenger-electron-notification/src/public-api.ts b/electron-projects/ucap-webmessenger-electron-notification/src/public-api.ts similarity index 100% rename from projects/ucap-webmessenger-electron-notification/src/public-api.ts rename to electron-projects/ucap-webmessenger-electron-notification/src/public-api.ts diff --git a/projects/ucap-webmessenger-electron-notification/src/test.ts b/electron-projects/ucap-webmessenger-electron-notification/src/test.ts similarity index 100% rename from projects/ucap-webmessenger-electron-notification/src/test.ts rename to electron-projects/ucap-webmessenger-electron-notification/src/test.ts diff --git a/electron-projects/ucap-webmessenger-electron-notification/tsconfig.lib.json b/electron-projects/ucap-webmessenger-electron-notification/tsconfig.lib.json new file mode 100644 index 0000000..33840d8 --- /dev/null +++ b/electron-projects/ucap-webmessenger-electron-notification/tsconfig.lib.json @@ -0,0 +1,12 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "../../out-tsc/lib", + "target": "es2015", + "declaration": true, + "inlineSources": true, + "types": [], + "lib": ["dom", "es2018"] + }, + "exclude": ["src/test.ts", "**/*.spec.ts"] +} diff --git a/projects/ucap-webmessenger-electron-core/tsconfig.spec.json b/electron-projects/ucap-webmessenger-electron-notification/tsconfig.spec.json similarity index 100% rename from projects/ucap-webmessenger-electron-core/tsconfig.spec.json rename to electron-projects/ucap-webmessenger-electron-notification/tsconfig.spec.json diff --git a/projects/ucap-webmessenger-electron-notification/tslint.json b/electron-projects/ucap-webmessenger-electron-notification/tslint.json similarity index 100% rename from projects/ucap-webmessenger-electron-notification/tslint.json rename to electron-projects/ucap-webmessenger-electron-notification/tslint.json diff --git a/electron-projects/ucap-webmessenger-electron/README.md b/electron-projects/ucap-webmessenger-electron/README.md new file mode 100644 index 0000000..67e9c69 --- /dev/null +++ b/electron-projects/ucap-webmessenger-electron/README.md @@ -0,0 +1,24 @@ +# UcapWebmessengerElectron + +This library was generated with [Angular CLI](https://github.com/angular/angular-cli) version 8.2.11. + +## Code scaffolding + +Run `ng generate component component-name --project ucap-webmessenger-electron` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module --project ucap-webmessenger-electron`. +> Note: Don't forget to add `--project ucap-webmessenger-electron` or else it will be added to the default project in your `angular.json` file. + +## Build + +Run `ng build ucap-webmessenger-electron` to build the project. The build artifacts will be stored in the `dist/` directory. + +## Publishing + +After building your library with `ng build ucap-webmessenger-electron`, go to the dist folder `cd dist/ucap-webmessenger-electron` and run `npm publish`. + +## Running unit tests + +Run `ng test ucap-webmessenger-electron` to execute the unit tests via [Karma](https://karma-runner.github.io). + +## Further help + +To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md). diff --git a/electron-projects/ucap-webmessenger-electron/karma.conf.js b/electron-projects/ucap-webmessenger-electron/karma.conf.js new file mode 100644 index 0000000..7173861 --- /dev/null +++ b/electron-projects/ucap-webmessenger-electron/karma.conf.js @@ -0,0 +1,34 @@ +// Karma configuration file, see link for more information +// https://karma-runner.github.io/1.0/config/configuration-file.html + +module.exports = function(config) { + config.set({ + basePath: '', + frameworks: ['jasmine'], + plugins: [ + require('karma-jasmine'), + require('karma-chrome-launcher'), + require('karma-jasmine-html-reporter'), + require('karma-coverage-istanbul-reporter') + ], + client: { + clearContext: false // leave Jasmine Spec Runner output visible in browser + }, + coverageIstanbulReporter: { + dir: require('path').join( + __dirname, + '../../coverage/ucap-webmessenger-electron' + ), + reports: ['html', 'lcovonly', 'text-summary'], + fixWebpackSourcePaths: true + }, + reporters: ['progress', 'kjhtml'], + port: 9876, + colors: true, + logLevel: config.LOG_INFO, + autoWatch: true, + browsers: ['Chrome'], + singleRun: false, + restartOnFileChange: true + }); +}; diff --git a/electron-projects/ucap-webmessenger-electron/package.json b/electron-projects/ucap-webmessenger-electron/package.json new file mode 100644 index 0000000..b977c9c --- /dev/null +++ b/electron-projects/ucap-webmessenger-electron/package.json @@ -0,0 +1,5 @@ +{ + "name": "@ucap-webmessenger/electron", + "version": "0.0.1", + "peerDependencies": {} +} diff --git a/electron-projects/ucap-webmessenger-electron/resources/image/128_128.png b/electron-projects/ucap-webmessenger-electron/resources/image/128_128.png new file mode 100644 index 0000000000000000000000000000000000000000..8156347fde4698e3ba8b665fd52d1f86a83541a5 GIT binary patch literal 3385 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 literal 0 HcmV?d00001 diff --git a/electron-projects/ucap-webmessenger-electron/resources/image/16_16.png b/electron-projects/ucap-webmessenger-electron/resources/image/16_16.png new file mode 100644 index 0000000000000000000000000000000000000000..f0d7ecf56742cb09ee904db0d6bd45e4b945fa91 GIT binary patch literal 503 zcmVxfnup%L} z$#@kI0S172+vhhh4NO>(P)?hS7mtmGF9U!_z^Faq(jaeLy5c%81T0H{OW71b^D_9p z&qJ)cu{^4|*2A49v1p;Ex7xb~zRZsIx&s7!j0q+1^1w01Y#NUqF&R|Bt_ZmT!xQ~`cz z&6s(|W8pAE{OGjOkG`b>5~ZSOj>r4$6ytx=l>o;oUh^e!bB z+cTu;E3jG)Fs(@F>D)o)S=O}dUM(qkZpwO`059ZDc`K`G0Pfn&CAxD&JQrY4yorKi tG>p4aiGiQ?C-ezO0W-kHKVq&a-~uC;d(y*vA7ual002ovPDHLkV1jkW=R*Jh literal 0 HcmV?d00001 diff --git a/electron-projects/ucap-webmessenger-electron/resources/image/256_256.png b/electron-projects/ucap-webmessenger-electron/resources/image/256_256.png new file mode 100644 index 0000000000000000000000000000000000000000..8c189aca514036906316fd80db70d1eb2f8e6744 GIT binary patch literal 6722 zcmb7J_cz?n_nuu=Sv>^Ndx_}1lMuZ}ZyP0g5YgTdUOLf<-X%yx7lMf0=s`jdqV9?i zogjMo`uqvsUuNc_pHzw1qbTqGe+J>-mdppaPwY(35(x~EsE!la8yF(c{ng-v z6mIBw=N4&6J>$C>;3*mQm|zqiB@X=u^1%e$g0O@EqrLCvwaROx=)&~4w=3d}zHtXB z@Og8>M2r*J=NCuoA!KqdUAw0ghLuYjx*B7abXw@U-L1jZ{<5*mUzQ6#1fD7h^h^jeYb zC&pp_e65@gLKwhtW8lAm%%^^z8I57<%cxxO3|-r{9b{q1#y0Cn&j5}dKpdA#UE!ARR9 z$wdyoPA%3_aKLOSj-KY~Rd|%O9jEAIdGH6A!CJXv3lNPYG|*fcS3xoS4gRhm^>5DY z)Wm*o_#3gQ(!@VX&-lQ!AAmGqcVIr;PlA}#R981QR z08!4eup4BMrk_NoxLCFMBmU*KFTFpWTBwg);-igTPY zC_Kax4|3nGv|qklm{TqV=NTDal@8wp*_D+STZ#saZ}w{{c6`T@+)zZdRO&xKQ8bLG zHR1jJ{NBZ*!WM9O?2u(V8e}tSPu&nalcWVQN;SOfHE_263o`1}C2QsRfq%trjYyLt zh1W)atJFmfvTkEcr$-KVM*JDRNMjh|0XK+?`T^xyM8?+(QAt5wK0P?w{wFjZ{W}b8 zS$4yU(up=htq}vh%9EZQX)60|cHU2==r;T9l;@u|<`{ zj`}cMWKu2Az!a6h@Zxm2R3t^9$kL~(Ev2kLuJ1N5R`qRpt6=~N8b{cMWjaY`_2p@j zuKptm4V|z!ctw7Y_3G<|Q>9p?h64@csJj$zjA#ksRiE(EoL?!{|$ zy((>Wy`w|>NcC-7UD=EwO+L_-@?X}tUEeEzkK}YwIHPZAy^*T0LN>J7GidOeX7Mez z?K=K&*8Z~|F2E`p&(!g*Txb%*F!pzBNJ)z#X zu5-JK3-$x>#5t8JoFc~U@>x{qKMIN9i?@MiBzt>4Ba3H?ot%e5?zweQ@KL&$+%-&i zDT~U0d6*4@wD;4#-L4P?`Cx_G0rofjnHZ&}x;BdbXxET8aJJ^-N?1+_hPIAL>BJuX4I+Uxy+0)`WJ#Is2U&&-P@) zrw0C2=scy2;sP2uzY58H@ycOwcvyJyKlst&YuM}?b{WK!toSnKo) zp_C+)@EAx8XBuN{{Bsb$;Ul-g@UQ@|N z8;XxJtm!Ya1WwP_8zjD+Y0%xoMe{qiI)$A*%Wv6vucS6}%QY{f>W*riC8*VLY#jN1 z?}fPhiIP|d1ozOqt1EicB=;T(wO(S4P_ZW8<^zc?MVm>T@#b!pd11BaFjUy@GB-#0 zfY{P=B9#WXbpadUc@o~?=l70%59^moY?cE00MmEu+*H-r2MHub9Vk^{$a!gwhPs7Q z7Fa*9DCDWjEooE{*_N&GH4{UxTjxu!<%w(dPnh1KH|>K7qQ|yM{eceERc7{!c`|irM5dpBD`nDOhewX!eG+O0~41U+-ya*S$V;R~8`#Exy8#_iw3_c45X4 zB!(W)1u-f6I#H*1uB7QHvKx+^plOvHiwOT5(?G(oxQf0^Pi!Wt*z*rrG+E7TWP{|B z-?GPzDfbSz!4r)AzC^$;%|&v49DB6F-Ll$d#Vj34Jo|rt?_P^t&YlFPt7-ddeG%fH z|DlYj3E5bKx!x^`^|PH_(g8w;5|14;)Aac#`PJp`mA6E*Kw<^bG=CSHlBI|#KXWX% z6&FYdnY>eWT7|t?db%G{?eCmJL4k`%mA=q)BzGI7I_(yo5F;od3uX_SEZ0-Q$8k_N z@yV-M-x-@u0^W9pKt8konvULX0og zu#pd=PjaQ^#**cw-1GQ>wr>jq&I&{fo(c1>#+oM!ce`;C^4IfNav_5Jb*yz`X{i6S z%F$-S*}NX>KasI3mi0#8V4_v6uM;nasO;3h`Pt`Jn&Yqjp~rYOW3 zJXl}f&-aN962nj4uN{^1>bI%h)2qy-9_xTyq66YsNAA1BxlaVzDS_qR)+$f@WM-?x zGEKQm2`_AXepYSwo|kI;Lc9=xFE>d46H@Z3;fA9vQW4r=m5?(TVv|~*{jjvY*PfcZ z?n7r8URwcpWf*vk92Nx4r+a62v7B4R52{KC?`6fE{S%#yY`p=o&4x(7@3$6pF>)NT zyE3r3G>$Edw>73*dD?gCx1W=g+n%vWUhtY2bzgY(L+ORS>;f3E&1@v@akYuB(s;y=OK=7$fq&`WUqoN5}7z zwHc6u!UCqjN^C9~Fm0kpJ$hDY&7a-?n-B{mSFNNhgpUJ+juwu7QKC>_doH}*C{9iZ zHO@u{NBsky)cJ&lQ)3HTQaHT=xGQH6OlIIS{iJJRb>KpW;Me)fP)QGb*Y5TGrMsX{ zp1zym>+F&?ex5lt64Yy_N?pUZAW+4rBoIf}<%wc2ko-Ew{U#BfKeQ@@&*tht4NYz2 zw_hl3;8!`l7JTJNf^i+fdKczY07V3dHcf=NI=vLk_auQNyKNf;IVe#26y9ME-8cmh z{5>-X6D@$W<@{nYo?rCQN?#E|r9z>awwl~ygbGnYn8#UFAnUl)Vt4VWe0CMV*KK9# zJ`1NjzZ|Pz_6-7ikBYI~NEN~aS+dcX=0Dd^`1W=@S^OA*{JhbHpECeIDLim8>U6rQ7oi3H1nT!Yk z<^l47Wm5bKI8ue;y_yK&@mY+QC7tw^7bZ1|?D}*L76b*Ip&pZ0EB{!e$kJr$1;GL5 z&E3uRzUri$O!Z+XD!~i3deLJd$}SJPnN(wu)g2Q0__^N$x6#*ro+K(XvgKqjWuh>< z+0=2g$9w~|pOha%Nz|N>gYY%f^jQ&?QN@py&>g3yr<&CcTVzhwK+57Wbd2nWo>!2U z-*!{|pD7cxV}A>%HaH_b{PMm%Mv41i_}Zt}wFS&!i{mFP!+R;V6o+;-+uq_06jx^gpRD7vif)w2fy}HRN6*x>HPlLj9~m7du?)6 zHEycAy))v!IjDWwB1H#S6xwg+K6~WlA#XP`;0C!w*b>lW$&7jQ+L*GBqQ8yZu7J4c6jBsBLhuce>oOFq}R!y)@n627s%ZHK$QoWyQ0 z+~xXLGmjI+*0JP3)%M=?PzH0j$n}f*R(<_avC+NhM|bvZRV^pF^y>Qd;=W8U93Y|Z zo)(K$=Urko_tHIk8=zPC^{)*vW`jpiMuq^9ipS=05NOsOEOKQri&+SN9N&ePY9{W- zl$6PbxH!;&g?{>vq$+QwAd%a_FHF)AM*g;0_T|vjtlxD`OJpBZ1jMKFopWCEna|!y ztN)O+l$DhF>K7cSyyH{d!)Lq=Q!t%^0-28XW+z&hkE{lz*`77btnmBt13xd;L^6qf z@O$g(!a4MX(7a_cdj_LQb<^Mq5ZZa%W_|g~r$ddtBuD^9?N65T=WTfGCPiuooA}0b8@r6pkN8G zTDyILn6m{|YvXh~Oa73!-|;Y>{p4ibGd-ppYg_hep^A&3#_gdU#C_X~0=M+|ce3KX;X%0s7Phmx>FkJh1!yKKV6O4O6yDKpmEJ-` zTAInjv)vFPNG*xwNaV4;s}6p9=wB;d*{f+b*cX8zkD6^YU4A1Zv>RvHXEs8q{tr0u zE;won?7OxIqG)HO=BMUA@x2^Y5k#nR5nkvTRx}g56VC3VOU5evT z-ot1)2XJN5Ci>kNE0t;$HTA$^{!<Xw&6Q)4 zKhZDxUS44nrhX*}K6IJ#@|;cHr>jO<^(g$!d-t%}sco)Ylvbo_AO$Eix4;55jGvnu zN7dc_(EdvDzMa3nt`)vY2b6#FX9MHezd+?H`_39NI&>RP)%%%P^T#aAOz0sb<1 zH|BTn>ea%Tca~=GQVae6e6zo`d}Aha+5fYF8&kv20IF2m?_8iJU9Sq0d^9~(O33DY zJ|b+B^nu}=_N2bIWlP@YGJ;{EDZM*1(xFbFlAdOS}ZgpVSh=)D!>S=ZO2p4hjqyAQut)G3| zvdrkiYMrE|>NA{y62T84&?N@!pD4PH!7%1hl8e7(e}wD@hR0zL-V;uTs;^U< zYpEeQB2^HD#cQ2ihL#Aqq!B@lwXn)z+q7wBjrRL$xbqXqpb-D{b)xfc_??B+F96Y0 zTOwfyO2vo+>*un3zu*JP)R6mA$XJWXI@<)U=;m81j`PgA@Ow9a<&6#iWmuU|pRU3N z@PykgrpzY7M?wQMt)lKwX;Z(XDQGVj`f# zx4gLmAKQ1bH{GRdJ30tcBBam*QL;RIu3NSAsm0m*O3>N{qv%Pd_VAy{DYfIRJi%L{ zw$n_`ZVdtB+(V4)NHAQmmfzAD;y}J17OWFL``_u@x^5(CIul1l|F^2HVZDc@8rz7l zfVLyF_}!aeQ3@RWJpL7L`R%HF^T%uR+v4E@`bS%ALzU@@w=8{jVOWBwJD}@_ak$)? z@YdG3efxo(c>`_#NbR80G1bQbvC<$*V-vQBDy?NDsjE$`RXCgQ+?9yi%lPl(G%95i zH*oSHeNX0$+StciGg|MB#iSIO=4#acGUKnWq)NfqeXeN=5Osgxgj9Q6z3VhH`LBJc`Mdh zl!un01n)1tQofpd`ODQ>Pxqy6R!X#86C(DTP0SFs_UnP^TxQLGRaEcXqv`SqM^%g7 za+g#M%lHn(YQOmVmkLj`>J3Pl^z8eXWJjV)EM}1F8gS(t%T0l?-?09;DUlyrrT6}a zcShJRgqvDf^{p=ke*YZ{-k7C98ElLGg-WIpG-))w-?O^Qr~X6$9u3^9N1Rmbovo$} zJRb6A*U9c73-kqCmSSkrm2M}U)g`|t0>c#v8L)G;a6v9x8$6Ze!%+!hi0$pOD`MRE zBmMhvUKPBBRYhEG)DZb4z}bgnGV#7GU5b8{_5M1TIyN20a;9wf?xx>=Vsu-g_=f}u z&{fac$pwpW#9?YsD&f_mEscx-*eZ%0;s>2uWeaTaELv(GC8D0i!*>mO7+ZUL2<*lj zXozoE?&aT#SfqhIrIC@cW`A`C4?I~pRoRb5{*}_}UNdu8kjhN2jJhql#@#r(O2R%b zOapCCyH`=LB5^LAGtz0O<7669@-HZ)%1)|nv^{|j4dlk)-bUrG@XEKzz`zhRI;KtA z%6yS{`oP3FBB*@keFMdIuL^7+2~k1m)<1!HR!;njtnIKER?ps}qXJuH)ES;Ym1&*s z&MZdFX3DoIX1iNIpnDwBYvQk z?Bb~**!-SG$uS;j0D>NvNLq~XE{ItVM(!BUdKek%4QPK(STS)8U@IcIrfHYPL!ZFa zw1)~|B}y@orGz-z>40+uNI*j|mlQBWNaKXtEkZO}E?U;G8xfVb0^tal0Pned!zV{Xl_{G5g?hwsWxoFAl z64oAXU2TL)FyR9|bRN1TTjV3=I9?u$w%#UYyAnWIteJ?D8!!4Q*(khZJpNI*xz~;# zsNszjZBfMrOu%Fin0s#MQyNefEua@KIXf(&ZtJo;XaeF`xc0X^#Mo@(tZ?2!NJ8*UzepDFePEBpGrc{IF8`^W()n`; zt|Gy~qH>QnK#RYT^?%fqBm3df6v4H5wg0viI}>b;U+vhLKo7>ErT442@f?AgfO+nd rx!GZYA|5;b|5sn-f1A}{&=p}5_GBfbHGJ&4Hw3iQ_0?+cq9XneL-=h6 literal 0 HcmV?d00001 diff --git a/electron-projects/ucap-webmessenger-electron/resources/image/32_32.png b/electron-projects/ucap-webmessenger-electron/resources/image/32_32.png new file mode 100644 index 0000000000000000000000000000000000000000..cded28d005bc161c6ff2a98a243ff976b5471276 GIT binary patch literal 886 zcmV-+1Bv{JP)`{AyT+>nMv2x@R{zM&?0w{MPumhN@T5XyZ;brw= z)6 zutC*MWpmi2`BO5*o;hWlUT(R;+54`WTk(V{u9T@B69aP!g1~&MiAJ;56j~Cad`}gv z&v!Fa2GTJ7Q^W81o^b{8t+LI{`z5hnW{ZKSJ5fHcxJkAg1g#R9tkxZ7_ zw_QAS2zA}J1c%*8+;t-$%y)@8BL>lsMt$!vvE~jQ^t$i%Yh8nEdHvO@1S7-WViq_x zrBh{ZSL5o{0-{k7y~0r8zSs`j*WYAQ>eN$-qp}S+Dl*`gfGFRJ^G3tD364#jOvia( z8E^{d^h-pdOTC8k@cUgL^j88^z*2duiT-h#99PLp%?xp7Hv=CEq&~k3@(>YelRqmg zl+Ela`CpsNd%N9)ZYl7KFS6Ott2GdFaxUOE0$%V4ST8@*{=*r-A6H}RvNX=^rT_o{ M07*qoM6N<$f>tT3m;e9( literal 0 HcmV?d00001 diff --git a/electron-projects/ucap-webmessenger-electron/resources/image/48_48.png b/electron-projects/ucap-webmessenger-electron/resources/image/48_48.png new file mode 100644 index 0000000000000000000000000000000000000000..0401b9a0d3f8c64bc20a30ee570f95b9d087ca0f GIT binary patch literal 1289 zcmV+k1@`)hP)KqXKPM1VYC1n2|Wfut|#n^(_zT4G{OUxE}&lz;@tqV2O+MR?6RFKmzz2co%5)qD08Ue@cPfz&YRr7s{(p z2s{cj0tbO*ZUwj{unG7LcwU-LP!PWOFmM{!I#&dq0geLICX42!Z2U2TgLUJg_-9_ zXaN(GQ(j02lNtc(>?S$wz%9UD(-m(ji}3Tt>PyN8fM?fx_NL)1J&{CW?W#buV?Fs|kHiuJR(Dy7B!JD4I8R$mG7BI2cbMNDl45?N5D3j zY$%IxVq*>0mgq{Ji-?Za+TwzYlbgVSfWUey0hOYwoTQi% zXdX(?YdF^swbp4W0*;?+I6Y~!TyuP!*mx>1?WK9of{K7XtVpB{LZjHFQ}G1-$uu3o zktnhbs0cW&Hk5MSfn&X+WJ{pN!Gx)TuAw3@kCdAP(Y(i{F*nQl6h%LcM>XpMxEa5tv=A-F(|U>_K6vZLz*1{W1q`?gvCSx*k~TOKbNyE6)KJ!<*%Pz)f$o zP4upSw@fP5fG{Nw^V;>?cBjW>cwHkeCxR)_^w=wNk``X>2ary+1z0UR$?bEXyg2*5 zWpV`e0x%M1YI4V1Mh{q zfY=S;{J#J@Wteu2|C4}#Xrg&4P3JL>AOQRezKc?gf`HAu00000NkvXXu0mjfflp;Q literal 0 HcmV?d00001 diff --git a/electron-projects/ucap-webmessenger-electron/resources/image/64_64.png b/electron-projects/ucap-webmessenger-electron/resources/image/64_64.png new file mode 100644 index 0000000000000000000000000000000000000000..fa0bb81e6ab0af859b0adadf027a83e60ca7c857 GIT binary patch literal 1688 zcmV;J250$+P)Ixm-hg$8Mpv^3f!;Ic{_#y3xGd> z-M~yY+wE3C!mPIf`+-?Nz@>5{nF4G94ge3iQDC$Ji2UyXM}eo^Xpkb~fgQjaAT`y3?R|^V@(vx)rtaY+7&Dj2=XC;Cjb<27J^s zw>mAZGorwiKp0r7nn}k3#2lRs^mW?0g(Lg3Vcg)3&I@|@U7d+V!K-v;2vrO4NfXO- z(yE>TKdZ;2G5hx3-JS&lu6zQWNdh(kV}TO8#H#Yo10HqKn)F~IyC!BBcHTn<04r3A zR}nzCwVya?>x4eZJhT%o&tQ&V&rodRTvD z&DUHR0L@rz*GD@6#8mgPPK|yaEFLw0wPObn^xJ>mS6e)@0Kq+SL1v1b0I%pwmAwLf zz8{~?`@?TjS^<@Yn^lXBhIv+btvctE0w|G@oJe-o5XPjadw0bRF%dv`tD}I^Mh~ql zKrA?hsML^>;Aj7&Os&ofiW_4hfKM)=4Y9I-$8~BLXfxOuQ6w#3>74^f59;Orr98yS z!fMszkD1M6R<%B;TDq9Znp#^))b#65h8o-ceLJ(ScGRTl8ae`GnLV)j1V-yLs50E5 zle~>1wbuoxaFACM5i>|P%3>x!z$}9qPNuWNQh>@P&jk<+H@ayiKtJ83a$Sp_LsQt$ z#AP`Mir+E7FmhzpN$p65u1mY4qORljQE3StW9ZGgwiazg&jPkrggSm7j*S3@m-D7{syf7Yo$CA25poWINmH#v#R9&#Sj9z) z!EWp2TJmZtU=r7H_g zm2xr^*65!t2h9?w4Ba1y@yjYfza;aw|7R%7-27rKvldh3tV5R!XA6hyGRvkSfba)C zwn{L-M1Znz3)2r2I_k*rtBuS&bct~EVRPZo9W#5Vx)}P%>U_@NU}Cp>BHZ;z-UZIo zIXC6sDndM;Ulgqf*;Wr#1qjRdy5ZS|!kp2mC56p-W53 z!p*EJu4(J|8^=mK+3gWQlW}{_^JQTb8eha`cGI}igOX23(@L>R=mn#CCeb%BV7GQC zk_R(yBy#bfm<`q4+vrjNFkGCTP9LN@%KEhMS^zWZ{Kn22eut~UvB@sfSWYRgV=LT z%4`5$1KxGodBM#rKs%cZLRu%}XR<2V!v75dAO}=roN}=|@Zidy&-_zkfG#`bAo<}< i;Ag~%6DQ7p1mJJ%?!KI{sEFzS000045bDP46hOx7_4S6Fo+k-*%fF5)VDUk zC&cyt|Nqx-+__DCHT$kw_FZ+Dpi1^V?d*Fx+4pooQumFr?`mYu29L1UVAu7@+g+8)rW- z$$ns#{lGl?&zp~5_nrqj@9X~ae_nq8y6~ZO_9NTu`?)C zC%^7JeO)r)iC4~3ACNa6Wi~u^&i?c6(~G3WXAu?8A}d~|HU547>1jaDv%s8Z!8!M4 zEq&sd^VC1*Sx^onG@gg$Jk74TaryG|@SNw7Ilz#4T2OcM#Hr^IIWM9?q4Yet@WIr% zH!fa^;i_%~#;9dUkRLE5s87B5?f13G+RV*=|Fkkrnc`BkvExI+my74Vm)zjKm92DV zI-}6NDbv2SB-jgxxBv5*^2RD4utxrMi|G!aA&g1h?k)@+tg;?J4rhT!WHFEiu{{{v zPG;Ky8IL_(977~7Cno?wQd(ka^798N4GemDa^Bk6($Us&c6DxUZftCf2Iub`ym&HF zKt&-X`+3s)2Txu-d-w3ADvyW&qgUnciXXpzva_(Ub{iRWFfMg+I($gkNonz-Q6Z6@C81owQUkWfAw}?k;by)2FnxRLM1}z*ClCuq(8dplX9JtdY$S84vk->ecc~Ga>{X0PKs+PD$l%yn< zq*^5xr2;7iBLhQAT|+}%L!%Hwb1MTgD?LgiiX_$l+3hB+!|`T_S^$% OVDNPHb6Mw<&;$VU_R)d> literal 0 HcmV?d00001 diff --git a/electron-projects/ucap-webmessenger-electron/resources/image/ico_64x64.ico b/electron-projects/ucap-webmessenger-electron/resources/image/ico_64x64.ico new file mode 100644 index 0000000000000000000000000000000000000000..8af2432e478284624daf3f5c329f97f22b95e28f GIT binary patch literal 109473 zcmeEv2VB%j_x955cQV#&K|oMc>|HG2MMcGiC@K~dR`l@p{ zrd#DW8W4v|!5+bW5F8F%_fKpTMR5>}7bnXb>nHCuHb|-aVH5S{=O(KYb>jmIXeH6EW(YCIu< zGM)t2m;#t8yw(iiwPq(#UMHjRHEzN0mC^6H!{T(fBjQWBBjYLK(SR|4ac~`Ut%-oi z!s|iWOyF9N?uX-RY@mOy6MKz2B$ndp#!+0oIO<1s31tk|a&tbQ`|vt4V_pp14W(Nkxmull~QLxqK!=3`nmD=_xbeid&LxS`@~S( zzJPvw`ux;{B5KFdBK~jv~h>WQKpbT$U6e9KyLTw5N?lXirW*Q6+`{ZDxtjh zrqkE*-j_lBg#6&NaR2;b5w(`vHHzYD!tc8Qx&zSl;9A^X03vPN0etGh zfe3W(3jhWB_njh@xSgY(bJgJ*T>u&Yd_81=3Oo z2!QmopwooXfkC9Focs2IV_fF(GWTBy_eNL*Kxq-DCl1Oj3J?!z>Ot#9tRI1%AU(|M z3*+d%`GA$o<7MtYFNp)?632J}f-o(D^t^|(P&D+Dzk`k@Kw3C72%9mkFAs54mSKN7 zgK?aBdyesJxbGamTp@6J<|idVdMMD10E?1KK}X(@9y>^jDPZ2AUGYOhLqq4lJ!}EH z0IvY}-bfk(By_|~fbop494K2kP_{x^5a{%DMrACGc_8NtA(U|L8DASidLGkdJu2^^jK*bt+4R6MVVX?!lPT9dvDCY_ zU&#BT@Hs$ToQ^X2p9J@x4449#n%EZ*E2?v#dIAEjdywf7)e|XpfoTysH1S8UcrHS#X#eUiP>~r1=lZdodwr{P(7$jdSD+)3y~gk{jb!+ z2W8T5hV}kq;yF-PY6bP1Z*(08*Go`6XCl;dP~E8_>A^ahEl7{m(y!E;S7A(BKuuY4 zgL({yJ1YLF@hGCMH5TrTfa+X0J-F_L(}UB5>U2my)8M|Oejq)2H^wsWe_HbHbX~7E z)b~nJJqYT2biInMTS0oh#uU<^)54OTulzzP1?p9_ex#)5u_PDxW$r&Db{}^r+#A=& zpuPy{p-_78hSFmOx_EZy2lc}LC$$#Y2GWB!k&e)Qq#vv!;E&s`7*YpCW$v#X8-nVX zs2)exSy8=Jm>zt6l%A_cGjWudtFQp#?2B0emz8RieJuM5*-K1EcwMd{#`xqn}N z5vn6|`wIc}QJfxpgD1W@bnx>24C<_lp#J>|?0`3HF9{Bz^Z-o6b!&A0Rb}qqD<+ZK zTUaNi>&0+?VS4cOOG_v!F|v>{0i75FV!sqnrKR{N1@;o#483pA=@D|sdb&veQE>m- zW$xcI+LzlC?vLx{sD3U?kN6H0bzoJpKu4gTy+%poA^IIffuNS5#>}4KRTvST&ys`!F48(gw!1-*dxUiIp2+pI>JA~+c3Gs*E&?VOh zR1cVl?#+7tu2FTkU8BE=-c|7X3QKxKIzsxvHWR*MVVluu5vIp{cuZk{Z&)9EZ`S*F ziQ>R}#8Ubl2fycZg?AA z`%~r9^AGdhtoQF2$$@t&1ZBV_`dtmbui^KU6Bj}X^ia8urXi!ptLR*E;;V2;Rq(cRc zsRGpCJU#f0BLe)MD&;6pl#{CHekg$2Lwf<107UH+oggCHWEzlZK&Ani2L7l9%Cu#n zHZ5@*M6{h!&{oL@^n^SK+E2xHl5G2yk|#*B3g8f+6fg|6 zhl7+rrPGhvTTxr9xV=@}2D~8oZ=&6mZs&xy&Me5&aH+f_^Ms!xkdRK)zZ3+(?I4|z zovNLF+-^*^9gFkSSKMYRZX<;}6#@=IUSwZl6W%>dgGmd4lI1 z+OkccO@05?IO_4kGVQlG{iyw4BJC(ImEDBx-KfnQ+TRiA{M3OwdD3mnxSbid>(XuC zjQo_7CzFLq)bV|>)W;8H+T)Q;P=BpN9x9!F)NYU4?gj1l>X4@obRR#nO>eS0qnl zzX#aK!6bot@w7}kHnN2)Kmq5Y(&@+T{(`puK9H9*QM)Z}OUCX0qWnPHF3Jm;p9zAx{ zSc39U*$u{ZZ~zL>cKuY;evR9(alZp@$No)ua4B=TimwrPM#$(gl4fW%4BGFF3U;gDNa2r9QvO7mSrD z!##nWu6jn{bd$17yY{d018vmGfJC~@9QVBt{VzCAxY6LzjU2%^9LSS3aKqI;jaokk z(j?5!qnkhQsgivl4Bpuq#8aO?m9fFyU!|9M9QsJ6(|tC~ej8!CJMIg>iFyQm2&g{* z_2VSR3C7?cX@>gPAWstMFJmLgew{l$^mrLk|H;hs{VERqD;{O~d~iP$ktb35ujkNx zQkM>j`+?9gZ`VwEj1byaSIeu6Bz z&w?K31?}}{tQXmLAHac747i%`cAzh`=iiv#PTfR zJXuUGlm1tgOFi_P_T!g``aDqI8S3-E{V+tH=mf*R_m6Yw@oOj^1bIPyjrXBn2W`mj zM7__a!{{Hg4+}p_I8P>E7vKzut(8uHpO|#fI5gCUgZoSveStVpxW5hcL)t*uhHPau zE}rg_#OGj1_n)#nOSoUoaov z9Q98VHZs?ZrOLN*MmMn+M6QXS4sbx(H!X0 zdn4*&#C>R}pOM+uC>(1^+lbjuEQNQpjR-s8JUK6*$J?U!GgX}SDy3fo`Ybh~M$vt5 zbRV3szZCbGqWq9~!hM4xJAoZgUda3qykpu3bMExENNX4?Ao1=}rSz-Q{l2L0*Bu}j zZf1aZ(IlZjRj+M$-YrCPq>d%+)pa*+a_(q z#yjz|^bGt$9RLUV-m9N}L7y}3d;SahFrSs_dzO|TX*MFcho2)so*sa|s}KH;1HOR+ zoGa5eTxtJ<`iW8Bxfr#fKlTuy6d>-mM*YNOUvcH~R0_|~aexYd1Aak(9?`!i{gQp& zI4|QMPkg$c8TY|siSvRb$_obK8;0OHih^fp9QX$g4S~NyJ`asK`7`Mk=SLa(%U1#3 z(fL6A_ZT=YSmL~hZ4?4|S^-do{2>2;{G1rV{6Kjpk%ubrN1R^V-_ItyLOv`27Xcpt z>B77eflLFu1HN4ZtOa!8c7n8G-yj%Qg2t8zfb#>gy6LZMer4y&G$7M}Oan3v{I@h9 zc(0@9bfF>xl^Ye41;X?49R=sZu%;yR`+POZg_f$63N1OhLdl^ewB*o|4+EXhaUSRk zNhhHM!ExkpV0;veauQ3r0Sn?)l5joxcUV$_>&giwogTQID#YPWAe~^K6^ip98Aw99 zY5nALVC*3sRuxFdJ{_-`MB}uE0h|D$aaw4HY?Em~rU97-WEzlZK&Ani24otLX`mbp zlsn!OkC$7PG9Jd3EnppYipFWOjnzcsQ1P62QZmM&qDpXeP)^n=bck(yoy1&n!JP6A zFjkcU_yC%g8MlhYuwp%6xg7T4vA9^0dJvEOEYoIA_P_ipT%k;sa}qsn63_1!k8>?E z<`8s}1>^H5KmuSWtpm&tB*}5SZ2FMGkzeKSSI5iAT7?e5I7K`*RxH`|@tPR>TLk(r zL*o@CKf;<*S3v}6lg6bw)5zH70P5eC&74L$u%yRW3R#>ky;aT+IXr5Sf)hn9%RCc^0TmC2Dyt2t^ z=U+U=TyoqzUVmoh*V@35SK@VFU~DII{5(0{yb?OGUGtT?ewlULDyat#wtVA#)y}_Q zeHmiBy=crm(no8+(=y}k$uZF6_xWHIgyp9Vw9-JKGPU?i_2jU4l6Nnoacnm*_PAr#wrSABO#($Ca$6+*{ zP5HOyeHVIS(sg&7)G$8bf%^8Z=&;F=ktGP6tCLh#MGK9~I#U_|dE9 z%k>Fp?W!uz?_lZ+#t!585=f2$T@>T7>-4&kY;z1qo%{igm;f&x+%7YgoaDi_-)4U_ z-vX^QH733h=pq@9rKZ<-BFBicjeW=KP?7rhO&qaal1RM`E;C07>i~_-rPps^+Ox88 zn9sn0@&5>(cpNr8?*z@$7$aEENvci|H!#q=8ggz8sgo*lJ!v2L1^x={aQ;;K%_-i80;S_E>JLR_5WyFm}8VjO#Cu7~3x~ zR|eLtV$540Gja;n-NHOEgSdf}QGRE$1#_dokDTA1DRGR} z3-g&Fb%MAdgwI*2^`gXT=YMeQ89e@8Vx2j1o&+)P2+frsH;*l8z>he}7V0@@{Wo$$ z^V=NeLp=ce$3@2cB-*D+duAAA&0)tf^OCUr>2;N>qXQVb|DGPdkLLA|bCo3KSimu) z51~%TJbE}o{y`^Cf%j}1n*W3Nae--n|%kvc(hJOJe8u{n)$S(ZehweawU<~`wgIY{nYoecSxeE#t>Lzgqqj0>z~jyU2t zH<4Zk5T6_IRy>ak?L*Iu2xy)uJ=e5sS%c~$YbQv}zfHCC-zO%4UXLBk%VL{rM$VxV z>ICNrNz8K;euCy=5`HA$;&-&0b;EPE=yDg8Gg8VLRQ3{UMUosS0Z#l~?fmzSc|fn3 zSZ2Nu%)ue`LChIq%MhA>XFCz*HzFS*^dnZl+n+GMifyZCS%Wy@V#MnIQ5$)6vybL{ z>=Dh^Bj*{B`XJ}-flk=A>qmt1_sHi4{OVOkxrcvS(Xs~de@588H1hp4eKmR?W zN0piHrvM(EMZ70CLi-ZXg&FvQZ^C)i_%~=?Ddvbo*~?Pa zAfDXwF7pmxgYy1c^4~pL3FZKOrPrqv%|U}XYU24#W#*z?KbS$!8$|g@f_I>ui@%D$ zoxtKpTo!$$5+Vd^!=klvec<?yI&!d#?M=(dod9Q-?V#W25Q!Wy9-z$)3i8jFMJ^=K*BhBbW zATvM@g1L!UVqFO4iV4;*tsqBO^}+BXW#(cs${O=wG1S{PbQ=ir^;-d=b}wXmgz9ee z945isB@@A%P0#^eQY9iFpAazp2ztseN^VTkSXX#Q~% z(7_#gj&9kxpzQNo$+^{)(T9K|zEB1?s!ZO|xz*iZ-YA~mTUK_0b+$kd0wJ?GDgEjzAAc7eX-gQE{F8 zfU?E~%G`!HFIAC$EdNZM2=t%|dO+*2KLeBk#CpK`D4!!G;8UJL`N9RL0xJBROViE& zEdNY>bck#T`JWFs2)GM~0HF2Pivb0IG{6@C@*(>GvjHstcwK*b?zOahRdj!K^Dq0o zOan3v$TT3+fJ_524ahVg)4=~N4M08eH*X%o>$wPJkWfB^&M-#^^BgI9Z3mtnl|{$Z zC_XJYdTD8#EJ;rjINv4s26aga_Z0O_J7q?3o@dO`{L;6oICMyDH2r}F`))A@qa>3phz zL>F*Gl24zGPC$o$_l5>Y!JW_|5#n_sP(-%LG$7M}Oan3v$TT3+fJ_524ahVg(|}9^ zG7ZQyAk%N5Uqoa*Ls#FnQK5Zs!NkBYpt`cM~}{9 ztVv%JaTxbkt+`M&O+u0VnQNl6tpiSSgK2+3+z{52F9K`@4yZ`wfrt+*Ywfe~qN1Fr z0Y3ca?T^;=Cc(x7ypOa$TX|3yd}2%lHa54lGSchi^=vZ*CwS6ayRp@)A72oRG!?a(_iQQBnW!2l()R!v5r% z|2WKC`=8_n_6KZoY03kU{V@-grrd{pEm%2#xqu~Ud$w>z_<(#yMLAK;GUGpQfA(*t z2)-kOxllPDu?PGF`reEHVh$t$2Sx!TzomigE={s=fVrok@Ed#t*jowU z0I2Y~FHDa7E9{Rrzyy;AI8Jha85VG264WE8G7xhB`GpIB+5o}(C$_ybN!p$*%(OpS zyds>agpU9_qIJyQ0a5^>b=@E=bNDn^vx&|OdjAkwm+McQ9C@ZBXE ztbBM4_HQHN0OkUg(zt;3vvGjj$7X-#zKZaH#Ycb*(OPaB0cb6Iw63QQpi(v`&#!U& zqwi%gznN7LJ|JGOabakp6%z%=8Cral%J{T}K0uuP1Gf!ZQK?-RB?KpNR#C3svTWD%2$e-wz}0 z&kRfQp%{F|3aQ`Xt0*VR<3lxk1lSjS)36x8v^|RV1^jpIkG~s;C6fy{UU5#SLVW^% zKd!7~@&L#6lS{$=wjvIYbqeY48sh$oKTu|XT~z=l0c>{1`mf3$ba|w7k?*G z{5{dCa6-g~Bz5|Gqv-pI%x@@?d??>Xgn<3W0~BZ;;Cd#L17!R+`UtRRTfip(e#WIq zw7zqdZI6Ci(;Ix_kHZSg0a7x31CC4jhT)>`X~K8$pdKOM0p>$_oM8HfMaij9m+%D+ zxB-p>4g1_W zulT_H4mb7>66J=lPKoOhxSU{0Y2^m+!4%pb_iu}%9tD>9Hcdrr&32CIf7<@j;km{f zASLDiQ)0hBN`a41fqKM8`rFaux03NUq_HGaf0Lk8D`_dAMz0gUE-)Wud7uqjHxOIYP|fP z2H%bs0qi-A!3E3whtl{Ut=#y-d@zS^VV?Jh zVSclV`K(KeCxHFiRfPjJZvUxZe-a`tAYS~E4;`TFcp~~5s>|v zz#JeY<^WT&aRS#Hn3CiH4*w25K;6I(- zKuXL5u~dZeVlRLa0BnxG`%ZEJg_&Hyagqx-%;W-!v+?0i`Up{5grLpx#Qs<+G_*=> z0+)aT^((@I8n^!>us;Ik04Yfx2*P!tthfcBBp~7f!3ic8a2#`hl$ZmgWaEPbCxmqf zX?*xi^@v>?2Eob&v`|{Xlxj2Ybv*GsK)I-F@a+|QOE(z1uRJ}OipYB9C-<4 z1yu%2KA^Y+Co1V9NFI=3HctF0A3?M~I9n!A@7`9415sfAa_faPZvP3${!EnNLnH7J zZ@~Uk8B~c6h#$ZS>=T%h^bI&n@&Jd~_)r-if$I%S$x@HlWS&61e^(_AL;we*eiQRw zVSmH{5|VtV1N?Xn_NU5#jSpyFl{oP~_7TkbB#tw=faAc4tyb~W#}8HFz;ob09k#Nd z#_c~IY|n(43ktxC>i`NkzyJ#$5J%WJQ6)aWwWRq7>=T$0`vp?6`G{(i8G9V#s4rEk z6MFtD?LQ9e&xDu{JAoS%ZGXf868{T6{E2!58y_Uf4D{Yne*4sE*O-db|JcsaXL6v% zpZ~F7e-dIYOb1?+0w|gThzBG{F5vM0f)mxLS0aBw`UV_k<3xEr0?LGYH>=cdkOlTf zW5`AC1T}8|G4UK@5ky>Q37kj+P_+FK2S|`Sz+ub*QvT2RAS^Q~Q*K}mkdloL3?E^= zG?5BD6qL^)97_8$%Q#}M%#{swS@qJcQT1jz*)#ylV;$psw#pL3#;^~y^4h=b0t zY(D;1`_P!N-q;7!xcx_g{V@bQmet$pak5JRs$No)eYy5ky(xxF((o{aoob;u5gG8sb1rZ`=`JcLV_sj6qT& zU>=YX^8ibd2RO{&!#b$51^DDpnW@E8Nijtw#uQSQTr(*XR3?z+1q$PGf|QjgGfd!J z^YV!-Dv@7E-zzh%mea=^@uW0?h!v4eK!2SsF zihx)EMT6u44g(LsM+kfamY4^m#5@qn;D@=eOh(!5^3g2GdL^n$F!@kXP6*qlE+5Y- zdn4Er{5V(KHi3CS%3pFqx{t75nNXE}qBs8v`ws)#BMuCY(+5x@U>=YX^MI6?2c#sq z0O6v-QdXOUzR8!YTjIJTDJ#i`(BOR5dleRx3feFKQ2W#^@5o=(``C_mta1O3>@PqN zNQwr@0~{U!$4D-qFv$ZP1|Bed1gk+{XXFR4Kj|wN8vsQEbAXg24{(^|0S-&?5#`z+mm8P|LTL`Rig;hvmq&jppe&~Y2e7YTaG_lL z6Mo`P^l4mluXH`|QH|SwDA<2!+;ad$1M`5CBo}ZP^MI6?2c!g_AZKV62flo@x= zf0y<%>f!Yqak+tcAe80WpIMh+@&U!G;UiqkD_sUe)wunK#Mb8yi7f_z{Skybz>?$v z4r30GlH>sn3wa>TkhJ|-$_$4EU!~R=(Y4S!K!P|4bzJn5C3?*GMeS{@7PA%8UxXXH>vPTsU5-IzVxa z+h04@ldBE(M;u@R^MI69;DpFvi~wzaTviBW=&L-&S<<6X@UDm2sYUOh5H8pL*k52C zkn%Ti;)r9V#s*~6xcvvkKBn!DI6#8Qg=+W+X%?jIPt+j}t@|eJRH^&j;Qdh4E&**2 z<=S7$yM{y?1m;8)`LNBnQgy)anzuhciaUr;aR-6@F-RWZFdH8#$O&o2qwO!OQyRnb zA0N(McPlBOsGr~AAItVFzKAk~=Ni`|%Co<;cMZ0BL{&Lqw+P0EReT)*-x|07Kzsz)S7Nw5F0 z_G_@bdsc@J7SrM@WB)BRZvO%N0`35?H3rE89IhrGq#2I3Kh!78px&3BRK$8(L0&1f zVMZcl3cfuxk^P*U%o1uHyt|{e>+3D7>7P#OEru5tVK=NEGO zgZ&XO2S|xIz?94~1C<@PtRN+n5z;o={@^3L4@rNv{Z3_ze1yj)=|19)C!7a$iR3tT zj-WI!-!3NheH!KYhzk0DG!Cd)joZH;Kb6~0Xn)KDQj%Q2VI~()yc{1t|Nj{`5TI8h`%##=PP7jiCep4)hg*$pvXX zg5&`iCO9F@V5td36zZ2}+md4oDHGK8h~GOf2SA30lUR$B|pM%Ku{|fu}j@ixa4R*#Ld4R)gd_cUYppPJJ zP?2yHdN0In5mM@vZ+XA&BVgVO*CYN^KNqS$oZR-y_TL|1_g4QJ`}c}j#O+1cAM*f9 z%mJnpapD(c25_V@8*2OCZI~o(kC0NYbe{i}?gy;Qwf{&z7g<(7`1fFS9>~s}&Oupd`TLLN$CuMU7NkR7yF`fOeb;zH8V|Nhr6z$Goqo zE=klSL>yr9q6+<7>!*}k_e0;0&?aqGS;5Zu}D#!_@9i+tr zy}pa<6qo~~B;PND;qu-enfH;_K2?SnOfH~!1^D2)A-cS_zl}`W|10d@1MJ@;dIA9K z&qNhC!7M8vF6|wUv`^lT5^DWOcn7OQy%PBet5I=OZVr3<5qW)-hAQ`SRge#NuSi`t z?mF0-^&4?D{`_~R?XL{>|3-5F@c`p@@Bw^8f|Pze(mp<@4@X)(g6%znc=xbqd0PR5*iR@2u0fm`7!0`(5;gsz+Dhk?rv9Ct{J_-72{f=i+ zX2>oqqGvxHY&(^-@2|o&ZvU=P94-y; z0bOZdu#2`o;s6s&9^g3k2~5f68_>Q#$p=)9uyNuK`G^ztqH_N-*t+Gf?9Du0y_KJm-?oiw}S z{r@WaV;%^kD%d_!WPg$iILyk4|06zvtqvhBGrmB7S3M{*{`>YJt7P0mt6rI1pFLVRo3|PPuiY6JOylDB(gur1r%oE0p3?0AIjqd zSxzv+%(4Q<|74jVu16F=JJkpPhsOT}`*()tpNX#sj?Xck==9g2@FOm&S=-^Pw2}#q|Mbtc1Y+LLNZ>0;f8tdHY9lxSbea+Fu-> z3%;TNAn*eu2bf_d2bghee>Ta=iC@$!k*~l$fhn>5nX+Ph$cOgISpb3UF$aXQI<`l@ zt?8|d?U`Jl;}SkX5A6Sq<^bXW2Gjn`IMe>jI2#Y}K2|=!z00dZl*fmP`v}x0J_LZq z$S?qW08aep?cWieeG;tpXZVLsQEkBPFGRM-9AHW&2bggt2go?Ky)?k*?GGYk%@PE$!Bo}a)of9hHJ6r*!0I?rna)698?az#p_Gb%A z;{w{x%7-fXh*Bs=_JQqH0C;SKq$GIopSOPpc(yUHy?-So`7jRbA4hWl@jwKkY#=$n z3^O^vjI;3o?<j7|oxTIv^!~ZG!V;*4nD;|iC*8{un0u+etPjW#R zX4;-D&a^)>j%_bZmd6R{J|Y+TIyV7o0R-bANLdaa{!iN<^MI6Mn@iRk1pC`VUE(yL zq+A|U#z!#i&lZ=)1+>3BK4?I@R@bN#fYtzEKRA-W0YP7QSuTjj%Fw>y|2zAWTtH!V zK4`-J^MFDCC4$KAEWQCBWAh2jeQX>c_m#&70VfK97v}&P0LlJO2_7&wLHYz?`2TzR zGx;FNi56gc2S9>|1C`}NrwY^~NZU&b3pf!6ZB_PwmH=t}o|WQ*%>G3Ek&O!iJ}83y z#{iB3zOr%w^8)*U@+8wAu*JnZNPu?BBY;r=1%RNxi?lymnCTbDIN>8?_OF2bC3zta z_SXS80RjOz0C5??4420P(J=`hf!e071J(lu0OSCYeVmvBq-5g&xsS9z5tiA%()Jg> zZwh#!0PmmOfCF;@8v&^P7y$STK)xayfca4b^~wxDBH%sXHoy(A0WclV2>|WcU~h4o zwe&D)f3`4be`Z+7f&aSwYr6eec7K@$WEzlZK&Ani24otLX+Wj{nFeGUkZC}s0htD5 z8jxu~rU97-WEzlZK&Ani24otLX+Wj{nFeGUkZC}s0htD58jxu~rU97-WEzlZK&Ani z2L6B40E|DBZI#qO661X47b=-wtjW@o{6Ur)Bd*GbE70*$J>l*wIXX}*ilbTM6qV&j z3kY!N5MLbELq~YxxEhK(iR0*orGj5UD2sz`SWLvx4e|wdp^tModTiW2sulTXCy+rx8&Hvyl;aXtF?`S`Gqt|Gn(E(g!4a1+Kwe5G>_m%zsbw-aYb z`1pu2phn+LoM1g+94E+87{>_`MsWg!w}-ug8{*sH8{*sH8|dNRQ-TYMqTq>O!>LER zoggm$IdV{9=R*HX^04?yvQT9;uw&YkvGwb;sskFXKYrY(8Q|F=D95R#3}TVRg_Rsm z=fd%$hR?DIPd{mUtj+AZl%AK-i{EUPkCWNNAzE2qMSt}5W;V9@MQ<;7 zQ#-~rQTzL4uUMmss5_|mxL$Es(*}isr+i%(OCA`=WeV%3ef1GE;BrRX^BFpPo z^Cm+!$I05~(e@iXtY1yMHg{B`VSQf9&EbrdE0XheDpB}wFz{gC;>g&L@j;G( z3dM5yZP#|Y*3&ijdDzguI>|kLrm|@tXRLz1+{JqN4J|ef=HH2(DrX~qr}h&wr@%oe zyEL5Nj+4Lk%AVhZ-|1sk|J#!z742{NX)Fne4H=mIw82KXMN0QuEHvoE_dZ}A<``p&4+vRKR zT4uyw$6GzjP07*q#EOS=e|)bqXnFpPc6VCj_s&{lY}NYkp+(o~Pv_|D);-jHxOtE0 z`58G|>X)_%&Nv;CH9ca+1ACRMru(~ z{;2yqbYhD?ZF9dJb#y}$wc**dcBW46zI$ZwzCPXR+nla9p3~5BZ4Wt9mC{;m);#o> zf5X@#%2V0?hi{!h%9$AtwX&bq9}zmgRhEVIK*QqBoD!#FN{i|b;j~$q(AW6Jd#xlB z*Ejqq^^$Ho)jYrSwAR*Aa9**yL~%(#P#3u*xe)m-M~}3cXrVj7*3L8MCr>Ya&qe!| ztA6ya-!ZB47loE~wcCeJG1xJ%d1*{myQ|}my6+2b=XN_j?I?F{)=;H=nqRscc31VR z^;G-TNlx1Mdo6i;czZNfo|_ZwAGq=Mf~$|h9M(66|SYZi@EpQ>JjT6^oyHHNMl#Ec{y&!+}>7bfYYE z3zP2~4X*p7hepp8338eXBSs#S-+x}Q;j>yMD;$eB`(mR)RIUHE(7ocW+U3owSMJI~ zcIG+FzN5RNRn`*+`D=GaZeH&zAK;WXDD}$Do{K<%ho=r}+2W<`V27D$o8$Ir1Rr*~ zJvLpp?vj9+3!PqNw=-;;{?s-6!Z}~%#97ZRPQRMvXC~iM-Zd^tC#~*mmrh%}KTeU~ z?)Sl3-gL&b=eF0z_V8HF>+)CH(w7=jg4~ANbkADZ-K15QJAsN9wkXve*nRp@jVD19 zM%UVSAji6BWm2I-*Q|!uTy{9#?bBkiP9vvta>G1Q^MW?{JFPwterT3Kqj61K$0V6F z$Qpgou-Q(#>21!o%{nMICRSH&UL9)>IschXZdq4L6xuA+;vdpzz0&fZd1I11^IqKC zYY}06|7Y!e*(LMk12i)lJE?E;%rWB3ozc(7{$|HVY7K8`CdIeXo~koEpZa)~uXAqD zi$TYCubVNrR*R*UrIz=bu4)qNz`tT_vNt%QtLN#Jm+Ys9_?>mUd&F;_!i)o)4vKac zJwIqPD4kY+$NL>Nug>Z6dqi{_X?K70f}To+S#wMp$*=u~)8RqO)s|sLY##bJ&Oa3A zGZ}smW9(wD#RGPv=4B7oQA^f) zzhv>+h}N69PMr^^*ghK++;rIJAL~D6*zEg0z@@v|xs11oqk^A(bT#pNV^e2|0e|ZR zzmOqCSC)=_)+}W@C)U@p%i5uD=k&cDZ@fFW%aCx>H3pv}lW!M33ZJ56YPdXc<#hQK zccyY;&7OTao!@!q-PK2P`uFqidDx+ow%Hmp)4|Q3+|qEK?f&d9P+o)5hwIx#@9OgU zn`q5bxRX1b;u25j6Hl-rBn#vsCcicaHUSyS{{Wu9xZ6i&GjY1!s^fv-o@i-HS& z1|zO7dpsvDrej-YrTKaO{&3*mm8Ld%s@xGLQOMYX6?((I7T{1Il zm{*6F*8cgaJB>zrJ+~d4+g~F|;r4Aa^Gvmq8XLFmT>HI+Atd{gM$BCgr3Uq;_xxfx z?FziI5VQYImXr8a-?W;5Uu(JKJ0Z*qtJ>w9j?tLpYuTFaZ zdifR}^{tXrdpO4Eo{@jQpYx-ErP3n#o5$R(W@;r(PHm{I){{RmeVax7>(+5q-9e zH;~te*>uQh-Q^QI+^5cN$PEiSnDbYokwN|Qf_QI7@mupI^1Q|yPI|n(C|Eg@=Ofp5 zRB%L2`eVNZ=C6#_M_FIm6V)&B`}5}>5psHR87k2sgC07_U*KhJj2PZkSIZ)LS;Vtb z?)#R_b&QC=Fga}5We2CRS<3FMTgIR5AUEqkj)B*Yu7Sf;mTFG^te-J{5J0y84E;^B5|D{wzseay&tsBm3tX|AyB)i^HKVg~w;Um}ENUlij{g%?njtN{`VT(0I_VX!;m`FxOS6(6T%PaHFwWQ3HXK^;Xv?A5rKJHkCI+tI=r3Kq z&2@F%9ucX^#eOkO(>-)|`h8Fx*+1E5_<;vSTK97twYtQ7Xl~r5_x@QD&9^tG;Wz*o}&#mUOg6W@fld_QG%cuHY6%SmVQBBu^hzZT+_tv|}*_R7gk z!%RwxFZAqi;ORss&*_tVW^3yu-WdB~T!gU-_)ov_IeK(Cv_UPU?-FQZHn8;_J%oUa7t||8rn>)eH0M+o-8~e{ox- zrQ@_?;@92<2`j=)vZ6-)trDy5yzte*rn|Ne34kNe}-gHznpdCma+N8kxO*9X@Q_d6e&sh(b|<*1*7 zEf<7!I_|0-9z5ub#q7VHDHf-xJEk@4dKb|*Dce~JIc9LR#@9zyZTXS3Yk$ZoL)1+JW=i+r1CGGsZQ@&pCF?<;&7eCQe z{#5$Ru_kVwOKvr`4sX6|@pYZ9Z5=h5T(FDFT)JUM^E)T{J(*S*x9-?3SXUkN% z{)uU`N1eQVYHM9>AOFdhzI+^dyXOR-^-qnu`rm{C+$K%w?8M-?wXJ7A8PK5BN|ULn z*NfcpydIw)G*3OF|Gx82dIyhO-y_L$(VBXcYwZnpx*ogXRN|?#qj6qsUc{GOAJmf+ zoQG~|Y}DJtL+9Y@v+ABsm%c>qK2wr5`-L^n%i)vpmFKoWIulL$Xa%nDe5SYgN5Jtf z4)RXTmgtAwzM5#?!lU<$f#;6A@NykubgFe$XT5$}2BYGO=VuQXRNq4POr7WVAN-~4 za>jJWXx{N=x3A2GV&dy`drB|eBKmZlV?c&)=WiW-QE-8xi{MA2F8sGcKLop zYrpL}&prB%bu&jD)rsuFNnDq!R^q=<+iY3$yz^_;oGp3X^po>YKF8yxg~7M=Ct5ig z?)o;#*xIi&Wob`b_z6{61}?a_=jQW*!PsObd+q8DvaLD}b)5R;_TjVeE9(KWPz}E54+Adl2%r!M*WwLku zSRL09+G^(moX$N8%IX@?z>_~jPGi;hbMB!-hs`>huc8#@uV%zgIl5<;-^qfdvkXTr zag5NrbtbZ>Q}I(%zeNsXzvwHO-X3TDcjEnj-Xv>%`^4BiSy_b^~?u>7C?+nW`N>47m6@ zW?WH(-h&B7CyS=n^R{!#Xq}l;TB~H#6-PZQzxj(S0^)Scd+$zHTx;Lq)~fy66yG*7 zYUpw#M{{>zuO(f5A=z1ymh}B1O2BdykzBWD8GN$m9`Q7oC8=PAD)Ov*p zw`g~Pf?f%?bV{bf)G?WpS|#3}{yj^h-;-;zPUNk9>pQ588<@)}Pyg$J{W<2BoR&Je z^(^V36m(_NK1=XpVl9Y`C^hh*7lHdlbdb) zj%_=+zMrOkit$t4u7d3~cCEI#U;{6o4 z=9h{R6K*&d2NbV=8lVoU{OYXtJmJHem|36Jy($6wTrO@k=xE)T-s$Nc``xv){Z$I5 zPh0g5nEYWs&byePW`E_?=ID1TQU|Bewdu6eCQ$>UpS|UNow;RjfVJk&4?4SF+SE?+ z^R@1}^<)u$fKN=v=LN$Iben`PbJ*>u4>=k)R(_m6P`Fo*8P5~HSY9t^vh>rEp7U=n zTG!?7QrFT>eZmBoI3ig3dUYayg!wR}v^)~rs)K6=3uTeo#PmS}&>x@p{O&WAY5tIj!!PH|gjy(~EB92`1i zlT(W0ciRnWy*xTx3F1%mdABZU=4dfBG5!?c0hJ zPkq_4%)4%wnZt_kyA1aD#T{>j>z#9%W@iRW(9ZF*TSI!Jh z4WFOY|NHEXUvnSt&N=t!!vRhc$Gr=KUFz6;xZ!frB-U{!M;E6JGR^>Qe$#QG0?QyNnTkji5u4`v*k^Am;FU;c2$pt@G?|JQ#Zm`)xH*3ro zC4-Ucj23*epFK*hiTS!UiS})bp07E4iyNV~_4E+8o&kR!j(Br4W`b^N;7jk^^gF#A z-hT5uzxbR-{oo88s|G!enJn$7_}#kEky$|w@?WgqMjh7F_KAJ9;O*g0b5F|!Ip#)F zox<$Y`d&KY_-2=7%m!+}Kpm5P@dtUE+y`F0(KG0+>5U#%_cf_;XAZ6bDvmBnY|796 z__E-=)9rh5_N^Mey;?uXV04W0)YY?l=NDhTLIroeFd{nNdePs_v)>Hsx<5zVO1|Ek zs@>IvuU*Qo4)tnypjvjZKkYjljPR?{rA&PZeAI( zeT)b9bEI2p%H8yh_Z&;?>YU5G8+yg(t=h(RPOH_9oL_t_eZ{hYH;dZXIb03iHGcN2 z+Ih8?^qb3x8L?WU@vHH}UoCDEN)0!T_oD zsrhvjlr_5@vS06Nzux+$PkTr62CFXBalBW{M!{fYPV~r!S#5XzWz*;4PR&^RdOVj` zoXeq^JAB|#8XnG|fVb z+%vUXyFHu}F*5I6quxrZ+E{mVY!<$-<%xk2abqsL^nj;rZ;|4(fS?Ud6a7k88d~ap zJnlK-v}VAF$sf{Fr@pz7xp6?jo39R<5e>}MuQaqiVx7a=Xm>c!VyJG$_)BNjKw;8x zcl3O{5d~8Z-BeF{m1eou^;-aZj&<|&qd5zG>kTQg_~|!zj+`^spks_-?#{GLfe((= z(atQIspLPxP(i`2i#jEjG0E3=0$izdaBfq6^Xo4QTFf~gW&gx`%)T_!f+y)slvScU z+Lml=7Z&a8Y<&0I?QjRpdWl~wqS`ifU+~SS>ENiSMK5^=qq_Ec;-_fSHRRyt!5kuk=vZQM&BK>@M^%Gj8sRTxdx4lp2c-q zxa`BaoCn4MaVu*bYjQXZ)+vJ$^_$(Ek69gF5cV;1(2=h}xrUF<*nZ5sGxXBM13-h*bM?J44|qL~E*SQ+w?f^L zk6~}l|IAgp*2Q6LhF8k#Iuo5-IM!}@dM`G-8fPHiBI?Y1-a6hE%>kq8*qmH)JNea0 zf6KHpiubvokv8X|A3aw0v(KJYr*Y#$)KoeDeZ{dla;O2WXPCa+#&08nL%+|Mb6u~y z;SCSh#QUp@3XeMUdck)&VB51Rv@&EF{y30jGR;h7Uv6^#r6n_3Wi3d5e{)A8d%1uK z3ehRUmTliSr+&VM!&_s6fCooP9U3_dHPAZ9nV=CdeTAKgLe`3fDl1Z2_DS#dp|G^g z*R+gzN{X3Z6Cy%y$8j$BbR2$F@san}MvbAK6#ZAuI=*hynHB0Ck`ASK8A1(tU_Y|~ z&UG8veN#QfDQ^Fwjuo^KG@KQ)shtwPuSPqCR+sP9$vwBw>z_TH4JLdDo?oPpcKH0_ zE9oC@_L#Kl?E1vv%2T|vT2C<8d`LdB8>i&P!=E zg7N#?WO;Y$_7U1cTMVBXY;dZH_YHk~rT)9HTJ{~^wW>dE zpZu_e!9JtS&qD*2^{t*syFxNn7&NC2Y)Z>^QNQ)spf|NQFU?WY{3=IYCB4`&u-O-t z{7}x|wgykFCj>pUKIvC@v7Ye=I2CoIpYgYR6Ctj0Hx z*X6WX{%~$qNY}c{7yDn=Oqy)pAUNiDfb-E7{XW-musM49?%8ewz@oL(YH9O3@;rHy zE*s^oO1D^Zbf?q4V=c}=8>Wfr8kMkk-NQeu^mw7;Vod^P%;>Ltt!W82;r6sPTbIq8 zXK+#Nq|dUW;i>Bv51xJQ&eaf2zpY=@H%ys7{Pe{GTkb04FMWSx8{A{Nu14-${-7o8 zpU+KMa`H&?$;shc(~ZtK-R5f@KDjl$QCEKdi2187colh$J(RhsOaUi``W6NhFr z9@hDFgYX#7mp->ADCoW$cD>=Gi$nZd7yjtcZme%tKX0FocJHSrtenzusV^^^=RbaK zn`U{2oF%E@-&-lY(dhZ^`!U~C!*i?Wt}E%4y*g;laO+PG>jj@&!GB&yiSNO=add#r zQ_#T6(&2i0{O&ec#o0I`ch?)vxS}PC+vvvEvWX~pq2W7L%V^0yTjjmp*-CuP=1s>! zep(D)UE0RJ{i5w1_N}LG6?UGo*(hY5g6?UjbsMdHR$gurW~jPi$>~FUs%KaG_fu8! zYPU~*-6&(XeedWZrJg}E6$=BpfBEF@?z6=0o0Hc0t9$>6hgPV~kE2uOzE*Ye@xSmz zIioRW+rz77>-+Dl&$Ajfag;%aq{TnR2Kl|6HZ^xk#}<)Rew$`;iyKZY41K&kP%Su9 z{_*|8pGUt}DHz{;WaQbxCes5q;FhHM8-!-qPt^I55 zrRTQ0N_JePbp1LyHVF0@8FFc4*Gc2-x5YH$8}cTD>YKx3tCp=eUMT8f?K zn52${vy}aI-1_kG^Rs{}HtHiY9~Qm6ovC6uG(16@--5ro`H^R%bs7!xHF}~J*LlAMwZ9mC_)~1~5 zw9flM%R%z_L!(pOyg&A^(Eo6I#*fUqPSJ@IyH1*99~=BvhEnNq^G3x`g7uoDY5J_I zFK+_B_7atu9){Uz?dC;3a^G}1?{)j4(IU5!R$STJABt zZC7_teX~~AFMT!Zu6%#8w%T$xH{B!A&aS=gyfRksx}I&qSG(H!K)>L|!#>^3nErBX zj_u%RKL44*u3hc}{X!W7vtiEdMv7asJfyKdW!>G$NW!gVK%4^6vAJ=Bsr`}Y@zw8j5KO-u(*TJOH2T!~%J z_Ildy24rYoBVCcz(qv)N4h)*8$2*`Mmsxtn<6a?84vW< zMEUGVw!0tnRyGGm0PjcK1 z&OWey^zD-O_!rCF-VD2Z-rF?>`Y+mLewxG0y1GYAq22K86HDtac&P6kS?F`sG-#n| z(TAs2ZrAI$bSq995EIr{by%II!zOfzxL>QgZ@5>l^zEh|Pux=Sm9qxuUklv5zL~vg z{)g6ut_H`{=k&_RHw{(i_e~AFS2)#1HTiLW*SF0N#hl>T%8y&kJL>DYrZ#Ufk5al* zH?vVb^#oG0v2mupLz@s~Bg3ru zeSwk}?|8K{E?U_?LNO%kfsKz|QSXj*XFv9CZ~yXf%dCxwIsu_p&kB96=(fo35$$w( z^1U8Km%F+LHb13#DIq>M_vc>=o6Z_CVeq90s?M=}%lhy1x>fAv_snbe4CS7WKAxL@ zHQsKqYP|QSB}INwcTOsBR!`ulKWuo^dtXTwRd{Q2)XKQ9l`*^G8f#1$wR-owg$-=D z5&hR_pNfyXYLxA|-oVp9bN|hdncJH}V`52Iloo&DF0($lCeKP2^Ox`~qH^6n|E0sX zs=e^)h_Jou{6EX7#Pvv9xk9rKaE)Z$U%ObesJny|9u0J>wh=7<&iby8g+qN((h$KuZV|Z zP7Zr!yZC|4#Csk_b9{FPZ%^d5%By{N4|3bc6n5pc&=h(VR^I3bzlHq&j3pPh30b+C!h$^IUTde3E-> zzA@B+r*HG&=;Zy1`*q@u4Y&2t-1mOKV5F7l-+pFKZ9xJ0b@JS{M!scCncK-Zi-Hy><`n>RP_F-!ycD z-~F%9Iz6L#R_Vi=o~A7m)v}%p*>wBc8_y0&mtWboc1XE@itCW1wD?)-GE%o32Y`mm*G&gW!SU2NC zYff5rc3Pfbf2>d6-3l8mhblFRQQX@lIeX@ToOicJD(M8q+)~!6?H-iu#W@;&@pSIm z=#+uFehXp-y>9us$?V(^o%^FZwz7J4tU0ziDrTR~u4uMk`nt1Sh!d&ab_Kodfa+uK|%69)wNeJ ztJnUMkJipz;qx1HKjS{oqIdY(fkB1aoU|15ICVbX9{DX2G!5^e(f<(>+Z`L<2E!JHbc%#KV-xki`#Jz-o}}9 zbGR{o{8Y;Fd1j^`7dS||kES=2T<_0pwxs`g6xW@9=9raB$H;eQI;{j?o6 z3HMjuRhnki!^vpVq3J_RJw2v7jN{ckk22FSY4WS|#i#$@au|PTeo$SYzHHa(UmmF& zjz10V;00#qCkcJ_54!4dRo4rQVIM?%PRMPoGV@UHs_v2eBDDE-u(pCLJXCrtqgoQ0s4&2}l0? zD@dLGaIfBP^#j)%&kB7w6agFx;W%{nu+iKK?Nc@aTmOZy?P1xiz|ll^^my`r{$8E+ WcXsnC&Hye3WAJqKb6Mw<&;$Tz-HmGi literal 0 HcmV?d00001 diff --git a/electron-projects/ucap-webmessenger-electron/resources/image/ico_64x64_r.png b/electron-projects/ucap-webmessenger-electron/resources/image/ico_64x64_r.png new file mode 100644 index 0000000000000000000000000000000000000000..84b33e4a79f2a4c29230f4b660b5d8d6c488c99d GIT binary patch literal 2082 zcmV+-2;KLIP)C0WWCL7eSb#r%2CM!`Ke_)+#EDXq|5)sh=!+=6j)EJ}2D2YOh zCPp10K?MmSAd{e|usNKODH_e3B<0Pe+gM8mc3bz-+xE8iUUSYp_x8L!=Q+2x-9Mgh za@yxU=XuWg{l34K=k1UnL4pJc5+q2F;Qth2Qu{#n9W!NS3Bm_}OMxuV4J<@x0}8-t zU>G3j7>6IcxH1pk~}tJeQ@&cMt{Wgtc7!2>dk0qc zW#*EAcp;!`RkrUo;Cnzu!k$>iZ|8Z(fiD3ME$iQ2s#!rp2$(%UT>rh-A`zYy;U^+w zVpunLsp&e~Q#YAszwZ{F`>?hZt^Ild`d?A4upm> z_PYjQ@4<`ux@MCAV-oauP`SJU__LGcgJgx21ECSKU#0Boe}Oep9O~)YIR5yZ2TZcZ zL2M6jtk8+@EW(mmwyvk_4+meHr8J~=H(mdu&a+EEIjcX0up(ycY89-Om8-uB>8~pn z3+I0iBwjE=7X_5j}odxAB)1UO249>s#QW!(Td8j{p_p7gH$6+ONRJOMP;LV$TH zEHoh8;vvM4+1s~WLHkV`QLXct95~AGHy>f5|8R9U^|Tjg%yzNx8=s_c`MDHF$2s}X zPEI|#yLvy+$5l?h(9h(SOF-GS{0Q(f;07Spq{-|7ie4-0*J9nwZJTZ7Ei`m5rt_Yg zQB4h@)|Gi_I`6rK#uew;4^}Pn$=r68$D2aKrQ$uOa-?1vbW3clL%tkG`B(&qaAnQ0 z+vs(ybk`!-Q3IQnA)*QPbXuI(W zQk`uc;ZyC+wB58xFPZ!KVefG+^~T%&-i=ODoCN}eY%Q5N@WfI%6xBIRWNy9MBY69^ zD^ab@;jdM$cj(?zA@c1LVUO4fEUTLUQ7ot>Gy4}`xjp}?mRqh~XD2+XtrZq6ADSM% zC;si5*+#j)@)9Hyd7W<8)lC43<~VVv3xTn{FEPIFb>~@%1$TbfZlC$|7Nns(Z0GwA zlH0S-+cDDpolIQ$UzpNK-&;Qc!ilgDh|0`3X0`WnAfwC%xAXJ+`G1Xzf|JH zeLFzBqt>+hyOgb#z1~1elTP{z>n1=HgJFp$$|L*)Ob!e(y7O<2kQ0(_BZp_R+0<5}I4?aU_Y{GM^G?C}zgU?J`*WE27 z)^+3;y0iUoBHaesuj4bdwR!iQj0s7_pWa8uN~&h6T7M*Q}SnW7nz=E zw_k>Iim3~i>%{Lj>1SRGD0@@#Hwf2)YnTg=A;J2}47uWG#R4Nge9YTU5#G;VuOWYO zO*f?`?fPf0Sv>AJ%)-+OVC0>3osXKHX{!yT1ok05-)-7C^HHFY6-jFEk{t4_=MJy~+sZq$kXd*G@I zRz)ufV$1`o*8Q9)-in)4y_xx;!Htq%X+4kX@|bw#5D%I&6#@-(BwaB2RJ{D^C z0KG+D1Xd%c*CfN2Ju6{KN|~% z!IZ}RccJ(oG94oR(`3)rCGKYgi8$hr@RY00xa(6N0nxGRF1Lj!VSUW`aRw{R(s=_D7OW1GfVIH7e|Xo`#gU zM*NSYbR$6dch)(Q{)JAFQ0vC1Ta}HR1tv(4AVGoz2@)hoFcSd(12+k9e}Bp0F#rGn M07*qoM6N<$g2<2*y8r+H literal 0 HcmV?d00001 diff --git a/electron-projects/ucap-webmessenger-electron/resources/image/lgRed/128_128.png b/electron-projects/ucap-webmessenger-electron/resources/image/lgRed/128_128.png new file mode 100644 index 0000000000000000000000000000000000000000..325560460f4051c24be52bb6d4ae26428d70a0ec GIT binary patch literal 4613 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 literal 0 HcmV?d00001 diff --git a/electron-projects/ucap-webmessenger-electron/resources/image/lgRed/16_16.png b/electron-projects/ucap-webmessenger-electron/resources/image/lgRed/16_16.png new file mode 100644 index 0000000000000000000000000000000000000000..84d630b50577277e5c02aa4c2b6afc83a3458543 GIT binary patch literal 551 zcmV+?0@(eDP)P>}Vh2U4ZiYIEeF1SRDs*k>W)WP(3MzsQ zf;a>;gQb!VE)I4ug9@cujSy@!)+X}qdpD+baOi=LLXZY@xtK=It++op$;$Ey2S=`8G<>f}{{M5qXf_u{6gVyur z(ZgG#iNghFRh*MfI5rcQEoDe z-4P$FR?0ICnB-iwV3|Z*c~Qe8fx~!jA&>`Z$F7M7dPnI>ve06b#p)Nns_bmB+c|;& zv|&?D2<5L~0ar7sgJn9hOwW}X7OGdDGZiUdJ}Ft=p?&RDSSQ^!x^j_D8+}+)+QUyo_Wz zHPg#XlOQVrCQOB3R6AZ_y<2R*jToJgS?U}GKH@IRV*A9^PEJxI)_&h&R|1Egk7!f}s0eSsmqc7J0lIU4I{t`-a z1C8!J?=vA_O>ZI8Mv{)p)|u}L`DI!1uAQu8pg#4*SWXNCw+zRni2Y{6nCLaceV<}c zfP4JL9%w^bW(K4%k3byO99z6gO*us-Ptf>dwJCTU-WviBc*_X0tnV4EJ6N0RF6-yE zokzqxhY2fDCLdyCz{amO+&ywoN&q4+=i|#2ca%oD4h@REV#`6d-7h;w8_jnWq&@Qi znGwFI`vUg_YGp0YS1V9r+_5ClE=4=20q4kAl?A&dx{>)m!(lUde&~qH&dPTyNO=Bx zJ5JsU4&WSD61(F$unS5v$`(xQ@+9?HI8`t170Y>ib5`t4EbWt3qzYCWc`-O!3Jkrz zQRJm~$n05r-q7Vad6%qh)W2KDw+zAahy-EU1f7Z!cY+I>IRGz)g>vA_g(Mz-8vbS+ ze*gX!`&LlxfmZ+$1Uxe5@HV-)ya0u=mjZUi9SmYtFwfC!4z@I5g3WcARfMOnv9Fwr zIY;fF906U#Jh?Omz__TooD0P=2UX%$Gy`{?j$_~^-{GOR?2?E_(9_$s|Sc`f;A1Tj8z~xaS{g0Rc z2j|7{yyKan9>cc+otcmhZ?$s0+>WNI6GyVIiQsT(is!yaC5t6rQH>`%eRz zOVQ{@UVv8AiFr;cY%(Qp+x_ZK`pm(pn4c1qn<`OJOneHkY#hffDyEne^9yd!o*Jw1 zi)4r($P6y`&-j60*@b_#)*h%2KgU+X6IUjnQ<%a-!}+|B3#RT+Q|e7fqULL4(EOb5 zI(a76QiXsFK}b%RrG$BwF@D4IXaBIrIexE6hcVVQh+2h_f2KOKkGFVfX^1XCans{# zU~p0*N;0n8O<2dA@GvEJO(hT308xQkmc|MP(u_G6|5~$@Z^99f#AwGW`m2{*QWr*q z1Z96^x??=iO=|fyn+_C_cPG(;1Y+JhPe7S#7$N2mYo5$deApMD!Jz+=>eDY}3dz2yjE{$98x~Fry`TdEwAglauZUuJRLadO zYujB7aHhJEoF%m^8kA^hkyH6%?tGU#GFswaIvx{d=D2%q17C##mjef146%wWTPfZ; zM|Ek6pI5aQN~5{O`loO-_M*jpX*@xbD-o)V#up=c<6EL!p1DvX9V0`x?=uX2WV((o zi5*fR zsb+Ap+vn=(~5Zw~?4&qLkrpFRRrC&;L82T%|rc~4$Xeu-s zof5zLClzi#_{iu)8p|8$MJ~OLzDBtb`<@qPbHV*8=FY%=ft(|_d6y2j!FYvo#STjX zrAaLc4Y_U-)8C;ObvGP@!>{-ANVy#5k>-04`-M2Cm$D(aI>MFKv1;}Cq^=DTqIa&C zfD{0?@4twulq`VO*OSiJTm3!~!P(^6kdW19vr_9qlCRkudZ5_ZwrPhuJU=bkf|h^O zu!%&TNv?HbAf|Z+6j%fQ*sw^-~72ZuCwdP zA`vND-F><=1V1X$h0s;#3<86Lys~^R(|@E25ZmQ!X|a>>HrC+=I5@mHY}Olhy+v$! z-pS^B&nDvEG=1677T}^|f46@|`y=%qmadpUS=;P<^jkf`8)IM6)?FjH8HX2gj~ouK zGDcaO-Y0;);M~gxu3J(_7;7w5-%w|hM}?ljG7;(B7MzIOP4yLjDDDp45TGxh^zN{P0(jY3P(`DKcfHqRKTFm*d^I60bF$HnPWd*|@8l)=KA!>Brv5t#x1A4B8(I3Qdx0Q$JXR3clag=2i_?AG8F)DwKVYTpd6(`wK)?<0ytVK! zhB1)>|J3&rV79~^E*by$#h^byhzZfZv@6jYvKRw$unLl}x^5##3qDj!U$RJJwI}ohZW0rYL zfGr!+@bM>FzYtAjp-jQ5wSQ;V^G^A_ z5$J!!@8F7#p3FzM4ut;f-DUE?vbME8g%9g^6z@9=TlX(X?^!cGYM~m8NWKX?*vP9PK^GpQK`^Hp7i6Rwadz( zA1lSvNiwICb~3_tORN>ElsHgmw;!$7awl*wp7p(*Mv0O?2*Jkv6qH8RU)>8!~*v{0>#J^=zB&SCs;`Gp!|hUzR-p;|U#AYQ0aW8&uH1glWU|-!)ve)W3Pw>C_Yp zbjYg9GL1y|nz@NxXy-INEom41crO28aFdL!QJbl~-R$=jS{{?SjpjTz#4q!L*=d40 z1AVM=9p`*^uH$1$Gh3)rzW-bLnlU&b+?QFKwlpV5)7GWihS?OAnmb6PT9z#S=a(%# z*T*?Z6NRO%(5>l_V8m0?6fu6rchk@-QJ!mgcKz$uxRaYl2VSyB684rv*&FVkb~`ZyXD9u z-=81)SZgP_BN;tJa;pNM9W$f zz}y^^FXOzsfc#XtXw?(oDC7|W>;lx;Hl^v_Rg|!3^__ zM4%z|+fP*3AM6)sBhg-Q8AmpYv~0;r;h%pN*}c*}**19Ev7UzhTrmKqY;L@b!6oka z@`}J}3Te5n>2ihRB%<8ZA#AJ0=Qr%vfG4=mwY?$CD6i}0M!}il<(?|Tke^J_GA)bo zw9p>eRjJJG$Xh==2974IuP?sJ=@%k9u0MYe&?|;}=S-~ef7cu}$<8WARhsGAIFE&1 z&_i%+QTvWOS*DSkok2JzHp!$<{&Dgb71dtC%*Tiy>4+Z3pF=EV`}sG8s6yPGfXU!LK78vl`yZa9r(wlGV?nL*ObW2it) zUrSH+o%dBwO~V-&#ab*(yk(bJ#x5go_G%KHrOZ>pH+vUaG@FC#vXmi+N*?(3A zdY@5~*L6MSx?*RfB%-RoOpg7J3Mb8F_qAsLRfTftyRb4YsYd;#8HpJIVy;Z_a(DvS zCzd@4YEnX@th8dTq@~D}<*bBol_u9D*H3KEP($e2Sib&}2WZ;%oCglg0~{?yp;uLC z92l$93r_iCj|0?O@OuW!R)tamSz6=J$)tuvwNkaKZNp2F=0h({{R$NQ5W-0vw<{v% z;om07X75mx^6^y&aP)5_3_pef!55ag)|P^6nvkEmAtYW9zdLF&atLi3uEwBe>A;DQ zWB+1urHT9cklj5}<(*@DzLO~Rh*F>Db(zYL?3sH^RVXg8bml}*Ed0$c2LT2~t8bm| z^&S;V+SVVN>2@|FG^Rv?DWb5Y7B0(V548*;Om#CTFcH_RBEie@KXO*)NzuC~I~9oC zU&2fUY>)mtc@_<6Y22p!<`(!2)W&UgY?=PS=sa1rD77vk_N$`_5u}{;S+q%&_m1lG zoIHppX4qA_#E`C!8S16Srt&+@-H-L=;Soyn*VJ2?hEFCUT2IDxUL9Vo)N z!c!Ui=N&N7Xv@);;i4M1R@4! z$CbH}Skm``QI;Q*agR=p=)y#uWH7YX%x21Kf~?@7TuG&b#JZ;QIt|%invKB>+-IDI zgGl1U!cNubJL276?lSi4(IQ*Q2(Y zcac}lrnu?T3*AUA0g8ahKQ~%rb0P|>e2rt6M=o;>fi_OeA%vuL+ef~s#J%-K#gZ}~ zg3I*&Xbev>F+!v=2C}He*S}-gBg4ytqW#EH~1^(b6i5WuT3j3_Y)& zuMCIedeif@m#%P-npE4$@1w5xJMFFQDejbEsFCi7e;G0pXEH#qwC4SWQc#Nqi05w3 zP36GdjVnaYUc!!u&&~H}p=|a+>71eK&6?jM){XH$dOf*edBR4_0XwA6ZG_K2;Ti0>m2R6UH!p-+uqOGO&?#hrD=*F`o-OWs?V0Te~e32+JfR zaxo=$)LuyxXL#ysieEnQ7Z`ODSd&s7>{sW?eEAg$cC*m7oYCVm#LO>#fl{(X4qiuglS)}3fM z_;VwFOlQ+&!$C6=u9%YcZLTF}HhDn%UU=d?Q}|v38+$u&w|@4(%U4~0W=TJn11UxS zGx%X|7a?ZcLZc$j{dRI7zlhM|6Ro!`fAfI{klucS8vfvOz&y!uu@jA2ME^=f_c)}D zi?r!mtCiAo;fnNkZxKl6MHSuykc34y-1J%=0+Q#Y>xyPer4rd^|RNl z=u{i*hkg;GhYti5(!6D77V%8#eJ%Z-m9KKundVzhbq9&)XBs}YDe|g#kah&ulHei| zvODrKjlY>j1b!^yvOVqPuy`Afm!3bQ9PGSQCeLrZw&efsXvWNzA7i6n;x@WtyyDsN z%5Qo@z*oRYc?yY`PxIv*kCN}4i-mp@*3Sn$ zp`5Y&dRe!nN$vuO=@T3uKeavhErE)I9@APTlsWPAf>Caf<|w|paG02VcAP3*iaNm0Mi9b_NrOV=B3BVA`JL#bkqr0~s|5 z1m)l8SIR}8qe->H$*MgWT_tg>xt}qfv+k{U*f+hUjuztHa<^-Neezw~ajz}kAC@&k_SUZ` zWdGR5;Z)E$zR~8BLP5;!Fk_!;_LC^|$>NilMd5!Ib;Ew1$SJ)rWEi|Se0gSSvO2$I zcuUiK;7x>D(fad!+lobU-p3f_{nuhS$H@V_rtM8d3zjUE!Cw!jRy%ARvNp#HW7>B+ zwp0Fplz3(m-5ysW!z`t!&EP^=?;|{_atj$tS zw$g5v!CB72b*Pb`Uu%dTkIH6mADB`e>PR2+sCp&ZA=D$_#0i zv&SZAo`ibikkS&+C`txaUgP_H)N6ygDNmpU&Q$T7+ZshZ{gmy1TqGU{>!34@n_C_y znwdsrmD2W%oXN{1IxTtvQ9ZWGB0^zcPImW2#_zR&FoWO34;xROkWssP zn-VtmjYH>Ei-?UukoPA!3goh75c-D+LcboO&eaMv;FDEESuf$Qk1a!Y`ZR==dH7@W4=lN_NFx23w}h=RdnA2h$j zx1)f6R%{blmHyHAWiPKUZMnAO-jIe`wV{E6R|u)MAG~0`9B_dBHw5k^Zh% z&3lD_hwy%&YjI+B2cOdne-`l&L|tGpCtaYdU*~S`!-K^-ieF-#^}9sSwT^t2-w0li zOj!ryt{pS0-rD?k;{rC7V6zx&2vO@t@;NNzT0DFAke55w zKu@~)SP;Q~ZvhoTQHNg9|K#X-K+y{em0nClZz{pvd}opFcBN@!Gv<0SzV&1X8NT_@ z-bY0W?D>u`Eb0Ea_4RVpQwq{1kaS!&7tW@AzP*0p<;A9&1i`&mL4B?o+Y))UpU&A8 z=8tO#EHJ6M8MOmxXbVMyJKH=2w9iD83 z!KVBa0lIG7>S`4ws8IorR<~Ln2~Vx|4m6&zrh98Tu*Mq2v|N#**CP4bIB|yx$L{r| zDA0CvOAd_WK^Hauik@IpBaCl1hvjBUa^VR+nxDjA(V#}={lK`cRxBV)1UobB*pb(L zc>-C@vU9|ygO^?MU8=peU$1D>X-jfN`izF`MISTPQs#;bFlzC`Lo+h}n{<`a%I0CX z;<5oxUlc_)RFeFOf5wSReL|>;D!D4+ZSXJ0n&df3$;~{);=kXd`S^&-ZtsDM%inlF zA$wsld#WY)u*lBtLJ95WGA-yXBr$vJPm%g(j-%{WV=9i(+c(<7()7;I2y|Lyj;w-6(Rd{6dDO|vglupASbINNWfysFERwZHg86>fGylV|I%)p0La z(`IT7?NUPTG%#}=GKYkQ*W&s|Ru>5LE$LPM(c#cAFxo0Aa6xH#!nhgf(Xa06D+m5@ZMbZS+G`QR-j&` z;yzO#br%EG;Ke;VAWILRi`FTG8tG>cuI`K25#L-4SX>wp9fs<4t$yb{f3B|dy89LN z-!UWtc*S10Uuj1{AT`uct~Fk>hPst01~H|a(Z1^U*g7W6u`x#dn}~aL?aJdIb?IDI zkeOqbg7hv@eI3%)R5RZ4k>0G#zas$v)8xNg zfM4QEAr%fw*G8khrcNbd$0ZYNZb#3Ov`>H2PgG-!9^Bb?)R@Hhu}K7f-PBo+sH3I`06a_cW!T9W^yeZi;qV=g2NFx=Aq?e4_Xm(=YDqQdq`dAxGJ`bW)0d~1oe)oQ z1?2Ii!F2lvhO0$AT{Tj3_NzzlkB4NApRfERfe1n%nLXHyTlKolo2#cbEqCV=>ZhZF zd){3))V0zMFAdq)y%+$HpD%^lpwI^Y;bS=?e<@{zPVGgJVu8f!q`@RI<@|zVQLK*98&3=!B`DG zH38gh$J@ikM)xedZ&-@k0y6^lbkV=yp(oyqOTQLiu%6R<$pN#YdEmaghKT3DnRH7{ zO<2j z^`+{5?oytjpBLsuq*4UagZrLmwCNR%^cNOv;!kL|5)FQZu*#(c&8?|mu&tOF_n zztgM+)%W2E`++y@79K|^nJE$7|Vt|d>!&sq(H9!oqY+&uJM&fiYhwd9$jE|)F3wCB|EyxJ*_ z!vE)u_P!r!es6UqovCU`qEvf_L45L{U-}CqS{`^>&}^UNawI_)4T9#H;k?OftBX-; zBn9>T6DU!AHPbveTb{;lv$HoYI*JO;+tXk2M?eN3wdLg*W|rgi1-TH;?KU};8h^KU z_a&e9+)zqHy#>+}A(yP_EUoB@KQDJQ*rNU*u4`Wg5Z%`c(grGA`OcK8|)bPeW za#a`Iq{>y;=}OW3SWI&^4C_jL-qNz=S;$so0@O%RSgze(1=COPYdqCjUiiNQb#t^Psht>-#cj-p2ms*LOeZ)2nDksO2%?B#pJJk^pYcktKN%3iLaSdca_} zoW;1%JD?=L^O8dxA5Y>p0k;wbc@URRd|Ga-^OUc3@2=4jHasECA{&R^*(`0L54m4E z&4rz>frbc*L^?-i$CMySO|)jRx{k!WiT0D+TpEo3b~bpm0MZqK=X1|Rp7Vo`v9i|o zN(i`rv<0e`YCM;ubwqXNUDg(=)oD^#Tyt?fpZLY;EtJenqM3a^@%@ovT>9T~@y0O? zx`h#ls_wwLk1&7StP`m;b$#|XAMmx;PULCjB^t5)d9t2=U;SOa=k=K5Q*32?(Bph{ z5e&?%g`AMJrj;$t&;Fq(Txqit^aAv361zr?M?od)(D-ZoM` zKwz9faJYB-9HwPfn5hY-OQYO3Fps}*aQ{V`(9zST4^!I9Q}?kj+7gzkH+ZXK;g zkRF}3AL{$P;PiO&KFGu)rR#d{$)#L4c#(#;vEKk`i*2lIB~}VN2&hrNOw+Pa`1Y~u zpkKwIKqZOGy9yWO-X(K-@@1yvp0~5+AhpV@RcQn{GE9 zjiciYW0WIfMh2;;uf-f=WKp<05P^i3yjWV}v*CiB8}*IC4PERCN-YCuGSqwe*6%_wpzv2~E9 z_4HXl^tjD=Q_ShXxw`t1VZ6aD4xr$En-09w>4;5^0rAf%l0m2c?^$R6<+*(UmLEY; YoE86&eshIS>34vFtg1};TND5P0huwZ0{{R3 literal 0 HcmV?d00001 diff --git a/electron-projects/ucap-webmessenger-electron/resources/image/lgRed/32_32.png b/electron-projects/ucap-webmessenger-electron/resources/image/lgRed/32_32.png new file mode 100644 index 0000000000000000000000000000000000000000..9f5fb1b0cb2395a97e5b008acdd66f59a27381dc GIT binary patch literal 1106 zcmV-Y1g-mtP)gV>}fM5~5q1QeqQJ{aFjh?+pG zKA`v@;)9yF#1F!QK_2{iK#dR$6_ZMU8bOQ8mTtS*U32f=kGXg6U8*noz)2=EcjnCb z|Iayd<_?$SzZP-uk7u7~L?r{<3)~9afG`he2gVUj06&QE1@L}$WcbgS_#{$;z8Hap zz&3;pKtkUkqG^fSPXRfE7qUMMAN%(N#G2m>Y)44?0K-U3|mWQJV z7`~Cg5C-e*u|>+)l&z;+eQVBH^R0XHL#Yj~J~|Tuv&3M#XhsP)7!4R`&V02c@wzc` zdPze9VsHE{MqqYhyD*|l8#^-eNxJmu{RG-G-?Zk#;(C9pa= z0*ZCIXQFwQT;7Gm6m5g6NZr)s>NG9wq;1`8NL-QZU5Lc|o)h!z#g2|P0UrRXq9U-= z-Dm_t=}4tY`kDKXgn`6`^wzad)vByP8r0ZXQ^Tx|w}4bV0mT(z8_@_9KKY7x=R8^m zZlR@b1&QvfxbWFwG#jumN4q zwMxje>jV}%1CN&Ca}gHTT`=0!YZ{!`cZm3c4n!FgatD0`t+9k&NR+RguM=plBOo!} z2A(wk1L7kglt#yF9f4|@b368W-(s*HtXPfzV4Jb_<1}HI(OHse_xqkLeS76?l0S%-dxN!ajtR;9Cm~xtk_sFtzA9Chu5DHQA(N zy$fsvE`$=$OUO3(2f`4-NDxqhM9f>dbr#b-T}=1(P`>=KV9q}V9tciG6Yv0k27VJE zqs^O_|Az?QBAhpiq;zBVsY2hK9f))SY5VcIOvA|$A-)vUo%&jT2C-hn*noo~e5!k& z`0*I@-=!m%r{QH_tNlHGQ39gXcp$v9?NU35a5usc9lUN_lM3)O@Qe8{1?JRgXiUA4 z(w<_q6W%0>3e-;6+??%`L>@dA*_%x4}@9lO;{)Ynm Y4cgJgoYP)bh=`f->n2tiZ?hKrt>d(A-^7LoHEs7j=Kl&hE^_d-G=I-8b*Od9w-q(Hyw*-rRfMz2EOW z=iGblr62_Nk!-0O_I`B6_3-A-LU!YYR)#tso zrBNG1#2aeS0IgRHJO{jhFhw6p>$_TnYOr*cA#4MF1KtBZMu-NH2qP*lZL!M32&WKs zpqOIUSw)mNQ;woCZDii3qWAzsv(=Y!e4F|{4^+XbqLmVacMjL8XB`M=xxwW8Q3 zik+eutbLaU0e=_LJE;;AILt~DbCL{r7e5_I|E@(C4=hFK$kHDu0m%$hY6hPjq(NZx z?|jy-tQLMoQG7UDMXu3wQep<(1ZS3(G8*fSw+k-cx1Jq+ivq0n`XXu2obOqFj^9?<_ zHtYt5{yPf@b5Y#wJ7adK*|dsrziwyrku8+Wo#=Flv~KB~I!1rrz}REkseWUnM#xRf z`12C#%%$bPQJ`kv1Qbhs{nk;+7EK4GP!%PNc>QTqX|X=bWyMs#wgNS@%+f1=WQN|J zaxVuNqv?!^;4Uk$p=>||L@_UvD;SwBj9Z3Lx#E6Jw{rP?R((cE1;>GcqO%uM+Eane z1G9j^c&C|tt&oyL!g{G(wSc0U3QNbRDz*M+O^!Pj+-`UEx>J1}So=hN1Vk}R18jKv zZAyB6JV7E7MU@R<=<|&MiVL}x zMQyzn==k&i@oSxypO4tQ4k;c){6-fYyT2jc6LE~10h@YiWNIqhaPG*P00uw%nOO5% z2LTi0ja~bbe~Xglx$%h$d{7M7&)C>o-Dt}5CLoGEegim3ISGg%bnQDz|Aos|mHF)6 zdpKi7=coZO&_B7>4d<#KPXBDwU@veHVI+ET=Xg4^I2%wR#+8@1Qd~Ed=&5rg;xR2& zz(L%j&-kzk`tKa=M)SKL%peOy#bA?HkU9Of_m>tDy-}jaPp2&v@98Gebdq>iS9GAc1t=p8;{oI+?*vgq5Md1`$(2blN0(>gSLcq``>V`Ok;Bz!SI%d8fPG z7_3HdB-BC1@NgMJ=$ka2jUSE=6U>ITSJv+_3FPBK;g-?tU3ap%gd^5@^-b$4 zJtSDK_SFCCt&Io5L(f4#+^Z0pMa*5t+}Mt}b;5!zNI?oxkb?a80{jDXMC0WWCL7eSb#r%2CM!`Ke_)+#EDXq|5)sh=!+=6j)EJ}2D2YOh zCPp10K?MmSAd{e|usNKODH_e3B<0Pe+gM8mc3bz-+xE8iUUSYp_x8L!=Q+2x-9Mgh za@yxU=XuWg{l34K=k1UnL4pJc5+q2F;Qth2Qu{#n9W!NS3Bm_}OMxuV4J<@x0}8-t zU>G3j7>6IcxH1pk~}tJeQ@&cMt{Wgtc7!2>dk0qc zW#*EAcp;!`RkrUo;Cnzu!k$>iZ|8Z(fiD3ME$iQ2s#!rp2$(%UT>rh-A`zYy;U^+w zVpunLsp&e~Q#YAszwZ{F`>?hZt^Ild`d?A4upm> z_PYjQ@4<`ux@MCAV-oauP`SJU__LGcgJgx21ECSKU#0Boe}Oep9O~)YIR5yZ2TZcZ zL2M6jtk8+@EW(mmwyvk_4+meHr8J~=H(mdu&a+EEIjcX0up(ycY89-Om8-uB>8~pn z3+I0iBwjE=7X_5j}odxAB)1UO249>s#QW!(Td8j{p_p7gH$6+ONRJOMP;LV$TH zEHoh8;vvM4+1s~WLHkV`QLXct95~AGHy>f5|8R9U^|Tjg%yzNx8=s_c`MDHF$2s}X zPEI|#yLvy+$5l?h(9h(SOF-GS{0Q(f;07Spq{-|7ie4-0*J9nwZJTZ7Ei`m5rt_Yg zQB4h@)|Gi_I`6rK#uew;4^}Pn$=r68$D2aKrQ$uOa-?1vbW3clL%tkG`B(&qaAnQ0 z+vs(ybk`!-Q3IQnA)*QPbXuI(W zQk`uc;ZyC+wB58xFPZ!KVefG+^~T%&-i=ODoCN}eY%Q5N@WfI%6xBIRWNy9MBY69^ zD^ab@;jdM$cj(?zA@c1LVUO4fEUTLUQ7ot>Gy4}`xjp}?mRqh~XD2+XtrZq6ADSM% zC;si5*+#j)@)9Hyd7W<8)lC43<~VVv3xTn{FEPIFb>~@%1$TbfZlC$|7Nns(Z0GwA zlH0S-+cDDpolIQ$UzpNK-&;Qc!ilgDh|0`3X0`WnAfwC%xAXJ+`G1Xzf|JH zeLFzBqt>+hyOgb#z1~1elTP{z>n1=HgJFp$$|L*)Ob!e(y7O<2kQ0(_BZp_R+0<5}I4?aU_Y{GM^G?C}zgU?J`*WE27 z)^+3;y0iUoBHaesuj4bdwR!iQj0s7_pWa8uN~&h6T7M*Q}SnW7nz=E zw_k>Iim3~i>%{Lj>1SRGD0@@#Hwf2)YnTg=A;J2}47uWG#R4Nge9YTU5#G;VuOWYO zO*f?`?fPf0Sv>AJ%)-+OVC0>3osXKHX{!yT1ok05-)-7C^HHFY6-jFEk{t4_=MJy~+sZq$kXd*G@I zRz)ufV$1`o*8Q9)-in)4y_xx;!Htq%X+4kX@|bw#5D%I&6#@-(BwaB2RJ{D^C z0KG+D1Xd%c*CfN2Ju6{KN|~% z!IZ}RccJ(oG94oR(`3)rCGKYgi8$hr@RY00xa(6N0nxGRF1Lj!VSUW`aRw{R(s=_D7OW1GfVIH7e|Xo`#gU zM*NSYbR$6dch)(Q{)JAFQ0vC1Ta}HR1tv(4AVGoz2@)hoFcSd(12+k9e}Bp0F#rGn M07*qoM6N<$g2<2*y8r+H literal 0 HcmV?d00001 diff --git a/electron-projects/ucap-webmessenger-electron/resources/image/lgRed/ico_64_64.png b/electron-projects/ucap-webmessenger-electron/resources/image/lgRed/ico_64_64.png new file mode 100644 index 0000000000000000000000000000000000000000..382110d2c880af8b7b035e90741f1aa7fc3bbd5b GIT binary patch literal 999 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstUx|vage(c z!@6@aFM%9|WRD45bDP46hOx7_4S6Fo+k-*%fF5)VDUk zC&cyt|Nqx-+__DCHT$kw_FZ+Dpi1^V?d*Fx+4pooQumFr?`mYu29L1UVAu7@+g+8)rW- z$$ns#{lGl?&zp~5_nrqj@9X~ae_nq8y6~ZO_9NTu`?)C zC%^7JeO)r)iC4~3ACNa6Wi~u^&i?c6(~G3WXAu?8A}d~|HU547>1jaDv%s8Z!8!M4 zEq&sd^VC1*Sx^onG@gg$Jk74TaryG|@SNw7Ilz#4T2OcM#Hr^IIWM9?q4Yet@WIr% zH!fa^;i_%~#;9dUkRLE5s87B5?f13G+RV*=|Fkkrnc`BkvExI+my74Vm)zjKm92DV zI-}6NDbv2SB-jgxxBv5*^2RD4utxrMi|G!aA&g1h?k)@+tg;?J4rhT!WHFEiu{{{v zPG;Ky8IL_(977~7Cno?wQd(ka^798N4GemDa^Bk6($Us&c6DxUZftCf2Iub`ym&HF zKt&-X`+3s)2Txu-d-w3ADvyW&qgUnciXXpzva_(Ub{iRWFfMg+I($gkNonz-Q6Z6@C81owQUkWfAw}?k;by)2FnxRLM1}z*ClCuq(8dplX9JtdY$S84vk->ecc~Ga>{X0PKs+PD$l%yn< zq*^5xr2;7iBLhQAT|+}%L!%Hwb1MTgD?LgiiX_$l+3hB+!|`T_S^$% OVDNPHb6Mw<&;$VU_R)d> literal 0 HcmV?d00001 diff --git a/electron-projects/ucap-webmessenger-electron/resources/image/lgRed/ico_64x64.ico b/electron-projects/ucap-webmessenger-electron/resources/image/lgRed/ico_64x64.ico new file mode 100644 index 0000000000000000000000000000000000000000..8af2432e478284624daf3f5c329f97f22b95e28f GIT binary patch literal 109473 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 WcXsnC&Hye3WAJqKb6Mw<&;$Tz-HmGi literal 0 HcmV?d00001 diff --git a/electron-projects/ucap-webmessenger-electron/resources/image/lgRed/ico_64x64_r.png b/electron-projects/ucap-webmessenger-electron/resources/image/lgRed/ico_64x64_r.png new file mode 100644 index 0000000000000000000000000000000000000000..84b33e4a79f2a4c29230f4b660b5d8d6c488c99d GIT binary patch literal 2082 zcmV+-2;KLIP)C0WWCL7eSb#r%2CM!`Ke_)+#EDXq|5)sh=!+=6j)EJ}2D2YOh zCPp10K?MmSAd{e|usNKODH_e3B<0Pe+gM8mc3bz-+xE8iUUSYp_x8L!=Q+2x-9Mgh za@yxU=XuWg{l34K=k1UnL4pJc5+q2F;Qth2Qu{#n9W!NS3Bm_}OMxuV4J<@x0}8-t zU>G3j7>6IcxH1pk~}tJeQ@&cMt{Wgtc7!2>dk0qc zW#*EAcp;!`RkrUo;Cnzu!k$>iZ|8Z(fiD3ME$iQ2s#!rp2$(%UT>rh-A`zYy;U^+w zVpunLsp&e~Q#YAszwZ{F`>?hZt^Ild`d?A4upm> z_PYjQ@4<`ux@MCAV-oauP`SJU__LGcgJgx21ECSKU#0Boe}Oep9O~)YIR5yZ2TZcZ zL2M6jtk8+@EW(mmwyvk_4+meHr8J~=H(mdu&a+EEIjcX0up(ycY89-Om8-uB>8~pn z3+I0iBwjE=7X_5j}odxAB)1UO249>s#QW!(Td8j{p_p7gH$6+ONRJOMP;LV$TH zEHoh8;vvM4+1s~WLHkV`QLXct95~AGHy>f5|8R9U^|Tjg%yzNx8=s_c`MDHF$2s}X zPEI|#yLvy+$5l?h(9h(SOF-GS{0Q(f;07Spq{-|7ie4-0*J9nwZJTZ7Ei`m5rt_Yg zQB4h@)|Gi_I`6rK#uew;4^}Pn$=r68$D2aKrQ$uOa-?1vbW3clL%tkG`B(&qaAnQ0 z+vs(ybk`!-Q3IQnA)*QPbXuI(W zQk`uc;ZyC+wB58xFPZ!KVefG+^~T%&-i=ODoCN}eY%Q5N@WfI%6xBIRWNy9MBY69^ zD^ab@;jdM$cj(?zA@c1LVUO4fEUTLUQ7ot>Gy4}`xjp}?mRqh~XD2+XtrZq6ADSM% zC;si5*+#j)@)9Hyd7W<8)lC43<~VVv3xTn{FEPIFb>~@%1$TbfZlC$|7Nns(Z0GwA zlH0S-+cDDpolIQ$UzpNK-&;Qc!ilgDh|0`3X0`WnAfwC%xAXJ+`G1Xzf|JH zeLFzBqt>+hyOgb#z1~1elTP{z>n1=HgJFp$$|L*)Ob!e(y7O<2kQ0(_BZp_R+0<5}I4?aU_Y{GM^G?C}zgU?J`*WE27 z)^+3;y0iUoBHaesuj4bdwR!iQj0s7_pWa8uN~&h6T7M*Q}SnW7nz=E zw_k>Iim3~i>%{Lj>1SRGD0@@#Hwf2)YnTg=A;J2}47uWG#R4Nge9YTU5#G;VuOWYO zO*f?`?fPf0Sv>AJ%)-+OVC0>3osXKHX{!yT1ok05-)-7C^HHFY6-jFEk{t4_=MJy~+sZq$kXd*G@I zRz)ufV$1`o*8Q9)-in)4y_xx;!Htq%X+4kX@|bw#5D%I&6#@-(BwaB2RJ{D^C z0KG+D1Xd%c*CfN2Ju6{KN|~% z!IZ}RccJ(oG94oR(`3)rCGKYgi8$hr@RY00xa(6N0nxGRF1Lj!VSUW`aRw{R(s=_D7OW1GfVIH7e|Xo`#gU zM*NSYbR$6dch)(Q{)JAFQ0vC1Ta}HR1tv(4AVGoz2@)hoFcSd(12+k9e}Bp0F#rGn M07*qoM6N<$g2<2*y8r+H literal 0 HcmV?d00001 diff --git a/main/resources/installer/woori.icns b/electron-projects/ucap-webmessenger-electron/resources/installer/woori.icns similarity index 100% rename from main/resources/installer/woori.icns rename to electron-projects/ucap-webmessenger-electron/resources/installer/woori.icns diff --git a/main/resources/installer/woori.ico b/electron-projects/ucap-webmessenger-electron/resources/installer/woori.ico similarity index 100% rename from main/resources/installer/woori.ico rename to electron-projects/ucap-webmessenger-electron/resources/installer/woori.ico diff --git a/main/resources/installer/woori_256x256.ico b/electron-projects/ucap-webmessenger-electron/resources/installer/woori_256x256.ico similarity index 100% rename from main/resources/installer/woori_256x256.ico rename to electron-projects/ucap-webmessenger-electron/resources/installer/woori_256x256.ico diff --git a/main/resources/installer/woori_256x256.png b/electron-projects/ucap-webmessenger-electron/resources/installer/woori_256x256.png similarity index 100% rename from main/resources/installer/woori_256x256.png rename to electron-projects/ucap-webmessenger-electron/resources/installer/woori_256x256.png diff --git a/main/resources/linuxicon/256x256.png b/electron-projects/ucap-webmessenger-electron/resources/linuxicon/256x256.png similarity index 100% rename from main/resources/linuxicon/256x256.png rename to electron-projects/ucap-webmessenger-electron/resources/linuxicon/256x256.png diff --git a/main/resources/notification/image/btn_call_message.png b/electron-projects/ucap-webmessenger-electron/resources/notification/image/btn_call_message.png similarity index 100% rename from main/resources/notification/image/btn_call_message.png rename to electron-projects/ucap-webmessenger-electron/resources/notification/image/btn_call_message.png diff --git a/main/resources/notification/image/btn_call_receive.png b/electron-projects/ucap-webmessenger-electron/resources/notification/image/btn_call_receive.png similarity index 100% rename from main/resources/notification/image/btn_call_receive.png rename to electron-projects/ucap-webmessenger-electron/resources/notification/image/btn_call_receive.png diff --git a/main/resources/notification/image/btn_call_refuse.png b/electron-projects/ucap-webmessenger-electron/resources/notification/image/btn_call_refuse.png similarity index 100% rename from main/resources/notification/image/btn_call_refuse.png rename to electron-projects/ucap-webmessenger-electron/resources/notification/image/btn_call_refuse.png diff --git a/main/resources/notification/image/btn_call_transfer.png b/electron-projects/ucap-webmessenger-electron/resources/notification/image/btn_call_transfer.png similarity index 100% rename from main/resources/notification/image/btn_call_transfer.png rename to electron-projects/ucap-webmessenger-electron/resources/notification/image/btn_call_transfer.png diff --git a/main/resources/notification/image/btn_close.png b/electron-projects/ucap-webmessenger-electron/resources/notification/image/btn_close.png similarity index 100% rename from main/resources/notification/image/btn_close.png rename to electron-projects/ucap-webmessenger-electron/resources/notification/image/btn_close.png diff --git a/main/resources/notification/image/btn_close_gray.png b/electron-projects/ucap-webmessenger-electron/resources/notification/image/btn_close_gray.png similarity index 100% rename from main/resources/notification/image/btn_close_gray.png rename to electron-projects/ucap-webmessenger-electron/resources/notification/image/btn_close_gray.png diff --git a/main/resources/notification/image/btn_noti_call.png b/electron-projects/ucap-webmessenger-electron/resources/notification/image/btn_noti_call.png similarity index 100% rename from main/resources/notification/image/btn_noti_call.png rename to electron-projects/ucap-webmessenger-electron/resources/notification/image/btn_noti_call.png diff --git a/main/resources/notification/image/img_nophoto_50.png b/electron-projects/ucap-webmessenger-electron/resources/notification/image/img_nophoto_50.png similarity index 100% rename from main/resources/notification/image/img_nophoto_50.png rename to electron-projects/ucap-webmessenger-electron/resources/notification/image/img_nophoto_50.png diff --git a/main/resources/notification/preload.js b/electron-projects/ucap-webmessenger-electron/resources/notification/preload.js similarity index 100% rename from main/resources/notification/preload.js rename to electron-projects/ucap-webmessenger-electron/resources/notification/preload.js diff --git a/main/resources/notification/sound/messageAlarm.mp3 b/electron-projects/ucap-webmessenger-electron/resources/notification/sound/messageAlarm.mp3 similarity index 100% rename from main/resources/notification/sound/messageAlarm.mp3 rename to electron-projects/ucap-webmessenger-electron/resources/notification/sound/messageAlarm.mp3 diff --git a/main/resources/notification/styles/noti_messege.css b/electron-projects/ucap-webmessenger-electron/resources/notification/styles/noti_messege.css similarity index 100% rename from main/resources/notification/styles/noti_messege.css rename to electron-projects/ucap-webmessenger-electron/resources/notification/styles/noti_messege.css diff --git a/main/resources/notification/template.html b/electron-projects/ucap-webmessenger-electron/resources/notification/template.html similarity index 100% rename from main/resources/notification/template.html rename to electron-projects/ucap-webmessenger-electron/resources/notification/template.html diff --git a/main/src/app/AppWindow.ts b/electron-projects/ucap-webmessenger-electron/src/app/AppWindow.ts similarity index 89% rename from main/src/app/AppWindow.ts rename to electron-projects/ucap-webmessenger-electron/src/app/AppWindow.ts index e39874c..d109d19 100644 --- a/main/src/app/AppWindow.ts +++ b/electron-projects/ucap-webmessenger-electron/src/app/AppWindow.ts @@ -26,7 +26,7 @@ export class AppWindow { private minWidth = 960; private minHeight = 660; - public constructor() { + public constructor(private appIconPath: string) { const savedWindowState = windowStateKeeper({ defaultWidth: this.minWidth, defaultHeight: this.minHeight @@ -52,7 +52,7 @@ export class AppWindow { nodeIntegration: true }, acceptFirstMouse: true, - icon: path.join(__dirname, 'resources/image', 'ico_64_64.png') + icon: this.appIconPath }; if (__DARWIN__) { @@ -60,7 +60,6 @@ export class AppWindow { } else if (__WIN32__) { windowOptions.frame = false; } else if (__LINUX__) { - windowOptions.icon = path.join(__dirname, 'static', 'icon-logo.png'); } this.window = new BrowserWindow(windowOptions); @@ -85,6 +84,19 @@ export class AppWindow { e.preventDefault(); } }); + } else if (__WIN32__) { + this.window.on(ElectronBrowserWindowChannel.Minimize, e => { + if (!quitting) { + e.preventDefault(); + this.window.hide(); + } + }); + this.window.on(ElectronBrowserWindowChannel.Close, e => { + if (!quitting) { + e.preventDefault(); + this.window.hide(); + } + }); } if (__WIN32__) { @@ -200,6 +212,10 @@ export class AppWindow { this.window.show(); } + public hide() { + this.window.hide(); + } + /** * Get the time (in milliseconds) spent loading the page. * diff --git a/main/src/crash/CrashWindow.ts b/electron-projects/ucap-webmessenger-electron/src/crash/CrashWindow.ts similarity index 100% rename from main/src/crash/CrashWindow.ts rename to electron-projects/ucap-webmessenger-electron/src/crash/CrashWindow.ts diff --git a/main/src/crash/show-uncaught-exception.ts b/electron-projects/ucap-webmessenger-electron/src/crash/show-uncaught-exception.ts similarity index 100% rename from main/src/crash/show-uncaught-exception.ts rename to electron-projects/ucap-webmessenger-electron/src/crash/show-uncaught-exception.ts diff --git a/main/src/global.d.ts b/electron-projects/ucap-webmessenger-electron/src/global.d.ts similarity index 100% rename from main/src/global.d.ts rename to electron-projects/ucap-webmessenger-electron/src/global.d.ts diff --git a/main/src/index.ts b/electron-projects/ucap-webmessenger-electron/src/index.ts similarity index 77% rename from main/src/index.ts rename to electron-projects/ucap-webmessenger-electron/src/index.ts index ed9548b..1304c60 100644 --- a/main/src/index.ts +++ b/electron-projects/ucap-webmessenger-electron/src/index.ts @@ -1,4 +1,12 @@ -import { app, ipcMain, IpcMainEvent, remote } from 'electron'; +import { + app, + ipcMain, + IpcMainEvent, + remote, + Tray, + Menu, + dialog +} from 'electron'; import * as path from 'path'; import * as url from 'url'; import * as fse from 'fs-extra'; @@ -12,7 +20,9 @@ import { UpdaterChannel, FileChannel, IdleStateChannel, - NotificationChannel + NotificationChannel, + ChatChannel, + MessengerChannel } from '@ucap-webmessenger/native-electron'; import { ElectronNotificationService } from '@ucap-webmessenger/electron-notification'; @@ -24,7 +34,12 @@ import { IdleChecker } from './lib/idle-checker'; import { NotificationRequest } from '@ucap-webmessenger/native'; import { ElectronAppChannel } from '@ucap-webmessenger/electron-core'; +const appIconPath = __LINUX__ + ? path.join(__dirname, 'static', 'icon-logo.png') + : path.join(__dirname, 'resources/image', 'ico_64_64.png'); + let appWindow: AppWindow | null = null; +let appTray: Tray | null = null; const launchTime = now(); let readyTime: number | null = null; @@ -90,7 +105,7 @@ if (isDuplicateInstance) { } function createWindow() { - const window = new AppWindow(); + const window = new AppWindow(appIconPath); if (__DEV__) { // const { @@ -142,6 +157,48 @@ function createWindow() { appWindow = window; } +function createTray() { + appTray = new Tray(appIconPath); + + const contextMenu = Menu.buildFromTemplate([ + { + label: '로그아웃', + // accelerator: 'Q', + // selector: 'terminate:', + click: () => { + appWindow.show(); + appWindow.browserWindow.webContents.send(MessengerChannel.Logout); + } + }, + { + label: '설정', + // accelerator: 'Q', + // selector: 'terminate:', + click: () => { + appWindow.show(); + appWindow.browserWindow.webContents.send(MessengerChannel.ShowSetting); + } + }, + { label: '버전', submenu: [{ label: 'Ver. ' + app.getVersion() }] }, + { + label: '종료', + // accelerator: 'Q', + // selector: 'terminate:', + click: () => { + // 메신저에 로그아웃 후 종료 + appWindow = null; + app.exit(); + } + } + ]); + appTray.setToolTip('UCapMessenger'); + appTray.setContextMenu(contextMenu); + + appTray.on('click', () => { + appWindow.isVisible() ? appWindow.hide() : appWindow.show(); + }); +} + // This method will be called when Electron has finished // initialization and is ready to create browser windows. // Some APIs can only be used after this event occurs. @@ -154,10 +211,12 @@ app.on(ElectronAppChannel.Ready, () => { createWindow(); + createTray(); + notificationService = new ElectronNotificationService({ width: 340, height: 100, - padding: 10, + padding: 0, borderRadius: 0, // appIcon: iconPath, displayTime: 5000, @@ -240,8 +299,9 @@ ipcMain.on( try { const buffer: Buffer = args[0]; const fileName: string = args[1]; + const mimeType: string = args[2]; let savePath: string = path.join( - !!args[2] ? args[2] : DefaultFolder.downloads(), + !!args[3] ? args[3] : DefaultFolder.downloads(), fileName ); savePath = await FileUtil.uniqueFileName(savePath); @@ -289,12 +349,15 @@ ipcMain.on( 'resources/notification/sound/messageAlarm.mp3' ) : '', - onClick: () => { - console.log('onClick'); + onClick: e => { + appWindow.browserWindow.webContents.send( + ChatChannel.OpenRoom, + noti.roomSeq + ); + appWindow.show(); + e.close(); } }); - - console.log('Channel.notify', noti); } ); diff --git a/main/src/lib/default-folder.ts b/electron-projects/ucap-webmessenger-electron/src/lib/default-folder.ts similarity index 100% rename from main/src/lib/default-folder.ts rename to electron-projects/ucap-webmessenger-electron/src/lib/default-folder.ts diff --git a/main/src/lib/file-util.ts b/electron-projects/ucap-webmessenger-electron/src/lib/file-util.ts similarity index 100% rename from main/src/lib/file-util.ts rename to electron-projects/ucap-webmessenger-electron/src/lib/file-util.ts diff --git a/main/src/lib/idle-checker.ts b/electron-projects/ucap-webmessenger-electron/src/lib/idle-checker.ts similarity index 89% rename from main/src/lib/idle-checker.ts rename to electron-projects/ucap-webmessenger-electron/src/lib/idle-checker.ts index fe8761c..98bad21 100644 --- a/main/src/lib/idle-checker.ts +++ b/electron-projects/ucap-webmessenger-electron/src/lib/idle-checker.ts @@ -51,11 +51,9 @@ export class IdleChecker { // global.opt_idleTimeLimit = limitedMin; this.startChecker(); - console.log('RESET IDLE TIMER in ' + limitedMin + 'm'); } public startChecker() { - console.log('Idle Checker Start'); if (!this.intervalObject) { this.intervalObject = setInterval(() => { this.doCheckIdle(); @@ -64,7 +62,6 @@ export class IdleChecker { } public destoryChecker() { - console.log('Idle Checker Destory'); if (!!this.intervalObject) { clearInterval(this.intervalObject); } diff --git a/main/src/lib/storage.ts b/electron-projects/ucap-webmessenger-electron/src/lib/storage.ts similarity index 100% rename from main/src/lib/storage.ts rename to electron-projects/ucap-webmessenger-electron/src/lib/storage.ts diff --git a/main/src/lib/window-state.ts b/electron-projects/ucap-webmessenger-electron/src/lib/window-state.ts similarity index 100% rename from main/src/lib/window-state.ts rename to electron-projects/ucap-webmessenger-electron/src/lib/window-state.ts diff --git a/main/src/util/now.ts b/electron-projects/ucap-webmessenger-electron/src/util/now.ts similarity index 100% rename from main/src/util/now.ts rename to electron-projects/ucap-webmessenger-electron/src/util/now.ts diff --git a/main/src/util/root.ts b/electron-projects/ucap-webmessenger-electron/src/util/root.ts similarity index 100% rename from main/src/util/root.ts rename to electron-projects/ucap-webmessenger-electron/src/util/root.ts diff --git a/main/tsconfig.main.json b/electron-projects/ucap-webmessenger-electron/tsconfig.electron.json similarity index 50% rename from main/tsconfig.main.json rename to electron-projects/ucap-webmessenger-electron/tsconfig.electron.json index 39a7730..d01f9dc 100644 --- a/main/tsconfig.main.json +++ b/electron-projects/ucap-webmessenger-electron/tsconfig.electron.json @@ -1,7 +1,7 @@ { "compilerOptions": { "baseUrl": "./", - "outDir": "../dist/main", + "outDir": "../../dist/main", "sourceMap": true, "declaration": false, "module": "commonjs", @@ -14,19 +14,24 @@ "lib": ["es2017", "es2016", "es2015", "dom"], "paths": { "@ucap-webmessenger/electron-core": [ - "../projects/ucap-webmessenger-electron-core/src/public-api" + "../ucap-webmessenger-electron-core/src/public-api" ], "@ucap-webmessenger/electron-notification": [ - "../projects/ucap-webmessenger-electron-notification/src/public-api" + "../ucap-webmessenger-electron-notification/src/public-api" + ], + "@ucap-webmessenger/core": [ + "../../projects/ucap-webmessenger-core/src/public-api" ], "@ucap-webmessenger/native": [ - "../projects/ucap-webmessenger-native/src/public-api" + "../../projects/ucap-webmessenger-native/src/public-api" ], "@ucap-webmessenger/native-electron": [ - "../projects/ucap-webmessenger-native-electron/src/public-api" + "../../projects/ucap-webmessenger-native-electron/src/public-api" + ], + "@ucap-webmessenger/electron": [ + "../../projects/ucap-webmessenger-electron/src/public-api" ] } }, - - "exclude": ["../node_modules", "**/*.spec.ts"] + "exclude": ["../../node_modules", "**/*.spec.ts"] } diff --git a/projects/ucap-webmessenger-electron-notification/tsconfig.spec.json b/electron-projects/ucap-webmessenger-electron/tsconfig.spec.json similarity index 100% rename from projects/ucap-webmessenger-electron-notification/tsconfig.spec.json rename to electron-projects/ucap-webmessenger-electron/tsconfig.spec.json diff --git a/electron-projects/ucap-webmessenger-electron/tslint.json b/electron-projects/ucap-webmessenger-electron/tslint.json new file mode 100644 index 0000000..37d97a1 --- /dev/null +++ b/electron-projects/ucap-webmessenger-electron/tslint.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tslint.json", + "rules": { + "directive-selector": [true, "attribute", "ucapElectron", "camelCase"], + "component-selector": [true, "element", "ucap-electron", "kebab-case"] + } +} diff --git a/main/resources/image/128_128.png b/main/resources/image/128_128.png deleted file mode 100644 index 197dd6fd0641e12e46b108ba6737adb324b3c417..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3840 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%0Ag$SRYDjnsn|G zW;NJX|1+Vf9h}w0ATh)R(o~owfUsBv=n;SlnlSsZw6IxT2UY76{+KXx5RD8`L54>F zsvSZk2fDI4#6djbmV@;24#QKv0}py5J34rO%AEN!$i=)EY5gDYND~MentU;#0WCzf?9Y;c3tg;HK*t}p8O=Nss^iF1I*j%k? zhiO)P`}(@*2%`_F3BgQY;dR32b1vXSg{EGaS_>>ILNbU!Qdk6#K@5_@B7h8HkQ5dH zWDtX-um~W77$k*702#y}DJ%leU&mw+hg4`H0nA@U={d+H0d~K@0_?Wd{BL-h41)Q; zL7X#%f1jTL;9dF|A1$=>P|~!pNlVq_`b+@$2R9Mwkj=NK*8l(j07*qoM6N<$f?S)U AX8-^I diff --git a/main/resources/image/ico_64x64.ico b/main/resources/image/ico_64x64.ico deleted file mode 100644 index d62de46c2302e77036aa78dcf5e0ab010ad689b1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 119658 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 G$o~U|r++&D diff --git a/main/resources/image/ico_64x64.png b/main/resources/image/ico_64x64.png deleted file mode 100644 index adbc1dd4f3a8d392d5bef410f26d22b5c9bbc9ff..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3170 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 IM6N<$g0NBY1^@s6 diff --git a/main/resources/image/ico_64x64_r.ico b/main/resources/image/ico_64x64_r.ico deleted file mode 100644 index 94ece2ba84eeba718b699cd599e0a518443e73e3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 107599 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 diff --git a/main/resources/image/ico_64x64_r.png b/main/resources/image/ico_64x64_r.png deleted file mode 100644 index caa67f2c45c7ca5566a0a69ed0385b34c88a77cc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1966 zcmV;f2T}NmP)Sd}2neKY^hpYIGa(_;S*&PgW!{r#SE&%5u<%ZycL zXD2jB1+W%CA^;ckOf&(=1+WXi31Y|zuob|@9!lB}wgK2g0!#;xDbE<6J7$mo=KzfF z;Y1DLJPDuz;407fo;w;zfKG{nkQCN0Fn0!v05XU{Qdk6#K@5_@B7h8HkQ5dHWDtX- zum~W77$k*702#y}DQr!7xyL(4**53>WW)u!W95N79Ll|eEvd0sH>n?vmRi5?@^? z!`lb1u={*n?NQhG7zZ!iLWG|SHcyXX?UpAU$huj_zdt&_4W|l~tQ{Zh*=^(rW_N*) zjutTTgwon3+`w5H@QDMPA(x>TUp`iZ8b?d z8*7|d_Y%T=)r-^QlDiG9tUaW!gbw&Lvyg351_rQgebW=9T_|JirAZ20D5+&_vUW@# zH`XSX5Xj-m|&Cw3C6N2!!hHRvtdWn&0Mz*FekBjo<$&$SZwT+KiP2tXSL?=Z1>AaT{ z#=*`8F@4_&{9K2#_GMIVa43KzG<`-qBq!s|EynIbrqq2-c>`e5<~NwU;D? zvhC;yUwnJ%HgiJ2hU2X7LR0Xl3yhBNVI)r$RHOI$WBmC&^M{~D^%xB;?QB>SXPFbG zy9*$}BnP-V+Of~JS#c~t!k{1zc5G;<7ltd;m3-M41>PDL4rhB?MlQBFtMS%c|CPzH8Ckpu1sN7MDQ`lENZ@ z3}TQJFT4OohruJ+H!<%(zDW%z}dmh7&Qi?0!&T}<=Ynv?*SSC`nlO-?$~|??-OR9GQwp?`LJ0>fUzw zI55nYzgC!45~?d)t9-zpJ^Pmu%YG>2_sNZ2CBzWS8U+e&X@jK<7{ZR9iup4OlA{nO z_;MqB21Hp+R2Y6H;Y_Jk0M1d+2={Nu6U-9e_cCn&R2%exvd{Ht%0Ag$SRYDjnsn|G zW;NJX|1+Vf9h}w0ATh)R(o~owfUsBv=n;SlnlSsZw6IxT2UY76{+KXx5RD8`L54>F zsvSZk2fDI4#6djbmV@;24#QKv0}py5J34rO%AEN!$i=)EY5gDYND~MentU;#0WCzf?9Y;c3tg;HK*t}p8O=Nss^iF1I*j%k? zhiO)P`}(@*2%`_F3BgQY;dR32b1vXSg{EGaS_>>ILNbU!Qdk6#K@5_@B7h8HkQ5dH zWDtX-um~W77$k*702#y}DJ%leU&mw+hg4`H0nA@U={d+H0d~K@0_?Wd{BL-h41)Q; zL7X#%f1jTL;9dF|A1$=>P|~!pNlVq_`b+@$2R9Mwkj=NK*8l(j07*qoM6N<$f?S)U AX8-^I diff --git a/package-lock.json b/package-lock.json index 53a0907..5152686 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1127,7 +1127,6 @@ "version": "8.2.12", "resolved": "https://registry.npmjs.org/@angular/core/-/core-8.2.12.tgz", "integrity": "sha512-wEFwhHCuuXynXAMeA1G+0KIYY0jqXYs7I8p+GO+ufKoUmzWHFTvtMJ6nvKgy+LmZTByO2gf9oVAAlRodNb8ttQ==", - "dev": true, "requires": { "tslib": "^1.9.0" } @@ -2095,6 +2094,22 @@ "ajv-keywords": "^3.1.0" } }, + "@electron/get": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@electron/get/-/get-1.7.0.tgz", + "integrity": "sha512-Xzo+xLQ+gwmGywFnFuG7HNIALPVJOCkvKagGxSXU1LC3s/j3h2Nku9OdwJ4KDkITeUuXfvAO5KS8rLGcmAunNQ==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "env-paths": "^2.2.0", + "fs-extra": "^8.1.0", + "global-agent": "^2.0.2", + "global-tunnel-ng": "^2.7.1", + "got": "^9.6.0", + "sanitize-filename": "^1.6.2", + "sumchecker": "^3.0.0" + } + }, "@hapi/address": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/@hapi/address/-/address-2.1.1.tgz", @@ -2789,6 +2804,15 @@ "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", "dev": true }, + "angular-split": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/angular-split/-/angular-split-3.0.2.tgz", + "integrity": "sha512-km59k1kEgVlplo2t4t5Ob43Vx16qVXWXsl5gbsdQtqrOW7341So4CFUmCjcZgfk1swu9RBaCdSQEqzNWOe/89w==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, "ansi-align": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.0.tgz", @@ -2985,12 +3009,6 @@ "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", "dev": true }, - "array-find-index": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", - "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", - "dev": true - }, "array-flatten": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", @@ -3131,6 +3149,15 @@ "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", "dev": true }, + "autolinker": { + "version": "3.11.1", + "resolved": "https://registry.npmjs.org/autolinker/-/autolinker-3.11.1.tgz", + "integrity": "sha512-6sAmetStorjXvwmV8MBxI5DGICHKD1B5EjdkIrq34X6YBDN6jj54EUHnoHgNqmNCclcf8c409zuVMNy449u80g==", + "dev": true, + "requires": { + "tslib": "^1.9.3" + } + }, "autoprefixer": { "version": "9.6.1", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.6.1.tgz", @@ -3619,6 +3646,13 @@ "multicast-dns-service-types": "^1.1.0" } }, + "boolean": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/boolean/-/boolean-2.0.3.tgz", + "integrity": "sha512-iHzXeFCXWrpjYE7DToXGCBPGZf0eVISqzL+4sgrOSYEKXnb59WHPFvGTTyCj6zJ/MuuLAxEn8zPkrTHHzlt3IA==", + "dev": true, + "optional": true + }, "boxen": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/boxen/-/boxen-3.2.0.tgz", @@ -3939,6 +3973,17 @@ "ssri": "^6.0.1", "unique-filename": "^1.1.1", "y18n": "^4.0.0" + }, + "dependencies": { + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } } }, "cache-base": { @@ -4038,24 +4083,6 @@ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true }, - "camelcase-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", - "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", - "dev": true, - "requires": { - "camelcase": "^2.0.0", - "map-obj": "^1.0.0" - }, - "dependencies": { - "camelcase": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", - "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", - "dev": true - } - } - }, "caniuse-lite": { "version": "1.0.30000989", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000989.tgz", @@ -4184,6 +4211,12 @@ } } }, + "classlist.js": { + "version": "1.1.20150312", + "resolved": "https://registry.npmjs.org/classlist.js/-/classlist.js-1.1.20150312.tgz", + "integrity": "sha1-HXCEL3Ai8I2awIbOaeWyUPLFd4k=", + "dev": true + }, "clean-css": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.1.tgz", @@ -4499,24 +4532,27 @@ } }, "conf": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/conf/-/conf-5.0.0.tgz", - "integrity": "sha512-lRNyt+iRD4plYaOSVTxu1zPWpaH0EOxgFIR1l3mpC/DGZ7XzhoGFMKmbl54LAgXcSu6knqWgOwdINkqm58N85A==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/conf/-/conf-6.2.0.tgz", + "integrity": "sha512-fvl40R6YemHrFsNiyP7TD0tzOe3pQD2dfT2s20WvCaq57A1oV+RImbhn2Y4sQGDz1lB0wNSb7dPcPIvQB69YNA==", "dev": true, "requires": { - "ajv": "^6.10.0", + "ajv": "^6.10.2", + "debounce-fn": "^3.0.1", "dot-prop": "^5.0.0", "env-paths": "^2.2.0", - "json-schema-typed": "^7.0.0", + "json-schema-typed": "^7.0.1", "make-dir": "^3.0.0", + "onetime": "^5.1.0", "pkg-up": "^3.0.1", + "semver": "^6.2.0", "write-file-atomic": "^3.0.0" }, "dependencies": { "dot-prop": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.1.1.tgz", - "integrity": "sha512-QCHI6Lkf+9fJMpwfAFsTvbiSh6ujoPmhCLiDvD/n4dGtLvHfhuBwPdN6z2x4YSOwwtTcLoO/LP70xELWGF/JVA==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.2.0.tgz", + "integrity": "sha512-uEUyaDKoSQ1M4Oq8l45hSE26SnTxL6snNnqvK/VWx5wJhmff5z0FUVJDKDanor/6w3kzE3i7XZOk+7wC0EXr1A==", "dev": true, "requires": { "is-obj": "^2.0.0" @@ -4551,6 +4587,17 @@ } } }, + "config-chain": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.12.tgz", + "integrity": "sha512-a1eOIcu8+7lUInge4Rpf/n4Krkf3Dd9lqhljRzII1/Zno/kRtUWnznPO3jOKBmTEktkt3fkxisUcivoj0ebzoA==", + "dev": true, + "optional": true, + "requires": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" + } + }, "configstore": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/configstore/-/configstore-4.0.0.tgz", @@ -4680,6 +4727,17 @@ "mkdirp": "^0.5.1", "rimraf": "^2.5.4", "run-queue": "^1.0.0" + }, + "dependencies": { + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } } }, "copy-descriptor": { @@ -4728,6 +4786,17 @@ "ssri": "^6.0.1", "unique-filename": "^1.1.1", "y18n": "^4.0.0" + }, + "dependencies": { + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } } }, "find-cache-dir": { @@ -4987,15 +5056,6 @@ "integrity": "sha1-QICG1AlVDCYxFVYZ6fp7ytw7mRs=", "dev": true }, - "currently-unhandled": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", - "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", - "dev": true, - "requires": { - "array-find-index": "^1.0.1" - } - }, "custom-event": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz", @@ -5051,6 +5111,15 @@ "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=", "dev": true }, + "debounce-fn": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/debounce-fn/-/debounce-fn-3.0.1.tgz", + "integrity": "sha512-aBoJh5AhpqlRoHZjHmOzZlRx+wz2xVwGL9rjs+Kj0EWUrL4/h4K7OD176thl2Tdoqui/AaA4xhHrNArGLAaI3Q==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, "debug": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", @@ -5231,6 +5300,15 @@ "dev": true } } + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } } } }, @@ -5496,14 +5574,22 @@ "dev": true }, "electron": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/electron/-/electron-6.1.2.tgz", - "integrity": "sha512-zVste1obJC645RrU4PP+CqU8Yq33h8fzS7zx3tWbiNnyRzp6m7O2bpgKLJBRZ/4BPRsNCqSSXm4vimyGPUXVaw==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/electron/-/electron-7.1.1.tgz", + "integrity": "sha512-NJPv4SuMJlRUtXBd/Ey9XKSLOZ4+hxsOrHHPXwrBQNNdeZesoSrTMgPymee/FwMRtrSt0Pz8NccEZUu/pxmbhQ==", "dev": true, "requires": { - "@types/node": "^10.12.18", - "electron-download": "^4.1.0", + "@electron/get": "^1.0.1", + "@types/node": "^12.0.12", "extract-zip": "^1.0.3" + }, + "dependencies": { + "@types/node": { + "version": "12.12.8", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.8.tgz", + "integrity": "sha512-XLla8N+iyfjvsa0KKV+BP/iGSoTmwxsu5Ci5sM33z9TjohF72DEz95iNvD6pPmemvbQgxAv/909G73gUn8QR7w==", + "dev": true + } } }, "electron-builder": { @@ -5656,55 +5742,13 @@ "semver": "^5.3.0" }, "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } - } - }, - "electron-download": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/electron-download/-/electron-download-4.1.1.tgz", - "integrity": "sha512-FjEWG9Jb/ppK/2zToP+U5dds114fM1ZOJqMAR4aXXL5CvyPE9fiqBK/9YcwC9poIFQTEJk/EM/zyRwziziRZrg==", - "dev": true, - "requires": { - "debug": "^3.0.0", - "env-paths": "^1.0.0", - "fs-extra": "^4.0.1", - "minimist": "^1.2.0", - "nugget": "^2.0.1", - "path-exists": "^3.0.0", - "rc": "^1.2.1", - "semver": "^5.4.1", - "sumchecker": "^2.0.2" - }, - "dependencies": { - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", "dev": true, "requires": { - "ms": "^2.1.1" - } - }, - "env-paths": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-1.0.0.tgz", - "integrity": "sha1-QWgTO0K7BcOKNbGuQ5fIKYqzaeA=", - "dev": true - }, - "fs-extra": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-4.0.3.tgz", - "integrity": "sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" + "glob": "^7.1.3" } }, "semver": { @@ -5757,9 +5801,9 @@ } }, "electron-log": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/electron-log/-/electron-log-3.0.8.tgz", - "integrity": "sha512-B9+eJ8z3UbDnWEz+G33SIJvEDeKLznHEV4sCu6bR31KuOdp3dYN046QBWbLNsvKU+lzFI6eOi+xNCpNHZvatiw==", + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/electron-log/-/electron-log-3.0.9.tgz", + "integrity": "sha512-kfV4riUqW8uooYLAfrlB3EJW66W29ePipJU4/Fm8UxQekVNcR2qHI/0tiJX3oWM79VdAUiiYqL9V49lhnSIO8g==", "dev": true }, "electron-publish": { @@ -5806,13 +5850,21 @@ } }, "electron-store": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/electron-store/-/electron-store-4.0.0.tgz", - "integrity": "sha512-qgkDetwB9bz+ZA7mNCQGm6zLJOMT4yBkTZ7f16M9iS0GcI/bOeOeFkLkIaJddTtPca7MOiaUM1imMjFqUfQgSA==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/electron-store/-/electron-store-5.1.0.tgz", + "integrity": "sha512-uhAF/4+zDb+y0hWqlBirEPEAR4ciCZDp4fRWGFNV62bG+ArdQPpXk7jS0MEVj3CfcG5V7hx7Dpq5oD+1j6GD8Q==", "dev": true, "requires": { - "conf": "^5.0.0", - "type-fest": "^0.5.2" + "conf": "^6.2.0", + "type-fest": "^0.7.1" + }, + "dependencies": { + "type-fest": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.7.1.tgz", + "integrity": "sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==", + "dev": true + } } }, "electron-to-chromium": { @@ -5822,26 +5874,43 @@ "dev": true }, "electron-updater": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/electron-updater/-/electron-updater-4.1.2.tgz", - "integrity": "sha512-4Sk8IW0LfOilDz+WAB/gEDmX7+FUFRbKHGN1zGjehPilnd6H9cmjgBHK6Xzq/FLq/uOHGJ6GX/9tsF+jr7CvnA==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/electron-updater/-/electron-updater-4.2.0.tgz", + "integrity": "sha512-GuS3g7HDh17x/SaFjxjswlWUaKHczksYkV2Xc5CKj/bZH0YCvTSHtOmnBAdAmCk99u/71p3zP8f0jIqDfGcjww==", "dev": true, "requires": { - "@types/semver": "^6.0.1", - "builder-util-runtime": "8.3.0", + "@types/semver": "^6.0.2", + "builder-util-runtime": "8.4.0", "fs-extra": "^8.1.0", "js-yaml": "^3.13.1", "lazy-val": "^1.0.4", "lodash.isequal": "^4.5.0", "pako": "^1.0.10", - "semver": "^6.2.0" + "semver": "^6.3.0" + }, + "dependencies": { + "builder-util-runtime": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-8.4.0.tgz", + "integrity": "sha512-CJB/eKfPf2vHrkmirF5eicVnbDCkMBbwd5tRYlTlgud16zFeqD7QmrVUAOEXdnsrcNkiLg9dbuUsQKtl/AwsYQ==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "sax": "^1.2.4" + } + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "dev": true + } } }, "electron-window-state": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/electron-window-state/-/electron-window-state-5.0.3.tgz", "integrity": "sha512-1mNTwCfkolXl3kMf50yW3vE2lZj0y92P/HYWFBrb+v2S/pCka5mdwN3cagKm458A7NjndSwijynXgcLWRodsVg==", - "dev": true, "requires": { "jsonfile": "^4.0.0", "mkdirp": "^0.5.1" @@ -6099,6 +6168,13 @@ "next-tick": "^1.0.0" } }, + "es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "dev": true, + "optional": true + }, "es6-iterator": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", @@ -6849,7 +6925,6 @@ "version": "8.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", - "dev": true, "requires": { "graceful-fs": "^4.2.0", "jsonfile": "^4.0.0", @@ -6908,12 +6983,6 @@ "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", "dev": true }, - "get-stdin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", - "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", - "dev": true - }, "get-stream": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", @@ -6973,6 +7042,31 @@ } } }, + "global-agent": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-2.1.6.tgz", + "integrity": "sha512-fL+xfraAlc1MXU8Gs0DAg/eHH+H1CjxbK+BLU3Qt55dAVMAQ8fH8k/UrLwV4A+Vk/hl/TePWuTxFnqJzCV1/Kw==", + "dev": true, + "optional": true, + "requires": { + "boolean": "^2.0.3", + "core-js": "^3.4.0", + "es6-error": "^4.1.1", + "matcher": "^2.0.0", + "roarr": "^2.14.4", + "semver": "^6.3.0", + "serialize-error": "^5.0.0" + }, + "dependencies": { + "core-js": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.4.1.tgz", + "integrity": "sha512-KX/dnuY/J8FtEwbnrzmAjUYgLqtk+cxM86hfG60LGiW3MmltIc2yAmDgBgEkfm0blZhUrdr1Zd84J2Y14mLxzg==", + "dev": true, + "optional": true + } + } + }, "global-dirs": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz", @@ -7017,12 +7111,37 @@ "which": "^1.2.14" } }, + "global-tunnel-ng": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/global-tunnel-ng/-/global-tunnel-ng-2.7.1.tgz", + "integrity": "sha512-4s+DyciWBV0eK148wqXxcmVAbFVPqtc3sEtUE/GTQfuU80rySLcMhUmHKSHI7/LDj8q0gDYI1lIhRRB7ieRAqg==", + "dev": true, + "optional": true, + "requires": { + "encodeurl": "^1.0.2", + "lodash": "^4.17.10", + "npm-conf": "^1.1.3", + "tunnel": "^0.0.6" + } + }, "globals": { "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", "dev": true }, + "globalthis": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.0.tgz", + "integrity": "sha512-vcCAZTJ3r5Qcu5l8/2oyVdoFwxKgfYnMTR2vwWeux/NAVZK3PwcMaWkdUIn4GJbmKuRK7xcvDsLuK+CKcXyodg==", + "dev": true, + "optional": true, + "requires": { + "define-properties": "^1.1.2", + "function-bind": "^1.1.1", + "object-keys": "^1.0.12" + } + }, "globby": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/globby/-/globby-7.1.1.tgz", @@ -7067,8 +7186,7 @@ "graceful-fs": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.2.tgz", - "integrity": "sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q==", - "dev": true + "integrity": "sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q==" }, "hammerjs": { "version": "2.0.8", @@ -7540,15 +7658,6 @@ "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", "dev": true }, - "indent-string": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", - "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", - "dev": true, - "requires": { - "repeating": "^2.0.0" - } - }, "indexof": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", @@ -7977,12 +8086,6 @@ "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", "dev": true }, - "is-utf8": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", - "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", - "dev": true - }, "is-windows": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", @@ -8216,6 +8319,15 @@ "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", "dev": true }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -8368,9 +8480,9 @@ "dev": true }, "json-schema-typed": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-7.0.1.tgz", - "integrity": "sha512-IqUK+Cqc8/MqHsCvv1TMccbKdBzoATOLHXZAF5UDu70/CCxo648cHUig24hc+XTK53TyeNk1UeVTlc2Haovtsw==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-7.0.2.tgz", + "integrity": "sha512-40FRIcBSz4y0Ego3gMpbkhtIgebpxKRgW/7i1FfDNL4/xEPQKBM12tKSiCZFNQvad5K4IS3I5Sc8cxza/KSwog==", "dev": true }, "json-stable-stringify": { @@ -8407,7 +8519,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", - "dev": true, "requires": { "graceful-fs": "^4.1.6" } @@ -9176,6 +9287,15 @@ "readable-stream": "^2.0.2" } }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -9384,36 +9504,6 @@ "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", "dev": true }, - "load-json-file": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", - "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0", - "strip-bom": "^2.0.0" - }, - "dependencies": { - "parse-json": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", - "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", - "dev": true, - "requires": { - "error-ex": "^1.2.0" - } - }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - } - } - }, "loader-runner": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz", @@ -9524,16 +9614,6 @@ "js-tokens": "^3.0.0 || ^4.0.0" } }, - "loud-rejection": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", - "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", - "dev": true, - "requires": { - "currently-unhandled": "^0.4.1", - "signal-exit": "^3.0.0" - } - }, "lowercase-keys": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", @@ -9643,12 +9723,6 @@ "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", "dev": true }, - "map-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", - "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", - "dev": true - }, "map-visit": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", @@ -9658,6 +9732,25 @@ "object-visit": "^1.0.0" } }, + "matcher": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/matcher/-/matcher-2.0.0.tgz", + "integrity": "sha512-nlmfSlgHBFx36j/Pl/KQPbIaqE8Zf0TqmSMjsuddHDg6PMSVgmyW9HpkLs0o0M1n2GIZ/S2BZBLIww/xjhiGng==", + "dev": true, + "optional": true, + "requires": { + "escape-string-regexp": "^2.0.0" + }, + "dependencies": { + "escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "optional": true + } + } + }, "md5.js": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", @@ -9702,24 +9795,6 @@ "integrity": "sha1-htcJCzDORV1j+64S3aUaR93K+bI=", "dev": true }, - "meow": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", - "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", - "dev": true, - "requires": { - "camelcase-keys": "^2.0.0", - "decamelize": "^1.1.2", - "loud-rejection": "^1.0.0", - "map-obj": "^1.0.1", - "minimist": "^1.1.3", - "normalize-package-data": "^2.3.4", - "object-assign": "^4.0.1", - "read-pkg-up": "^1.0.1", - "redent": "^1.0.0", - "trim-newlines": "^1.0.0" - } - }, "merge-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", @@ -9987,7 +10062,6 @@ "version": "0.5.1", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "dev": true, "requires": { "minimist": "0.0.8" }, @@ -9995,8 +10069,7 @@ "minimist": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" } } }, @@ -10018,6 +10091,17 @@ "mkdirp": "^0.5.1", "rimraf": "^2.5.4", "run-queue": "^1.0.3" + }, + "dependencies": { + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } } }, "ms": { @@ -10373,6 +10457,26 @@ "integrity": "sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g==", "dev": true }, + "npm-conf": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/npm-conf/-/npm-conf-1.1.3.tgz", + "integrity": "sha512-Yic4bZHJOt9RCFbRP3GgpqhScOY4HH3V2P8yBj6CeYq118Qr+BLXqT2JvpJ00mryLESpgOxf5XlFv4ZjXxLScw==", + "dev": true, + "optional": true, + "requires": { + "config-chain": "^1.1.11", + "pify": "^3.0.0" + }, + "dependencies": { + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true, + "optional": true + } + } + }, "npm-package-arg": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-6.1.0.tgz", @@ -10508,38 +10612,6 @@ "path-key": "^2.0.0" } }, - "nugget": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/nugget/-/nugget-2.0.1.tgz", - "integrity": "sha1-IBCVpIfhrTYIGzQy+jytpPjQcbA=", - "dev": true, - "requires": { - "debug": "^2.1.3", - "minimist": "^1.1.0", - "pretty-bytes": "^1.0.2", - "progress-stream": "^1.1.0", - "request": "^2.45.0", - "single-line-log": "^1.1.2", - "throttleit": "0.0.2" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } - } - }, "null-check": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/null-check/-/null-check-1.0.0.tgz", @@ -10910,6 +10982,15 @@ "semver": "^5.4.1" } }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, "semver": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", @@ -11368,16 +11449,6 @@ "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", "dev": true }, - "pretty-bytes": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-1.0.4.tgz", - "integrity": "sha1-CiLoIQYJrTVUL4yNXSFZr/B1HIQ=", - "dev": true, - "requires": { - "get-stdin": "^4.0.1", - "meow": "^3.1.0" - } - }, "private": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", @@ -11396,67 +11467,6 @@ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "dev": true }, - "progress-stream": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/progress-stream/-/progress-stream-1.2.0.tgz", - "integrity": "sha1-LNPP6jO6OonJwSHsM0er6asSX3c=", - "dev": true, - "requires": { - "speedometer": "~0.1.2", - "through2": "~0.2.3" - }, - "dependencies": { - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true - }, - "object-keys": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-0.4.0.tgz", - "integrity": "sha1-KKaq50KN0sOpLz2V8hM13SBOAzY=", - "dev": true - }, - "readable-stream": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", - "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", - "dev": true - }, - "through2": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/through2/-/through2-0.2.3.tgz", - "integrity": "sha1-6zKE2k6jEbbMis42U3SKUqvyWj8=", - "dev": true, - "requires": { - "readable-stream": "~1.1.9", - "xtend": "~2.1.1" - } - }, - "xtend": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-2.1.2.tgz", - "integrity": "sha1-bv7MKk2tjmlixJAbM3znuoe10os=", - "dev": true, - "requires": { - "object-keys": "~0.4.0" - } - } - } - }, "promise": { "version": "7.3.1", "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", @@ -11491,6 +11501,13 @@ } } }, + "proto-list": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", + "integrity": "sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk=", + "dev": true, + "optional": true + }, "protoduck": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/protoduck/-/protoduck-5.0.1.tgz", @@ -11555,6 +11572,17 @@ "pify": "^2.0.0", "pinkie-promise": "^2.0.0", "rimraf": "^2.2.8" + }, + "dependencies": { + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } } }, "globby": { @@ -11645,6 +11673,17 @@ "rimraf": "^2.5.2", "semver": "^5.3.0", "xml2js": "^0.4.17" + }, + "dependencies": { + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } } } } @@ -11957,65 +11996,6 @@ } } }, - "read-pkg-up": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", - "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", - "dev": true, - "requires": { - "find-up": "^1.0.0", - "read-pkg": "^1.0.0" - }, - "dependencies": { - "find-up": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", - "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", - "dev": true, - "requires": { - "path-exists": "^2.0.0", - "pinkie-promise": "^2.0.0" - } - }, - "path-exists": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", - "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", - "dev": true, - "requires": { - "pinkie-promise": "^2.0.0" - } - }, - "path-type": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", - "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" - } - }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - }, - "read-pkg": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", - "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", - "dev": true, - "requires": { - "load-json-file": "^1.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^1.0.0" - } - } - } - }, "readable-stream": { "version": "2.3.6", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", @@ -12052,16 +12032,6 @@ "picomatch": "^2.0.4" } }, - "redent": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", - "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", - "dev": true, - "requires": { - "indent-string": "^2.1.0", - "strip-indent": "^1.0.1" - } - }, "reflect-metadata": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", @@ -12343,9 +12313,9 @@ "dev": true }, "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.0.tgz", + "integrity": "sha512-NDGVxTsjqfunkds7CqsOiEnxln4Bo7Nddl3XhS4pXg5OzwkLqJ971ZVAAnB+DDLnF76N+VnDEiBHaVV8I06SUg==", "dev": true, "requires": { "glob": "^7.1.3" @@ -12361,6 +12331,30 @@ "inherits": "^2.0.1" } }, + "roarr": { + "version": "2.14.5", + "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.14.5.tgz", + "integrity": "sha512-jwwW1NEYh06O+teTDJt4ETtLdxb3BBir0vThgQQGz88bYypRv69jQhMVRKI8Ps/6LeqsRBGe28V3Awjy38LFoQ==", + "dev": true, + "optional": true, + "requires": { + "boolean": "^2.0.3", + "detect-node": "^2.0.4", + "globalthis": "^1.0.0", + "json-stringify-safe": "^5.0.1", + "semver-compare": "^1.0.0", + "sprintf-js": "^1.1.2" + }, + "dependencies": { + "sprintf-js": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", + "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==", + "dev": true, + "optional": true + } + } + }, "rollup": { "version": "1.21.4", "resolved": "https://registry.npmjs.org/rollup/-/rollup-1.21.4.tgz", @@ -12468,7 +12462,6 @@ "version": "6.5.3", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.3.tgz", "integrity": "sha512-wuYsAYYFdWTAnAaPoKGNhfpWwKZbJW+HgAJ+mImp+Epl7BG8oNWBCTyRM8gba9k4lk8BgWdoYm21Mo/RYhhbgA==", - "dev": true, "requires": { "tslib": "^1.9.0" } @@ -12577,6 +12570,15 @@ "xml2js": "^0.4.17" }, "dependencies": { + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, "tmp": { "version": "0.0.30", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.30.tgz", @@ -12603,6 +12605,13 @@ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "dev": true }, + "semver-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w=", + "dev": true, + "optional": true + }, "semver-diff": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-2.1.0.tgz", @@ -12700,6 +12709,25 @@ } } }, + "serialize-error": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-5.0.0.tgz", + "integrity": "sha512-/VtpuyzYf82mHYTtI4QKtwHa79vAdU5OQpNPAmE/0UDdlGT0ZxHwC+J6gXkw29wwoVI8fMPsfcVHOwXtUQYYQA==", + "dev": true, + "optional": true, + "requires": { + "type-fest": "^0.8.0" + }, + "dependencies": { + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true, + "optional": true + } + } + }, "serialize-javascript": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.9.1.tgz", @@ -12867,37 +12895,6 @@ "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", "dev": true }, - "single-line-log": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/single-line-log/-/single-line-log-1.1.2.tgz", - "integrity": "sha1-wvg/Jzo+GhbtsJlWYdoO1e8DM2Q=", - "dev": true, - "requires": { - "string-width": "^1.0.1" - }, - "dependencies": { - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dev": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - } - } - }, "slash": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", @@ -13407,12 +13404,6 @@ "chalk": "^2.0.1" } }, - "speedometer": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/speedometer/-/speedometer-0.1.4.tgz", - "integrity": "sha1-mHbb0qFp0xFUAtSObqYynIgWpQ0=", - "dev": true - }, "split-string": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", @@ -13652,30 +13643,12 @@ "ansi-regex": "^2.0.0" } }, - "strip-bom": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", - "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", - "dev": true, - "requires": { - "is-utf8": "^0.2.0" - } - }, "strip-eof": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", "dev": true }, - "strip-indent": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", - "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", - "dev": true, - "requires": { - "get-stdin": "^4.0.1" - } - }, "strip-json-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", @@ -13755,29 +13728,12 @@ } }, "sumchecker": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-2.0.2.tgz", - "integrity": "sha1-D0LBDl0F2l1C7qPlbDOZo31sWz4=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.0.tgz", + "integrity": "sha512-yreseuC/z4iaodVoq07XULEOO9p4jnQazO7mbrnDSvWAU/y2cbyIKs+gWJptfcGu9R+1l27K8Rkj0bfvqnBpgQ==", "dev": true, "requires": { - "debug": "^2.2.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } + "debug": "^4.1.0" } }, "supports-color": { @@ -13953,12 +13909,6 @@ } } }, - "throttleit": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-0.0.2.tgz", - "integrity": "sha1-z+34jmDADdlpe2H90qg0OptoDq8=", - "dev": true - }, "through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", @@ -14094,12 +14044,6 @@ "integrity": "sha512-4hjqbObwlh2dLyW4tcz0Ymw0ggoaVDMveUB9w8kFSQScdRLo0gxO9J7WFcUBo+W3C1TLdFIEwNOWebgZZ0RH9Q==", "dev": true }, - "trim-newlines": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", - "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", - "dev": true - }, "trim-right": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", @@ -14197,6 +14141,13 @@ "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", "dev": true }, + "tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", + "dev": true, + "optional": true + }, "tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", @@ -14373,8 +14324,7 @@ "universalify": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "dev": true + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" }, "unpipe": { "version": "1.0.0", @@ -15387,6 +15337,12 @@ "minimalistic-assert": "^1.0.0" } }, + "web-animations-js": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/web-animations-js/-/web-animations-js-2.3.2.tgz", + "integrity": "sha512-TOMFWtQdxzjWp8qx4DAraTWTsdhxVSiWa6NkPFSaPtZ1diKUxTn4yTix73A1euG1WbSOMMPcY51cnjTIHrGtDA==", + "dev": true + }, "webdriver-js-extender": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/webdriver-js-extender/-/webdriver-js-extender-2.1.0.tgz", diff --git a/package.json b/package.json index 2253cb4..5770fb0 100644 --- a/package.json +++ b/package.json @@ -2,21 +2,31 @@ "name": "ucap-webmessenger", "version": "0.0.0", "scripts": { + "postinstall": "electron-builder install-app-deps", "ng": "ng", "start": "npm-run-all -p start:renderer start:main", - "start:main": "wait-on http-get://localhost:4200/ && npm run build:main:dev && electron --nolazy --inspect-brk=9229 .", - "start:renderer": "ng serve", - "start:web": "cross-env UCAP_ENV=WEB ng serve", - "start:production": "npm run build:renderer && npm run build:main:prod && electron --nolazy --inspect-brk=9229 .", - "build:renderer": "cross-env NODE_ENV=production ng build --base-href ./", - "build:main:dev": "cross-env NODE_ENV=development TS_NODE_PROJECT='./config/tsconfig.webpack.json' parallel-webpack --config=config/main.webpack.config.ts", - "build:main:prod": "cross-env NODE_ENV=production TS_NODE_PROJECT='./config/tsconfig.webpack.json' NODE_OPTIONS='--max_old_space_size=4096' parallel-webpack --config=config/main.webpack.config.ts", + "start:main": "wait-on http-get://localhost:4200/ && npm run build:main:development && electron --nolazy --inspect-brk=9229 .", + "start:renderer": "cross-env UCAP_ENV_RUNTIME=ELECTRON ng serve -c renderer-development", + "start:browser": "cross-env UCAP_ENV_RUNTIME=BROWSER ng serve -c browser-development -o", + "build": "npm-run-all -p build:renderer build:main:production", + "build:renderer": "cross-env NODE_ENV=production ng build -c renderer-development --base-href ./", + "build:browser": "cross-env UCAP_ENV_RUNTIME=BROWSER ng build -c browser-development --base-href ./NextMessenger_POC", + "build:main:development": "cross-env NODE_ENV=development TS_NODE_PROJECT='./config/tsconfig.webpack.json' parallel-webpack --config=config/main.webpack.config.ts", + "build:main:production": "cross-env NODE_ENV=production TS_NODE_PROJECT='./config/tsconfig.webpack.json' NODE_OPTIONS='--max_old_space_size=4096' parallel-webpack --config=config/main.webpack.config.ts", + "electron:local": "electron .", + "electron:windows": "npm run build && electron-builder build --windows", + "electron:mac": "npm run build && electron-builder build --mac", + "electron:linux": "npm run build && electron-builder build --linux", "test": "ng test", "lint": "ng lint", "e2e": "ng e2e" }, "private": true, "dependencies": { + "@angular/core": "~8.2.12", + "electron-window-state": "^5.0.3", + "fs-extra": "^8.1.0", + "rxjs": "^6.5.2", "tslib": "^1.10.0" }, "devDependencies": { @@ -29,7 +39,6 @@ "@angular/common": "~8.2.12", "@angular/compiler": "~8.2.12", "@angular/compiler-cli": "~8.2.12", - "@angular/core": "~8.2.12", "@angular/flex-layout": "^8.0.0-beta.27", "@angular/forms": "~8.2.12", "@angular/language-service": "~8.2.12", @@ -60,8 +69,11 @@ "@types/webpack": "^4.39.5", "@types/webpack-merge": "^4.1.5", "@types/webpack-node-externals": "^1.6.3", + "angular-split": "^3.0.2", + "autolinker": "^3.11.1", "awesome-node-loader": "^1.1.1", "awesome-typescript-loader": "^5.2.1", + "classlist.js": "^1.1.20150312", "clean-webpack-plugin": "^3.0.0", "copy-webpack-plugin": "^5.0.4", "codelyzer": "^5.0.0", @@ -70,17 +82,15 @@ "cross-env": "^5.2.1", "detect-browser": "^4.6.0", "devtron": "^1.4.0", - "electron": "^6.1.2", + "electron": "^7.1.1", "electron-builder": "^21.2.0", "electron-debug": "^3.0.1", "electron-devtools-installer": "^2.2.4", - "electron-log": "^3.0.8", + "electron-log": "^3.0.9", "electron-reload": "^1.5.0", - "electron-store": "^4.0.0", - "electron-updater": "^4.1.2", - "electron-window-state": "^5.0.3", + "electron-store": "^5.1.0", + "electron-updater": "^4.2.0", "file-saver": "^2.0.2", - "fs-extra": "^8.1.0", "filesize": "^4.1.2", "hammerjs": "^2.0.8", "jasmine-core": "~3.4.0", @@ -95,11 +105,11 @@ "ngrx-store-freeze": "^0.2.4", "ngx-logger": "^4.0.5", "ngx-perfect-scrollbar": "^8.0.0", + "rimraf": "^3.0.0", "npm-run-all": "^4.1.5", "parallel-webpack": "^2.4.0", "protractor": "~5.4.0", "queueing-subject": "^0.3.4", - "rxjs": "^6.5.2", "semver": "^6.3.0", "ts-node": "~7.0.0", "tsickle": "^0.37.0", @@ -107,10 +117,11 @@ "tslint": "~5.15.0", "typescript": "~3.5.3", "wait-on": "^3.3.0", + "web-animations-js": "^2.3.2", "webpack": "4.39.2", "webpack-cli": "^3.3.7", "webpack-node-externals": "^1.7.2", "zone.js": "~0.9.1" }, - "main": "./dist/main/main.js" + "main": "./dist/ucap-webmessenger-electron/main.js" } diff --git a/projects/ucap-webmessenger-api-common/src/lib/config/module-config.ts b/projects/ucap-webmessenger-api-common/src/lib/config/module-config.ts new file mode 100644 index 0000000..4364908 --- /dev/null +++ b/projects/ucap-webmessenger-api-common/src/lib/config/module-config.ts @@ -0,0 +1,7 @@ +import { ModuleConfig as CoreModuleConfig } from '@ucap-webmessenger/core'; + +import { Urls } from './urls'; + +export interface ModuleConfig extends CoreModuleConfig { + acceptableFileExtensions: string[]; +} diff --git a/projects/ucap-webmessenger-api-common/src/lib/types/token.ts b/projects/ucap-webmessenger-api-common/src/lib/config/token.ts similarity index 100% rename from projects/ucap-webmessenger-api-common/src/lib/types/token.ts rename to projects/ucap-webmessenger-api-common/src/lib/config/token.ts diff --git a/projects/ucap-webmessenger-api-common/src/lib/config/urls.ts b/projects/ucap-webmessenger-api-common/src/lib/config/urls.ts new file mode 100644 index 0000000..0c61bd5 --- /dev/null +++ b/projects/ucap-webmessenger-api-common/src/lib/config/urls.ts @@ -0,0 +1,12 @@ +export interface Urls { + fileProfileSave: string; + fileTalkDownload: string; + fileTalkSave: string; + fileTalkShare: string; + massTalkDownload: string; + massTalkSave: string; + transMassTalkDownload: string; + transMassTalkSave: string; + translationReq: string; + translationSave: string; +} diff --git a/projects/ucap-webmessenger-api-common/src/lib/services/common-api.service.ts b/projects/ucap-webmessenger-api-common/src/lib/services/common-api.service.ts index f67b1c9..00a1443 100644 --- a/projects/ucap-webmessenger-api-common/src/lib/services/common-api.service.ts +++ b/projects/ucap-webmessenger-api-common/src/lib/services/common-api.service.ts @@ -9,8 +9,6 @@ import { import { Observable, Subject } from 'rxjs'; import { map, filter } from 'rxjs/operators'; -import { _MODULE_CONFIG } from '../types/token'; -import { ModuleConfig } from '../types/module-config'; import { FileProfileSaveRequest, FileProfileSaveResponse, @@ -71,14 +69,26 @@ import { decodeTranslationSave } from '../apis/translation-save'; +import { _MODULE_CONFIG } from '../config/token'; +import { ModuleConfig } from '../config/module-config'; +import { Urls } from '../config/urls'; +import { UrlConfig } from '@ucap-webmessenger/core'; + @Injectable({ providedIn: 'root' }) export class CommonApiService { + readonly urls: Urls; + constructor( @Inject(_MODULE_CONFIG) private moduleConfig: ModuleConfig, private httpClient: HttpClient - ) {} + ) { + this.urls = UrlConfig.getUrls( + this.moduleConfig.hostConfig, + this.moduleConfig.urls + ); + } public fileProfileSave( req: FileProfileSaveRequest, @@ -86,9 +96,7 @@ export class CommonApiService { ): Observable { return this.httpClient .post( - !!fileProfileSaveUrl - ? fileProfileSaveUrl - : this.moduleConfig.urls.fileProfileSave, + !!fileProfileSaveUrl ? fileProfileSaveUrl : this.urls.fileProfileSave, {}, { params: encodeFileProfileSave(req) @@ -103,9 +111,7 @@ export class CommonApiService { ): string { const httpReq = new HttpRequest( 'GET', - !!fileTalkDownloadUrl - ? fileTalkDownloadUrl - : this.moduleConfig.urls.fileTalkDownload, + !!fileTalkDownloadUrl ? fileTalkDownloadUrl : this.urls.fileTalkDownload, {}, { params: encodeFileTalkDownload(req) @@ -121,9 +127,7 @@ export class CommonApiService { ): Observable { const httpReq = new HttpRequest( 'POST', - !!fileTalkDownloadUrl - ? fileTalkDownloadUrl - : this.moduleConfig.urls.fileTalkDownload, + !!fileTalkDownloadUrl ? fileTalkDownloadUrl : this.urls.fileTalkDownload, encodeFormDataFileTalkDownload(req), { reportProgress: true, responseType: 'blob' } ); @@ -159,7 +163,7 @@ export class CommonApiService { ): Observable { const httpReq = new HttpRequest( 'POST', - !!fileTalkSaveUrl ? fileTalkSaveUrl : this.moduleConfig.urls.fileTalkSave, + !!fileTalkSaveUrl ? fileTalkSaveUrl : this.urls.fileTalkSave, encodeFileTalkSave(req), { reportProgress: true, responseType: 'text' as 'json' } ); @@ -182,15 +186,26 @@ export class CommonApiService { ); } - public acceptableExtensionForFileTalk(extensions: string[]): boolean { + public acceptableExtensionForFileTalk( + extensions: string[] + ): { accept: boolean; reject: string[] } { + let accept = true; + const reject: string[] = []; for (const extension of extensions) { if ( - -1 === this.moduleConfig.acceptableFileExtensions.indexOf(extension.toLowerCase()) + -1 === + this.moduleConfig.acceptableFileExtensions.indexOf( + extension.toLowerCase() + ) ) { - return false; + reject.push(extension); + accept = false; } } - return true; + return { + accept, + reject + }; } public fileTalkShare( @@ -198,7 +213,7 @@ export class CommonApiService { ): Observable { return this.httpClient .post( - this.moduleConfig.urls.fileTalkShare, + this.urls.fileTalkShare, {}, { params: encodeFileTalkShare(req) @@ -212,7 +227,7 @@ export class CommonApiService { ): Observable { return this.httpClient .post( - this.moduleConfig.urls.massTalkDownload, + this.urls.massTalkDownload, {}, { params: encodeMassTalkDownload(req), @@ -227,7 +242,7 @@ export class CommonApiService { ): Observable { return this.httpClient .post( - this.moduleConfig.urls.massTalkSave, + this.urls.massTalkSave, {}, { params: encodeMassTalkSave(req), @@ -242,7 +257,7 @@ export class CommonApiService { ): Observable { return this.httpClient .post( - this.moduleConfig.urls.transMassTalkDownload, + this.urls.transMassTalkDownload, {}, { params: encodeTransMassTalkDownload(req) @@ -256,7 +271,7 @@ export class CommonApiService { ): Observable { return this.httpClient .post( - this.moduleConfig.urls.transMassTalkSave, + this.urls.transMassTalkSave, {}, { params: encodeTransMassTalkSave(req) @@ -270,7 +285,7 @@ export class CommonApiService { ): Observable { return this.httpClient .post( - this.moduleConfig.urls.translationReq, + this.urls.translationReq, {}, { params: encodeTranslationReq(req) @@ -284,7 +299,7 @@ export class CommonApiService { ): Observable { return this.httpClient .post( - this.moduleConfig.urls.translationSave, + this.urls.translationSave, {}, { params: encodeTranslationSave(req) diff --git a/projects/ucap-webmessenger-api-common/src/lib/types/module-config.ts b/projects/ucap-webmessenger-api-common/src/lib/types/module-config.ts deleted file mode 100644 index 8728921..0000000 --- a/projects/ucap-webmessenger-api-common/src/lib/types/module-config.ts +++ /dev/null @@ -1,15 +0,0 @@ -export interface ModuleConfig { - urls: { - fileProfileSave: string; - fileTalkDownload: string; - fileTalkSave: string; - fileTalkShare: string; - massTalkDownload: string; - massTalkSave: string; - transMassTalkDownload: string; - transMassTalkSave: string; - translationReq: string; - translationSave: string; - }; - acceptableFileExtensions: string[]; -} diff --git a/projects/ucap-webmessenger-api-common/src/lib/ucap-common-api.module.ts b/projects/ucap-webmessenger-api-common/src/lib/ucap-common-api.module.ts index 3e3baff..2292850 100644 --- a/projects/ucap-webmessenger-api-common/src/lib/ucap-common-api.module.ts +++ b/projects/ucap-webmessenger-api-common/src/lib/ucap-common-api.module.ts @@ -1,15 +1,16 @@ import { NgModule, ModuleWithProviders } from '@angular/core'; -import { _MODULE_CONFIG } from './types/token'; import { CommonApiService } from './services/common-api.service'; -import { ModuleConfig } from './types/module-config'; + +import { _MODULE_CONFIG } from './config/token'; +import { ModuleConfig } from './config/module-config'; const SERVICES = [CommonApiService]; @NgModule({ declarations: [], imports: [], - exports: [] + exports: [], }) export class UCapCommonApiModule { public static forRoot( @@ -17,7 +18,7 @@ export class UCapCommonApiModule { ): ModuleWithProviders { return { ngModule: UCapCommonApiModule, - providers: [{ provide: _MODULE_CONFIG, useValue: config }, ...SERVICES] + providers: [{ provide: _MODULE_CONFIG, useValue: config }, ...SERVICES], }; } } diff --git a/projects/ucap-webmessenger-api-common/src/public-api.ts b/projects/ucap-webmessenger-api-common/src/public-api.ts index 0a47d28..82e0038 100644 --- a/projects/ucap-webmessenger-api-common/src/public-api.ts +++ b/projects/ucap-webmessenger-api-common/src/public-api.ts @@ -2,8 +2,6 @@ * Public API Surface of ucap-webmessenger-api-common */ -export * from './lib/types/module-config'; - export * from './lib/apis/file-profile-save'; export * from './lib/apis/file-talk-download'; export * from './lib/apis/file-talk-save'; @@ -21,3 +19,6 @@ export * from './lib/models/file-upload-item'; export * from './lib/services/common-api.service'; export * from './lib/ucap-common-api.module'; + +export * from './lib/config/urls'; +export * from './lib/config/module-config'; diff --git a/projects/ucap-webmessenger-api-external/src/lib/config/module-config.ts b/projects/ucap-webmessenger-api-external/src/lib/config/module-config.ts new file mode 100644 index 0000000..7091509 --- /dev/null +++ b/projects/ucap-webmessenger-api-external/src/lib/config/module-config.ts @@ -0,0 +1,5 @@ +import { ModuleConfig as CoreModuleConfig } from '@ucap-webmessenger/core'; + +import { Urls } from './urls'; + +export interface ModuleConfig extends CoreModuleConfig {} diff --git a/projects/ucap-webmessenger-api-external/src/lib/types/token.ts b/projects/ucap-webmessenger-api-external/src/lib/config/token.ts similarity index 100% rename from projects/ucap-webmessenger-api-external/src/lib/types/token.ts rename to projects/ucap-webmessenger-api-external/src/lib/config/token.ts diff --git a/projects/ucap-webmessenger-api-external/src/lib/config/urls.ts b/projects/ucap-webmessenger-api-external/src/lib/config/urls.ts new file mode 100644 index 0000000..850d22d --- /dev/null +++ b/projects/ucap-webmessenger-api-external/src/lib/config/urls.ts @@ -0,0 +1,6 @@ +export interface Urls { + checkUserInfoEx: string; + companyList: string; + tokenUpdate: string; + urlInfo: string; +} diff --git a/projects/ucap-webmessenger-api-external/src/lib/services/external-api.service.ts b/projects/ucap-webmessenger-api-external/src/lib/services/external-api.service.ts index a4730ad..5da6b02 100644 --- a/projects/ucap-webmessenger-api-external/src/lib/services/external-api.service.ts +++ b/projects/ucap-webmessenger-api-external/src/lib/services/external-api.service.ts @@ -4,51 +4,61 @@ import { HttpClient } from '@angular/common/http'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; -import { _MODULE_CONFIG } from '../types/token'; -import { ModuleConfig } from '../types/module-config'; import { CheckUserInfoExRequest, CheckUserInfoExResponse, encodeCheckUserInfoEx, - decodeCheckUserInfoEx + decodeCheckUserInfoEx, } from '../apis/check-user-info-ex'; import { CompanyListRequest, CompanyListResponse, encodeCompanyList, - decodeCompanyList + decodeCompanyList, } from '../apis/company-list'; import { TokenUpdateRequest, TokenUpdateResponse, encodeTokenUpdate, - decodeTokenUpdate + decodeTokenUpdate, } from '../apis/token-update'; import { UrlInfoResponse, UrlInfoRequest, encodeUrlInfo, - decodeUrlInfo + decodeUrlInfo, } from '../apis/url-info'; +import { _MODULE_CONFIG } from '../config/token'; +import { ModuleConfig } from '../config/module-config'; +import { Urls } from '../config/urls'; +import { UrlConfig } from '@ucap-webmessenger/core'; + @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class ExternalApiService { + readonly urls: Urls; + constructor( @Inject(_MODULE_CONFIG) private moduleConfig: ModuleConfig, private httpClient: HttpClient - ) {} + ) { + this.urls = UrlConfig.getUrls( + this.moduleConfig.hostConfig, + this.moduleConfig.urls + ); + } public checkUserInfoEx( req: CheckUserInfoExRequest ): Observable { return this.httpClient .post( - this.moduleConfig.urls.checkUserInfoEx, + this.urls.checkUserInfoEx, {}, { - params: encodeCheckUserInfoEx(req) + params: encodeCheckUserInfoEx(req), } ) .pipe(map(res => decodeCheckUserInfoEx(res))); @@ -57,10 +67,10 @@ export class ExternalApiService { public companyList(req: CompanyListRequest): Observable { return this.httpClient .post( - this.moduleConfig.urls.companyList, + this.urls.companyList, {}, { - params: encodeCompanyList(req) + params: encodeCompanyList(req), } ) .pipe(map(res => decodeCompanyList(res))); @@ -69,10 +79,10 @@ export class ExternalApiService { public tokenUpdate(req: TokenUpdateRequest): Observable { return this.httpClient .post( - this.moduleConfig.urls.tokenUpdate, + this.urls.tokenUpdate, {}, { - params: encodeTokenUpdate(req) + params: encodeTokenUpdate(req), } ) .pipe(map(res => decodeTokenUpdate(res))); @@ -81,10 +91,10 @@ export class ExternalApiService { public urlInfo(req: UrlInfoRequest): Observable { return this.httpClient .post( - this.moduleConfig.urls.urlInfo, + this.urls.urlInfo, {}, { - params: encodeUrlInfo(req) + params: encodeUrlInfo(req), } ) .pipe(map(res => decodeUrlInfo(res))); diff --git a/projects/ucap-webmessenger-api-external/src/lib/types/module-config.ts b/projects/ucap-webmessenger-api-external/src/lib/types/module-config.ts deleted file mode 100644 index 6b3b036..0000000 --- a/projects/ucap-webmessenger-api-external/src/lib/types/module-config.ts +++ /dev/null @@ -1,8 +0,0 @@ -export interface ModuleConfig { - urls: { - checkUserInfoEx: string; - companyList: string; - tokenUpdate: string; - urlInfo: string; - }; -} diff --git a/projects/ucap-webmessenger-api-external/src/lib/ucap-external-api.module.ts b/projects/ucap-webmessenger-api-external/src/lib/ucap-external-api.module.ts index c21dada..c6283c7 100644 --- a/projects/ucap-webmessenger-api-external/src/lib/ucap-external-api.module.ts +++ b/projects/ucap-webmessenger-api-external/src/lib/ucap-external-api.module.ts @@ -1,15 +1,16 @@ import { NgModule, ModuleWithProviders } from '@angular/core'; -import { _MODULE_CONFIG } from './types/token'; import { ExternalApiService } from './services/external-api.service'; -import { ModuleConfig } from './types/module-config'; + +import { _MODULE_CONFIG } from './config/token'; +import { ModuleConfig } from './config/module-config'; const SERVICES = [ExternalApiService]; @NgModule({ declarations: [], imports: [], - exports: [] + exports: [], }) export class UCapExternalApiModule { public static forRoot( @@ -17,7 +18,7 @@ export class UCapExternalApiModule { ): ModuleWithProviders { return { ngModule: UCapExternalApiModule, - providers: [{ provide: _MODULE_CONFIG, useValue: config }, ...SERVICES] + providers: [{ provide: _MODULE_CONFIG, useValue: config }, ...SERVICES], }; } } diff --git a/projects/ucap-webmessenger-api-external/src/public-api.ts b/projects/ucap-webmessenger-api-external/src/public-api.ts index b9a5d5a..375f649 100644 --- a/projects/ucap-webmessenger-api-external/src/public-api.ts +++ b/projects/ucap-webmessenger-api-external/src/public-api.ts @@ -2,8 +2,6 @@ * Public API Surface of ucap-webmessenger-api-public */ -export * from './lib/types/module-config'; - export * from './lib/models/company'; export * from './lib/apis/check-user-info-ex'; @@ -14,3 +12,6 @@ export * from './lib/apis/url-info'; export * from './lib/services/external-api.service'; export * from './lib/ucap-external-api.module'; + +export * from './lib/config/urls'; +export * from './lib/config/module-config'; diff --git a/projects/ucap-webmessenger-api-message/README.md b/projects/ucap-webmessenger-api-message/README.md new file mode 100644 index 0000000..8265c6c --- /dev/null +++ b/projects/ucap-webmessenger-api-message/README.md @@ -0,0 +1,24 @@ +# UcapWebmessengerApiMessage + +This library was generated with [Angular CLI](https://github.com/angular/angular-cli) version 8.2.11. + +## Code scaffolding + +Run `ng generate component component-name --project ucap-webmessenger-api-message` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module --project ucap-webmessenger-api-message`. +> Note: Don't forget to add `--project ucap-webmessenger-api-message` or else it will be added to the default project in your `angular.json` file. + +## Build + +Run `ng build ucap-webmessenger-api-message` to build the project. The build artifacts will be stored in the `dist/` directory. + +## Publishing + +After building your library with `ng build ucap-webmessenger-api-message`, go to the dist folder `cd dist/ucap-webmessenger-api-message` and run `npm publish`. + +## Running unit tests + +Run `ng test ucap-webmessenger-api-message` to execute the unit tests via [Karma](https://karma-runner.github.io). + +## Further help + +To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md). diff --git a/projects/ucap-webmessenger-electron-core/karma.conf.js b/projects/ucap-webmessenger-api-message/karma.conf.js similarity index 97% rename from projects/ucap-webmessenger-electron-core/karma.conf.js rename to projects/ucap-webmessenger-api-message/karma.conf.js index 5eed153..30ab22f 100644 --- a/projects/ucap-webmessenger-electron-core/karma.conf.js +++ b/projects/ucap-webmessenger-api-message/karma.conf.js @@ -16,7 +16,7 @@ module.exports = function (config) { clearContext: false // leave Jasmine Spec Runner output visible in browser }, coverageIstanbulReporter: { - dir: require('path').join(__dirname, '../../coverage/ucap-webmessenger-electron-core'), + dir: require('path').join(__dirname, '../../coverage/ucap-webmessenger-api-message'), reports: ['html', 'lcovonly', 'text-summary'], fixWebpackSourcePaths: true }, diff --git a/projects/ucap-webmessenger-electron-core/ng-package.json b/projects/ucap-webmessenger-api-message/ng-package.json similarity index 68% rename from projects/ucap-webmessenger-electron-core/ng-package.json rename to projects/ucap-webmessenger-api-message/ng-package.json index e9e6f76..7b43c75 100644 --- a/projects/ucap-webmessenger-electron-core/ng-package.json +++ b/projects/ucap-webmessenger-api-message/ng-package.json @@ -1,6 +1,6 @@ { "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", - "dest": "../../dist/ucap-webmessenger-electron-core", + "dest": "../../dist/ucap-webmessenger-api-message", "lib": { "entryFile": "src/public-api.ts" } diff --git a/projects/ucap-webmessenger-electron-core/package.json b/projects/ucap-webmessenger-api-message/package.json similarity index 72% rename from projects/ucap-webmessenger-electron-core/package.json rename to projects/ucap-webmessenger-api-message/package.json index a7ad1a3..2bc9d39 100644 --- a/projects/ucap-webmessenger-electron-core/package.json +++ b/projects/ucap-webmessenger-api-message/package.json @@ -1,5 +1,5 @@ { - "name": "@ucap-webmessenger/electron-core", + "name": "@ucap-webmessenger/api-message", "version": "0.0.1", "peerDependencies": { "@angular/common": "^8.2.11", diff --git a/projects/ucap-webmessenger-api-message/src/lib/apis/del.ts b/projects/ucap-webmessenger-api-message/src/lib/apis/del.ts new file mode 100644 index 0000000..16bf092 --- /dev/null +++ b/projects/ucap-webmessenger-api-message/src/lib/apis/del.ts @@ -0,0 +1,31 @@ +import { + APIRequest, + APIResponse, + APIDecoder, + APIJsonEncoder, + MessageAPIResponse +} from '@ucap-webmessenger/api'; +import { DeviceType } from '@ucap-webmessenger/core'; +import { MessageType } from '../types/message.type'; + +export interface DelRequest extends APIRequest { + userSeq: number; + deviceType: DeviceType; + tokenKey: string; + type: MessageType; + + msgList: { msgId: number }[]; +} + +export interface DelResponse extends MessageAPIResponse {} + +export const encodeDel: APIJsonEncoder = (req: DelRequest) => { + return JSON.stringify(req); +}; + +export const decodeDel: APIDecoder = (res: any) => { + return { + responseCode: res.responseCode, + responseMsg: res.responseMsg + } as DelResponse; +}; diff --git a/projects/ucap-webmessenger-api-message/src/lib/apis/detail.ts b/projects/ucap-webmessenger-api-message/src/lib/apis/detail.ts new file mode 100644 index 0000000..99d3ee4 --- /dev/null +++ b/projects/ucap-webmessenger-api-message/src/lib/apis/detail.ts @@ -0,0 +1,182 @@ +import { + APIRequest, + MessageAPIResponse, + APIDecoder, + APIJsonEncoder +} from '@ucap-webmessenger/api'; +import { DeviceType } from '@ucap-webmessenger/core'; +import { MessageType } from '../types/message.type'; +import { CategoryType } from '../types/category.type'; + +export interface DetailRequest extends APIRequest { + userSeq: number; + deviceType: DeviceType; + tokenKey: string; + type: MessageType; + + msgId: number; +} + +export interface DetailResponse extends MessageAPIResponse { + msgInfo: { + msgId: number; + category: CategoryType; + title: string; + titleYn: boolean; + }; + + content: { + type: MessageType; + sendUserSeq: number; + sendUserName: string; + reservationTime: string | null; + sendYn: boolean; + regDate: string; + attachmentYn: boolean; + smsYn: boolean; + fileAllow: string; + }[]; + + recvList: { + userSeq: number; + userName: string; + cancelYn: boolean; + readDate: string; + readYn: boolean; + }[]; +} + +export const encodeDetail: APIJsonEncoder = ( + req: DetailRequest +) => { + return JSON.stringify(req); +}; + +export const decodeDetail: APIDecoder = (res: any) => { + return { + responseCode: res.responseCode, + responseMsg: res.responseMsg, + + msgInfo: null, + + content: [], + + recvList: [] + } as DetailResponse; +}; + +export interface ReadRequest extends APIRequest { + userSeq: number; + deviceType: DeviceType; + tokenKey: string; + type: MessageType; + + msgId: number; +} + +export interface ReadResponse extends MessageAPIResponse {} + +export const encodeRead: APIJsonEncoder = (req: ReadRequest) => { + return JSON.stringify(req); +}; + +export const decodeRead: APIDecoder = (res: any) => { + return { + responseCode: res.responseCode, + responseMsg: res.responseMsg + } as ReadResponse; +}; + +export interface ReadCheckResponse extends MessageAPIResponse { + recvList: { + userSeq: number; + userName: string; + cancelYn: boolean; + readDate: string; + readYn: boolean; + }[]; +} + +export const decodeReadCheck: APIDecoder = (res: any) => { + return { + responseCode: res.responseCode, + responseMsg: res.responseMsg, + + recvList: [] + } as ReadCheckResponse; +}; + +export interface CancelRequest extends APIRequest { + userSeq: number; + deviceType: DeviceType; + tokenKey: string; + type: MessageType; + + msgId: number; + recvUserList: { userSeq: number }[]; +} + +export interface CancelResponse extends MessageAPIResponse {} + +export const encodeCancel: APIJsonEncoder = ( + req: CancelRequest +) => { + return JSON.stringify(req); +}; + +export const decodeCancel: APIDecoder = (res: any) => { + return { + responseCode: res.responseCode, + responseMsg: res.responseMsg + } as CancelResponse; +}; + +export interface CancelReservationRequest extends APIRequest { + userSeq: number; + deviceType: DeviceType; + tokenKey: string; + type: MessageType; + + msgId: number; +} + +export interface CancelReservationResponse extends MessageAPIResponse {} + +export const encodeCancelReservation: APIJsonEncoder = ( + req: CancelReservationRequest +) => { + return JSON.stringify(req); +}; + +export const decodeCancelReservation: APIDecoder = ( + res: any +) => { + return { + responseCode: res.responseCode, + responseMsg: res.responseMsg + } as CancelReservationResponse; +}; + +export interface RetrieveResourceFileRequest extends APIRequest { + userSeq: number; + deviceType: DeviceType; + tokenKey: string; + type: MessageType; + + msgId: number; + resUrl: string; +} + +export interface RetrieveResourceFileResponse extends MessageAPIResponse {} + +export const encodeRetrieveResourceFile: APIJsonEncoder = ( + req: RetrieveResourceFileRequest +) => { + return JSON.stringify(req); +}; + +export const decodeRetrieveResourceFile: APIDecoder = ( + res: any +) => { + return {} as RetrieveResourceFileResponse; +}; diff --git a/projects/ucap-webmessenger-api-message/src/lib/apis/edit-reservation-ex.ts b/projects/ucap-webmessenger-api-message/src/lib/apis/edit-reservation-ex.ts new file mode 100644 index 0000000..c0562cf --- /dev/null +++ b/projects/ucap-webmessenger-api-message/src/lib/apis/edit-reservation-ex.ts @@ -0,0 +1,42 @@ +import { + APIRequest, + MessageAPIResponse, + APIJsonEncoder, + APIDecoder +} from '@ucap-webmessenger/api'; +import { DeviceType } from '@ucap-webmessenger/core'; +import { CategoryType } from '../types/category.type'; +import { ContentType } from '../types/content.type'; + +export interface EditReservationRequest extends APIRequest { + userSeq: number; + deviceType: DeviceType; + tokenKey: string; + msgId: number; + category: CategoryType; + title: string; + titleYn: boolean; + listOrder: ContentType[]; + reservationTime: string; + smsYn: boolean; + + textContent: { text: string }[]; + recvUserList: { userSeq: number; userName: string }[]; +} + +export interface EditReservationResponse extends MessageAPIResponse {} + +export const encodeEditReservation: APIJsonEncoder = ( + req: EditReservationRequest +) => { + return JSON.stringify(req); +}; + +export const decodeEditReservation: APIDecoder = ( + res: any +) => { + return { + responseCode: res.responseCode, + responseMsg: res.responseMsg + } as EditReservationResponse; +}; diff --git a/projects/ucap-webmessenger-api-message/src/lib/apis/my-message.ts b/projects/ucap-webmessenger-api-message/src/lib/apis/my-message.ts new file mode 100644 index 0000000..d112997 --- /dev/null +++ b/projects/ucap-webmessenger-api-message/src/lib/apis/my-message.ts @@ -0,0 +1,104 @@ +import { + APIRequest, + MessageAPIResponse, + APIJsonEncoder, + APIDecoder +} from '@ucap-webmessenger/api'; +import { DeviceType } from '@ucap-webmessenger/core'; + +export interface SaveMyRequest extends APIRequest { + userSeq: number; + deviceType: DeviceType; + tokenKey: string; + userName: string; + message: string; +} + +export interface SaveMyResponse extends MessageAPIResponse {} + +export const encodeSaveMy: APIJsonEncoder = ( + req: SaveMyRequest +) => { + return JSON.stringify(req); +}; + +export const decodeSaveMy: APIDecoder = (res: any) => { + return { + responseCode: res.responseCode, + responseMsg: res.responseMsg + } as SaveMyResponse; +}; + +export interface RetrieveMyRequest extends APIRequest { + userSeq: number; + deviceType: DeviceType; + tokenKey: string; + pageSize: number; + pageCount: number; +} + +export interface RetrieveMyResponse extends MessageAPIResponse { + msgList: { + msgId: number; + message: string; + }[]; +} + +export const encodeRetrieveMy: APIJsonEncoder = ( + req: RetrieveMyRequest +) => { + return JSON.stringify(req); +}; + +export const decodeRetrieveMy: APIDecoder = (res: any) => { + return { + responseCode: res.responseCode, + responseMsg: res.responseMsg, + + msgList: [] + } as RetrieveMyResponse; +}; + +export interface DelMyRequest extends APIRequest { + userSeq: number; + deviceType: DeviceType; + tokenKey: string; +} + +export interface DelMyResponse extends MessageAPIResponse {} + +export const encodeDelMy: APIJsonEncoder = ( + req: DelMyRequest +) => { + return JSON.stringify(req); +}; + +export const decodeDelMy: APIDecoder = (res: any) => { + return { + responseCode: res.responseCode, + responseMsg: res.responseMsg + } as DelMyResponse; +}; + +export interface EditMyRequest extends APIRequest { + userSeq: number; + deviceType: DeviceType; + tokenKey: string; + msgId: number; + message: string; +} + +export interface EditMyResponse extends MessageAPIResponse {} + +export const encodeEditMy: APIJsonEncoder = ( + req: EditMyRequest +) => { + return JSON.stringify(req); +}; + +export const decodeEditMy: APIDecoder = (res: any) => { + return { + responseCode: res.responseCode, + responseMsg: res.responseMsg + } as EditMyResponse; +}; diff --git a/projects/ucap-webmessenger-api-message/src/lib/apis/retrieve.ts b/projects/ucap-webmessenger-api-message/src/lib/apis/retrieve.ts new file mode 100644 index 0000000..bd5b7c6 --- /dev/null +++ b/projects/ucap-webmessenger-api-message/src/lib/apis/retrieve.ts @@ -0,0 +1,181 @@ +import { + APIRequest, + MessageAPIResponse, + APIDecoder, + APIJsonEncoder +} from '@ucap-webmessenger/api'; +import { DeviceType } from '@ucap-webmessenger/core'; +import { MessageType } from '../types/message.type'; +import { MessageList } from '../models/message-list'; +import { CategoryType } from '../types/category.type'; +import { ContentType } from '../types/content.type'; + +export interface RetrieveRequest extends APIRequest { + userSeq: number; + deviceType: DeviceType; + tokenKey: string; + type: MessageType; + pageSize: number; + pageCount: number; +} +export interface RetrieveSearchRequest extends RetrieveRequest { + searchTitle: string; + searchName: string; + searchContent: string; +} + +export interface RetrieveResponse extends MessageAPIResponse { + pageCount: number; + pageSize: number; + totalCount: number; + + messageList: MessageList[]; +} + +export const encodeRetrieve: APIJsonEncoder = ( + req: RetrieveRequest +) => { + return JSON.stringify(req); +}; +export const encodeRetrieveSearch: APIJsonEncoder = ( + req: RetrieveResponse +) => { + return JSON.stringify(req); +}; + +export const decodeRetrieveSend: APIDecoder = (res: any) => { + const messageList: MessageList[] = []; + if (!!res.msgList && res.msgList.length > 0) { + for (const msgList of res.msgList) { + messageList.push({ + ...msgList, + userCount: + !msgList.userCount || msgList.userCount === null + ? 0 + : msgList.userCount, + userReadCount: + !msgList.userReadCount || msgList.userReadCount === null + ? 0 + : msgList.userReadCount, + titleYn: msgList.titleYn === 'Y' ? true : false, + attachmentYn: msgList.attachmentYn === 'Y' ? true : false + } as MessageList); + } + } + + return { + responseCode: res.responseCode, + responseMsg: res.responseMsg, + + pageCount: res.pageCount, + pageSize: res.pageSize, + totalCount: res.totalCount, + + messageList + } as RetrieveResponse; +}; + +export const decodeRetrieveReceive: APIDecoder = ( + res: any +) => { + const messageList: MessageList[] = []; + if (!!res.msgList && res.msgList.length > 0) { + for (const msgList of res.msgList) { + messageList.push({ + ...msgList, + userCount: + !msgList.userCount || msgList.userCount === null + ? 0 + : msgList.userCount, + userReadCount: + !msgList.userReadCount || msgList.userReadCount === null + ? 0 + : msgList.userReadCount, + titleYn: msgList.titleYn === 'Y' ? true : false, + attachmentYn: msgList.attachmentYn === 'Y' ? true : false, + readYn: msgList.readYn === 'Y' ? true : false + } as MessageList); + } + } + + return { + responseCode: res.responseCode, + responseMsg: res.responseMsg, + + pageCount: res.pageCount, + pageSize: res.pageSize, + totalCount: res.totalCount, + + messageList + } as RetrieveResponse; +}; + +export const decodeRetrieveReservation: APIDecoder = ( + res: any +) => { + const messageList: MessageList[] = []; + if (!!res.msgList && res.msgList.length > 0) { + for (const msgList of res.msgList) { + messageList.push({ + ...msgList, + + userCount: + !msgList.userCount || msgList.userCount === null + ? 0 + : msgList.userCount, + userReadCount: + !msgList.userReadCount || msgList.userReadCount === null + ? 0 + : msgList.userReadCount, + titleYn: msgList.titleYn === 'Y' ? true : false, + attachmentYn: msgList.attachmentYn === 'Y' ? true : false + } as MessageList); + } + } + + return { + responseCode: res.responseCode, + responseMsg: res.responseMsg, + + pageCount: res.pageCount, + pageSize: res.pageSize, + totalCount: res.totalCount, + + messageList + } as RetrieveResponse; +}; + +export const decodeRetrieveSearch: APIDecoder = ( + res: any +) => { + const messageList: MessageList[] = []; + if (!!res.msgList && res.msgList.length > 0) { + for (const msgList of res.msgList) { + messageList.push({ + ...msgList, + + userCount: + !msgList.userCount || msgList.userCount === null + ? 0 + : msgList.userCount, + userReadCount: + !msgList.userReadCount || msgList.userReadCount === null + ? 0 + : msgList.userReadCount, + titleYn: msgList.titleYn === 'Y' ? true : false, + attachmentYn: msgList.attachmentYn === 'Y' ? true : false + } as MessageList); + } + } + + return { + responseCode: res.responseCode, + responseMsg: res.responseMsg, + + pageCount: res.pageCount, + pageSize: res.pageSize, + totalCount: res.totalCount, + + messageList + } as RetrieveResponse; +}; diff --git a/projects/ucap-webmessenger-api-message/src/lib/apis/send-copy.ts b/projects/ucap-webmessenger-api-message/src/lib/apis/send-copy.ts new file mode 100644 index 0000000..e61f623 --- /dev/null +++ b/projects/ucap-webmessenger-api-message/src/lib/apis/send-copy.ts @@ -0,0 +1,41 @@ +import { + APIRequest, + MessageAPIResponse, + APIJsonEncoder, + APIDecoder +} from '@ucap-webmessenger/api'; +import { DeviceType } from '@ucap-webmessenger/core'; +import { CategoryType } from '../types/category.type'; +import { ContentType } from '../types/content.type'; + +export interface SendCopyRequest extends APIRequest { + userSeq: number; + deviceType: DeviceType; + tokenKey: string; + userName: string; + msgId: number; + category: CategoryType; + title: string; + titleYn: boolean; + listOrder: ContentType[]; + reservationTime: string; + smsYn: boolean; + + textContent: { text: string }[]; + recvUserList: { userSeq: number; userName: string }[]; +} + +export interface SendCopyResponse extends MessageAPIResponse {} + +export const encodeSendCopy: APIJsonEncoder = ( + req: SendCopyRequest +) => { + return JSON.stringify(req); +}; + +export const decodeSendCopy: APIDecoder = (res: any) => { + return { + responseCode: res.responseCode, + responseMsg: res.responseMsg + } as SendCopyResponse; +}; diff --git a/projects/ucap-webmessenger-api-message/src/lib/apis/send.ts b/projects/ucap-webmessenger-api-message/src/lib/apis/send.ts new file mode 100644 index 0000000..1e97bc0 --- /dev/null +++ b/projects/ucap-webmessenger-api-message/src/lib/apis/send.ts @@ -0,0 +1,41 @@ +import { + APIRequest, + MessageAPIResponse, + APIJsonEncoder, + APIDecoder +} from '@ucap-webmessenger/api'; +import { DeviceType } from '@ucap-webmessenger/core'; +import { MessageType } from '../types/message.type'; +import { CategoryType } from '../types/category.type'; +import { ContentType } from '../types/content.type'; + +export interface SendRequest extends APIRequest { + userSeq: number; + deviceType: DeviceType; + tokenKey: string; + type: MessageType; + + userName: string; + category: CategoryType; + title: string; + titleYn: boolean; + listOrder: ContentType[]; + reservationTime: string; + smsYn: boolean; + + textContent: { text: string }[]; + recvUserList: { userSeq: number; userName: string }[]; +} + +export interface SendResponse extends MessageAPIResponse {} + +export const encodeSend: APIJsonEncoder = (req: SendRequest) => { + return JSON.stringify(req); +}; + +export const decodeSend: APIDecoder = (res: any) => { + return { + responseCode: res.responseCode, + responseMsg: res.responseMsg + } as SendResponse; +}; diff --git a/projects/ucap-webmessenger-api-message/src/lib/apis/unread-count.ts b/projects/ucap-webmessenger-api-message/src/lib/apis/unread-count.ts new file mode 100644 index 0000000..75db2a2 --- /dev/null +++ b/projects/ucap-webmessenger-api-message/src/lib/apis/unread-count.ts @@ -0,0 +1,33 @@ +import { + APIRequest, + MessageAPIResponse, + APIJsonEncoder, + APIDecoder +} from '@ucap-webmessenger/api'; +import { DeviceType } from '@ucap-webmessenger/core'; + +export interface UnreadCountRequest extends APIRequest { + userSeq: number; + deviceType: DeviceType; + tokenKey: string; +} + +export interface UnreadCountResponse extends MessageAPIResponse { + unreadCount: number; +} + +export const encodeUnreadCount: APIJsonEncoder = ( + req: UnreadCountRequest +) => { + return JSON.stringify(req); +}; + +export const decodeUnreadCount: APIDecoder = ( + res: any +) => { + return { + responseCode: res.responseCode, + responseMsg: res.responseMsg, + unreadCount: res.unreadCount + } as UnreadCountResponse; +}; diff --git a/projects/ucap-webmessenger-api-message/src/lib/config/module-config.ts b/projects/ucap-webmessenger-api-message/src/lib/config/module-config.ts new file mode 100644 index 0000000..7091509 --- /dev/null +++ b/projects/ucap-webmessenger-api-message/src/lib/config/module-config.ts @@ -0,0 +1,5 @@ +import { ModuleConfig as CoreModuleConfig } from '@ucap-webmessenger/core'; + +import { Urls } from './urls'; + +export interface ModuleConfig extends CoreModuleConfig {} diff --git a/projects/ucap-webmessenger-api-message/src/lib/config/token.ts b/projects/ucap-webmessenger-api-message/src/lib/config/token.ts new file mode 100644 index 0000000..95f75df --- /dev/null +++ b/projects/ucap-webmessenger-api-message/src/lib/config/token.ts @@ -0,0 +1,5 @@ +import { InjectionToken } from '@angular/core'; + +export const _MODULE_CONFIG = new InjectionToken( + '@ucap-webmessenger/api-message config of module' +); diff --git a/projects/ucap-webmessenger-api-message/src/lib/config/urls.ts b/projects/ucap-webmessenger-api-message/src/lib/config/urls.ts new file mode 100644 index 0000000..78f47ae --- /dev/null +++ b/projects/ucap-webmessenger-api-message/src/lib/config/urls.ts @@ -0,0 +1,47 @@ +export interface Urls { + /** 발신 메시지 list 조회 */ + retrieveSendMessageList: string; + /** 수신 메시지 list 조회 */ + retrieveRecvMessageList: string; + /** 예약 메시지 list 조회 */ + retrieveReservationMessageList: string; + /** 메시지 검색 list 조회 */ + retrieveSearchMessage: string; + + /** 메시지 발송 */ + sendNewMessage: string; + + /** 메시지 상세 정보 조회 */ + retrieveMessageDetail: string; + /** 메시지 읽음 */ + readMessage: string; + /** 읽음 확인 */ + retrieveReadCheck: string; + /** 발송 취소 */ + cancelMessage: string; + /** 예약 메시지 발송 취소 */ + cancelReservationMessage: string; + /** content Resource 파일 요청 */ + retrieveResourceFile: string; + + /** 메시지 삭제 */ + deleteMessage: string; + + /** My 메시지 등록 */ + saveMyMessage: string; + /** My 메시지 조회 */ + retrieveMyMessage: string; + /** My 메시지 삭제 */ + deleteMyMessage: string; + /** My 메시지 수정 */ + editMyMessage: string; + + /** 예약 메시지 수정 - 썸네일 기능 완료된 경우 */ + editReservationMessageEx: string; + + /** 메시지 전달 - 메시지 복사 후, 발송 */ + sendCopyMessage: string; + + /** 읽지 않은 메시지 개수 조회 */ + retrieveUnreadCount: string; +} diff --git a/projects/ucap-webmessenger-api-message/src/lib/models/message-list.ts b/projects/ucap-webmessenger-api-message/src/lib/models/message-list.ts new file mode 100644 index 0000000..3d34e52 --- /dev/null +++ b/projects/ucap-webmessenger-api-message/src/lib/models/message-list.ts @@ -0,0 +1,34 @@ +import { MessageType } from '../types/message.type'; +import { CategoryType } from '../types/category.type'; +import { ContentType } from '../types/content.type'; + +export interface MessageList { + /** 메시지 ID */ + msgId: number; + /** 메시지 카테고리 */ + category: CategoryType; + /** 메시지 TITLE */ + title: string; + /** 메시지 제목 입력 여부 */ + titleYn: boolean; + /** 메시지 TYPE */ + type: MessageType; + /** 수신자의 시퀀스 */ + userSeq?: number; + /** 수신자 */ + userName: string; + /** 수신자 수 */ + userCount?: number; + /** 읽은 사람 수 */ + userReadCount?: number; + /** 예약시간 :: DATE 형식은 "yyyy-MM-dd HH:mm:ss" (단 초는 00고정. 예약메일은 분단위까지만) */ + reservationTime?: string; + /** 발신시간 */ + regDate: string; + /** CONTENT 타입 */ + resType: ContentType; + /** 첨부파일 존재여부 */ + attachmentYn: boolean; + /** 읽음여부 */ + readYn?: boolean; +} diff --git a/projects/ucap-webmessenger-api-message/src/lib/services/message-api.service.spec.ts b/projects/ucap-webmessenger-api-message/src/lib/services/message-api.service.spec.ts new file mode 100644 index 0000000..bf2baae --- /dev/null +++ b/projects/ucap-webmessenger-api-message/src/lib/services/message-api.service.spec.ts @@ -0,0 +1,12 @@ +import { TestBed } from '@angular/core/testing'; + +import { MessageApiService } from './message-api.service'; + +describe('MessageApiService', () => { + beforeEach(() => TestBed.configureTestingModule({})); + + it('should be created', () => { + const service: MessageApiService = TestBed.get(MessageApiService); + expect(service).toBeTruthy(); + }); +}); diff --git a/projects/ucap-webmessenger-api-message/src/lib/services/message-api.service.ts b/projects/ucap-webmessenger-api-message/src/lib/services/message-api.service.ts new file mode 100644 index 0000000..444ca0c --- /dev/null +++ b/projects/ucap-webmessenger-api-message/src/lib/services/message-api.service.ts @@ -0,0 +1,293 @@ +import { Injectable, Inject } from '@angular/core'; +import { HttpClient, HttpHeaders } from '@angular/common/http'; + +import { Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; + +import { _MODULE_CONFIG } from '../config/token'; +import { ModuleConfig } from '../config/module-config'; +import { UrlConfig } from '@ucap-webmessenger/core'; +import { Urls } from '../config/urls'; +import { + RetrieveRequest, + RetrieveResponse, + encodeRetrieve, + decodeRetrieveSend, + decodeRetrieveReceive, + decodeRetrieveReservation, + RetrieveSearchRequest, + decodeRetrieveSearch +} from '../apis/retrieve'; +import { + SendRequest, + SendResponse, + encodeSend, + decodeSend +} from '../apis/send'; +import { + DetailRequest, + DetailResponse, + encodeDetail, + decodeDetail, + ReadRequest, + ReadResponse, + encodeRead, + decodeRead, + ReadCheckResponse, + decodeReadCheck, + CancelRequest, + CancelResponse, + encodeCancel, + decodeCancel, + CancelReservationRequest, + CancelReservationResponse, + encodeCancelReservation, + decodeCancelReservation, + RetrieveResourceFileRequest, + RetrieveResourceFileResponse, + encodeRetrieveResourceFile, + decodeRetrieveResourceFile +} from '../apis/detail'; +import { DelRequest, DelResponse, decodeDel, encodeDel } from '../apis/del'; +import { + SaveMyRequest, + SaveMyResponse, + encodeSaveMy, + decodeSaveMy, + RetrieveMyRequest, + RetrieveMyResponse, + encodeRetrieveMy, + decodeRetrieveMy, + DelMyRequest, + DelMyResponse, + encodeDelMy, + decodeDelMy, + EditMyRequest, + EditMyResponse, + encodeEditMy, + decodeEditMy +} from '../apis/my-message'; +import { + EditReservationRequest, + EditReservationResponse, + encodeEditReservation, + decodeEditReservation +} from '../apis/edit-reservation-ex'; +import { + SendCopyRequest, + SendCopyResponse, + encodeSendCopy, + decodeSendCopy +} from '../apis/send-copy'; +import { + UnreadCountRequest, + UnreadCountResponse, + encodeUnreadCount, + decodeUnreadCount +} from '../apis/unread-count'; + +@Injectable({ + providedIn: 'root' +}) +export class MessageApiService { + readonly urls: Urls; + + headers: HttpHeaders; + + constructor( + @Inject(_MODULE_CONFIG) private moduleConfig: ModuleConfig, + private httpClient: HttpClient + ) { + this.urls = UrlConfig.getUrls( + this.moduleConfig.hostConfig, + this.moduleConfig.urls + ); + + this.headers = new HttpHeaders({ + 'Content-Type': 'application/json; charset=utf-8' + }); + } + + /** retrieve */ + public retrieveSendMessage( + req: RetrieveRequest + ): Observable { + return this.httpClient + .post(this.urls.retrieveSendMessageList, encodeRetrieve(req), { + headers: this.headers + }) + .pipe(map(res => decodeRetrieveSend(res))); + } + public retrieveReceiveMessage( + req: RetrieveRequest + ): Observable { + return this.httpClient + .post(this.urls.retrieveRecvMessageList, encodeRetrieve(req), { + headers: this.headers + }) + .pipe(map(res => decodeRetrieveReceive(res))); + } + public retrieveReservationMessage( + req: RetrieveRequest + ): Observable { + return this.httpClient + .post( + this.urls.retrieveReservationMessageList, + encodeRetrieve(req), + { + headers: this.headers + } + ) + .pipe(map(res => decodeRetrieveReservation(res))); + } + public retrieveSearchMessage( + req: RetrieveSearchRequest + ): Observable { + return this.httpClient + .post(this.urls.retrieveSearchMessage, encodeRetrieve(req), { + headers: this.headers + }) + .pipe(map(res => decodeRetrieveSearch(res))); + } + + /** send */ + public sendMessage(req: SendRequest): Observable { + return this.httpClient + .post(this.urls.sendNewMessage, encodeSend(req), { + headers: this.headers + }) + .pipe(map(res => decodeSend(res))); + } + + /** detail */ + public detailMessage(req: DetailRequest): Observable { + return this.httpClient + .post(this.urls.retrieveMessageDetail, encodeDetail(req), { + headers: this.headers + }) + .pipe(map(res => decodeDetail(res))); + } + public readMessage(req: ReadRequest): Observable { + return this.httpClient + .post(this.urls.readMessage, encodeRead(req), { + headers: this.headers + }) + .pipe(map(res => decodeRead(res))); + } + public retrieveReadCheck(req: ReadRequest): Observable { + return this.httpClient + .post(this.urls.retrieveReadCheck, encodeRead(req), { + headers: this.headers + }) + .pipe(map(res => decodeReadCheck(res))); + } + public cancelMessage(req: CancelRequest): Observable { + return this.httpClient + .post(this.urls.cancelMessage, encodeCancel(req), { + headers: this.headers + }) + .pipe(map(res => decodeCancel(res))); + } + public cancelReservationMessage( + req: CancelReservationRequest + ): Observable { + return this.httpClient + .post( + this.urls.cancelReservationMessage, + encodeCancelReservation(req), + { + headers: this.headers + } + ) + .pipe(map(res => decodeCancelReservation(res))); + } + public retrieveResourceFile( + req: RetrieveResourceFileRequest + ): Observable { + return this.httpClient + .post( + this.urls.retrieveResourceFile, + encodeRetrieveResourceFile(req), + { + headers: this.headers + } + ) + .pipe(map(res => decodeRetrieveResourceFile(res))); + } + + /** del */ + public deleteMessage(req: DelRequest): Observable { + return this.httpClient + .post(this.urls.deleteMessage, encodeDel(req), { + headers: this.headers + }) + .pipe(map(res => decodeDel(res))); + } + + /** save my message */ + public saveMyMessage(req: SaveMyRequest): Observable { + return this.httpClient + .post(this.urls.saveMyMessage, encodeSaveMy(req), { + headers: this.headers + }) + .pipe(map(res => decodeSaveMy(res))); + } + public retrieveMyMessage( + req: RetrieveMyRequest + ): Observable { + return this.httpClient + .post(this.urls.retrieveMyMessage, encodeRetrieveMy(req), { + headers: this.headers + }) + .pipe(map(res => decodeRetrieveMy(res))); + } + public deleteMyMessage(req: DelMyRequest): Observable { + return this.httpClient + .post(this.urls.deleteMyMessage, encodeDelMy(req), { + headers: this.headers + }) + .pipe(map(res => decodeDelMy(res))); + } + public editMyMessage(req: EditMyRequest): Observable { + return this.httpClient + .post(this.urls.editMyMessage, encodeEditMy(req), { + headers: this.headers + }) + .pipe(map(res => decodeEditMy(res))); + } + + /** edit reservation */ + public editReservationMessageEx( + req: EditReservationRequest + ): Observable { + return this.httpClient + .post( + this.urls.editReservationMessageEx, + encodeEditReservation(req), + { + headers: this.headers + } + ) + .pipe(map(res => decodeEditReservation(res))); + } + + /** send-copy(forward) */ + public sendCopyMessage(req: SendCopyRequest): Observable { + return this.httpClient + .post(this.urls.sendCopyMessage, encodeSendCopy(req), { + headers: this.headers + }) + .pipe(map(res => decodeSendCopy(res))); + } + + /** unread count */ + public retrieveUnreadCount( + req: UnreadCountRequest + ): Observable { + return this.httpClient + .post(this.urls.retrieveUnreadCount, encodeUnreadCount(req), { + headers: this.headers + }) + .pipe(map(res => decodeUnreadCount(res))); + } +} diff --git a/projects/ucap-webmessenger-api-message/src/lib/types/category.type.ts b/projects/ucap-webmessenger-api-message/src/lib/types/category.type.ts new file mode 100644 index 0000000..f45094a --- /dev/null +++ b/projects/ucap-webmessenger-api-message/src/lib/types/category.type.ts @@ -0,0 +1,6 @@ +export enum CategoryType { + /** 일반 */ + General = 'G', + /** 공지 */ + Notice = 'N' +} diff --git a/projects/ucap-webmessenger-api-message/src/lib/types/content.type.ts b/projects/ucap-webmessenger-api-message/src/lib/types/content.type.ts new file mode 100644 index 0000000..c626cb0 --- /dev/null +++ b/projects/ucap-webmessenger-api-message/src/lib/types/content.type.ts @@ -0,0 +1,8 @@ +export enum ContentType { + /** 텍스트 */ + Text = 'T', + /** 이미지파일 */ + Image = 'F', + /** 첨부파일 */ + AttachFile = 'A' +} diff --git a/projects/ucap-webmessenger-api-message/src/lib/types/message.search.type.ts b/projects/ucap-webmessenger-api-message/src/lib/types/message.search.type.ts new file mode 100644 index 0000000..6bc9dea --- /dev/null +++ b/projects/ucap-webmessenger-api-message/src/lib/types/message.search.type.ts @@ -0,0 +1,5 @@ +export enum MessageSearchType { + Title = 'T', + Name = 'N', + Contents = 'C' +} diff --git a/projects/ucap-webmessenger-api-message/src/lib/types/message.type.ts b/projects/ucap-webmessenger-api-message/src/lib/types/message.type.ts new file mode 100644 index 0000000..416651b --- /dev/null +++ b/projects/ucap-webmessenger-api-message/src/lib/types/message.type.ts @@ -0,0 +1,6 @@ +export enum MessageType { + Send = 'S', + Receive = 'R', + Reservation = 'B', + All = 'A' +} diff --git a/projects/ucap-webmessenger-api-message/src/lib/ucap-message-api.module.ts b/projects/ucap-webmessenger-api-message/src/lib/ucap-message-api.module.ts new file mode 100644 index 0000000..601084a --- /dev/null +++ b/projects/ucap-webmessenger-api-message/src/lib/ucap-message-api.module.ts @@ -0,0 +1,24 @@ +import { NgModule, ModuleWithProviders } from '@angular/core'; + +import { MessageApiService } from './services/message-api.service'; + +import { _MODULE_CONFIG } from './config/token'; +import { ModuleConfig } from './config/module-config'; + +const SERVICES = [MessageApiService]; + +@NgModule({ + declarations: [], + imports: [], + exports: [], +}) +export class UCapMessageApiModule { + public static forRoot( + config: ModuleConfig + ): ModuleWithProviders { + return { + ngModule: UCapMessageApiModule, + providers: [{ provide: _MODULE_CONFIG, useValue: config }, ...SERVICES], + }; + } +} diff --git a/projects/ucap-webmessenger-api-message/src/public-api.ts b/projects/ucap-webmessenger-api-message/src/public-api.ts new file mode 100644 index 0000000..f51e2be --- /dev/null +++ b/projects/ucap-webmessenger-api-message/src/public-api.ts @@ -0,0 +1,24 @@ +/* + * Public API Surface of ucap-webmessenger-api-message + */ +export * from './lib/apis/del'; +export * from './lib/apis/detail'; +export * from './lib/apis/edit-reservation-ex'; +export * from './lib/apis/my-message'; +export * from './lib/apis/retrieve'; +export * from './lib/apis/send-copy'; +export * from './lib/apis/send'; + +export * from './lib/services/message-api.service'; + +export * from './lib/ucap-message-api.module'; + +export * from './lib/models/message-list'; + +export * from './lib/types/category.type'; +export * from './lib/types/content.type'; +export * from './lib/types/message.search.type'; +export * from './lib/types/message.type'; + +export * from './lib/config/urls'; +export * from './lib/config/module-config'; diff --git a/projects/ucap-webmessenger-api-message/src/test.ts b/projects/ucap-webmessenger-api-message/src/test.ts new file mode 100644 index 0000000..978c64f --- /dev/null +++ b/projects/ucap-webmessenger-api-message/src/test.ts @@ -0,0 +1,21 @@ +// This file is required by karma.conf.js and loads recursively all the .spec and framework files + +import 'zone.js/dist/zone'; +import 'zone.js/dist/zone-testing'; +import { getTestBed } from '@angular/core/testing'; +import { + BrowserDynamicTestingModule, + platformBrowserDynamicTesting +} from '@angular/platform-browser-dynamic/testing'; + +declare const require: any; + +// First, initialize the Angular testing environment. +getTestBed().initTestEnvironment( + BrowserDynamicTestingModule, + platformBrowserDynamicTesting() +); +// Then we find all the tests. +const context = require.context('./', true, /\.spec\.ts$/); +// And load the modules. +context.keys().map(context); diff --git a/projects/ucap-webmessenger-electron-core/tsconfig.lib.json b/projects/ucap-webmessenger-api-message/tsconfig.lib.json similarity index 100% rename from projects/ucap-webmessenger-electron-core/tsconfig.lib.json rename to projects/ucap-webmessenger-api-message/tsconfig.lib.json diff --git a/projects/ucap-webmessenger-api-message/tsconfig.spec.json b/projects/ucap-webmessenger-api-message/tsconfig.spec.json new file mode 100644 index 0000000..16da33d --- /dev/null +++ b/projects/ucap-webmessenger-api-message/tsconfig.spec.json @@ -0,0 +1,17 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "../../out-tsc/spec", + "types": [ + "jasmine", + "node" + ] + }, + "files": [ + "src/test.ts" + ], + "include": [ + "**/*.spec.ts", + "**/*.d.ts" + ] +} diff --git a/projects/ucap-webmessenger-api-message/tslint.json b/projects/ucap-webmessenger-api-message/tslint.json new file mode 100644 index 0000000..75be226 --- /dev/null +++ b/projects/ucap-webmessenger-api-message/tslint.json @@ -0,0 +1,17 @@ +{ + "extends": "../../tslint.json", + "rules": { + "directive-selector": [ + true, + "attribute", + "ucapApiMessage", + "camelCase" + ], + "component-selector": [ + true, + "element", + "ucap-api-message", + "kebab-case" + ] + } +} diff --git a/projects/ucap-webmessenger-api-public/src/lib/apis/update-info.ts b/projects/ucap-webmessenger-api-public/src/lib/apis/update-info.ts index 37bd0a9..1afb5f5 100644 --- a/projects/ucap-webmessenger-api-public/src/lib/apis/update-info.ts +++ b/projects/ucap-webmessenger-api-public/src/lib/apis/update-info.ts @@ -4,7 +4,7 @@ import { APIResponse, APIEncoder, APIDecoder, - ParameterUtil + ParameterUtil, } from '@ucap-webmessenger/api'; export interface UpdateInfoRequest extends APIRequest { @@ -19,7 +19,7 @@ export interface UpdateInfoResponse extends APIResponse { } const updateInfoEncodeMap = { - deviceType: 'p_device_type' + deviceType: 'p_device_type', }; export const encodeUpdateInfo: APIEncoder = ( @@ -35,6 +35,6 @@ export const decodeUpdateInfo: APIDecoder = (res: any) => { appVersion: res.AppVer, installUrl: res.InstallURL, launcherAppVersion: res.LauncherAppVer, - launcherInstallUrl: res.LauncherInstallURL + launcherInstallUrl: res.LauncherInstallURL, } as UpdateInfoResponse; }; diff --git a/projects/ucap-webmessenger-api-public/src/lib/config/module-config.ts b/projects/ucap-webmessenger-api-public/src/lib/config/module-config.ts new file mode 100644 index 0000000..7091509 --- /dev/null +++ b/projects/ucap-webmessenger-api-public/src/lib/config/module-config.ts @@ -0,0 +1,5 @@ +import { ModuleConfig as CoreModuleConfig } from '@ucap-webmessenger/core'; + +import { Urls } from './urls'; + +export interface ModuleConfig extends CoreModuleConfig {} diff --git a/projects/ucap-webmessenger-api-public/src/lib/types/token.ts b/projects/ucap-webmessenger-api-public/src/lib/config/token.ts similarity index 100% rename from projects/ucap-webmessenger-api-public/src/lib/types/token.ts rename to projects/ucap-webmessenger-api-public/src/lib/config/token.ts diff --git a/projects/ucap-webmessenger-api-public/src/lib/config/urls.ts b/projects/ucap-webmessenger-api-public/src/lib/config/urls.ts new file mode 100644 index 0000000..0156cf5 --- /dev/null +++ b/projects/ucap-webmessenger-api-public/src/lib/config/urls.ts @@ -0,0 +1,4 @@ +export interface Urls { + versionInfo2: string; + updateInfo: string; +} diff --git a/projects/ucap-webmessenger-api-public/src/lib/services/public-api.service.ts b/projects/ucap-webmessenger-api-public/src/lib/services/public-api.service.ts index d3dd1f9..739fdb6 100644 --- a/projects/ucap-webmessenger-api-public/src/lib/services/public-api.service.ts +++ b/projects/ucap-webmessenger-api-public/src/lib/services/public-api.service.ts @@ -4,39 +4,49 @@ import { HttpClient } from '@angular/common/http'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; -import { _MODULE_CONFIG } from '../types/token'; import { VersionInfo2Request, VersionInfo2Response, encodeVersionInfo2, - decodeVersionInfo2 + decodeVersionInfo2, } from '../apis/version-info2'; import { UpdateInfoRequest, UpdateInfoResponse, encodeUpdateInfo, - decodeUpdateInfo + decodeUpdateInfo, } from '../apis/update-info'; -import { ModuleConfig } from '../types/module-config'; + +import { _MODULE_CONFIG } from '../config/token'; +import { ModuleConfig } from '../config/module-config'; +import { UrlConfig } from '@ucap-webmessenger/core'; +import { Urls } from '../config/urls'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class PublicApiService { + readonly urls: Urls; + constructor( @Inject(_MODULE_CONFIG) private moduleConfig: ModuleConfig, private httpClient: HttpClient - ) {} + ) { + this.urls = UrlConfig.getUrls( + this.moduleConfig.hostConfig, + this.moduleConfig.urls + ); + } public versionInfo2( req: VersionInfo2Request ): Observable { return this.httpClient .post( - this.moduleConfig.urls.versionInfo2, + this.urls.versionInfo2, {}, { - params: encodeVersionInfo2(req) + params: encodeVersionInfo2(req), } ) .pipe(map((res: any) => decodeVersionInfo2(res))); @@ -45,10 +55,10 @@ export class PublicApiService { public updateInfo(req: UpdateInfoRequest): Observable { return this.httpClient .post( - this.moduleConfig.urls.updateInfo, + this.urls.updateInfo, {}, { - params: encodeUpdateInfo(req) + params: encodeUpdateInfo(req), } ) .pipe(map(res => decodeUpdateInfo(res))); diff --git a/projects/ucap-webmessenger-api-public/src/lib/types/module-config.ts b/projects/ucap-webmessenger-api-public/src/lib/types/module-config.ts deleted file mode 100644 index 4ed6691..0000000 --- a/projects/ucap-webmessenger-api-public/src/lib/types/module-config.ts +++ /dev/null @@ -1,6 +0,0 @@ -export interface ModuleConfig { - urls: { - versionInfo2: string; - updateInfo: string; - }; -} diff --git a/projects/ucap-webmessenger-api-public/src/lib/ucap-public-api.module.ts b/projects/ucap-webmessenger-api-public/src/lib/ucap-public-api.module.ts index 3424d78..cc92903 100644 --- a/projects/ucap-webmessenger-api-public/src/lib/ucap-public-api.module.ts +++ b/projects/ucap-webmessenger-api-public/src/lib/ucap-public-api.module.ts @@ -1,15 +1,16 @@ import { NgModule, ModuleWithProviders } from '@angular/core'; -import { _MODULE_CONFIG } from './types/token'; import { PublicApiService } from './services/public-api.service'; -import { ModuleConfig } from './types/module-config'; + +import { _MODULE_CONFIG } from './config/token'; +import { ModuleConfig } from './config/module-config'; const SERVICES = [PublicApiService]; @NgModule({ declarations: [], imports: [], - exports: [] + exports: [], }) export class UCapPublicApiModule { public static forRoot( @@ -17,7 +18,7 @@ export class UCapPublicApiModule { ): ModuleWithProviders { return { ngModule: UCapPublicApiModule, - providers: [{ provide: _MODULE_CONFIG, useValue: config }, ...SERVICES] + providers: [{ provide: _MODULE_CONFIG, useValue: config }, ...SERVICES], }; } } diff --git a/projects/ucap-webmessenger-api-public/src/public-api.ts b/projects/ucap-webmessenger-api-public/src/public-api.ts index eb7db5e..41cd5c4 100644 --- a/projects/ucap-webmessenger-api-public/src/public-api.ts +++ b/projects/ucap-webmessenger-api-public/src/public-api.ts @@ -7,7 +7,9 @@ export * from './lib/apis/version-info2'; export * from './lib/services/public-api.service'; -export * from './lib/types/module-config'; export * from './lib/types/sync-mode.type'; export * from './lib/ucap-public-api.module'; + +export * from './lib/config/urls'; +export * from './lib/config/module-config'; diff --git a/projects/ucap-webmessenger-api/src/lib/apis/api.ts b/projects/ucap-webmessenger-api/src/lib/apis/api.ts index b4b9b04..399b218 100644 --- a/projects/ucap-webmessenger-api/src/lib/apis/api.ts +++ b/projects/ucap-webmessenger-api/src/lib/apis/api.ts @@ -1,6 +1,7 @@ import { HttpParams } from '@angular/common/http'; import { StatusCode } from '../types/status-code.type'; +import { MessageStatusCode } from '../types/message-status-code.type'; export interface APIRequest { _id?: string; @@ -11,9 +12,16 @@ export interface APIResponse { statusCode: StatusCode; errorMessage: string; } +export interface MessageAPIResponse { + _id?: string; + responseCode: MessageStatusCode; + responseMsg: string; +} export type APIEncoder = (req: REQ) => HttpParams; +export type APIJsonEncoder = (req: REQ) => string; + export type APIFormDataEncoder = (req: REQ) => FormData; export type APIDecoder = (res: any) => RES; diff --git a/projects/ucap-webmessenger-api/src/lib/types/message-status-code.type.ts b/projects/ucap-webmessenger-api/src/lib/types/message-status-code.type.ts new file mode 100644 index 0000000..b275cd5 --- /dev/null +++ b/projects/ucap-webmessenger-api/src/lib/types/message-status-code.type.ts @@ -0,0 +1,28 @@ +export enum MessageStatusCode { + /** 성공 */ + Success = '00', + /** 사용자 토큰이 일치하지 않을 경우 발생 (인증 실패) */ + Fail_Auth = '10', + /** 메시지 타입이 일치하지 않을 경우 발생 */ + Fail_Msg_Type = '20', + /** 메시지 아이디가 NULL일 경우 발생 */ + Fail_Msg_Id_Null = '21', + /** 예약 메시지 발송 시, 예약 시간이 지정되어있지 않을 경우 발생 */ + Fail_Msg_Reservation_Time_Null = '22', + /** 메시지 발송 시, 제목이 최대 길이보다 길 경우 발생 */ + Fail_Msg_Title_length = '23', + /** 예약 메시지 발송 시, 현재 시간 10분 전일 경우 발생 */ + Fail_Msg_Reservation_Time_Less = '24', + /** 예약 메시지 수정 시, 발송 시간 10분 전일 경우 발생 */ + Fail_Msg_Edit_Reservation_Time_Less = '25', + /** 예약 메시지 수정 및 발송 취소 시, 이미 발송된 메시지일 경우 발생 */ + Fail_Msg_Edit_Cancel_Reservation_Sended = '26', + /** 파일 업로드 시, 경로가 올바르지 않을 경우 발생 */ + Fail_File_Path = '40', + /** 파일 업로드 시, 파일 크기가 최대 크기보다 클 경우 발생 */ + Fail_File_Size = '41', + /** 파일 업로드 시, 업로드할 수 없는 파일 형식일 경우 발생 */ + Fail_File_Ext = '42', + /** Exception 에러 발생 */ + Fail = '99' +} diff --git a/projects/ucap-webmessenger-api/src/lib/types/status-code.type.ts b/projects/ucap-webmessenger-api/src/lib/types/status-code.type.ts index 89d7a3f..5c279b1 100644 --- a/projects/ucap-webmessenger-api/src/lib/types/status-code.type.ts +++ b/projects/ucap-webmessenger-api/src/lib/types/status-code.type.ts @@ -1,4 +1,4 @@ export enum StatusCode { Success = '200', - Fail = '500' + Fail = '500', } diff --git a/projects/ucap-webmessenger-api/src/public-api.ts b/projects/ucap-webmessenger-api/src/public-api.ts index 6669470..67b54c8 100644 --- a/projects/ucap-webmessenger-api/src/public-api.ts +++ b/projects/ucap-webmessenger-api/src/public-api.ts @@ -4,6 +4,7 @@ export * from './lib/apis/api'; +export * from './lib/types/message-status-code.type'; export * from './lib/types/status-code.type'; export * from './lib/utils/json.util'; diff --git a/projects/ucap-webmessenger-app/src/app/app-provider.module.ts b/projects/ucap-webmessenger-app/src/app/app-provider.module.ts index 05b2801..f33498e 100644 --- a/projects/ucap-webmessenger-app/src/app/app-provider.module.ts +++ b/projects/ucap-webmessenger-app/src/app/app-provider.module.ts @@ -1,31 +1,59 @@ -import { NgModule, APP_INITIALIZER } from '@angular/core'; - -import { UCAP_NATIVE_SERVICE } from '@ucap-webmessenger/native'; -import { ElectronNativeService } from '@ucap-webmessenger/native-electron'; - -import { RESOLVERS } from './resolvers'; -import { SERVICES } from './services'; -import { AppService } from './services/app.service'; - -export function initializeApp(appService: AppService) { - return (): Promise => { - return appService.postInit(); - }; -} - -@NgModule({ - imports: [], - exports: [], - providers: [ - ...SERVICES, - ...RESOLVERS, - { - provide: APP_INITIALIZER, - useFactory: initializeApp, - deps: [AppService], - multi: true - }, - { provide: UCAP_NATIVE_SERVICE, useClass: ElectronNativeService } - ] -}) -export class AppProviderModule {} +import { NgModule, APP_INITIALIZER, Type } from '@angular/core'; + +import { HttpClient } from '@angular/common/http'; + +import { UCAP_NATIVE_SERVICE, NativeService } from '@ucap-webmessenger/native'; + +import { RESOLVERS } from './resolvers'; +import { SERVICES } from './services'; +import { AppService } from './services/app.service'; + +import { environment } from '../environments/environment'; +import { BrowserNativeService } from '@ucap-webmessenger/native-browser'; +import { ElectronNativeService } from '@ucap-webmessenger/native-electron'; + +export function initializeApp( + appService: AppService, + nativeService: NativeService +) { + return (): Promise => { + return appService.postInit(); + }; +} + +// export function nativeServiceFactory(httpClient: HttpClient) { +// if ('browser' === environment.runtime) { +// return import('@ucap-webmessenger/native-browser').then( +// m => new m.BrowserNativeService(httpClient) +// ); +// } else { +// return import('@ucap-webmessenger/native-electron').then( +// m => new m.ElectronNativeService() +// ); +// } +// } + +@NgModule({ + imports: [], + exports: [], + providers: [ + { + provide: UCAP_NATIVE_SERVICE, + // useFactory: nativeServiceFactory, + useClass: + 'browser' === environment.runtime + ? BrowserNativeService + : ElectronNativeService, + deps: [HttpClient], + }, + ...SERVICES, + ...RESOLVERS, + { + provide: APP_INITIALIZER, + useFactory: initializeApp, + deps: [AppService, UCAP_NATIVE_SERVICE], + multi: true, + }, + ], +}) +export class AppProviderModule {} diff --git a/projects/ucap-webmessenger-app/src/app/app-routing.module.ts b/projects/ucap-webmessenger-app/src/app/app-routing.module.ts index 6b51755..34ed1d5 100644 --- a/projects/ucap-webmessenger-app/src/app/app-routing.module.ts +++ b/projects/ucap-webmessenger-app/src/app/app-routing.module.ts @@ -10,26 +10,26 @@ const routes: Routes = [ import('./pages/messenger/messenger.page.module').then( m => m.AppMessengerPageModule ), - canActivate: [AppAuthGuard] + canActivate: [AppAuthGuard], }, { path: 'account', loadChildren: () => import('./pages/account/account.page.module').then( m => m.AppAccountPageModule - ) + ), }, { path: 'template', loadChildren: () => import('./pages/template/template.page.module').then( m => m.AppTemplatePageModule - ) - } + ), + }, ]; @NgModule({ imports: [RouterModule.forRoot(routes, { enableTracing: false })], - exports: [RouterModule] + exports: [RouterModule], }) export class AppRoutingModule {} diff --git a/projects/ucap-webmessenger-app/src/app/app-translate.module.ts b/projects/ucap-webmessenger-app/src/app/app-translate.module.ts index 5faaf63..77a3fd7 100644 --- a/projects/ucap-webmessenger-app/src/app/app-translate.module.ts +++ b/projects/ucap-webmessenger-app/src/app/app-translate.module.ts @@ -6,16 +6,13 @@ import { UCAP_NATIVE_SERVICE, NativeService } from '@ucap-webmessenger/native'; export async function createTranslateLoader(nativeService: NativeService) { // tslint:disable-next-line: variable-name - let _TranslateLoader; - _TranslateLoader = await import('@ucap-webmessenger/native-browser').then( - m => m.TranslateBrowserLoader - ); - _TranslateLoader = await import('@ucap-webmessenger/native-electron').then( - m => m.TranslateElectronLoader + const translateLoader = nativeService.getTranslateLoader( + './assets/i18n/', + '.json' ); // return new TranslateBrowserLoader(nativeService, './assets/i18n/', '.json'); - return new _TranslateLoader(nativeService, './assets/i18n/', '.json'); + return translateLoader; } @NgModule({ diff --git a/projects/ucap-webmessenger-app/src/app/app.component.ts b/projects/ucap-webmessenger-app/src/app/app.component.ts index 5447618..e80551d 100644 --- a/projects/ucap-webmessenger-app/src/app/app.component.ts +++ b/projects/ucap-webmessenger-app/src/app/app.component.ts @@ -1,4 +1,5 @@ import { Component } from '@angular/core'; +import { SplashScreenService } from '@ucap-webmessenger/ui'; @Component({ selector: 'app-root', @@ -6,5 +7,5 @@ import { Component } from '@angular/core'; styleUrls: ['./app.component.scss'] }) export class AppComponent { - title = 'ucap-webmessenger-app'; + constructor(private splashScreenService: SplashScreenService) {} } diff --git a/projects/ucap-webmessenger-app/src/app/app.module.ts b/projects/ucap-webmessenger-app/src/app/app.module.ts index 2e3bc2e..3899a04 100644 --- a/projects/ucap-webmessenger-app/src/app/app.module.ts +++ b/projects/ucap-webmessenger-app/src/app/app.module.ts @@ -1,118 +1,106 @@ -import { NgModule } from '@angular/core'; -import { BrowserModule } from '@angular/platform-browser'; -import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; -import { HttpClientModule } from '@angular/common/http'; - -import { MatProgressBarModule } from '@angular/material/progress-bar'; - -import { PerfectScrollbarModule } from 'ngx-perfect-scrollbar'; - -import { UCapCommonApiModule } from '@ucap-webmessenger/api-common'; -import { UCapExternalApiModule } from '@ucap-webmessenger/api-external'; -import { UCapPublicApiModule } from '@ucap-webmessenger/api-public'; - -import { UCapPiModule } from '@ucap-webmessenger/pi'; - -import { UCapProtocolModule } from '@ucap-webmessenger/protocol'; -import { UCapAuthenticationProtocolModule } from '@ucap-webmessenger/protocol-authentication'; -import { UCapEventProtocolModule } from '@ucap-webmessenger/protocol-event'; -import { UCapGroupProtocolModule } from '@ucap-webmessenger/protocol-group'; -import { UCapInfoProtocolModule } from '@ucap-webmessenger/protocol-info'; -import { UCapInnerProtocolModule } from '@ucap-webmessenger/protocol-inner'; -import { UCapOptionProtocolModule } from '@ucap-webmessenger/protocol-option'; -import { UCapRoomProtocolModule } from '@ucap-webmessenger/protocol-room'; -import { UCapServiceProtocolModule } from '@ucap-webmessenger/protocol-service'; -import { UCapStatusProtocolModule } from '@ucap-webmessenger/protocol-status'; -import { UCapSyncProtocolModule } from '@ucap-webmessenger/protocol-sync'; - -import { UCapUiModule } from '@ucap-webmessenger/ui'; -import { UCapUiAccountModule } from '@ucap-webmessenger/ui-account'; - -import { UCapWebStorageModule } from '@ucap-webmessenger/web-storage'; - -import { UCapUtilModule } from '@ucap-webmessenger/util'; - -import { LoggerModule, NgxLoggerLevel } from 'ngx-logger'; - -import { environment } from '../environments/environment'; - -import { AppProviderModule } from './app-provider.module'; -import { AppRoutingModule } from './app-routing.module'; -import { AppStoreModule } from './app-store.module'; -import { AppTranslateModule } from './app-translate.module'; - -import { AppComponent } from './app.component'; - -import { GUARDS } from './guards'; -import { AppMessengerLayoutModule } from './layouts/messenger/messenger.layout.module'; -import { AppNativeLayoutModule } from './layouts/native/native.layout.module'; - -@NgModule({ - imports: [ - BrowserModule, - BrowserAnimationsModule, - - HttpClientModule, - - MatProgressBarModule, - - PerfectScrollbarModule, - - UCapCommonApiModule.forRoot({ - urls: environment.urls.apiCommon, - acceptableFileExtensions: - environment.modules.event.acceptableFileExtensions - }), - - UCapPublicApiModule.forRoot({ - urls: environment.urls.apiPublic - }), - UCapExternalApiModule.forRoot({ - urls: environment.urls.apiExternal - }), - - UCapPiModule.forRoot({ - urls: environment.urls.pi - }), - - UCapProtocolModule.forRoot({ - urls: environment.urls.protocol, - reconnect: environment.protocol.reconnect, - requestId: environment.protocol.requestId - }), - UCapAuthenticationProtocolModule.forRoot(), - UCapEventProtocolModule.forRoot(), - UCapGroupProtocolModule.forRoot(), - UCapInfoProtocolModule.forRoot(), - UCapInnerProtocolModule.forRoot(), - UCapOptionProtocolModule.forRoot(), - UCapRoomProtocolModule.forRoot(), - UCapServiceProtocolModule.forRoot(), - UCapStatusProtocolModule.forRoot(), - UCapSyncProtocolModule.forRoot(), - - UCapUiModule.forRoot(), - UCapUiAccountModule.forRoot(), - - UCapWebStorageModule.forRoot(), - - UCapUtilModule.forRoot(), - - AppProviderModule, - AppRoutingModule, - AppStoreModule, - AppTranslateModule, - - AppMessengerLayoutModule, - AppNativeLayoutModule, - - LoggerModule.forRoot({ - level: NgxLoggerLevel.DEBUG - }) - ], - providers: [...GUARDS], - declarations: [AppComponent], - bootstrap: [AppComponent], - entryComponents: [] -}) -export class AppModule {} +import { NgModule } from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { HttpClientModule } from '@angular/common/http'; + +import { MatProgressBarModule } from '@angular/material/progress-bar'; + +import { PerfectScrollbarModule } from 'ngx-perfect-scrollbar'; + +import { UCapUiModule } from '@ucap-webmessenger/ui'; +import { UCapUiAccountModule } from '@ucap-webmessenger/ui-account'; + +import { UCapWebStorageModule } from '@ucap-webmessenger/web-storage'; + +import { UCapUtilModule } from '@ucap-webmessenger/util'; + +import { UCapCommonApiModule } from '@ucap-webmessenger/api-common'; +import { UCapExternalApiModule } from '@ucap-webmessenger/api-external'; +import { UCapMessageApiModule } from '@ucap-webmessenger/api-message'; +import { UCapPublicApiModule } from '@ucap-webmessenger/api-public'; + +import { UCapPiModule } from '@ucap-webmessenger/pi'; + +import { UCapProtocolModule } from '@ucap-webmessenger/protocol'; +import { UCapAuthenticationProtocolModule } from '@ucap-webmessenger/protocol-authentication'; +import { UCapEventProtocolModule } from '@ucap-webmessenger/protocol-event'; +import { UCapGroupProtocolModule } from '@ucap-webmessenger/protocol-group'; +import { UCapInfoProtocolModule } from '@ucap-webmessenger/protocol-info'; +import { UCapInnerProtocolModule } from '@ucap-webmessenger/protocol-inner'; +import { UCapOptionProtocolModule } from '@ucap-webmessenger/protocol-option'; +import { UCapRoomProtocolModule } from '@ucap-webmessenger/protocol-room'; +import { UCapServiceProtocolModule } from '@ucap-webmessenger/protocol-service'; +import { UCapStatusProtocolModule } from '@ucap-webmessenger/protocol-status'; +import { UCapSyncProtocolModule } from '@ucap-webmessenger/protocol-sync'; + +import { LoggerModule, NgxLoggerLevel } from 'ngx-logger'; + +import { AppProviderModule } from './app-provider.module'; +import { AppRoutingModule } from './app-routing.module'; +import { AppStoreModule } from './app-store.module'; +import { AppTranslateModule } from './app-translate.module'; + +import { AppComponent } from './app.component'; + +import { GUARDS } from './guards'; +import { AppMessengerLayoutModule } from './layouts/messenger/messenger.layout.module'; +import { AppNativeLayoutModule } from './layouts/native/native.layout.module'; + +import { environment } from '../environments/environment'; + +@NgModule({ + imports: [ + BrowserModule, + BrowserAnimationsModule, + + HttpClientModule, + + MatProgressBarModule, + + PerfectScrollbarModule, + + UCapCommonApiModule.forRoot(environment.commonApiModuleConfig), + + UCapPublicApiModule.forRoot(environment.publicApiModuleConfig), + UCapExternalApiModule.forRoot(environment.externalApiModuleConfig), + UCapMessageApiModule.forRoot(environment.messageApiModuleConfig), + + UCapPiModule.forRoot(environment.piModuleConfig), + + UCapProtocolModule.forRoot(environment.protocolModuleConfig), + UCapAuthenticationProtocolModule.forRoot(), + UCapEventProtocolModule.forRoot(), + UCapGroupProtocolModule.forRoot(), + UCapInfoProtocolModule.forRoot(), + UCapInnerProtocolModule.forRoot(), + UCapOptionProtocolModule.forRoot(), + UCapRoomProtocolModule.forRoot(), + UCapServiceProtocolModule.forRoot(), + UCapStatusProtocolModule.forRoot(), + UCapSyncProtocolModule.forRoot(), + + UCapUiModule.forRoot(), + UCapUiAccountModule.forRoot(), + + UCapWebStorageModule.forRoot(), + + UCapUtilModule.forRoot(), + + AppProviderModule, + AppRoutingModule, + AppStoreModule, + AppTranslateModule, + + AppMessengerLayoutModule, + AppNativeLayoutModule, + + LoggerModule.forRoot({ + level: NgxLoggerLevel.DEBUG + }) + ], + providers: [...GUARDS], + declarations: [AppComponent], + bootstrap: [AppComponent], + entryComponents: [] +}) +export class AppModule {} diff --git a/projects/ucap-webmessenger-app/src/app/app.theme.scss b/projects/ucap-webmessenger-app/src/app/app.theme.scss index e636333..88f3bd5 100644 --- a/projects/ucap-webmessenger-app/src/app/app.theme.scss +++ b/projects/ucap-webmessenger-app/src/app/app.theme.scss @@ -103,24 +103,51 @@ body.theme-pink-dark { // ----------------------------------------------------------------------------------------------------- // Define the primary, accent and warn palettes -$pink-light-theme-primary-palette: mat-palette($mat-grey,800); -$pink-light-theme-accent-palette: mat-palette($lg-red, 400); -$pink-light-theme-warn-palette: mat-palette($mat-red); +$lgRed-light-theme-primary-palette: mat-palette($mat-grey,800); +$lgRed-light-theme-accent-palette: mat-palette($lg-red, 400); +$lgRed-light-theme-warn-palette: mat-palette($mat-cyan); + // Create the Material theme object -$pink-light-theme: mat-light-theme( - $pink-light-theme-primary-palette, - $pink-light-theme-accent-palette, - $pink-light-theme-warn-palette +$lgRed-light-theme: mat-light-theme( + $lgRed-light-theme-primary-palette, + $lgRed-light-theme-accent-palette, + $lgRed-light-theme-warn-palette ); // Add ".theme-pink-dark" class to the body to activate this theme. // Class name must start with "theme-" !!! -body.theme-default { +body.theme-lgRed{ // Generate the Angular Material theme - @include angular-material-theme($pink-light-theme); + @include angular-material-theme($lgRed-light-theme); // Apply the theme to the user components - @include components-theme($pink-light-theme); - @include ucap-material-theme($pink-light-theme); + @include components-theme($lgRed-light-theme); + @include ucap-material-theme($lgRed-light-theme); +} + +// ----------------------------------------------------------------------------------------------------- +//aqua-blue-daesang +// ----------------------------------------------------------------------------------------------------- + +$aquaBlue-light-theme-primary-palette: mat-palette($daesang-grey, 900); +$aquaBlue-theme-accent-palette: mat-palette($aquaBlue-daesang); +$aquaBlue-theme-warn-palette: mat-palette($mat-orange); + +// Create the Material theme object +$aquaBlue-light-theme: mat-light-theme( + $aquaBlue-light-theme-primary-palette, + $aquaBlue-theme-accent-palette, + $aquaBlue-theme-warn-palette +); + +// Add ".theme-pink-dark" class to the body to activate this theme. +// Class name must start with "theme-" !!! +body.theme-default { + // Generate the Angular Material theme + @include angular-material-theme($aquaBlue-light-theme); + + // Apply the theme to the user components + @include components-theme($aquaBlue-light-theme); + @include ucap-material-theme($aquaBlue-light-theme); } diff --git a/projects/ucap-webmessenger-app/src/app/layouts/common/dialogs/file-viewer.dialog.component.html b/projects/ucap-webmessenger-app/src/app/layouts/common/dialogs/file-viewer.dialog.component.html index 343050b..21049de 100644 --- a/projects/ucap-webmessenger-app/src/app/layouts/common/dialogs/file-viewer.dialog.component.html +++ b/projects/ucap-webmessenger-app/src/app/layouts/common/dialogs/file-viewer.dialog.component.html @@ -1,11 +1,6 @@ - diff --git a/projects/ucap-webmessenger-app/src/app/layouts/common/dialogs/file-viewer.dialog.component.ts b/projects/ucap-webmessenger-app/src/app/layouts/common/dialogs/file-viewer.dialog.component.ts index d084019..b8a2aab 100644 --- a/projects/ucap-webmessenger-app/src/app/layouts/common/dialogs/file-viewer.dialog.component.ts +++ b/projects/ucap-webmessenger-app/src/app/layouts/common/dialogs/file-viewer.dialog.component.ts @@ -3,7 +3,7 @@ import { OnInit, OnDestroy, Inject, - EventEmitter + EventEmitter, } from '@angular/core'; import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material'; @@ -15,7 +15,7 @@ import { take, map, finalize, tap } from 'rxjs/operators'; import { SnackBarService } from '@ucap-webmessenger/ui'; import { FileDownloadItem, - CommonApiService + CommonApiService, } from '@ucap-webmessenger/api-common'; export interface FileViewerDialogData { @@ -31,7 +31,7 @@ export interface FileViewerDialogResult {} @Component({ selector: 'app-layout-common-file-viewer', templateUrl: './file-viewer.dialog.component.html', - styleUrls: ['./file-viewer.dialog.component.scss'] + styleUrls: ['./file-viewer.dialog.component.scss'], }) export class FileViewerDialogComponent implements OnInit, OnDestroy { fileInfo: FileEventJson; @@ -40,8 +40,6 @@ export class FileViewerDialogComponent implements OnInit, OnDestroy { deviceType: DeviceType; token: string; - fileDownloadItem: FileDownloadItem; - fileDownloadUrl: string; constructor( @@ -66,7 +64,7 @@ export class FileViewerDialogComponent implements OnInit, OnDestroy { userSeq: this.userSeq, deviceType: this.deviceType, token: this.token, - attachmentsSeq: this.fileInfo.attachmentSeq + attachmentsSeq: this.fileInfo.attachmentSeq, }, this.downloadUrl ); @@ -76,8 +74,7 @@ export class FileViewerDialogComponent implements OnInit, OnDestroy { ngOnDestroy(): void {} - onDownload(): void { - this.fileDownloadItem = new FileDownloadItem(); + onDownload(fileDownloadItem: FileDownloadItem): void { this.commonApiService .fileTalkDownload( { @@ -85,37 +82,36 @@ export class FileViewerDialogComponent implements OnInit, OnDestroy { deviceType: this.deviceType, token: this.token, attachmentsSeq: this.fileInfo.attachmentSeq, - fileDownloadItem: this.fileDownloadItem + fileDownloadItem, }, this.downloadUrl ) .pipe( take(1), map(async rawBlob => { - const blob = rawBlob.slice( - 0, - rawBlob.size, - MimeUtil.getMimeFromExtension(this.fileInfo.fileExt) - ); + const mimeType = MimeUtil.getMimeFromExtension(this.fileInfo.fileExt); + const blob = rawBlob.slice(0, rawBlob.size, mimeType); FileUtil.fromBlobToBuffer(blob) .then(buffer => { this.nativeService - .saveFile(buffer, this.fileInfo.fileName) - .pipe(take(1)) - .subscribe(result => { + .saveFile(buffer, this.fileInfo.fileName, mimeType) + .then(result => { if (!!result) { this.snackBarService.open( `파일이 경로[${result}]에 저장되었습니다.`, '', { duration: 3000, - verticalPosition: 'bottom' + verticalPosition: 'bottom', } ); } else { this.snackBarService.open('파일 저장에 실패하였습니다.'); } + }) + .catch(reason => { + this.snackBarService.open('파일 저장에 실패하였습니다.'); }); }) .catch(reason => { @@ -123,7 +119,9 @@ export class FileViewerDialogComponent implements OnInit, OnDestroy { }); }), finalize(() => { - this.fileDownloadItem = undefined; + setTimeout(() => { + fileDownloadItem.downloadingProgress$ = undefined; + }, 1000); }) ) .subscribe(); diff --git a/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/index.ts b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/index.ts index 7348ada..08c254a 100644 --- a/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/index.ts +++ b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/index.ts @@ -2,14 +2,18 @@ import { IntroComponent } from './intro.component'; import { LeftSideComponent } from './left-side.component'; import { MessagesComponent } from './messages.component'; import { RightSideComponent } from './right-side.component'; +import { RightDrawerComponent } from './right-drawer.component'; import { LEFT_SIDENAV_COMPONENTS } from './left-sidenav'; +import { RIGHT_DRAWER_COMPONENTS } from './right-drawer'; export const COMPONENTS = [ IntroComponent, LeftSideComponent, MessagesComponent, RightSideComponent, + RightDrawerComponent, - ...LEFT_SIDENAV_COMPONENTS + ...LEFT_SIDENAV_COMPONENTS, + ...RIGHT_DRAWER_COMPONENTS ]; diff --git a/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/left-side.component.html b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/left-side.component.html index 77e14cd..e2cffc4 100644 --- a/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/left-side.component.html +++ b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/left-side.component.html @@ -8,50 +8,73 @@ -
- +
+
- - -
- + matTooltip="Chat" + matTooltipPosition="after" + > + - + d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z" + >
-
-
- +
+ @@ -61,33 +84,125 @@
- - + + - -
- - device_hub--> +
+ + + +
+ + + + + + + +
+
+ +
+ + + + +
+ ; +export class LeftSideComponent implements OnInit, OnDestroy { + @Output() + openProfile = new EventEmitter< + UserInfo | UserInfoSS | UserInfoF | UserInfoDN + >(); + + @ViewChildren('tabs') tabs: QueryList>; + currentTabLable: string; + + badgeChatUnReadCount: number; + badgeChatUnReadCountSubscription: Subscription; /** 조직도에서 부서원 선택 */ selectedUserList: (UserInfo | UserInfoSS | UserInfoF | UserInfoDN)[] = []; @@ -47,18 +72,42 @@ export class LeftSideComponent implements OnInit { MainMenu = MainMenu; + sessionVerinfo: VersionInfo2Response; + loginRes: LoginResponse; + constructor( private store: Store, private dialogService: DialogService, + private sessionStorageService: SessionStorageService, private logger: NGXLogger - ) {} + ) { + this.loginRes = this.sessionStorageService.get( + KEY_LOGIN_RES_INFO + ); + this.sessionVerinfo = this.sessionStorageService.get( + KEY_VER_INFO + ); + } ngOnInit() { - this.badgeChatUnReadCount$ = this.store.pipe( - select(AppStore.MessengerSelector.SyncSelector.selectChatUnreadCount) - ); + this.badgeChatUnReadCountSubscription = this.store + .pipe( + select(AppStore.MessengerSelector.SyncSelector.selectChatUnreadCount) + ) + .subscribe(count => { + this.badgeChatUnReadCount = count; + }); this.setFabInitial(MainMenu.Group); + this.currentTabLable = MainMenu.Group; + } + + ngOnDestroy(): void { + if (!!this.badgeChatUnReadCountSubscription) { + this.badgeChatUnReadCountSubscription.unsubscribe(); + } + + this.logger.debug('-----------------------LeftSideComponent ngOnDestroy'); } async onClickNewChat(type: string = 'NORMAL') { @@ -106,7 +155,7 @@ export class LeftSideComponent implements OnInit { if (!!result && !!result.choice && result.choice) { if ( !!result.selectedUserList && - result.selectedUserList.length > 0 && + // result.selectedUserList.length > 0 && result.groupName.trim().length > 0 ) { const userSeqs: number[] = []; @@ -122,8 +171,21 @@ export class LeftSideComponent implements OnInit { } } + onClickOpenProfile(userInfo: UserInfo | UserInfoSS | UserInfoF | UserInfoDN) { + this.openProfile.emit(userInfo); + } + onSelectedTabChange(event: MatTabChangeEvent) { this.setFabInitial(event.tab.ariaLabel); + this.currentTabLable = event.tab.ariaLabel; + + this.tabs.forEach(tab => { + if (`tabs-${event.index}` === tab.nativeElement.id) { + tab.nativeElement.style.display = 'block'; + } else { + tab.nativeElement.style.display = 'none'; + } + }); } setFabInitial(type: string) { switch (type) { @@ -212,6 +274,18 @@ export class LeftSideComponent implements OnInit { ); } } + onToggleUser(userInfo: UserInfo | UserInfoSS | UserInfoF | UserInfoDN) { + if ( + this.selectedUserList.filter(user => user.seq === userInfo.seq).length === + 0 + ) { + this.selectedUserList = [...this.selectedUserList, userInfo]; + } else { + this.selectedUserList = this.selectedUserList.filter( + item => item.seq !== userInfo.seq + ); + } + } /** FAB */ onClickFab(params: { btn: any }) { diff --git a/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/left-sidenav/chat.component.html b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/left-sidenav/chat.component.html index e240d96..77d1ec4 100644 --- a/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/left-sidenav/chat.component.html +++ b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/left-sidenav/chat.component.html @@ -57,26 +57,24 @@ [roomInfo]="room" [roomUserInfo]="getRoomUserList(room)" [sessionVerinfo]="sessionVerinfo" - (click)="onSelectedRoom(room)" + (click)="onClickContextMenu('SELECT_ROOM', room)" (contextmenu)="onContextMenuChat($event, room)" + class="ucap-clickable" > - +
-
+
-
-
- +
+
+
+ +
+
검색결과({{ searchUserInfos.length }}명)
+ + + + + + +
- + - + --> @@ -166,23 +218,40 @@ (ucapClickOutside)="groupContextMenuTrigger.closeMenu()" > - - - - + + + + - diff --git a/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/left-sidenav/group.component.scss b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/left-sidenav/group.component.scss index 966be1f..dc2984b 100644 --- a/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/left-sidenav/group.component.scss +++ b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/left-sidenav/group.component.scss @@ -1,14 +1,6 @@ @charset 'utf-8'; .current-head { - display: flex; - justify-content: center; - padding: 0 10px; - height: 70px; - background-color: #eeeeee; - background: #f15f79; - background: -webkit-linear-gradient(to right, #352a37, #f15f79); - background: linear-gradient(to right, #352a37, #ef4c73); - color:#ffffff; + color: #ffffff; h3 { display: inline-flex; padding-left: 10px; @@ -19,11 +11,12 @@ margin-left: auto; display: inline-flex; align-items: center; - svg{ - stroke:#333333; + svg { + stroke: #333333; } } } + .search-result { height: calc(100% - 130px); overflow: auto; @@ -32,8 +25,27 @@ display: flex; height: 40px; } + .ps { + .ps-content { + position: relative; + width: 100%; + height: 100%; + } + } } ::ng-deep .mat-tab-body-content { height: 100%; overflow: unset; } +.mat-menu-item { + display: flex; + align-items: center; + svg { + margin-right: 10px; + } +} + +.list-item-frame { + width: 100%; + height: 100%; +} diff --git a/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/left-sidenav/group.component.ts b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/left-sidenav/group.component.ts index 9f300c1..94d52a4 100644 --- a/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/left-sidenav/group.component.ts +++ b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/left-sidenav/group.component.ts @@ -56,6 +56,11 @@ import { EditGroupDialogData, EditGroupDialogResult } from '@app/layouts/messenger/dialogs/group/edit-group.dialog.component'; +import { + SelectGroupDialogComponent, + SelectGroupDialogResult, + SelectGroupDialogData +} from '../../dialogs/group/select-group.dialog.component'; @Component({ selector: 'app-layout-chat-left-sidenav-group', @@ -66,8 +71,12 @@ import { export class GroupComponent implements OnInit, OnDestroy { @Output() newGroupAndMember = new EventEmitter(); + @Output() + openProfile = new EventEmitter< + UserInfo | UserInfoSS | UserInfoF | UserInfoDN + >(); - @ViewChild('groupExpansionPanel', { static: true }) + @ViewChild('groupExpansionPanel', { static: false }) groupExpansionPanel: GroupExpansionPanelComponent; @ViewChild('profileContextMenuTrigger', { static: true }) @@ -161,10 +170,11 @@ export class GroupComponent implements OnInit, OnDestroy { } ngOnDestroy(): void { - this.logger.debug('ngOnDestroy'); if (!!this.loginResSubscription) { this.loginResSubscription.unsubscribe(); } + + this.logger.debug('-----------------------GroupComponent ngOnDestroy'); } async onClickGroupMenu(menuType: string) { @@ -277,22 +287,167 @@ export class GroupComponent implements OnInit, OnDestroy { this.searchUserInfos = []; } - getShowContextMenu(userInfo: UserInfo | UserInfoF) { + /** 그룹 header > more 버튼 > visible of context menu */ + getShowGroupContextMenu(menuType: string, group: GroupDetailData) { + // 즐겨찾기 그룹 숨김메뉴 + if ( + menuType === 'DIV1' || + menuType === 'RENAME' || + menuType === 'EDIT_MEMBER' || + menuType === 'DELETE' + ) { + if (!group || group === undefined) { + return false; + } + } + + // 기본 그룹 숨김메뉴 + if (menuType === 'RENAME' || menuType === 'DELETE') { + if (!!group && group.seq === 0) { + return false; + } + } + + return true; + } + + /** 그룹 > 그룹원 > visible of context menu */ + getShowProfileContextMenu( + menuType: string, + userInfo: UserInfo | UserInfoF, + group?: GroupDetailData + ) { if (userInfo.seq === this.loginRes.userSeq) { return false; - } else { - return true; } + + if (!group || group === undefined) { + if ( + menuType === 'REGISTER_FAVORITE' || + menuType === 'SEND_NOTE' || + menuType === 'REGISTER_NICKNAME' + ) { + // continue; + } else { + return false; + } + } + + return true; } - onClickProfileContextMenu(menuType: string, userInfo: UserInfo | UserInfoF) { + async onClickProfileContextMenu( + menuType: string, + userInfo: UserInfo | UserInfoF, + group?: GroupDetailData + ) { this.logger.debug( 'onClickProfileContextMenu', 'menuType', menuType, 'userInfo', - userInfo + userInfo, + 'group', + group ); switch (menuType) { + case 'VIEW_PROFILE': + this.openProfile.emit(userInfo); + break; + case 'CHAT': + this.onSelectBuddy(userInfo); + break; + case 'REMOVE_FROM_GROUP': + { + const result = await this.dialogService.open< + ConfirmDialogComponent, + ConfirmDialogData, + ConfirmDialogResult + >(ConfirmDialogComponent, { + width: '360px', + data: { + title: 'Delete Member in Group', + html: `[${userInfo.name} ${userInfo.grade}]를 [${group.name}]그룹에서 삭제하시겠습니까?` + } + }); + + if (!!result && !!result.choice && result.choice) { + const trgtUserSeq = group.userSeqs.filter( + user => user !== userInfo.seq + ); + this.store.dispatch( + SyncStore.updateGroupMember({ + oldGroup: group, + trgtUserSeq + }) + ); + } + } + break; + case 'COPY_BUDDY': + { + const result = await this.dialogService.open< + SelectGroupDialogComponent, + SelectGroupDialogData, + SelectGroupDialogResult + >(SelectGroupDialogComponent, { + width: '600px', + data: { + title: 'Group Select' + } + }); + + if (!!result && !!result.choice && result.choice) { + if (!!result.group) { + const oldGroup: GroupDetailData = result.group; + const trgtUserSeq: number[] = []; + let exist = false; + result.group.userSeqs.map(seq => { + trgtUserSeq.push(seq); + if (seq === userInfo.seq) { + exist = true; + } + }); + + if (!exist) { + trgtUserSeq.push(userInfo.seq); + } + + this.store.dispatch( + SyncStore.updateGroupMember({ + oldGroup, + trgtUserSeq + }) + ); + } + } + } + break; + case 'MOVE_BUDDY': + { + const result = await this.dialogService.open< + SelectGroupDialogComponent, + SelectGroupDialogData, + SelectGroupDialogResult + >(SelectGroupDialogComponent, { + width: '600px', + data: { + title: 'Group Select' + } + }); + + if (!!result && !!result.choice && result.choice) { + if (!!result.group) { + this.store.dispatch( + SyncStore.moveGroupMember({ + fromGroup: group, + toGroup: result.group, + trgtUserSeq: [userInfo.seq] + }) + ); + } + } + } + break; case 'REGISTER_FAVORITE': this.store.dispatch( SyncStore.updateBuddy({ @@ -304,9 +459,14 @@ export class GroupComponent implements OnInit, OnDestroy { } } + onClickOpenProfile(userInfo: UserInfo | UserInfoSS | UserInfoF | UserInfoDN) { + this.openProfile.emit(userInfo); + } + onContextMenuProfile( event: MouseEvent, - userInfo: UserInfo | UserInfoSS | UserInfoF | UserInfoDN + userInfo: UserInfo | UserInfoSS | UserInfoF | UserInfoDN, + group: GroupDetailData ) { event.preventDefault(); event.stopPropagation(); @@ -314,7 +474,7 @@ export class GroupComponent implements OnInit, OnDestroy { this.profileContextMenuPosition.x = event.clientX + 'px'; this.profileContextMenuPosition.y = event.clientY + 'px'; this.profileContextMenuTrigger.menu.focusFirstItem('mouse'); - this.profileContextMenuTrigger.menuData = { userInfo }; + this.profileContextMenuTrigger.menuData = { userInfo, group }; this.profileContextMenuTrigger.openMenu(); } diff --git a/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/left-sidenav/index.ts b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/left-sidenav/index.ts index e957286..7e0cfc9 100644 --- a/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/left-sidenav/index.ts +++ b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/left-sidenav/index.ts @@ -2,10 +2,12 @@ import { CallComponent } from './call.component'; import { ChatComponent } from './chat.component'; import { GroupComponent } from './group.component'; import { OrganizationComponent } from './organization.component'; +import { MessageBoxComponent } from './message.component'; export const LEFT_SIDENAV_COMPONENTS = [ CallComponent, ChatComponent, GroupComponent, - OrganizationComponent + OrganizationComponent, + MessageBoxComponent ]; diff --git a/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/left-sidenav/message.component.html b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/left-sidenav/message.component.html new file mode 100644 index 0000000..c2c1736 --- /dev/null +++ b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/left-sidenav/message.component.html @@ -0,0 +1,198 @@ +
+
+

쪽지

+
+ +
+
+ +
+ + + + 수신 + + +
+
+
+ image + attach_file +
    +
  • {{ message.userName }}
  • +
  • {{ message.title }}
  • +
+
+
+ {{ message.regDate | dateToStringFormat: 'MM:DD' }} +
+
+
+
+ + + 발신 + + +
+
+
+ image + attach_file +
    +
  • {{ message.userName }}
  • +
  • {{ message.title }}
  • +
+
+
+ {{ message.regDate | dateToStringFormat: 'MM:DD' }} +
+
+
+
+ + + 예약 + + +
+
+
+ image + attach_file +
    +
  • {{ message.userName }}
  • +
  • {{ message.title }}
  • +
+
+
+ {{ message.regDate | dateToStringFormat: 'MM:DD' }} +
+
+
+
+
+
+
+
+
+ + + 전체 + 수신 + 발신 + 예약 + + + + 이름 + 제목 + 내용 + +
+
+
+
+
+ image + attach_file +
    +
  • {{ message.userName }}
  • +
  • {{ message.title }}
  • +
+
+
+ {{ message.regDate | dateToStringFormat: 'MM:DD' }} +
+
+
+
+
diff --git a/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/left-sidenav/message.component.scss b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/left-sidenav/message.component.scss new file mode 100644 index 0000000..50961ba --- /dev/null +++ b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/left-sidenav/message.component.scss @@ -0,0 +1,67 @@ +.current-head { + h3 { + display: inline-flex; + padding-left: 10px; + align-items: center; + color: #ffffff; + } + .btn-box { + height: 100%; + margin-left: auto; + display: inline-flex; + align-items: center; + } +} + +.list-search { + display: flex; + flex-direction: row; + height: 60px; + align-items: center; + padding: 0; + font-size: 14px; + background-color: #f9f9f9; + border-bottom: 1px solid #dddddd; + .searchbox { + width: 100%; + height: 100%; + } +} +::ng-deep .searchbox { + .mat-form-field { + display: block; + .mat-form-field-wrapper { + padding: 0; + padding-bottom: 0 !important; + height: 100%; + .mat-form-field-flex { + height: 59px; + padding: 0 10px 0 20px; + align-items: center; + .mat-form-field-infix { + width: 90%; + font-size: 14px; + border: none; + } + .mat-form-field-suffix { + .mat-icon { + line-height: 24px; + } + } + } + } + } + .mat-form-field-appearance-legacy { + .mat-form-field-wrapper { + padding: 0; + } + .mat-form-field-underline { + bottom: 0; + background-color: unset !important; + } + } +} + +.mat-tab-label-active { + opacity: 1; +} diff --git a/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/left-sidenav/message.component.spec.ts b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/left-sidenav/message.component.spec.ts new file mode 100644 index 0000000..807c302 --- /dev/null +++ b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/left-sidenav/message.component.spec.ts @@ -0,0 +1,24 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { MessageBoxComponent } from './message.component'; + +describe('MessageBoxComponent', () => { + let component: MessageBoxComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [MessageBoxComponent] + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(MessageBoxComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/left-sidenav/message.component.ts b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/left-sidenav/message.component.ts new file mode 100644 index 0000000..acc6451 --- /dev/null +++ b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/left-sidenav/message.component.ts @@ -0,0 +1,317 @@ +import { + Component, + OnInit, + OnDestroy, + Output, + EventEmitter, + ViewChild, + Input, + AfterViewChecked +} from '@angular/core'; +import { Subscription, of } from 'rxjs'; +import { Store, select } from '@ngrx/store'; +import { tap, map, catchError } from 'rxjs/operators'; + +import * as AppStore from '@app/store'; + +import { UserInfo } from '@ucap-webmessenger/protocol-room'; +import { VersionInfo2Response } from '@ucap-webmessenger/api-public'; +import { SessionStorageService } from '@ucap-webmessenger/web-storage'; +import { KEY_VER_INFO } from '@app/types/ver-info.type'; +import { DialogService } from '@ucap-webmessenger/ui'; +import { LoginResponse } from '@ucap-webmessenger/protocol-authentication'; +import { KEY_LOGIN_RES_INFO } from '@app/types/login-res-info.type'; +import { + MessageApiService, + MessageType, + RetrieveRequest, + MessageList, + RetrieveSearchRequest, + MessageSearchType +} from '@ucap-webmessenger/api-message'; +import { DeviceType } from '@ucap-webmessenger/core'; +import { MessageStatusCode } from '@ucap-webmessenger/api'; +import { ContentType } from '@ucap-webmessenger/api-message'; +import { FormGroup, FormBuilder } from '@angular/forms'; +import { + MatTabGroup, + MatSelectChange, + MatRadioChange +} from '@angular/material'; + +@Component({ + selector: 'app-layout-chat-left-sidenav-message', + templateUrl: './message.component.html', + styleUrls: ['./message.component.scss'] +}) +export class MessageBoxComponent + implements OnInit, OnDestroy, AfterViewChecked { + @Input() + isVisible = false; + + @ViewChild('tabs', { static: false }) tabs: MatTabGroup; + isInitTabs = false; + + fgSearch: FormGroup; + fgSearchType: FormGroup; + + loginRes: LoginResponse; + sessionVerinfo: VersionInfo2Response; + + messageList: MessageList[] = []; + + messageRecieveListSubscription: Subscription; + messageSendListSubscription: Subscription; + messageReservationListSubscription: Subscription; + messageSearchListSubscription: Subscription; + + defaultPageSize = 100; // 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; + + ContentType = ContentType; + MessageType = MessageType; + MessageSearchType = MessageSearchType; + + isSearch = false; + + constructor( + private store: Store, + private formBuilder: FormBuilder, + private sessionStorageService: SessionStorageService, + private dialogService: DialogService, + private messageApiService: MessageApiService + ) { + this.loginRes = this.sessionStorageService.get( + KEY_LOGIN_RES_INFO + ); + this.sessionVerinfo = this.sessionStorageService.get( + KEY_VER_INFO + ); + } + + ngOnInit() { + this.fgSearch = this.formBuilder.group({ + searchInput: null + }); + this.fgSearchType = this.formBuilder.group({ + searchMessageType: [MessageType.All], + searchMessageSearchType: [MessageSearchType.Name] + }); + + // 초기 검색은 수신함. + this.getRetrieveMessage(MessageType.Receive, this.recieveCurrentPage); + + if (!!this.tabs) { + this.tabs.realignInkBar(); + } + } + + ngAfterViewChecked(): void { + if (!!this.tabs && !this.isInitTabs && this.isVisible) { + this.isInitTabs = true; + this.tabs.realignInkBar(); + } + } + + 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(); + } + } + + onSelectedIndexChange(value: number) { + switch (value) { + case 0: + { + // Recieve + this.getRetrieveMessage(MessageType.Receive, this.recieveCurrentPage); + } + break; + case 1: + { + // Send + this.getRetrieveMessage(MessageType.Send, this.sendCurrentPage); + } + break; + case 2: + { + // Reservation + this.getRetrieveMessage( + MessageType.Reservation, + this.reservationCurrentPage + ); + } + break; + } + } + + onChangeSelection(event: MatSelectChange) { + this.searchCurrentPage = 0; + this.getSearchMessage( + event.value, + this.fgSearchType.get('searchMessageSearchType').value, + this.fgSearch.get('searchInput').value + ); + } + onChangeSearchType(event: MatRadioChange) { + this.searchCurrentPage = 0; + this.getSearchMessage( + this.fgSearchType.get('searchMessageType').value, + event.value, + this.fgSearch.get('searchInput').value + ); + } + onKeyDownEnter(event: KeyboardEvent, search: string) { + event.preventDefault(); + event.stopPropagation(); + + this.searchCurrentPage = 0; + + this.getSearchMessage( + MessageType.All, + MessageSearchType.Name, + search.trim() + ); + } + getSearchMessage( + messageType: MessageType, + searchType: MessageSearchType, + 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( + map(res => { + console.log(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(console.log(error))) + ) + .subscribe(); + } + + onClickSearchCancel() { + this.isSearch = false; + this.getRetrieveMessage(MessageType.Receive, this.recieveCurrentPage); + } + + 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( + map(res => { + console.log(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(console.log(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( + map(res => { + console.log(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(console.log(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( + map(res => { + console.log(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(console.log(error))) + ) + .subscribe(); + } + break; + } + } +} diff --git a/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/left-sidenav/organization.component.html b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/left-sidenav/organization.component.html index 9481b09..de64189 100644 --- a/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/left-sidenav/organization.component.html +++ b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/left-sidenav/organization.component.html @@ -1,36 +1,60 @@ -
-
+
+
+

조직도

+
+ + +
+
+
-
-
-
+
+
+
+ {{ getSelectedDepartmentName() }} -
-
- + + 검색결과({{ searchUserInfos.length }}명) - -
-
-
- + +
+
+ + +
+
+
+
+ +
-
+
- Loading... - + + +
-
  • -
  • -
@@ -58,7 +66,7 @@ - + + + - + - @@ -92,10 +142,14 @@
- +
+
+ +
+
@@ -139,6 +195,16 @@ class="file-drop-zone" >
+ +
+ +
+
@@ -150,6 +216,8 @@ [fileUploadQueue]="fileUploadQueue" (send)="onSendMessage($event)" (sendFiles)="onFileSelected($event)" + (clearView)="clearView()" + (toggleStickerSelector)="onShowToggleStickerSelector($event)" >
diff --git a/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/messages.component.scss b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/messages.component.scss index e8dada5..4000bfc 100644 --- a/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/messages.component.scss +++ b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/messages.component.scss @@ -24,12 +24,12 @@ } .chat-toolbar { width: 100%; - height: 80px; + height: 70px; min-height: 70px; align-items: center; background-color: #ffffff !important; border-bottom: 1px solid #dddddd; - box-shadow: 0 3px 6px rgba(0,0,0,.16); + box-shadow: 0 3px 6px rgba(0, 0, 0, 0.16); .chat-header { width: 100%; align-items: center; @@ -45,7 +45,6 @@ width: 40px; height: 40px; border-radius: 50%; - background-color: #604850; color: #efefef; font-size: 16px; line-height: 40px; @@ -68,7 +67,7 @@ height: 20px; span { border-radius: 10px; - padding: 1px 10px; + padding: 2px 10px; margin-right: 6px; font-size: 13px; } @@ -95,12 +94,26 @@ .file-drop-zone { position: absolute; - background-color: rgb(180, 180, 180); + padding: 10px 10px 0 10px; + background-color: rgb(54, 54, 54, 0.8); + bottom: 0; + width: 100%; + } + } - top: calc(100% - 200px); - left: 20%; - width: 60%; - height: 200px; + .sticker-selector-container { + position: absolute; + bottom: 0; + left: 0; + width: 100%; + background-color: transparent; + + .sticker-selector-zone { + position: absolute; + padding: 10px 10px 0 10px; + background-color: rgba(250, 255, 255, 0.8); + bottom: 0; + width: 100%; } } } diff --git a/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/messages.component.ts b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/messages.component.ts index 13066a1..b48e4d3 100644 --- a/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/messages.component.ts +++ b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/messages.component.ts @@ -4,7 +4,9 @@ import { OnDestroy, ViewChild, ElementRef, - AfterViewInit + AfterViewInit, + Output, + EventEmitter } from '@angular/core'; import { ucapAnimations, @@ -31,19 +33,26 @@ import { isRecallable, InfoResponse, EventJson, - FileEventJson + FileEventJson, + StickerEventJson } from '@ucap-webmessenger/protocol-event'; import * as AppStore from '@app/store'; import * as EventStore from '@app/store/messenger/event'; 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 { LoginResponse } from '@ucap-webmessenger/protocol-authentication'; -import { SessionStorageService } from '@ucap-webmessenger/web-storage'; +import { + SessionStorageService, + LocalStorageService +} from '@ucap-webmessenger/web-storage'; import { EnvironmentsInfo, KEY_ENVIRONMENTS_INFO, - UserSelectDialogType + UserSelectDialogType, + RightDrawer, + KEY_STICKER_HISTORY } from '@app/types'; import { RoomInfo, UserInfo, RoomType } from '@ucap-webmessenger/protocol-room'; import { tap, take, map, catchError } from 'rxjs/operators'; @@ -74,7 +83,14 @@ import { FileViewerDialogData, FileViewerDialogResult } from '@app/layouts/common/dialogs/file-viewer.dialog.component'; -import { CONST, FileUtil } from '@ucap-webmessenger/core'; +import { + CONST, + FileUtil, + StickerFilesInfo, + StickerUtil, + StickerInfo, + StickerMap +} from '@ucap-webmessenger/core'; import { PerfectScrollbarComponent } from 'ngx-perfect-scrollbar'; import { StatusCode } from '@ucap-webmessenger/api'; import { @@ -82,6 +98,12 @@ import { EditChatRoomDialogResult, EditChatRoomDialogData } from '../dialogs/chat/edit-chat-room.dialog.component'; +import { + SelectGroupDialogComponent, + SelectGroupDialogResult, + SelectGroupDialogData +} from '../dialogs/group/select-group.dialog.component'; +import { GroupDetailData } from '@ucap-webmessenger/protocol-sync'; @Component({ selector: 'app-layout-messenger-messages', @@ -90,6 +112,9 @@ import { animations: ucapAnimations }) export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit { + @Output() + openProfile = new EventEmitter(); + @ViewChild('messageBoxContainer', { static: true }) private messageBoxContainer: ElementRef; @@ -134,11 +159,16 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit { /** Timer 대화방의 대화 삭제를 위한 interval */ interval: any; + /** About Sticker */ + isShowStickerSelector = false; + selectedSticker: StickerFilesInfo; + snackBarPreviewEvent: MatSnackBarRef; constructor( private store: Store, private sessionStorageService: SessionStorageService, + private localStorageService: LocalStorageService, private commonApiService: CommonApiService, private clipboardService: ClipboardService, private dialogService: DialogService, @@ -175,7 +205,7 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit { select(AppStore.MessengerSelector.RoomSelector.roomInfo), tap(roomInfo => { this.roomInfo = roomInfo; - + this.clearView(); this.setEventMoreInit(); }) ) @@ -255,6 +285,15 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit { // this.readyToReply(); } + /** + * 채팅방의 여러 팝업들을 닫아준다. + */ + clearView() { + // Sticker Selector Clear.. + this.isShowStickerSelector = false; + this.selectedSticker = undefined; + } + getRoomName() { if (!this.roomInfo || !this.userInfoList) { return '대화방명을 가져오고 있습니다..'; @@ -316,6 +355,43 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit { } } + getShowContextMenu(menuType: string) { + if ( + menuType === 'OPEN_ROOM_USER' || + menuType === 'ADD_MEMBER' || + menuType === 'ADD_GROUP' || + menuType === 'EDIT_ROOM' + ) { + if ( + !this.roomInfo || + !this.roomInfo.roomType || + this.roomInfo.roomType === RoomType.Mytalk || + this.roomInfo.roomType === RoomType.Allim || + this.roomInfo.roomType === RoomType.Bot || + this.roomInfo.roomType === RoomType.Link + ) { + return false; + } + } + + return true; + } + + getShowUnreadCount(): boolean { + if (!this.roomInfo || this.roomInfo === undefined) { + return true; + } + if ( + this.roomInfo.roomType === RoomType.Mytalk || + this.roomInfo.roomType === RoomType.Bot || + this.roomInfo.roomType === RoomType.Allim + ) { + return false; + } + + return true; + } + readyToReply(): void { setTimeout(() => { this.focusReplyInput(); @@ -416,22 +492,58 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit { async onSendMessage(message: string) { this.setEventMoreInit(); - if (!message || message.trim().length === 0) { - const result = await this.dialogService.open< - AlertDialogComponent, - AlertDialogData, - AlertDialogResult - >(AlertDialogComponent, { - width: '360px', - data: { - title: 'Alert', - message: `대화내용을 입력해주세요.` - } - }); - return; + if (!this.selectedSticker) { + if (!message || message.trim().length === 0) { + const result = await this.dialogService.open< + AlertDialogComponent, + AlertDialogData, + AlertDialogResult + >(AlertDialogComponent, { + width: '360px', + data: { + title: 'Alert', + message: `대화내용을 입력해주세요.` + } + }); + return; + } } - if (message.trim().length > CONST.MASSTEXT_LEN) { + if (!!this.selectedSticker) { + // Send Sticker + if (message.trim().length > CONST.MASSTEXT_LEN) { + const result = await this.dialogService.open< + AlertDialogComponent, + AlertDialogData, + AlertDialogResult + >(AlertDialogComponent, { + width: '360px', + data: { + title: 'Alert', + message: `스티커를 포함할 경우 ${CONST.MASSTEXT_LEN}자 이상 보낼 수 없습니다.` + } + }); + return; + } + + const stickerJson: StickerEventJson = { + name: '스티커', + file: this.selectedSticker.index, + chat: message.trim() + }; + this.store.dispatch( + EventStore.send({ + senderSeq: this.loginRes.userSeq, + req: { + roomSeq: this.roomInfo.roomSeq, + eventType: EventType.Sticker, + sentMessage: JSON.stringify(stickerJson) + } + }) + ); + this.isShowStickerSelector = false; + this.setStickerHistory(this.selectedSticker); + } else if (message.trim().length > CONST.MASSTEXT_LEN) { // MASS TEXT this.store.dispatch( EventStore.sendMass({ @@ -472,18 +584,17 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit { } async onFileViewer(fileInfo: FileEventJson) { - this.logger.debug('onFileViewer', fileInfo); const result = await this.dialogService.open< FileViewerDialogComponent, FileViewerDialogData, FileViewerDialogResult >(FileViewerDialogComponent, { position: { - top: '30px' + top: '50px' }, maxWidth: '100vw', maxHeight: '100vh', - height: 'calc(100% - 30px)', + height: 'calc(100% - 50px)', width: '100%', hasBackdrop: false, panelClass: 'app-dialog-full', @@ -503,6 +614,7 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit { } onFileDragEnter(items: DataTransferItemList) { + this.clearView(); this.logger.debug('onFileDragEnter', items); } @@ -516,6 +628,7 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit { async onFileSelected(fileUploadItems: FileUploadItem[]) { this.logger.debug('onFileSelected', fileUploadItems); + this.clearView(); const info = { senderSeq: this.loginRes.userSeq, @@ -595,6 +708,18 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit { }, error => { this.logger.debug('onFileSelected error', error); + this.fileUploadQueue.onUploadComplete(); + + this.dialogService.open< + AlertDialogComponent, + AlertDialogData, + AlertDialogResult + >(AlertDialogComponent, { + data: { + title: 'Alert', + html: `파일전송에 실패하였습니다.
계속 문제 발생 시 관리자에게 문의하세요.` + } + }); }, () => { this.fileUploadQueue.onUploadComplete(); @@ -740,7 +865,7 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit { ConfirmDialogData, ConfirmDialogResult >(ConfirmDialogComponent, { - width: '220px', + width: '400px', data: { title: 'Delete', html: `선택한 메시지를 삭제하시겠습니까?
삭제된 메시지는 내 대화방에서만 적용되며 상대방의 대화방에서는 삭제되지 않습니다.` @@ -764,7 +889,7 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit { ConfirmDialogData, ConfirmDialogResult >(ConfirmDialogComponent, { - width: '220px', + width: '400px', data: { title: 'ReCall', html: `해당 대화를 회수하시겠습니까?
상대방 대화창에서도 회수됩니다.` @@ -789,6 +914,33 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit { async onClickContextMenu(menuType: string) { switch (menuType) { + case 'OPEN_ALBUM_LIST': + { + this.store.dispatch( + ChatStore.selectedRightDrawer({ + req: RightDrawer.AlbumBox + }) + ); + } + break; + case 'OPEN_FILE_LIST': + { + this.store.dispatch( + ChatStore.selectedRightDrawer({ + req: RightDrawer.FileBox + }) + ); + } + break; + case 'OPEN_ROOM_USER': + { + this.store.dispatch( + ChatStore.selectedRightDrawer({ + req: RightDrawer.RoomUser + }) + ); + } + break; case 'ADD_MEMBER': { const result = await this.dialogService.open< @@ -807,13 +959,17 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit { }); if (!!result && !!result.choice && result.choice) { - const userSeqs: number[] = []; + const userSeqs: number[] = this.userInfoList.map( + userInfo => userInfo.seq + ); if ( !!result.selectedUserList && result.selectedUserList.length > 0 ) { - result.selectedUserList.map(user => { - userSeqs.push(user.seq); + result.selectedUserList.forEach(user => { + if (userSeqs.indexOf(user.seq) < 0) { + userSeqs.push(user.seq); + } }); } @@ -833,6 +989,40 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit { } } break; + case 'ADD_GROUP': + { + const result = await this.dialogService.open< + SelectGroupDialogComponent, + SelectGroupDialogData, + SelectGroupDialogResult + >(SelectGroupDialogComponent, { + width: '600px', + data: { + title: 'Group Select' + } + }); + + if (!!result && !!result.choice && result.choice) { + if (!!result.group) { + const oldGroup: GroupDetailData = result.group; + const trgtUserSeq: number[] = []; + result.group.userSeqs.map(seq => trgtUserSeq.push(seq)); + this.userInfoList + .filter(v => result.group.userSeqs.indexOf(v.seq) < 0) + .forEach(user => { + trgtUserSeq.push(user.seq); + }); + + this.store.dispatch( + SyncStore.updateGroupMember({ + oldGroup, + trgtUserSeq + }) + ); + } + } + } + break; case 'EDIT_ROOM': { const result = await this.dialogService.open< @@ -890,4 +1080,42 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit { break; } } + + onClickOpenProfile(userInfo: UserInfo) { + if ( + this.roomInfo.roomType !== RoomType.Allim && + this.roomInfo.roomType !== RoomType.Bot + ) { + this.openProfile.emit(userInfo); + } + } + + /** About Sticker */ + onShowToggleStickerSelector() { + this.isShowStickerSelector = !this.isShowStickerSelector; + } + + onSelectedSticker(stickerInfo: StickerFilesInfo) { + this.selectedSticker = stickerInfo; + } + setStickerHistory(sticker: StickerFilesInfo) { + const history = this.localStorageService.get(KEY_STICKER_HISTORY); + + if (!!history && history.length > 0) { + const stickers: string[] = []; + [sticker.index, ...history.filter(hist => hist !== sticker.index)].map( + (s, i) => { + if (i < 10) { + stickers.push(s); + } + } + ); + + this.localStorageService.set(KEY_STICKER_HISTORY, stickers); + } else { + this.localStorageService.set(KEY_STICKER_HISTORY, [ + sticker.index + ]); + } + } } diff --git a/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer.component.html b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer.component.html new file mode 100644 index 0000000..2fc056d --- /dev/null +++ b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer.component.html @@ -0,0 +1,32 @@ +
+

{{ selectedRightDrawer }}

+ + + +
+ + + + + + + + + + + + + + + diff --git a/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer.component.scss b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer.component.scss new file mode 100644 index 0000000..1d0e594 --- /dev/null +++ b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer.component.scss @@ -0,0 +1,19 @@ +.rightDrawer-title{ + height: 60px; + border-bottom: 1px solid #dddddd; + font-size: 16px; + align-items: center; + display: flex; + padding: 0 20px; + font-weight: 600; + span{ + margin-left:auto; + } +} +::ng-deep .mat-tab-labels { + display: flex; + width: 100%; + border-bottom: 2px solid #dddddd; + flex: 1 1 auto; + justify-content: space-around; +} \ No newline at end of file diff --git a/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer.component.spec.ts b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer.component.spec.ts new file mode 100644 index 0000000..d8a9a8b --- /dev/null +++ b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { RightDrawerComponent } from './right-drawer.component'; + +describe('RightDrawerComponent', () => { + let component: RightDrawerComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ RightDrawerComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(RightDrawerComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer.component.ts b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer.component.ts new file mode 100644 index 0000000..7016d3f --- /dev/null +++ b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer.component.ts @@ -0,0 +1,39 @@ +import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core'; +import { RightDrawer } from '@app/types'; +import { UserInfo } from '@ucap-webmessenger/protocol-room'; +import { + UserInfoSS, + UserInfoF, + UserInfoDN, +} from '@ucap-webmessenger/protocol-query'; + +@Component({ + selector: 'app-layout-messenger-right-drawer', + templateUrl: './right-drawer.component.html', + styleUrls: ['./right-drawer.component.scss'], +}) +export class RightDrawerComponent implements OnInit { + @Input() + selectedRightDrawer: RightDrawer; + + @Output() + openProfile = new EventEmitter< + UserInfo | UserInfoSS | UserInfoF | UserInfoDN + >(); + @Output() + closeRightDrawer = new EventEmitter(); + + RightDrawer = RightDrawer; + + constructor() {} + + ngOnInit() {} + + onClickOpenProfile(userInfo: UserInfo | UserInfoSS | UserInfoF | UserInfoDN) { + this.openProfile.emit(userInfo); + } + + onClickClose() { + this.closeRightDrawer.emit(); + } +} diff --git a/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer/album-box.component.html b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer/album-box.component.html new file mode 100644 index 0000000..7440c9c --- /dev/null +++ b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer/album-box.component.html @@ -0,0 +1,154 @@ +
+
+ + + + +
+
+ + + +
+ + + + + + Select File. +
+ +
+ + + + + + + + + + + Select File. +
+
+ +
+ + +
+
    +
  • {{ selectedFile.info.name }}
  • +
  • + size : + {{ selectedFile.info.size | ucapBytes }} +
  • +
  • + date : + {{ selectedFile.info.sendDate | dateToStringFormat: 'YYYY.MM.DD' }} +
  • +
+
+
+
+ +
+
+
+
+ + + + +
+
+
+
+
+
+ + + + + + + +
+
+
+
+
+
+ + +
+
diff --git a/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer/album-box.component.scss b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer/album-box.component.scss new file mode 100644 index 0000000..0facfaf --- /dev/null +++ b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer/album-box.component.scss @@ -0,0 +1,123 @@ +@mixin ellipsis($row) { + overflow: hidden; + text-overflow: ellipsis; + @if $row == 1 { + display: block; + white-space: nowrap; + word-wrap: normal; + } @else if $row >= 2 { + display: -webkit-box; + -webkit-line-clamp: $row; + -webkit-box-orient: vertical; + word-wrap: break-word; + } +} + +::ng-deep .rightDrawer-albumbox { + height: 100%; + overflow: hidden; + .mat-tab-labels { + .mat-tab-label { + width: 50%; + } + } +} + +.select-filebox{ + display:flex; + flex-flow: column; + margin:10px; + padding:10px; + border:1px solid #cccccc; + border-radius: 4px; + .select-file{ + color: #212121; + border-bottom: 1px dotted #dddddd; + text-align:center; + padding-bottom:10px; + } + ul{ + padding-top:10px; + li{ + @include ellipsis(1); + &.name{ + font-weight:600; + } + } + } + .empty-msg{ + display:inline-flex; + flex-flow: column; + margin:auto 0; + align-items: center; + justify-content: center; + color:#999999; + span{ + padding:6px; + } + } +} + +.search-list{ + display:flex; + padding:0 10px; + height: calc(100% - 450px); + overflow-y: auto; + flex-wrap: wrap; + + .img-item { + cursor: pointer; + margin-bottom:10px; + margin-right:9px; + position: relative; + height: 150px; + dl{ + dt{ + display: flex; + justify-content: center; + align-items: center; + width: 120px; + height: 120px; + background-color: #efefef; + border: 1px dotted #cccccc; + box-sizing: border-box; + img{ + width:100%; + height:100%; + } + } + dd{ + .btn-download{ + margin-left:auto; + } + } + } + &:nth-child(3n+0){ + margin-right:0; + } + } +} + +::ng-deep .album-scrollbar{ + .ps{ + .ps-content{ + display:flex; + flex-flow: wrap; + } + } +} +.preview-image, +.preview-video{ + max-height: 140px; +} + +.btn-box { + position:absolute; + bottom:0; + height:50px; + margin-bottom:10px; + width:100%; + button { + margin: 5px; + } +} diff --git a/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer/album-box.component.spec.ts b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer/album-box.component.spec.ts new file mode 100644 index 0000000..77cb6d3 --- /dev/null +++ b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer/album-box.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { AlbumBoxComponent } from './album-box.component'; + +describe('AlbumBoxComponent', () => { + let component: AlbumBoxComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ AlbumBoxComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(AlbumBoxComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer/album-box.component.ts b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer/album-box.component.ts new file mode 100644 index 0000000..772559c --- /dev/null +++ b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer/album-box.component.ts @@ -0,0 +1,198 @@ +import { Component, OnInit, ViewChild, OnDestroy } from '@angular/core'; +import { MatPaginator, MatTableDataSource } from '@angular/material'; +import { + FileInfo, + FileDownloadInfo, + FileType, +} from '@ucap-webmessenger/protocol-file'; +import { Subscription, combineLatest } from 'rxjs'; +import { Store, select } from '@ngrx/store'; + +import * as AppStore from '@app/store'; +import * as ChatStore from '@app/store/messenger/chat'; +import { tap, map } from 'rxjs/operators'; +import { + Info, + EventJson, + FileEventJson, +} from '@ucap-webmessenger/protocol-event'; +import { FileUtil } from '@ucap-webmessenger/core'; +import { CommonApiService } from '@ucap-webmessenger/api-common'; +import { LoginResponse } from '@ucap-webmessenger/protocol-authentication'; +import { SessionStorageService } from '@ucap-webmessenger/web-storage'; +import { KEY_LOGIN_RES_INFO } from '@app/types/login-res-info.type'; +import { EnvironmentsInfo, KEY_ENVIRONMENTS_INFO } from '@app/types'; +import { VersionInfo2Response } from '@ucap-webmessenger/api-public'; +import { KEY_VER_INFO } from '@app/types/ver-info.type'; + +export interface FileInfoTotal { + info: FileInfo; + checkInfo: FileDownloadInfo[]; + eventInfo?: Info; +} + +@Component({ + selector: 'app-layout-chat-right-drawer-album-box', + templateUrl: './album-box.component.html', + styleUrls: ['./album-box.component.scss'], +}) +export class AlbumBoxComponent implements OnInit, OnDestroy { + filteredList: FileInfoTotal[] = []; + fileInfoTotal: FileInfoTotal[]; + fileInfoList: FileInfo[]; + fileInfoListSubscription: Subscription; + + selectedFile: FileInfoTotal; + selectedFileList: FileInfoTotal[] = []; + + loginRes: LoginResponse; + environmentsInfo: EnvironmentsInfo; + sessionVerinfo: VersionInfo2Response; + + FileType = FileType; + currentTabIndex = 0; + + constructor( + private store: Store, + private sessionStorageService: SessionStorageService, + private commonApiService: CommonApiService + ) { + this.loginRes = this.sessionStorageService.get( + KEY_LOGIN_RES_INFO + ); + this.environmentsInfo = this.sessionStorageService.get( + KEY_ENVIRONMENTS_INFO + ); + this.sessionVerinfo = this.sessionStorageService.get( + KEY_VER_INFO + ); + } + + ngOnInit() { + this.fileInfoListSubscription = combineLatest([ + this.store.pipe(select(AppStore.MessengerSelector.RoomSelector.roomInfo)), + this.store.pipe( + select(AppStore.MessengerSelector.EventSelector.selectAllFileInfoList) + ), + this.store.pipe( + select( + AppStore.MessengerSelector.EventSelector.selectAllFileInfoCheckList + ) + ), + this.store.pipe( + select(AppStore.MessengerSelector.EventSelector.selectAllInfoList) + ), + ]) + .pipe( + tap(() => (this.fileInfoTotal = [])), + tap(([roomInfo, fileInfoList, fileInfoCheckList, eventList]) => { + this.fileInfoList = fileInfoList.filter(fileInfo => { + if ( + fileInfo.roomSeq === roomInfo.roomSeq && + (fileInfo.type === FileType.Image || + fileInfo.type === FileType.Video) + ) { + return true; + } else { + return false; + } + }); + + this.fileInfoList.map(fileInfo => { + const events = eventList.filter( + event => event.seq === fileInfo.eventSeq + ); + + this.fileInfoTotal.push({ + info: fileInfo, + checkInfo: fileInfoCheckList.filter( + checkInfo => checkInfo.seq === fileInfo.seq + ), + eventInfo: + events.length > 0 ? (events[0] as Info) : null, + }); + }); + + this.onSelectedIndexChange(this.currentTabIndex); + }) + ) + .subscribe(); + } + + ngOnDestroy(): void { + if (!!this.fileInfoListSubscription) { + this.fileInfoListSubscription.unsubscribe(); + } + } + + getExtention(name: string): string { + return FileUtil.getExtension(name); + } + + getImageUrl(fileInfo: FileInfoTotal): string { + return this.commonApiService.urlForFileTalkDownload( + { + userSeq: this.loginRes.userSeq, + deviceType: this.environmentsInfo.deviceType, + token: this.loginRes.tokenString, + attachmentsSeq: fileInfo.info.seq, + }, + this.sessionVerinfo.downloadUrl + ); + } + + onSelectedIndexChange(index: number) { + this.selectedFile = null; + this.currentTabIndex = index; + if (this.currentTabIndex === 0) { + // Image + this.filteredList = this.fileInfoTotal.filter( + fileInfo => fileInfo.info.type === FileType.Image + ); + } else { + // Video + this.filteredList = this.fileInfoTotal.filter( + fileInfo => fileInfo.info.type === FileType.Video + ); + } + } + + onClickImage(event: MouseEvent, fileInfo: FileInfoTotal) { + if (!!event) { + event.preventDefault(); + event.stopPropagation(); + } + + this.selectedFile = fileInfo; + } + + getCheckItem(fileInfo: FileInfoTotal) { + if (this.selectedFileList) { + if ( + this.selectedFileList.filter( + info => info.info.seq === fileInfo.info.seq + ).length > 0 + ) { + return true; + } else { + return false; + } + } else { + return false; + } + } + onCheckItem(value: boolean, fileInfo: FileInfoTotal) { + if (value) { + this.onClickImage(undefined, fileInfo); + this.selectedFileList.push(fileInfo); + } else { + this.selectedFileList = this.selectedFileList.filter( + info => info.info.seq !== fileInfo.info.seq + ); + } + } + + onClickDownload(fileInfo: FileInfoTotal) { + console.log(fileInfo); + } +} diff --git a/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer/file-box.component.html b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer/file-box.component.html new file mode 100644 index 0000000..fbb91cc --- /dev/null +++ b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer/file-box.component.html @@ -0,0 +1,204 @@ +
+
+ + + + +
+
+ +
+ + + + + Select File. +
+
+ +
+
+
+
+
    +
  • {{ selectedFile.info.name }}
  • +
  • size : {{ selectedFile.info.size | ucapBytes }}
  • +
  • + date : + {{ selectedFile.info.sendDate | dateToStringFormat: 'YYYY.MM.DD' }} +
  • +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ + + + + + + + + + + + + + + + +
+ + + + + + + Name + +
+ {{ element.info.name }} +
+
+ + {{ element.info.sendDate | dateToStringFormat: 'YYYY.MM.DD' }} ~ + 2020.01.23 +
+
sendDate + + {{ element.info.size | ucapBytes }} +
+
+
+ +
diff --git a/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer/file-box.component.scss b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer/file-box.component.scss new file mode 100644 index 0000000..bd6e8c3 --- /dev/null +++ b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer/file-box.component.scss @@ -0,0 +1,171 @@ +@mixin ellipsis($row) { + overflow: hidden; + text-overflow: ellipsis; + @if $row == 1 { + display: block; + white-space: nowrap; + word-wrap: normal; + } @else if $row >= 2 { + display: -webkit-box; + -webkit-line-clamp: $row; + -webkit-box-orient: vertical; + word-wrap: break-word; + } +} + +::ng-deep .rightDrawer-filebox { + width: 100%; + height: calc(100% - 60px); + .rightDrawer-tab { + .mat-tab-label { + width: 50%; + } + } +} + +.select-filebox { + position: relative; + display: flex; + flex-flow: column; + margin: 10px; + border: 1px solid #cccccc; + border-radius: 4px; + .select-flie { + display: flex; + flex-flow: row; + align-items: center; + color: #212121; + align-items: center; + padding: 10px; + border-bottom: 1px dotted #dddddd; + ul { + padding: 0; + .name { + font-weight: 600; + } + } + } + .empty-msg { + display: inline-flex; + flex-flow: column; + margin: auto 0; + align-items: center; + justify-content: center; + color: #999999; + span { + padding: 6px; + } + } + .select-file-option { + position: absolute; + display: flex; + justify-content: space-between; + width: 100%; + text-align: center; + padding: 10px 0; + bottom: 4px; + span { + width: 28px; + height: 28px; + display: inline-flex; + text-align: center; + justify-content: center; + align-items: center; + margin: 0 20px; + cursor: pointer; + svg { + } + &:hover { + border-radius: 50%; + background-color: #999999; + color: #ffffff !important; + } + } + } +} + +.mat-table { + width: 100%; + position: relative; + th.infos { + padding: 10px; + } + tr.mat-row { + height: 70px; + .file-info { + padding: 16px; + display: grid; + height: 70px; + .file-name { + font-weight: 600; + margin-bottom: 2px; + width: 100%; + @include ellipsis(1); + } + .download-period { + font-size: 12px; + width: 100%; + @include ellipsis(1); + } + } + } +} +.table-box { + height: calc(100% - 440px); + overflow-y: auto; +} +.mat-paginator-container { + display: flex; + flex-flow: column; +} + +.mat-row:hover { + background: rgba(0, 0, 0, 0.04); + cursor: pointer; +} + +.footer-fix { + position: absolute; + bottom: 0; + height: 160px; + flex-direction: column; + box-sizing: border-box; + display: flex; + border-top: 1px solid #dddddd; + .btn-box { + height: 50px; + padding-bottom: 10px; + width: 100%; + background-color: #ffffff; + button { + margin: 5px; + } + } +} + +::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; + } +} diff --git a/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer/file-box.component.spec.ts b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer/file-box.component.spec.ts new file mode 100644 index 0000000..065e1da --- /dev/null +++ b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer/file-box.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { FileBoxComponent } from './file-box.component'; + +describe('FileBoxComponent', () => { + let component: FileBoxComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ FileBoxComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(FileBoxComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer/file-box.component.ts b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer/file-box.component.ts new file mode 100644 index 0000000..fb51a0b --- /dev/null +++ b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer/file-box.component.ts @@ -0,0 +1,215 @@ +import { Component, OnInit, ViewChild, OnDestroy } from '@angular/core'; +import { MatPaginator, MatTableDataSource, MatSort } from '@angular/material'; +import { + FileInfo, + FileDownloadInfo, + FileType +} from '@ucap-webmessenger/protocol-file'; +import { Subscription, combineLatest } from 'rxjs'; +import { Store, select } from '@ngrx/store'; + +import * as AppStore from '@app/store'; +import * as ChatStore from '@app/store/messenger/chat'; +import { tap, map } from 'rxjs/operators'; +import { FileUtil } from '@ucap-webmessenger/core'; +import { LoginResponse } from '@ucap-webmessenger/protocol-authentication'; +import { SessionStorageService } from '@ucap-webmessenger/web-storage'; +import { KEY_LOGIN_RES_INFO } from '@app/types/login-res-info.type'; + +export interface FileInfoTotal { + info: FileInfo; + checkInfo: FileDownloadInfo[]; +} + +@Component({ + selector: 'app-layout-chat-right-drawer-file-box', + templateUrl: './file-box.component.html', + styleUrls: ['./file-box.component.scss'] +}) +export class FileBoxComponent implements OnInit, OnDestroy { + displayedColumns: string[] = ['check', 'name', 'sendDate']; + dataSource = new MatTableDataSource(); + + fileInfoTotal: FileInfoTotal[]; + fileInfoList: FileInfo[]; + fileInfoListSubscription: Subscription; + + selectedFile: FileInfoTotal; + selectedFileList: FileInfoTotal[] = []; + + loginRes: LoginResponse; + + currentTabIndex = 0; + + @ViewChild(MatPaginator, { static: true }) paginator: MatPaginator; + @ViewChild(MatSort, { static: true }) sort: MatSort; + + constructor( + private store: Store, + private sessionStorageService: SessionStorageService + ) { + this.loginRes = this.sessionStorageService.get( + KEY_LOGIN_RES_INFO + ); + } + + ngOnInit() { + this.fileInfoListSubscription = combineLatest([ + this.store.pipe(select(AppStore.MessengerSelector.RoomSelector.roomInfo)), + this.store.pipe( + select(AppStore.MessengerSelector.EventSelector.selectAllFileInfoList) + ), + this.store.pipe( + select( + AppStore.MessengerSelector.EventSelector.selectAllFileInfoCheckList + ) + ) + ]) + .pipe( + tap(() => (this.fileInfoTotal = [])), + tap(([roomInfo, fileInfoList, fileInfoCheckList]) => { + this.fileInfoList = fileInfoList.filter(fileInfo => { + if ( + fileInfo.roomSeq === roomInfo.roomSeq && + (fileInfo.type === FileType.File || + fileInfo.type === FileType.Sound) + ) { + return true; + } else { + return false; + } + }); + + this.fileInfoList.map(fileInfo => { + this.fileInfoTotal.push({ + info: fileInfo, + checkInfo: fileInfoCheckList.filter( + checkInfo => checkInfo.seq === fileInfo.seq + ) + }); + }); + + this.onSelectedIndexChange(this.currentTabIndex); + }) + ) + .subscribe(); + + this.dataSource.sortingDataAccessor = (item, property) => { + switch (property) { + case 'name': + return item.info.name; + case 'size': + return item.info.size; + case 'sendDate': + return item.info.sendDate; + default: + return item.info[property]; + } + }; + this.dataSource.sort = this.sort; + this.dataSource.paginator = this.paginator; + } + + ngOnDestroy(): void { + if (!!this.fileInfoListSubscription) { + this.fileInfoListSubscription.unsubscribe(); + } + } + + getExtention(name: string): string { + return FileUtil.getExtension(name); + } + + onSelectedIndexChange(index: number) { + this.selectedFile = null; + this.currentTabIndex = index; + if (this.currentTabIndex === 0) { + // Receive + this.dataSource.data = this.fileInfoTotal.filter( + fileInfo => fileInfo.info.senderSeq !== this.loginRes.userSeq + ); + } else { + // send + this.dataSource.data = this.fileInfoTotal.filter( + fileInfo => fileInfo.info.senderSeq === this.loginRes.userSeq + ); + } + } + + getCheckAllUser() { + const data = this.dataSource + .sortData(this.dataSource.data, this.sort) + .filter((u, i) => i >= this.paginator.pageSize * this.paginator.pageIndex) + .filter((u, i) => i < this.paginator.pageSize); + + if (data.length === 0) { + return false; + } + + if ( + data.filter( + dInfo => + this.selectedFileList.filter( + fileInfo => fileInfo.info.seq === dInfo.info.seq + ).length === 0 + ).length > 0 + ) { + return false; + } else { + return true; + } + } + onCheckAllkUser(value: boolean) { + const data = this.dataSource + .sortData(this.dataSource.data, this.sort) + .filter((u, i) => i >= this.paginator.pageSize * this.paginator.pageIndex) + .filter((u, i) => i < this.paginator.pageSize); + + if (!!data && data.length > 0) { + if (value) { + this.selectedFileList.push( + ...data.filter(dInfo => + this.selectedFileList.filter( + fileInfo => fileInfo.info.seq !== dInfo.info.seq + ) + ) + ); + } else { + this.selectedFileList = this.selectedFileList.filter( + fileInfo => + !( + data.filter(dInfo => dInfo.info.seq === fileInfo.info.seq) + .length > 0 + ) + ); + } + } + } + getCheckUser(fileInfo: FileInfoTotal) { + if (this.selectedFileList) { + if ( + this.selectedFileList.filter( + info => info.info.seq === fileInfo.info.seq + ).length > 0 + ) { + return true; + } else { + return false; + } + } else { + return false; + } + } + onCheckUser(value: boolean, fileInfo: FileInfoTotal) { + if (value) { + this.selectedFileList.push(fileInfo); + } else { + this.selectedFileList = this.selectedFileList.filter( + info => info.info.seq !== fileInfo.info.seq + ); + } + } + onClickRow(row: FileInfoTotal) { + this.selectedFile = row; + } +} diff --git a/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer/index.ts b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer/index.ts new file mode 100644 index 0000000..c51042c --- /dev/null +++ b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer/index.ts @@ -0,0 +1,9 @@ +import { FileBoxComponent } from './file-box.component'; +import { AlbumBoxComponent } from './album-box.component'; +import { RoomUserListComponent } from './room-user-list.component'; + +export const RIGHT_DRAWER_COMPONENTS = [ + FileBoxComponent, + AlbumBoxComponent, + RoomUserListComponent +]; diff --git a/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer/room-user-list.component.html b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer/room-user-list.component.html new file mode 100644 index 0000000..326e568 --- /dev/null +++ b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer/room-user-list.component.html @@ -0,0 +1,25 @@ +
+
+ + +
+
+ + +
+
diff --git a/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer/room-user-list.component.scss b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer/room-user-list.component.scss new file mode 100644 index 0000000..7954480 --- /dev/null +++ b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer/room-user-list.component.scss @@ -0,0 +1,16 @@ +@import "../../../../../../../ucap-webmessenger-ui/src/assets/scss/partials/presence"; +.list { + width: 100%; + height: 100%; + overflow: hidden; + + .search-list { + overflow: auto; + } +} + +.btn-box { + button { + margin: 5px; + } +} diff --git a/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer/room-user-list.component.spec.ts b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer/room-user-list.component.spec.ts new file mode 100644 index 0000000..d6ce153 --- /dev/null +++ b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer/room-user-list.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { RoomUserListComponent } from './room-user-list.component'; + +describe('RoomUserListComponent', () => { + let component: RoomUserListComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ RoomUserListComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(RoomUserListComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer/room-user-list.component.ts b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer/room-user-list.component.ts new file mode 100644 index 0000000..35c695c --- /dev/null +++ b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/right-drawer/room-user-list.component.ts @@ -0,0 +1,172 @@ +import { + Component, + OnInit, + OnDestroy, + Output, + EventEmitter +} from '@angular/core'; +import { Subscription } from 'rxjs'; +import { Store, select } from '@ngrx/store'; +import { tap, map } from 'rxjs/operators'; + +import * as AppStore from '@app/store'; +import * as SyncStore from '@app/store/messenger/sync'; +import * as RoomStore from '@app/store/messenger/room'; + +import { UserInfo } from '@ucap-webmessenger/protocol-room'; +import { VersionInfo2Response } from '@ucap-webmessenger/api-public'; +import { SessionStorageService } from '@ucap-webmessenger/web-storage'; +import { KEY_VER_INFO } from '@app/types/ver-info.type'; +import { DialogService } from '@ucap-webmessenger/ui'; +import { + SelectGroupDialogComponent, + SelectGroupDialogResult, + SelectGroupDialogData +} from '../../dialogs/group/select-group.dialog.component'; +import { GroupDetailData } from '@ucap-webmessenger/protocol-sync'; +import { + CreateChatDialogComponent, + CreateChatDialogResult, + CreateChatDialogData +} from '../../dialogs/chat/create-chat.dialog.component'; +import { UserSelectDialogType } from '@app/types'; +import { LoginResponse } from '@ucap-webmessenger/protocol-authentication'; +import { KEY_LOGIN_RES_INFO } from '@app/types/login-res-info.type'; + +@Component({ + selector: 'app-layout-chat-right-drawer-room-user-list', + templateUrl: './room-user-list.component.html', + styleUrls: ['./room-user-list.component.scss'] +}) +export class RoomUserListComponent implements OnInit, OnDestroy { + @Output() + openProfile = new EventEmitter(); + + userInfoList: UserInfo[]; + userInfoListSubscription: Subscription; + + loginRes: LoginResponse; + sessionVerinfo: VersionInfo2Response; + + constructor( + private store: Store, + private sessionStorageService: SessionStorageService, + private dialogService: DialogService + ) { + this.loginRes = this.sessionStorageService.get( + KEY_LOGIN_RES_INFO + ); + this.sessionVerinfo = this.sessionStorageService.get( + KEY_VER_INFO + ); + } + + ngOnInit() { + this.userInfoListSubscription = this.store + .pipe( + select(AppStore.MessengerSelector.RoomSelector.selectUserinfolist), + tap(userInfoList => { + this.userInfoList = userInfoList; + }) + ) + .subscribe(); + } + + ngOnDestroy(): void { + if (!!this.userInfoListSubscription) { + this.userInfoListSubscription.unsubscribe(); + } + } + + getStatusBulkInfo(buddy: UserInfo) { + return this.store.pipe( + select( + AppStore.MessengerSelector.StatusSelector.selectEntitiesStatusBulkInfo + ), + map(statusBulkInfo => + !!statusBulkInfo ? statusBulkInfo[buddy.seq] : undefined + ) + ); + } + + onClickOpenProfile(userInfo: UserInfo) { + this.openProfile.emit(userInfo); + } + + async onClickAddMember() { + const result = await this.dialogService.open< + CreateChatDialogComponent, + CreateChatDialogData, + CreateChatDialogResult + >(CreateChatDialogComponent, { + width: '600px', + data: { + type: UserSelectDialogType.EditChatMember, + title: 'Edit Chat Member', + curRoomUser: this.userInfoList.filter( + user => user.seq !== this.loginRes.userSeq + ) + } + }); + + if (!!result && !!result.choice && result.choice) { + const userSeqs: number[] = this.userInfoList.map( + userInfo => userInfo.seq + ); + if (!!result.selectedUserList && result.selectedUserList.length > 0) { + result.selectedUserList.forEach(user => { + if (userSeqs.indexOf(user.seq) < 0) { + userSeqs.push(user.seq); + } + }); + } + + if (userSeqs.length > 0) { + // include me + userSeqs.push(this.loginRes.userSeq); + + this.store.dispatch( + RoomStore.inviteOrOpen({ + req: { + divCd: 'Invite', + userSeqs + } + }) + ); + } + } + } + + async onClickAddGroup() { + const result = await this.dialogService.open< + SelectGroupDialogComponent, + SelectGroupDialogData, + SelectGroupDialogResult + >(SelectGroupDialogComponent, { + width: '600px', + data: { + title: 'Group Select' + } + }); + + if (!!result && !!result.choice && result.choice) { + if (!!result.group) { + const oldGroup: GroupDetailData = result.group; + const trgtUserSeq: number[] = []; + result.group.userSeqs.map(seq => trgtUserSeq.push(seq)); + this.userInfoList + .filter(v => result.group.userSeqs.indexOf(v.seq) < 0) + .forEach(user => { + trgtUserSeq.push(user.seq); + }); + + this.store.dispatch( + SyncStore.updateGroupMember({ + oldGroup, + trgtUserSeq + }) + ); + } + } + } +} diff --git a/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/chat/create-chat.dialog.component.html b/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/chat/create-chat.dialog.component.html index 23a40c6..2ee7eae 100644 --- a/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/chat/create-chat.dialog.component.html +++ b/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/chat/create-chat.dialog.component.html @@ -34,9 +34,25 @@ > - group + + + + + + + -
+
-
+
-
-
- +
+
+
+ +
+
검색결과
@@ -98,10 +128,13 @@
@@ -112,20 +145,6 @@ chat
-
- {{ selectedUserList.length }}명 + {{ selectedUserList.length }}명
@@ -167,7 +188,12 @@ > No - diff --git a/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/chat/create-chat.dialog.component.scss b/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/chat/create-chat.dialog.component.scss index 711b0d9..d803389 100644 --- a/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/chat/create-chat.dialog.component.scss +++ b/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/chat/create-chat.dialog.component.scss @@ -6,21 +6,25 @@ ::ng-deep .dialog-org { .oraganization-tab { width: 100%; - height:380px; + height: 380px; border-bottom: 1px solid #dddddd; position: relative; .oraganization-tab-tree { display: inline-flex; width: 50%; - height:100% !important; + height: 100% !important; border-right: 1px solid #dddddd; overflow: auto; + .tab-tree-frame { + width: 100%; + height: 100%; + } } .select-list { display: inline-flex; flex-direction: column; width: 50%; - height:100% !important; + height: 100% !important; overflow: auto; .search-list { overflow: auto; @@ -33,12 +37,12 @@ .list-chip { height: 100px; width: 100%; - padding:10px; - border:1px solid #dddddd; + padding: 10px; + border: 1px solid #dddddd; overflow: auto; - background-color:#f9f9f9; + background-color: #f9f9f9; } -.mat-chip.mat-standard-chip .mat-chip-remove{ +.mat-chip.mat-standard-chip .mat-chip-remove { line-height: 24px; } .confirm-card { @@ -64,15 +68,42 @@ } } } + ::ng-deep .mat-dialog-container .mat-tab-body-wrapper { height: 380px; -} -.list-panel{ - overflow: auto; - height: calc(100% - 60px); - .group-expansion{ - .list-item{ - height:70px; - } + width: 100%; + .mat-tab-body { + width: 100%; + height: 100%; + .mat-tab-body-content { + width: 100%; + height: 100%; + } } } +.mat-tab-frame { + width: 100%; + height: 100%; + .list-panel { + width: 100%; + height: calc(100% - 60px); + .group-expansion { + .list-item { + height: 70px; + } + } + } +} +::ng-deep .mat-card > .mat-tab-labels { + border-bottom: 2px solid #dddddd; +} +::ng-deep .ps-content { + .cdk-virtual-scroll-viewport { + height: 100%; + } +} + +.list-item-frame { + width: 100%; + height: 100%; +} diff --git a/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/chat/create-chat.dialog.component.ts b/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/chat/create-chat.dialog.component.ts index 0881d26..a9bd462 100644 --- a/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/chat/create-chat.dialog.component.ts +++ b/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/chat/create-chat.dialog.component.ts @@ -48,6 +48,12 @@ import { UserInfoShort, UserInfo as RoomUserInfo } from '@ucap-webmessenger/protocol-room'; +import { + ConfirmDialogComponent, + ConfirmDialogResult, + ConfirmDialogData, + DialogService +} from '@ucap-webmessenger/ui'; export interface CreateChatDialogData { type?: string; @@ -62,7 +68,8 @@ export interface CreateChatDialogData { | UserInfoSS | UserInfoF | UserInfoDN - | RoomUserInfo)[]; + | RoomUserInfo + )[]; } export interface CreateChatDialogResult { @@ -72,7 +79,8 @@ export interface CreateChatDialogResult { | UserInfoSS | UserInfoF | UserInfoDN - | RoomUserInfo)[]; + | RoomUserInfo + )[]; selectedRoom?: RoomInfo; groupName?: string; oldGroup?: GroupDetailData; @@ -94,13 +102,16 @@ export class CreateChatDialogComponent implements OnInit, OnDestroy { private sessionStorageService: SessionStorageService, private queryProtocolService: QueryProtocolService, private formBuilder: FormBuilder, - private logger: NGXLogger + private logger: NGXLogger, + private dialogService: DialogService ) { this.sessionVerinfo = this.sessionStorageService.get( KEY_VER_INFO ); } + currentTabIndex: number; + UserSelectDialogType = UserSelectDialogType; loginRes: LoginResponse; @@ -132,7 +143,8 @@ export class CreateChatDialogComponent implements OnInit, OnDestroy { | UserInfoSS | UserInfoF | UserInfoDN - | RoomUserInfo)[] = []; + | RoomUserInfo + )[] = []; isShowSelectedUserList = true; selectedRoom: RoomInfo; @@ -227,6 +239,8 @@ export class CreateChatDialogComponent implements OnInit, OnDestroy { if (this.data.type === UserSelectDialogType.EditChatMember) { this.selectedUserList = this.data.curRoomUser; } + + this.currentTabIndex = 0; } ngOnDestroy(): void { @@ -274,6 +288,8 @@ export class CreateChatDialogComponent implements OnInit, OnDestroy { } onSelectedTabChange(tabChangeEvent: MatTabChangeEvent): void { + this.currentTabIndex = tabChangeEvent.index; + if (tabChangeEvent.index === 2) { this.selectedUserList = []; this.isShowSelectedUserList = false; @@ -366,6 +382,7 @@ export class CreateChatDialogComponent implements OnInit, OnDestroy { } } + /** 조직도 > 부서원 전체 선택 */ onCheckAllUser(params: { isChecked: boolean; userInfos: (UserInfo | UserInfoSS | UserInfoF | UserInfoDN)[]; @@ -404,6 +421,19 @@ export class CreateChatDialogComponent implements OnInit, OnDestroy { item => item.seq !== params.userInfo.seq ); } + console.log(this.selectedUserList); + } + onToggleUser(userInfo: UserInfo | UserInfoSS | UserInfoF | UserInfoDN) { + if ( + this.selectedUserList.filter(user => user.seq === userInfo.seq).length === + 0 + ) { + this.selectedUserList = [...this.selectedUserList, userInfo]; + } else { + this.selectedUserList = this.selectedUserList.filter( + item => item.seq !== userInfo.seq + ); + } } /** 대화방 > 대화방 선택 :: 해당 팝업에서는 대화방을 중복 선택하지 않는다 */ @@ -427,12 +457,13 @@ export class CreateChatDialogComponent implements OnInit, OnDestroy { } getCheckableRoom(roomInfo: RoomInfo) { - if (!!this.data.ignoreRoom && this.data.ignoreRoom.length > 0) { - return !( - this.data.ignoreRoom.filter(room => room.roomSeq === roomInfo.roomSeq) - .length > 0 - ); - } + // // 현재 방도 체크 전달 할수 있도록 수정. (끌어올리기 개념.) + // if (!!this.data.ignoreRoom && this.data.ignoreRoom.length > 0) { + // return !( + // this.data.ignoreRoom.filter(room => room.roomSeq === roomInfo.roomSeq) + // .length > 0 + // ); + // } return true; } getCheckedRoom(roomInfo: RoomInfo) { @@ -449,8 +480,41 @@ export class CreateChatDialogComponent implements OnInit, OnDestroy { ); } + getBtnValid() { + if (this.data.type === UserSelectDialogType.NewGroup) { + return this.inputForm.invalid; + } else { + return false; + } + } + /** 팝업의 선택 이벤트 전달. */ - onClickChoice(choice: boolean): void { + async onClickChoice(choice: boolean): Promise { + if (this.data.type === UserSelectDialogType.NewGroup) { + let cfmMsg = `새로운 그룹을 추가하시겠습니까?`; + if (this.selectedUserList.length === 0) { + cfmMsg += `
빈 그룹으로 생성됩니다.`; + } + const result = await this.dialogService.open< + ConfirmDialogComponent, + ConfirmDialogData, + ConfirmDialogResult + >(ConfirmDialogComponent, { + width: '400px', + data: { + title: 'Delete', + html: cfmMsg + } + }); + + if (!!result && !!result.choice && result.choice) { + this.doAction(choice); + } + } else { + this.doAction(choice); + } + } + doAction(choice: boolean) { this.dialogRef.close({ choice, selectedUserList: this.selectedUserList, diff --git a/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/chat/edit-chat-room.dialog.component.html b/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/chat/edit-chat-room.dialog.component.html index 6b205e6..8924fa5 100644 --- a/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/chat/edit-chat-room.dialog.component.html +++ b/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/chat/edit-chat-room.dialog.component.html @@ -5,7 +5,7 @@
- + + + {{ data.title }} + + + +

+
+
+ + + + diff --git a/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/chat/mass-detail.component.scss b/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/chat/mass-detail.component.scss new file mode 100644 index 0000000..9968576 --- /dev/null +++ b/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/chat/mass-detail.component.scss @@ -0,0 +1,25 @@ +::ng-deep .mat-card-header-tex { + margin: 0; +} +.confirm-card { + min-width: 500px; + .mat-card-header { + margin-bottom: 20px; + .mat-card-header-text { + .mat-card-title { + margin: 0 -16px; + } + } + } + .button-farm { + text-align: right; + .mat-primary { + margin-left: 4px; + } + } +} + +.contnets { + max-height: 500px; + word-break: break-word; +} diff --git a/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/chat/mass-detail.component.spec.ts b/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/chat/mass-detail.component.spec.ts new file mode 100644 index 0000000..bf54e62 --- /dev/null +++ b/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/chat/mass-detail.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { MassDetailComponent } from './mass-detail.component'; + +describe('MassDetailComponent', () => { + let component: MassDetailComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ MassDetailComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(MassDetailComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/chat/mass-detail.component.ts b/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/chat/mass-detail.component.ts new file mode 100644 index 0000000..d06b360 --- /dev/null +++ b/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/chat/mass-detail.component.ts @@ -0,0 +1,58 @@ +import { + Component, + OnInit, + Inject, + ElementRef, + AfterViewInit +} from '@angular/core'; +import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material'; +import { UCAP_NATIVE_SERVICE, NativeService } from '@ucap-webmessenger/native'; + +export interface MassDetailDialogData { + title: string; + contents: string; +} + +// tslint:disable-next-line: no-empty-interface +export interface MassDetailDialogResult {} + +@Component({ + selector: 'app-layout-messenger-mass-detail', + templateUrl: './mass-detail.component.html', + styleUrls: ['./mass-detail.component.scss'] +}) +export class MassDetailComponent implements OnInit, AfterViewInit { + constructor( + public dialogRef: MatDialogRef< + MassDetailDialogData, + MassDetailDialogResult + >, + @Inject(MAT_DIALOG_DATA) public data: MassDetailDialogData, + private elementRef: ElementRef, + @Inject(UCAP_NATIVE_SERVICE) private nativeService: NativeService + ) {} + + ngOnInit() {} + + ngAfterViewInit(): void { + if ( + !!this.elementRef.nativeElement && + !!this.elementRef.nativeElement.querySelector('a') + ) { + const elements = this.elementRef.nativeElement.querySelectorAll('a'); + elements.forEach(element => { + element.addEventListener('click', this.onClickEvent.bind(this)); + }); + } + } + + onClickEvent(event: MouseEvent) { + this.nativeService.openDefaultBrowser( + (event.target as HTMLAnchorElement).text + ); + } + + onClickConfirm(): void { + this.dialogRef.close(); + } +} diff --git a/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/group/edit-group.dialog.component.html b/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/group/edit-group.dialog.component.html index 06db2dd..24f3c32 100644 --- a/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/group/edit-group.dialog.component.html +++ b/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/group/edit-group.dialog.component.html @@ -28,4 +28,4 @@ Yes - + \ No newline at end of file diff --git a/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/group/edit-group.dialog.component.scss b/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/group/edit-group.dialog.component.scss index d6fb4a0..22d70b5 100644 --- a/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/group/edit-group.dialog.component.scss +++ b/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/group/edit-group.dialog.component.scss @@ -23,5 +23,4 @@ form{ .mat-form-field{ width:100%; } -} - +} \ No newline at end of file diff --git a/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/group/select-group.dialog.component.html b/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/group/select-group.dialog.component.html index afc155e..ee657ad 100644 --- a/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/group/select-group.dialog.component.html +++ b/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/group/select-group.dialog.component.html @@ -40,16 +40,18 @@
- - -
-
@@ -57,7 +59,10 @@ *ngFor="let groupBuddy of groupBuddyList$ | async" [value]="groupBuddy.group" > - {{ groupBuddy.group.name }} + {{ groupBuddy.group.name }} + ({{ groupBuddy.buddyList.length }}명) +
+ + + + + + diff --git a/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/settings/messenger-settings.dialog.component.scss b/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/settings/messenger-settings.dialog.component.scss new file mode 100644 index 0000000..df5512b --- /dev/null +++ b/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/settings/messenger-settings.dialog.component.scss @@ -0,0 +1,112 @@ +::ng-deep .setting-frame { + padding: 16px; + height: 100%; + min-width: 500px; + position: relative; + + .mat-dialog-container { + position: relative; + } + + .mat-card-header { + position: relative; + width: 100%; + border-bottom: 1px solid #dddddd; + margin-bottom: 12px; + } + + .mat-card-content { + flex: 0 0 auto; + display: flex; + align-items: flex-start; + height: calc(100% - 100px); + border-bottom: 1px solid #dddddd; + .setting-tab { + position: relative; + width: 100%; + height: 100%; + .mat-tab-group { + flex-direction: row; + .mat-tab-header { + width: 160px; + .mat-tab-labels { + flex-direction: column; + .mat-tab-label { + padding: 0 10px; + align-content: flex-start; + text-align: left; + align-items: self-start; + justify-content: flex-start; + } + .mat-ink-bar { + display: none; + } + } + } + .mat-tab-body-wrapper { + border-left: 1px solid #dddddd; + position: relative; + height: 100%; + padding: 0 0 10px 10px; + } + } + } + } + + .button-farm { + text-align: right; + position: absolute; + width: 100%; + bottom: 10px; + .mat-primary { + margin-left: 4px; + } + } +} + +::ng-deep .setting-tab { + .mat-tab-group { + position: relative; + height: 100%; + width: 100%; + .mat-tab-header { + width: 160px; + flex-flow: column; + .mat-tab-label-container { + .mat-tab-list { + .mat-tab-labels { + border-bottom: 0; + padding-right: 10px; + .mat-tab-label { + padding: 0 10px; + } + } + } + } + } + + .mat-tab-body-wrapper { + .mat-tab-body { + .mat-tab-body-conten { + position: relative; + width: 100%; + height: 100%; + .mat-list-base { + position: relative; + } + } + } + } + } +} + +::ng-deep .setting-category { + .mat-list-base { + position: relative; + .mat-list-item { + font-size: 15px; + } + .mat-divider { + } + } +} diff --git a/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/settings/messenger-settings.dialog.component.spec.ts b/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/settings/messenger-settings.dialog.component.spec.ts new file mode 100644 index 0000000..6656611 --- /dev/null +++ b/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/settings/messenger-settings.dialog.component.spec.ts @@ -0,0 +1,27 @@ +/* tslint:disable:no-unused-variable */ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { By } from '@angular/platform-browser'; +import { DebugElement } from '@angular/core'; + +import { MessengerSettingsDialogComponent } from './messenger-settings.dialog.component'; + +describe('MessengerSettingsDialogComponent', () => { + let component: MessengerSettingsDialogComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [MessengerSettingsDialogComponent] + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(MessengerSettingsDialogComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/settings/messenger-settings.dialog.component.ts b/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/settings/messenger-settings.dialog.component.ts new file mode 100644 index 0000000..5f6a1ea --- /dev/null +++ b/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/settings/messenger-settings.dialog.component.ts @@ -0,0 +1,57 @@ +import { Component, OnInit, Inject, Renderer2 } from '@angular/core'; +import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material'; +import { KEY_LOGIN_RES_INFO } from '@app/types/login-res-info.type'; +import { KEY_VER_INFO } from '@app/types/ver-info.type'; +import { SessionStorageService } from '@ucap-webmessenger/web-storage'; + +import { Store } from '@ngrx/store'; + +import { DialogService } from '@ucap-webmessenger/ui'; +import { VersionInfo2Response } from '@ucap-webmessenger/api-public'; +import { LoginResponse } from '@ucap-webmessenger/protocol-authentication'; +import { map } from 'rxjs/operators'; +import { DOCUMENT } from '@angular/common'; + +export interface MessengerSettingsDialogData {} + +export interface MessengerSettingsDialogResult {} + +@Component({ + selector: 'app-messenger-settings-dialog', + templateUrl: './messenger-settings.dialog.component.html', + styleUrls: ['./messenger-settings.dialog.component.scss'] +}) +export class MessengerSettingsDialogComponent implements OnInit { + loginRes: LoginResponse; + sessionVerinfo: VersionInfo2Response; + + constructor( + public dialogRef: MatDialogRef< + MessengerSettingsDialogData, + MessengerSettingsDialogResult + >, + @Inject(MAT_DIALOG_DATA) public data: MessengerSettingsDialogData, + private dialogService: DialogService, + private sessionStorageService: SessionStorageService, + private store: Store, + @Inject(DOCUMENT) private document: Document, + private renderer2: Renderer2 + ) { + this.sessionVerinfo = this.sessionStorageService.get( + KEY_VER_INFO + ); + this.loginRes = this.sessionStorageService.get( + KEY_LOGIN_RES_INFO + ); + } + + ngOnInit() {} + + onSelectTheme(theme: string): void { + this.renderer2.setAttribute(this.document.body, 'class', theme); + } + + onClickChoice(choice: boolean): void { + this.dialogRef.close({}); + } +} diff --git a/projects/ucap-webmessenger-app/src/app/layouts/messenger/messenger.layout.module.ts b/projects/ucap-webmessenger-app/src/app/layouts/messenger/messenger.layout.module.ts index f6daeba..66fb9ec 100644 --- a/projects/ucap-webmessenger-app/src/app/layouts/messenger/messenger.layout.module.ts +++ b/projects/ucap-webmessenger-app/src/app/layouts/messenger/messenger.layout.module.ts @@ -22,7 +22,14 @@ import { MatTabsModule } from '@angular/material/tabs'; import { MatToolbarModule } from '@angular/material/toolbar'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatInputModule } from '@angular/material/input'; -import { MatCheckboxModule } from '@angular/material'; +import { + MatCheckboxModule, + MatTableModule, + MatPaginatorModule, + MatRippleModule, + MatSortModule, + MatTooltipModule +} from '@angular/material'; import { MatListModule } from '@angular/material/list'; import { MatChipsModule } from '@angular/material/chips'; @@ -37,6 +44,7 @@ import { UCapUiRoomModule } from '@ucap-webmessenger/ui-room'; import { UCapUiProfileModule } from '@ucap-webmessenger/ui-profile'; import { UCapUiGroupModule } from '@ucap-webmessenger/ui-group'; import { UCapUiOrganizationModule } from '@ucap-webmessenger/ui-organization'; +import { UCapUiSettingsModule } from '@ucap-webmessenger/ui-settings'; import { AppCommonLayoutModule } from '@app/layouts/common/common.layout.module'; @@ -70,6 +78,11 @@ import { DIALOGS } from './dialogs'; MatCheckboxModule, MatRadioModule, MatSelectModule, + MatTableModule, + MatSortModule, + MatPaginatorModule, + MatRippleModule, + MatTooltipModule, PerfectScrollbarModule, @@ -79,6 +92,7 @@ import { DIALOGS } from './dialogs'; UCapUiProfileModule, UCapUiGroupModule, UCapUiOrganizationModule, + UCapUiSettingsModule, AppCommonLayoutModule ], diff --git a/projects/ucap-webmessenger-app/src/app/layouts/native/components/top-bar.component.html b/projects/ucap-webmessenger-app/src/app/layouts/native/components/top-bar.component.html index 9dc1c69..8fae978 100644 --- a/projects/ucap-webmessenger-app/src/app/layouts/native/components/top-bar.component.html +++ b/projects/ucap-webmessenger-app/src/app/layouts/native/components/top-bar.component.html @@ -1,9 +1,60 @@
UCAP M Messenger
+
@@ -23,19 +84,45 @@ > - - - + + + - + - diff --git a/projects/ucap-webmessenger-ui-account/src/lib/components/login.component.scss b/projects/ucap-webmessenger-ui-account/src/lib/components/login.component.scss index 7bc3a60..6b5cdeb 100644 --- a/projects/ucap-webmessenger-ui-account/src/lib/components/login.component.scss +++ b/projects/ucap-webmessenger-ui-account/src/lib/components/login.component.scss @@ -1,13 +1,12 @@ - .login-form { position: relative; width: 384px; max-width: 384px; padding: 50px; text-align: center; - background-color: rgba( 255, 255, 255, 0.8 ); + background-color: rgba(255, 255, 255, 0.8); border-radius: 0px; - box-shadow: 4px 4px 0px rgba(0,0,0,0.1); + box-shadow: 4px 4px 0px rgba(0, 0, 0, 0.1); .mat-title { margin: 16px 0 32px 0; @@ -89,14 +88,12 @@ } } } - .policy{ + .policy { position: absolute; - bottom:0; - width:100%; - padding:10px; - left:0; - //background-color: #4f586b; - background-color: #555555; + bottom: 0; + width: 100%; + padding: 10px; + left: 0; color: #ffffff; } diff --git a/projects/ucap-webmessenger-ui-account/src/lib/components/login.component.ts b/projects/ucap-webmessenger-ui-account/src/lib/components/login.component.ts index 7a93679..33ca9dc 100644 --- a/projects/ucap-webmessenger-ui-account/src/lib/components/login.component.ts +++ b/projects/ucap-webmessenger-ui-account/src/lib/components/login.component.ts @@ -5,7 +5,8 @@ import { Output, EventEmitter, ViewChild, - ElementRef + ElementRef, + ChangeDetectorRef } from '@angular/core'; import { FormGroup, FormBuilder, Validators } from '@angular/forms'; import { Company } from '@ucap-webmessenger/api-external'; @@ -20,6 +21,10 @@ import { LoginInfo, KEY_LOGIN_INFO } from '@app/types'; export class LoginComponent implements OnInit { @Input() companyList?: Company[]; + @Input() + loginBtnText?: string; + @Input() + loginBtnEnable: boolean; @Output() login = new EventEmitter<{ @@ -36,6 +41,7 @@ export class LoginComponent implements OnInit { constructor( private formBuilder: FormBuilder, + private changeDetectorRef: ChangeDetectorRef, private localStorageService: LocalStorageService ) {} @@ -43,20 +49,23 @@ export class LoginComponent implements OnInit { const loginInfo: LoginInfo = this.localStorageService.get( KEY_LOGIN_INFO ); - let companyCode = ''; - let loginId = ''; - let remember = false; - if (loginInfo && loginInfo.companyCode && loginInfo.loginId) { - companyCode = loginInfo.companyCode; - loginId = loginInfo.loginId; - remember = true; - } this.loginForm = this.formBuilder.group({ - companyCode: [companyCode, [Validators.required]], - loginId: [loginId, [Validators.required]], + companyCode: ['', [Validators.required]], + loginId: ['', [Validators.required]], loginPw: ['', Validators.required], - remember: [remember] + remember: [false] }); + + if (loginInfo && loginInfo.companyCode && loginInfo.loginId) { + this.loginForm.get('companyCode').setValue(loginInfo.companyCode); + this.loginForm.get('loginId').setValue(loginInfo.loginId); + this.loginForm.get('remember').setValue(true); + this.changeDetectorRef.detectChanges(); + } + + if (!this.loginBtnText || this.loginBtnText.trim().length === 0) { + this.loginBtnText = 'LOGIN'; + } } onClickLogin() { diff --git a/projects/ucap-webmessenger-ui-chat/src/lib/components/form.component.html b/projects/ucap-webmessenger-ui-chat/src/lib/components/form.component.html index b309bc0..d0fbf30 100644 --- a/projects/ucap-webmessenger-ui-chat/src/lib/components/form.component.html +++ b/projects/ucap-webmessenger-ui-chat/src/lib/components/form.component.html @@ -16,13 +16,17 @@ (change)="onChangeFileInput()" /> - -
diff --git a/projects/ucap-webmessenger-ui-chat/src/lib/components/form.component.ts b/projects/ucap-webmessenger-ui-chat/src/lib/components/form.component.ts index 0821586..bc575b8 100644 --- a/projects/ucap-webmessenger-ui-chat/src/lib/components/form.component.ts +++ b/projects/ucap-webmessenger-ui-chat/src/lib/components/form.component.ts @@ -1,70 +1,81 @@ -import { - Component, - OnInit, - Output, - EventEmitter, - ViewChild, - ElementRef, - Input -} from '@angular/core'; -import { NgForm } from '@angular/forms'; -import { FileUploadItem } from '@ucap-webmessenger/api-common'; -import { FileUploadQueueComponent } from '@ucap-webmessenger/ui'; - -@Component({ - selector: 'ucap-chat-form', - templateUrl: './form.component.html', - styleUrls: ['./form.component.scss'] -}) -export class FormComponent implements OnInit { - @Input() - fileUploadQueue: FileUploadQueueComponent; - - @Output() - send = new EventEmitter(); - - @Output() - sendFiles = new EventEmitter(); - - @ViewChild('replyForm', { static: false }) - replyForm: NgForm; - - @ViewChild('replyInput', { static: false }) - replyInput: ElementRef; - - @ViewChild('fileInput', { static: false }) - fileInput: ElementRef; - - constructor() {} - - ngOnInit() {} - - focus(): void { - this.replyInput.nativeElement.focus(); - } - - onSend(event: Event) { - event.preventDefault(); - - this.send.emit(this.replyForm.form.value.message); - this.replyForm.reset(); - } - - onClickFileInput() { - this.fileInput.nativeElement.click(); - } - - onChangeFileInput() { - const fileUploadItems = FileUploadItem.fromFiles( - this.fileInput.nativeElement.files - ); - - if (!!this.fileUploadQueue) { - this.fileUploadQueue.onFileSelected(fileUploadItems); - } - - this.sendFiles.emit(fileUploadItems); - - this.fileInput.nativeElement.value = ''; - } -} +import { + Component, + OnInit, + Output, + EventEmitter, + ViewChild, + ElementRef, + Input +} from '@angular/core'; +import { NgForm } from '@angular/forms'; +import { FileUploadItem } from '@ucap-webmessenger/api-common'; +import { FileUploadQueueComponent } from '@ucap-webmessenger/ui'; + +@Component({ + selector: 'ucap-chat-form', + templateUrl: './form.component.html', + styleUrls: ['./form.component.scss'] +}) +export class FormComponent implements OnInit { + @Input() + fileUploadQueue: FileUploadQueueComponent; + + @Output() + send = new EventEmitter(); + + @Output() + sendFiles = new EventEmitter(); + + @Output() + toggleStickerSelector = new EventEmitter(); + + @Output() + clearView = new EventEmitter(); + + @ViewChild('replyForm', { static: false }) + replyForm: NgForm; + + @ViewChild('replyInput', { static: false }) + replyInput: ElementRef; + + @ViewChild('fileInput', { static: false }) + fileInput: ElementRef; + + constructor() {} + + ngOnInit() {} + + focus(): void { + this.replyInput.nativeElement.focus(); + } + + onSend(event: Event) { + event.preventDefault(); + + this.send.emit(this.replyForm.form.value.message); + this.replyForm.reset(); + } + + onClickFileInput() { + this.clearView.emit(); + this.fileInput.nativeElement.click(); + } + + onChangeFileInput() { + const fileUploadItems = FileUploadItem.fromFiles( + this.fileInput.nativeElement.files + ); + + if (!!this.fileUploadQueue) { + this.fileUploadQueue.onFileSelected(fileUploadItems); + } + + this.sendFiles.emit(fileUploadItems); + + this.fileInput.nativeElement.value = ''; + } + + onClickStickerSelector() { + this.toggleStickerSelector.emit(); + } +} diff --git a/projects/ucap-webmessenger-ui-chat/src/lib/components/message-box/date-splitter.component.ts b/projects/ucap-webmessenger-ui-chat/src/lib/components/message-box/date-splitter.component.ts index 5605c29..9009f0d 100644 --- a/projects/ucap-webmessenger-ui-chat/src/lib/components/message-box/date-splitter.component.ts +++ b/projects/ucap-webmessenger-ui-chat/src/lib/components/message-box/date-splitter.component.ts @@ -1,11 +1,12 @@ import { Component, OnInit, Input } from '@angular/core'; import { Info, EventJson } from '@ucap-webmessenger/protocol-event'; import { DatePipe } from '@angular/common'; +import moment from 'moment'; @Component({ selector: 'ucap-chat-message-box-date-splitter', templateUrl: './date-splitter.component.html', - styleUrls: ['./date-splitter.component.scss'] + styleUrls: ['./date-splitter.component.scss'], }) export class DateSplitterComponent implements OnInit { @Input() @@ -17,7 +18,7 @@ export class DateSplitterComponent implements OnInit { ngOnInit() { this.dateInfo = this.datePipe.transform( - this.message.sendDate, + moment(this.message.sendDate).toDate(), 'yyyy.MM.dd EEE' ); } diff --git a/projects/ucap-webmessenger-ui-chat/src/lib/components/message-box/file.component.ts b/projects/ucap-webmessenger-ui-chat/src/lib/components/message-box/file.component.ts index 7526159..e8b1faf 100644 --- a/projects/ucap-webmessenger-ui-chat/src/lib/components/message-box/file.component.ts +++ b/projects/ucap-webmessenger-ui-chat/src/lib/components/message-box/file.component.ts @@ -42,7 +42,7 @@ export class FileComponent implements OnInit { getExpiredFile() { if ( !!this.eventInfoStatus && - this.eventInfoStatus.validFileBaseSeq < this.message.seq + this.eventInfoStatus.validFileBaseSeq <= this.message.seq ) { return false; } else { diff --git a/projects/ucap-webmessenger-ui-chat/src/lib/components/message-box/image.component.scss b/projects/ucap-webmessenger-ui-chat/src/lib/components/message-box/image.component.scss index d53cddf..7f520d9 100644 --- a/projects/ucap-webmessenger-ui-chat/src/lib/components/message-box/image.component.scss +++ b/projects/ucap-webmessenger-ui-chat/src/lib/components/message-box/image.component.scss @@ -1,3 +1,7 @@ .bubble-main{ padding:10px; + img{ + height:140px; + width:auto; + } } \ No newline at end of file diff --git a/projects/ucap-webmessenger-ui-chat/src/lib/components/message-box/mass.component.html b/projects/ucap-webmessenger-ui-chat/src/lib/components/message-box/mass.component.html index 60cb938..ec9326d 100644 --- a/projects/ucap-webmessenger-ui-chat/src/lib/components/message-box/mass.component.html +++ b/projects/ucap-webmessenger-ui-chat/src/lib/components/message-box/mass.component.html @@ -1,7 +1,7 @@
- + - {{ message.sendDate | date: 'short' }} + {{ moment(message.sendDate).toDate() | date: 'short' }}
diff --git a/projects/ucap-webmessenger-ui-chat/src/lib/components/message-box/mass.component.ts b/projects/ucap-webmessenger-ui-chat/src/lib/components/message-box/mass.component.ts index 0d29682..7c4053b 100644 --- a/projects/ucap-webmessenger-ui-chat/src/lib/components/message-box/mass.component.ts +++ b/projects/ucap-webmessenger-ui-chat/src/lib/components/message-box/mass.component.ts @@ -2,11 +2,12 @@ import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core'; import { Info, MassTextEventJson } from '@ucap-webmessenger/protocol-event'; import { NGXLogger } from 'ngx-logger'; import { StatusCode } from '@ucap-webmessenger/api'; +import moment from 'moment'; @Component({ selector: 'ucap-chat-message-box-mass', templateUrl: './mass.component.html', - styleUrls: ['./mass.component.scss'] + styleUrls: ['./mass.component.scss'], }) export class MassComponent implements OnInit { @Input() @@ -19,6 +20,8 @@ export class MassComponent implements OnInit { eventMassSeq: number; detailButteonShow = true; + moment = moment; + constructor(private logger: NGXLogger) {} ngOnInit() { diff --git a/projects/ucap-webmessenger-ui-chat/src/lib/components/message-box/sticker.component.html b/projects/ucap-webmessenger-ui-chat/src/lib/components/message-box/sticker.component.html index 1e244cb..5af9deb 100644 --- a/projects/ucap-webmessenger-ui-chat/src/lib/components/message-box/sticker.component.html +++ b/projects/ucap-webmessenger-ui-chat/src/lib/components/message-box/sticker.component.html @@ -6,9 +6,6 @@ onerror="this.src='assets/sticker/sticker_default.png'" /> -
  • +
  • diff --git a/projects/ucap-webmessenger-ui-chat/src/lib/components/message-box/sticker.component.ts b/projects/ucap-webmessenger-ui-chat/src/lib/components/message-box/sticker.component.ts index 03c18b7..c2975dd 100644 --- a/projects/ucap-webmessenger-ui-chat/src/lib/components/message-box/sticker.component.ts +++ b/projects/ucap-webmessenger-ui-chat/src/lib/components/message-box/sticker.component.ts @@ -1,28 +1,61 @@ -import { Component, OnInit, Input } from '@angular/core'; +import { + Component, + OnInit, + Input, + Inject, + ElementRef, + AfterViewInit +} from '@angular/core'; import { Info, StickerEventJson } from '@ucap-webmessenger/protocol-event'; import { NGXLogger } from 'ngx-logger'; import { StickerInfo } from '../../models/sticker-info.json'; +import { UCAP_NATIVE_SERVICE, NativeService } from '@ucap-webmessenger/native'; @Component({ selector: 'ucap-chat-message-box-sticker', templateUrl: './sticker.component.html', styleUrls: ['./sticker.component.scss'] }) -export class StickerComponent implements OnInit { +export class StickerComponent implements OnInit, AfterViewInit { @Input() message: Info; - contentJson?: StickerInfo; + contents: string; stickerUrl?: string; - constructor(private logger: NGXLogger) {} + constructor( + private logger: NGXLogger, + private elementRef: ElementRef, + @Inject(UCAP_NATIVE_SERVICE) private nativeService: NativeService + ) {} ngOnInit() { try { if (!!this.message.sentMessageJson.file) { this.stickerUrl = `assets/sticker/sticker_s_${this.message.sentMessageJson.file}.png`; } + if (!!this.message.sentMessageJson.chat) { + this.contents = this.message.sentMessageJson.chat; + } } catch (e) { this.logger.error(e); } } + + ngAfterViewInit(): void { + if ( + !!this.elementRef.nativeElement && + !!this.elementRef.nativeElement.querySelector('a') + ) { + const elements = this.elementRef.nativeElement.querySelectorAll('a'); + elements.forEach(element => { + element.addEventListener('click', this.onClickEvent.bind(this)); + }); + } + } + + onClickEvent(event: MouseEvent) { + this.nativeService.openDefaultBrowser( + (event.target as HTMLAnchorElement).text + ); + } } diff --git a/projects/ucap-webmessenger-ui-chat/src/lib/components/message-box/text.component.html b/projects/ucap-webmessenger-ui-chat/src/lib/components/message-box/text.component.html index 474d482..c9750ca 100644 --- a/projects/ucap-webmessenger-ui-chat/src/lib/components/message-box/text.component.html +++ b/projects/ucap-webmessenger-ui-chat/src/lib/components/message-box/text.component.html @@ -1,3 +1,3 @@
    - +
    diff --git a/projects/ucap-webmessenger-ui-chat/src/lib/components/message-box/text.component.scss b/projects/ucap-webmessenger-ui-chat/src/lib/components/message-box/text.component.scss index b75dae7..e207a63 100644 --- a/projects/ucap-webmessenger-ui-chat/src/lib/components/message-box/text.component.scss +++ b/projects/ucap-webmessenger-ui-chat/src/lib/components/message-box/text.component.scss @@ -1,9 +1,9 @@ .bubble-main { padding: 14px; - text-align:left; - span{ + text-align: left; + span { word-wrap: break-word; white-space: pre-wrap; - word-break: keep-all; + word-break: break-word; } -} \ No newline at end of file +} diff --git a/projects/ucap-webmessenger-ui-chat/src/lib/components/message-box/text.component.ts b/projects/ucap-webmessenger-ui-chat/src/lib/components/message-box/text.component.ts index 17482ad..5973e59 100644 --- a/projects/ucap-webmessenger-ui-chat/src/lib/components/message-box/text.component.ts +++ b/projects/ucap-webmessenger-ui-chat/src/lib/components/message-box/text.component.ts @@ -1,17 +1,45 @@ -import { Component, OnInit, Input } from '@angular/core'; +import { + Component, + OnInit, + Input, + ElementRef, + AfterViewInit, + Inject +} from '@angular/core'; import { Info, EventJson } from '@ucap-webmessenger/protocol-event'; +import { NativeService, UCAP_NATIVE_SERVICE } from '@ucap-webmessenger/native'; @Component({ selector: 'ucap-chat-message-box-text', templateUrl: './text.component.html', styleUrls: ['./text.component.scss'] }) -export class TextComponent implements OnInit { +export class TextComponent implements OnInit, AfterViewInit { @Input() message: Info; - test = `가




    바사`; - constructor() {} + constructor( + private elementRef: ElementRef, + @Inject(UCAP_NATIVE_SERVICE) private nativeService: NativeService + ) {} ngOnInit() {} + + ngAfterViewInit(): void { + if ( + !!this.elementRef.nativeElement && + !!this.elementRef.nativeElement.querySelector('a') + ) { + const elements = this.elementRef.nativeElement.querySelectorAll('a'); + elements.forEach(element => { + element.addEventListener('click', this.onClickEvent.bind(this)); + }); + } + } + + onClickEvent(event: MouseEvent) { + this.nativeService.openDefaultBrowser( + (event.target as HTMLAnchorElement).text + ); + } } diff --git a/projects/ucap-webmessenger-ui-chat/src/lib/components/message-box/video.component.html b/projects/ucap-webmessenger-ui-chat/src/lib/components/message-box/video.component.html index 7072870..d969a13 100644 --- a/projects/ucap-webmessenger-ui-chat/src/lib/components/message-box/video.component.html +++ b/projects/ucap-webmessenger-ui-chat/src/lib/components/message-box/video.component.html @@ -1,7 +1,5 @@
    - - -
    +
    +
    @@ -43,12 +43,14 @@ @@ -78,6 +80,7 @@ [base]="profileImageRoot" [path]="getUserProfile(message.senderSeq)" [default]="'assets/images/img_nophoto_50.png'" + (click)="onClickOpenProfile($event, getUerInfo(message.senderSeq))" /> + +
  • +
    + + +
    +
  • +
    + + +
  • +
    + + + + 내 프로필 + + + 즐겨찾기 + + ({{ node.countOfChildren }}명) + + {{ + node.groupDetail.name + }} + + ({{ node.countOfChildren }}명) + + - - - - - - - - - 즐겨찾기 - ({{ favoritBuddyList.length }}명) - - - - - - - - - - - - -
    {{ groupBuddy.group.name }}
    - ({{ groupBuddy.buddyList.length }}명) -
    - - - - - -
    - - - - -
    - + + + +
    +
      +
      +
      + +
      +
    +
  • +
    + + diff --git a/projects/ucap-webmessenger-ui-group/src/lib/components/expansion-panel.component.scss b/projects/ucap-webmessenger-ui-group/src/lib/components/expansion-panel.component.scss index 3ad0856..2763d73 100644 --- a/projects/ucap-webmessenger-ui-group/src/lib/components/expansion-panel.component.scss +++ b/projects/ucap-webmessenger-ui-group/src/lib/components/expansion-panel.component.scss @@ -1,66 +1,108 @@ @charset 'utf-8'; -:host { - display: flex; - flex: 1; - flex-direction: column; - - .more-spacer { - flex: 1 1 auto; +.group-tree { + ul, + li { + margin-top: 0; + margin-bottom: 0; + list-style-type: none; + margin-left: 10px; + } + .group-tree-node-invisible { + display: none; } } -::ng-deep .groupExpansionPanel .mat-expansion-panel-body { - padding: 0; -} -.mat-expansion-panel-header { - padding: 0 20px; - .mat-expansion-panel-header-title { - align-items: center; - font-size: 13px; - } - .mat-expansion-panel-header-description { - margin-right: 0; - } -} +.tree-node-frame { + border-bottom: 1px solid #dddddd; + height: 40px; -.mat-icon-button { - margin-right: 0; - width: inherit; + .tree-node-header { + width: 100%; + + .title-name { + display: inline-flex; + flex: 1 1 auto; + } + } + .mat-tree-node { + width: 100%; + &:hover { + background-color: #f4f4f4; + border: 1px solid #cccccc; + border-radius: 4px; + box-shadow: 0 1px 4px rgba(32, 33, 36, 0.1); + } + } } -.mat-icon { - font-size: 20px; -} -.groupList { - .mat-expansion-panel-header { - padding: 0 20px; - .mat-content { - color: #666666; - overflow: unset; - .panel-title{ - display:inline-flex; - .title-name{ - display:inline-flex; - flex:1 1 auto; - } - .number{ - margin-left:6px; - display: inline-flex; - flex: 0 0 auto; - } +.mat-tree-node { + &[aria-level='1'] { + position: relative; + widows: 100px; + height: 100%; + li { + margin-left: 0; + width: 100%; + height: 100%; + .mat-tree-node { + width: 100%; + height: 100%; } } } } -.box-more-spacer { - margin-right: 0; + +.mat-tree-node[aria-level='0'][node-type='Profile'] { + position: relative; + widows: 100px; + height: 100%; + li { + margin-left: 0; + width: 100%; + height: 100%; + .mat-tree-node { + width: 100%; + height: 100%; + } + } } -::ng-deep .mat-content{ - overflow: unset; -} +.path { + display: flex; + padding: 6px 4px; + align-items: center; + .btn-toggle { + padding: 0; + min-width: 0; + width: 20px; + height: 20px; + flex-shrink: 0; + line-height: 20px; + margin-right: 10px; + } + .group-check { + margin-left: auto; + margin-right: 16px; + } -.number{ - margin-left:6px; - display: inline-flex; - flex: 0 0 auto; - } \ No newline at end of file + .group-menu { + margin-left: auto; + opacity: 0.6; + } + + ul { + li:last-chlid { + border-bottom: 1px solid #dddddd; + } + } + .horizontal-line { + width: 10px; + height: 1px; + background-color: #dddddd; + display: inline-block; + vertical-align: middle; + margin-left: -10px; + } + + .dept-name { + padding-left: 10px; + } +} diff --git a/projects/ucap-webmessenger-ui-group/src/lib/components/expansion-panel.component.ts b/projects/ucap-webmessenger-ui-group/src/lib/components/expansion-panel.component.ts index bd1e596..13cfdb9 100644 --- a/projects/ucap-webmessenger-ui-group/src/lib/components/expansion-panel.component.ts +++ b/projects/ucap-webmessenger-ui-group/src/lib/components/expansion-panel.component.ts @@ -6,13 +6,16 @@ import { EventEmitter, ViewChild, ContentChild, - TemplateRef + TemplateRef, + AfterViewInit, + ChangeDetectorRef, + OnDestroy } from '@angular/core'; import { ucapAnimations } from '@ucap-webmessenger/ui'; import { GroupDetailData, UserInfo } from '@ucap-webmessenger/protocol-sync'; -import { MatAccordion } from '@angular/material'; +import { MatTreeFlattener, MatTree } from '@angular/material'; import { ExpansionPanelItemDirective } from '../directives/expansion-panel-item.directive'; import { UserInfoSS, @@ -20,6 +23,34 @@ import { UserInfoDN } from '@ucap-webmessenger/protocol-query'; import { NGXLogger } from 'ngx-logger'; +import { VirtualScrollTreeFlatDataSource } from '@ucap-webmessenger/ui'; +import { FlatTreeControl } from '@angular/cdk/tree'; +import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling'; +import { PerfectScrollbarDirective } from 'ngx-perfect-scrollbar'; +import { Subscription } from 'rxjs'; + +enum NodeType { + None = 'None', + Favorit = 'Favorit', + Profile = 'Profile', + Buddy = 'Buddy' +} + +interface GroupNode { + nodeType: NodeType; + userInfo?: UserInfo; + groupDetail?: GroupDetailData; + children?: GroupNode[]; +} + +interface FlatNode { + expandable: boolean; + level: number; + nodeType: NodeType; + countOfChildren?: number; + userInfo?: UserInfo; + groupDetail?: GroupDetailData; +} @Component({ selector: 'ucap-group-expansion-panel', @@ -27,15 +58,84 @@ import { NGXLogger } from 'ngx-logger'; styleUrls: ['./expansion-panel.component.scss'], animations: ucapAnimations }) -export class ExpansionPanelComponent implements OnInit { +export class ExpansionPanelComponent + implements OnInit, OnDestroy, AfterViewInit { @Input() - groupBuddyList: { group: GroupDetailData; buddyList: UserInfo[] }[]; + set myProfileInfo(userInfo: UserInfo) { + if (!userInfo) { + return; + } + const groupNode: GroupNode = { + nodeType: NodeType.Profile, + userInfo, + children: [] + }; + + this.profileNodes = [groupNode]; + + this.refreshRootNodeList(); + } + @Input() - favoritBuddyList?: UserInfo[]; + set favoritBuddyList(userInfoList: UserInfo[]) { + if (!userInfoList || 0 === userInfoList.length) { + return; + } + const groupNode: GroupNode = { + nodeType: NodeType.Favorit, + children: [] + }; + + userInfoList.forEach(userInfo => { + groupNode.children.push({ + nodeType: NodeType.Favorit, + userInfo + }); + }); + + this.favoritNodes = [groupNode]; + this.refreshRootNodeList(); + } + @Input() - myProfileInfo?: UserInfo; + set groupBuddyList( + list: { group: GroupDetailData; buddyList: UserInfo[] }[] + ) { + if (!list || 0 === list.length) { + return; + } + + this.groupList = list; + + this.buddyNodes = []; + + for (const item of list) { + const groupNode: GroupNode = { + nodeType: NodeType.Buddy, + groupDetail: item.group, + children: [] + }; + + item.buddyList.sort((a, b) => + a.name < b.name ? -1 : a.name > b.name ? 1 : 0 + ); + + item.buddyList.forEach(userInfo => { + groupNode.children.push({ + nodeType: NodeType.Buddy, + groupDetail: item.group, + userInfo + }); + }); + + this.buddyNodes.push(groupNode); + } + this.refreshRootNodeList(); + } + @Input() checkable = false; + @Input() /** 선택된 사용자의 리스트 */ selectedUserList?: (UserInfo | UserInfoSS | UserInfoF | UserInfoDN)[] = []; @@ -54,22 +154,102 @@ export class ExpansionPanelComponent implements OnInit { @ContentChild(ExpansionPanelItemDirective, { read: TemplateRef, - static: true + static: false }) expansionPanelItemTemplateRef: TemplateRef; - @ViewChild('groupAccordion', { static: true }) groupAccordion: MatAccordion; + @ViewChild('groupTree', { static: false }) + groupTree: MatTree; - constructor(private logger: NGXLogger) {} + @ViewChild('cvsvGroup', { static: false }) + cvsvGroup: CdkVirtualScrollViewport; - ngOnInit() {} + @ViewChild(PerfectScrollbarDirective, { static: false }) + psDirectiveRef?: PerfectScrollbarDirective; + + NodeType = NodeType; + + profileNodes: GroupNode[] = []; + favoritNodes: GroupNode[] = []; + buddyNodes: GroupNode[] = []; + + rootNodeList: GroupNode[] = []; + treeControl: FlatTreeControl; + treeFlattener: MatTreeFlattener; + dataSource: VirtualScrollTreeFlatDataSource; + + groupList: { group: GroupDetailData; buddyList: UserInfo[] }[]; + + treeControlExpansionChangeSubscription: Subscription; + + constructor( + private changeDetectorRef: ChangeDetectorRef, + private logger: NGXLogger + ) { + this.treeControl = new FlatTreeControl( + node => node.level, + node => node.expandable + ); + + this.treeFlattener = new MatTreeFlattener( + (node: GroupNode, level: number) => { + return { + expandable: !!node.children && node.children.length > 0, + level, + nodeType: node.nodeType, + countOfChildren: + !!node.children && node.children.length > 0 + ? node.children.length + : 0, + userInfo: node.userInfo, + groupDetail: node.groupDetail + }; + }, + node => node.level, + node => node.expandable, + node => node.children + ); + + this.dataSource = new VirtualScrollTreeFlatDataSource( + this.treeControl, + this.treeFlattener + ); + } + + ngOnInit() { + this.treeControlExpansionChangeSubscription = this.treeControl.expansionModel.changed.subscribe( + () => { + this.psDirectiveRef.update(); + } + ); + } + + ngOnDestroy(): void { + if (!!this.treeControlExpansionChangeSubscription) { + this.treeControlExpansionChangeSubscription.unsubscribe(); + } + } + + ngAfterViewInit(): void { + this.dataSource.cdkVirtualScrollViewport = this.cvsvGroup; + } + + // hasChild = (_: number, node: FlatNode) => node.expandable; + isHeader = (_: number, node: FlatNode) => + NodeType.Profile !== node.nodeType && 0 === node.level; expandMore() { - this.groupAccordion.openAll(); + this.groupTree.treeControl.expandAll(); + if (!!this.psDirectiveRef) { + this.psDirectiveRef.scrollToTop(); + } } expandLess() { - this.groupAccordion.closeAll(); + this.groupTree.treeControl.collapseAll(); + if (!!this.psDirectiveRef) { + this.psDirectiveRef.scrollToTop(); + } } onClickMore(event: MouseEvent, group: GroupDetailData) { @@ -77,30 +257,34 @@ export class ExpansionPanelComponent implements OnInit { } /** 그룹리스트가 checkable 할 경우 checkbox 의 change 이벤트를 상위 컴포넌트로 전달한다. */ - onChangeCheck( - value: boolean, - groupBuddyList: { group: GroupDetailData; buddyList: UserInfo[] } - ) { - this.checkGroup.emit({ - isChecked: value, - groupBuddyList - }); + onChangeCheck(value: boolean, group: GroupDetailData, nodeType: NodeType) { + const groupInfos = this.groupList.filter( + groupInfo => groupInfo.group.seq === group.seq + ); + + if (groupInfos.length > 0) { + this.checkGroup.emit({ + isChecked: value, + groupBuddyList: groupInfos[0] + }); + } } /** 그룹리스트가 checkable 할 경우 checkbox 의 isChecked 를 관장하며, 하위 부서원들의 전체선택여부를 판단한다. */ - getCheckedGroup(groupBuddy: { - group: GroupDetailData; - buddyList: UserInfo[]; - }) { - if (groupBuddy.buddyList.length === 0) { + getCheckedGroup(group: GroupDetailData) { + if (!group || group === undefined) { return false; } + + if (group.userSeqs.length === 0) { + return false; + } + if (!!this.selectedUserList && this.selectedUserList.length > 0) { let allExist = true; - groupBuddy.buddyList.some(groupUser => { + group.userSeqs.some(seq => { if ( - this.selectedUserList.filter(item => item.seq === groupUser.seq) - .length === 0 + this.selectedUserList.filter(item => item.seq === seq).length === 0 ) { allExist = false; return true; @@ -110,4 +294,17 @@ export class ExpansionPanelComponent implements OnInit { } return false; } + + private refreshRootNodeList(): void { + this.rootNodeList = [ + ...this.profileNodes, + ...this.favoritNodes, + ...this.buddyNodes + ]; + this.dataSource.data = this.rootNodeList; + + if (!!this.psDirectiveRef) { + this.psDirectiveRef.scrollToTop(); + } + } } diff --git a/projects/ucap-webmessenger-ui-group/src/lib/ucap-ui-group.module.ts b/projects/ucap-webmessenger-ui-group/src/lib/ucap-ui-group.module.ts index 44be219..2c25817 100644 --- a/projects/ucap-webmessenger-ui-group/src/lib/ucap-ui-group.module.ts +++ b/projects/ucap-webmessenger-ui-group/src/lib/ucap-ui-group.module.ts @@ -4,13 +4,18 @@ import { ReactiveFormsModule } from '@angular/forms'; import { FlexLayoutModule } from '@angular/flex-layout'; +import { ScrollingModule } from '@angular/cdk/scrolling'; + +import { MatRippleModule, MatCheckboxModule } from '@angular/material'; import { MatButtonModule } from '@angular/material/button'; import { MatExpansionModule } from '@angular/material/expansion'; import { MatIconModule } from '@angular/material/icon'; +import { MatTreeModule } from '@angular/material/tree'; + +import { PerfectScrollbarModule } from 'ngx-perfect-scrollbar'; import { ExpansionPanelComponent } from './components/expansion-panel.component'; import { ExpansionPanelItemDirective } from './directives/expansion-panel-item.directive'; -import { MatCheckboxModule } from '@angular/material'; const COMPONENTS = [ExpansionPanelComponent]; const DIALOGS = []; @@ -22,10 +27,17 @@ const SERVICES = []; CommonModule, ReactiveFormsModule, FlexLayoutModule, + + ScrollingModule, + MatButtonModule, MatExpansionModule, MatIconModule, - MatCheckboxModule + MatTreeModule, + MatCheckboxModule, + MatRippleModule, + + PerfectScrollbarModule ], exports: [...COMPONENTS, ...DIRECTIVES], declarations: [...COMPONENTS, ...DIRECTIVES], diff --git a/projects/ucap-webmessenger-ui-organization/src/lib/components/tenant-search.component.html b/projects/ucap-webmessenger-ui-organization/src/lib/components/tenant-search.component.html index 78f847a..35b12ae 100644 --- a/projects/ucap-webmessenger-ui-organization/src/lib/components/tenant-search.component.html +++ b/projects/ucap-webmessenger-ui-organization/src/lib/components/tenant-search.component.html @@ -15,7 +15,7 @@ - {{ node.title }} -
    -
      + + + +
      -
      - +
      +
      +
      + +
    • +
      {{ node.name }}
      +
    • + + + +
      +
      +
      +
      +
    • +
      + + + {{ node.name }}
      -
    - - - + + + + + diff --git a/projects/ucap-webmessenger-ui-organization/src/lib/components/tree.component.scss b/projects/ucap-webmessenger-ui-organization/src/lib/components/tree.component.scss index 42a3c36..9d210d5 100644 --- a/projects/ucap-webmessenger-ui-organization/src/lib/components/tree.component.scss +++ b/projects/ucap-webmessenger-ui-organization/src/lib/components/tree.component.scss @@ -1,61 +1,93 @@ -@charset 'utf-8'; .organization-tree { - padding:10px; - ul, + padding: 5px; + + .tree-node-closer-container { + position: relative; + // border: 1px dotted grey; + // border-width: 0 0 1px 1px; + + .tree-node-closer-top { + width: 15px; + height: 40px; + position: absolute; + border: 1px dotted grey; + border-width: 0 0 1px 1px; + top: -40px; + // left: 20px; + } + .tree-node-closer-bottom { + width: 15px; + height: 40px; + position: absolute; + border: 1px dotted grey; + border-width: 0 0 1px 1px; + top: 0px; + // left: 20px; + } + + .tree-node-closer-top[sub-node] { + border-width: 0 0 0 1px; + } + .tree-node-closer-top[sub-node][last-node] { + border-width: 0 0 0 0; + } + + .tree-node-closer-bottom[expanded] { + border-width: 0 0 1px 0px; + } + .tree-node-closer-bottom[last-node] { + border-width: 0 0 1px 0px; + } + .tree-node-closer-bottom[sub-node] { + border-width: 0 0 0 1px; + } + .tree-node-closer-bottom[sub-node][last-node] { + border-width: 0 0 0 0; + } + } + li { margin-top: 0; margin-bottom: 0; list-style-type: none; - padding-left: 6px; } - .organization-tree-node-invisible { - display: none; - } -} -.mat-tree-node { - min-height: 30px; - font-size: 13px; - padding-left:20px; - margin-top:4px; - &:hover { - background-color: #f4f4f4; - border:1px solid #cccccc; - border-radius:4px; - box-shadow: 0 1px 4px rgba(32, 33, 36, 0.1); - } -} + // li:last-child { + // border-left: 1px solid white; + // margin-left: -41px; + // } + .tree-has-child { + height: 40px; + min-height: 40px; + li { + display: flex; + align-items: center; + cursor: pointer; + width: 100%; -.path { - //border: 1px solid #dddddd; - padding: 4px; - margin-top: 10px; - //box-shadow: 0 1px 4px rgba(32, 33, 36, 0.1); - + ul{ - li:last-chlid{ - border-bottom:1px solid #dddddd; + .tree-node-body { + .tree-node-expand-btn { + background-color: white; + } + } + } + } + + .tree-no-child { + height: 40px; + min-height: 40px; + font-size: 13px; + li { + display: flex; + align-items: center; + width: 100%; + height: 100%; + cursor: pointer; + + .tree-node-body { + padding-left: 40px; + } } } } -.mat-icon-button{ - padding: 0; - min-width: 0; - width: 20px; - height: 20px; - flex-shrink: 0; - line-height: 20px; - margin-right:10px; - .mat-icon-rtl-mirror{ - border: 1px solid #dddddd; - padding: 2px; - font-size: 14px; - min-width: 14px; - min-height: 14px; - line-height: 14px; - width:20px; - height:20px; - box-shadow: 0 2px 1px rgba(48, 48, 48, 0.2); - border-radius: 50%; - } -} diff --git a/projects/ucap-webmessenger-ui-organization/src/lib/components/tree.component.ts b/projects/ucap-webmessenger-ui-organization/src/lib/components/tree.component.ts index f542168..78e8ce0 100644 --- a/projects/ucap-webmessenger-ui-organization/src/lib/components/tree.component.ts +++ b/projects/ucap-webmessenger-ui-organization/src/lib/components/tree.component.ts @@ -6,136 +6,233 @@ import { ViewChild, Output, EventEmitter, - AfterViewInit + AfterViewInit, + OnDestroy } from '@angular/core'; -import { MatTreeNestedDataSource, MatTree } from '@angular/material'; -import { NestedTreeControl } from '@angular/cdk/tree'; -import { BehaviorSubject } from 'rxjs'; +import { MatTreeFlattener, MatTree } from '@angular/material'; import { NGXLogger } from 'ngx-logger'; import { DeptInfo } from '@ucap-webmessenger/protocol-query'; import { LoginResponse } from '@ucap-webmessenger/protocol-authentication'; +import { FlatTreeControl } from '@angular/cdk/tree'; +import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling'; +import { VirtualScrollTreeFlatDataSource } from '@ucap-webmessenger/ui'; +import { ucapAnimations } from '@ucap-webmessenger/ui'; +import { trigger, transition, style, animate } from '@angular/animations'; +import { PerfectScrollbarDirective } from 'ngx-perfect-scrollbar'; +import { Subscription } from 'rxjs'; -export class OraganizationNode { - private childNodeBehaviorSubject: BehaviorSubject; - private childNodeList: OraganizationNode[]; +interface OrganizationNode { + deptInfo: DeptInfo; + name: string; + children?: OrganizationNode[]; +} - get title(): string { - return this.deptInfo.name; - } - - get children(): BehaviorSubject { - if (!this.childNodeBehaviorSubject) { - this.childNodeBehaviorSubject = new BehaviorSubject( - undefined === this.childNodeList ? [] : this.childNodeList - ); - } - return this.childNodeBehaviorSubject; - } - - constructor(public deptInfo: DeptInfo) {} - - addChild(childNode: OraganizationNode) { - if (!this.childNodeList) { - this.childNodeList = []; - } - this.childNodeList.push(childNode); - } +/** Flat node with expandable and level information */ +interface FlatNode { + expandable: boolean; + name: string; + level: number; + deptInfo: DeptInfo; } @Component({ selector: 'ucap-organization-tree', templateUrl: './tree.component.html', - styleUrls: ['./tree.component.scss'] + styleUrls: ['./tree.component.scss'], + animations: [ + ucapAnimations, + trigger('romvoeAdd', [ + transition('remove <=> add', [ + style({ + transform: `rotate(45deg)`, + opacity: 0 + }), + animate('.2s 0s ease-out') + ]) + ]) + ] }) -export class TreeComponent implements OnInit, AfterViewInit { +export class TreeComponent implements OnInit, OnDestroy, AfterViewInit { @Output() selected = new EventEmitter(); @Input() loginRes: LoginResponse; - @Input() - set oraganizationList(deptInfo: DeptInfo[]) { - const nodeMap = new Map(); - const rootNodeList: OraganizationNode[] = []; - const remainChildNodeList: OraganizationNode[] = []; - let myNode: OraganizationNode; - deptInfo.forEach(value => { - const node = new OraganizationNode(value); - if (nodeMap.has(value.seq)) { - this.logger.warn('duplicate seq', value.seq); + @Input() + set oraganizationList(deptInfoList: DeptInfo[]) { + if (!deptInfoList || 0 === deptInfoList.length) { + return; + } + const nodeMap = new Map(); + const rootNodeList: OrganizationNode[] = []; + const remainChildNodeList: OrganizationNode[] = []; + let myNode: OrganizationNode; + + deptInfoList.forEach(deptInfo => { + const node: OrganizationNode = { + deptInfo, + name: deptInfo.name, + children: [] + }; + if (nodeMap.has(deptInfo.seq)) { + this.logger.warn('duplicate seq', deptInfo.seq); return; } - nodeMap.set(value.seq, node); + nodeMap.set(deptInfo.seq, node); - if (value.seq === this.loginRes.departmentCode) { + if (deptInfo.seq === this.loginRes.departmentCode) { myNode = node; } - if (0 === value.parentSeq) { + if (0 === deptInfo.parentSeq) { rootNodeList.push(node); return; } - if (nodeMap.has(value.parentSeq)) { - nodeMap.get(value.parentSeq).addChild(node); + if (nodeMap.has(deptInfo.parentSeq)) { + nodeMap.get(deptInfo.parentSeq).children.push(node); } else { remainChildNodeList.push(node); } }); - remainChildNodeList.forEach(value => { - if (nodeMap.has(value.deptInfo.parentSeq)) { - nodeMap.get(value.deptInfo.parentSeq).addChild(value); + remainChildNodeList.forEach(node => { + if (nodeMap.has(node.deptInfo.parentSeq)) { + nodeMap.get(node.deptInfo.parentSeq).children.push(node); } }); - this.dataSource.data = rootNodeList; - // console.log('myNode', myNode); - // console.log('this.dataSource.data', this.dataSource.data[0]); - // this.treeControl.expandDescendants(this.dataSource.data[2]); - // console.log('this.dataSource', this.dataSource); - // console.log('this.dataSource.data', this.dataSource.data); - // console.log('this.treeControl', this.treeControl); - // console.log('this.treeControl.dataNodes', this.treeControl.dataNodes); - - // const myNode = this.treeControl.dataNodes.filter( - // node => node.deptInfo.seq === this.loginRes.departmentCode - // ); - // if (!!myNode && myNode.length > 0) { - // this.treeControl.expand(myNode[0]); - // } } - @ViewChild('orgranizationTree', { static: true }) - orgranizationTree: MatTree; + @ViewChild('cvsvOrganization', { static: false }) + cvsvOrganization: CdkVirtualScrollViewport; - levels = new Map(); - treeControl: NestedTreeControl; + @ViewChild('orgranizationTree', { static: false }) + orgranizationTree: MatTree; - dataSource: MatTreeNestedDataSource; + @ViewChild(PerfectScrollbarDirective, { static: false }) + psDirectiveRef?: PerfectScrollbarDirective; + + treeControl: FlatTreeControl; + treeFlattener: MatTreeFlattener; + dataSource: VirtualScrollTreeFlatDataSource; + + treeControlExpansionChangeSubscription: Subscription; constructor( private changeDetectorRef: ChangeDetectorRef, private logger: NGXLogger ) { - this.treeControl = new NestedTreeControl( - this.getChildren + this.treeControl = new FlatTreeControl( + node => node.level, + node => node.expandable ); - this.dataSource = new MatTreeNestedDataSource(); + this.treeFlattener = new MatTreeFlattener( + (node: OrganizationNode, level: number) => { + return { + expandable: !!node.children && node.children.length > 0, + name: node.name, + level, + deptInfo: node.deptInfo + }; + }, + node => node.level, + node => node.expandable, + node => node.children + ); + this.dataSource = new VirtualScrollTreeFlatDataSource< + OrganizationNode, + FlatNode + >(this.treeControl, this.treeFlattener); } - ngOnInit() {} + ngOnInit() { + this.treeControlExpansionChangeSubscription = this.treeControl.expansionModel.changed.subscribe( + () => { + this.psDirectiveRef.update(); + } + ); + } - ngAfterViewInit(): void {} + ngOnDestroy(): void { + this.logger.debug('-----------------------TreeComponent ngOnDestroy'); + if (!!this.treeControlExpansionChangeSubscription) { + this.treeControlExpansionChangeSubscription.unsubscribe(); + } + } - getChildren = (node: OraganizationNode) => node.children; + ngAfterViewInit(): void { + this.dataSource.cdkVirtualScrollViewport = this.cvsvOrganization; + } - hasChildren = (index: number, node: OraganizationNode) => - 0 < node.children.value.length; + hasChild = (_: number, node: FlatNode) => node.expandable; - onClickNode(node: OraganizationNode) { + isLastNode(node: FlatNode, depth: number): boolean { + const i = this.findNodeIndex(node, depth); + + if (-1 === i) { + return false; + } + + if (i === this.dataSource.expandedDataSubject.value.length - 1) { + return true; + } + + const n = this.dataSource.expandedDataSubject.value[i]; + + for ( + let idx = i + 1; + idx < this.dataSource.expandedDataSubject.value.length; + idx++ + ) { + const element = this.dataSource.expandedDataSubject.value[idx]; + if (n.level === element.level) { + return false; + } + if (n.level > element.level) { + return true; + } + } + return true; + } + + appendDivArray(level: number): number[] { + if (0 === level) { + return []; + } + + return Array.from({ length: level }, (_, i: number) => i); + } + + onClickNode(node: OrganizationNode) { this.selected.emit(node.deptInfo); } + + private findNodeIndex(node: FlatNode, depth: number): number { + if (!node) { + return -1; + } + const i = this.dataSource.expandedDataSubject.value.findIndex(n => { + return node.deptInfo.seq === n.deptInfo.seq; + }); + + if (0 === depth) { + return i; + } + return this.findNodeIndex(this.findParentNode(i), depth - 1); + } + + private findParentNode(index: number): FlatNode { + const node = this.dataSource.expandedDataSubject.value[index]; + + for (let idx = index - 1; idx >= 0; idx--) { + const n = this.dataSource.expandedDataSubject.value[idx]; + if (n.level === node.level - 1) { + return n; + } + } + return undefined; + } } diff --git a/projects/ucap-webmessenger-ui-organization/src/lib/ucap-ui-organization.module.ts b/projects/ucap-webmessenger-ui-organization/src/lib/ucap-ui-organization.module.ts index 14ee788..e51a5a0 100644 --- a/projects/ucap-webmessenger-ui-organization/src/lib/ucap-ui-organization.module.ts +++ b/projects/ucap-webmessenger-ui-organization/src/lib/ucap-ui-organization.module.ts @@ -2,14 +2,20 @@ import { NgModule, ModuleWithProviders } from '@angular/core'; import { CommonModule } from '@angular/common'; import { ReactiveFormsModule } from '@angular/forms'; +import { FlexLayoutModule } from '@angular/flex-layout'; + import { ScrollingModule } from '@angular/cdk/scrolling'; +import { MatRippleModule } from '@angular/material'; + import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; import { MatInputModule } from '@angular/material/input'; import { MatSelectModule } from '@angular/material/select'; import { MatTreeModule } from '@angular/material/tree'; +import { PerfectScrollbarModule } from 'ngx-perfect-scrollbar'; + import { TenantSearchComponent } from './components/tenant-search.component'; import { TreeComponent } from './components/tree.component'; @@ -21,13 +27,17 @@ const SERVICES = []; CommonModule, ReactiveFormsModule, + FlexLayoutModule, ScrollingModule, + MatRippleModule, MatButtonModule, MatIconModule, MatInputModule, MatSelectModule, - MatTreeModule + MatTreeModule, + + PerfectScrollbarModule ], exports: [...COMPONENTS], declarations: [...COMPONENTS] diff --git a/projects/ucap-webmessenger-ui-profile/src/lib/components/my-profile-widget.component.html b/projects/ucap-webmessenger-ui-profile/src/lib/components/my-profile-widget.component.html new file mode 100644 index 0000000..5a20d38 --- /dev/null +++ b/projects/ucap-webmessenger-ui-profile/src/lib/components/my-profile-widget.component.html @@ -0,0 +1,12 @@ +
    + + + + 내프로필 +
    diff --git a/projects/ucap-webmessenger-ui-profile/src/lib/components/my-profile-widget.component.scss b/projects/ucap-webmessenger-ui-profile/src/lib/components/my-profile-widget.component.scss new file mode 100644 index 0000000..8dd8ce6 --- /dev/null +++ b/projects/ucap-webmessenger-ui-profile/src/lib/components/my-profile-widget.component.scss @@ -0,0 +1,26 @@ +.myprofile { + position: absolute; + display: flex; + flex-flow: column; + justify-content: center; + height: 80px; + width: 68px; + bottom: 10px; + color: #ffffff; + font-size: 11px; + text-align: center; + + .myprofile-img { + display: block; + border-radius: 10px; + height: 42px; + width: 42px; + background-color: #efefef; + align-self: center; + margin-bottom: 6px; + + .thumbnail { + border-radius: 10px; + } + } +} diff --git a/projects/ucap-webmessenger-ui-profile/src/lib/components/my-profile-widget.component.spec.ts b/projects/ucap-webmessenger-ui-profile/src/lib/components/my-profile-widget.component.spec.ts new file mode 100644 index 0000000..55d2b46 --- /dev/null +++ b/projects/ucap-webmessenger-ui-profile/src/lib/components/my-profile-widget.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { MyProfileWidgetComponent } from './my-profile-widget.component'; + +describe('MyProfileWidgetComponent', () => { + let component: MyProfileWidgetComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ MyProfileWidgetComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(MyProfileWidgetComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/projects/ucap-webmessenger-ui-profile/src/lib/components/my-profile-widget.component.ts b/projects/ucap-webmessenger-ui-profile/src/lib/components/my-profile-widget.component.ts new file mode 100644 index 0000000..8f0c6bb --- /dev/null +++ b/projects/ucap-webmessenger-ui-profile/src/lib/components/my-profile-widget.component.ts @@ -0,0 +1,38 @@ +import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core'; +import { UserInfo } from '@ucap-webmessenger/protocol-room'; +import { + UserInfoSS, + UserInfoF, + UserInfoDN +} from '@ucap-webmessenger/protocol-query'; + +@Component({ + selector: 'ucap-profile-my-profile-widget', + templateUrl: './my-profile-widget.component.html', + styleUrls: ['./my-profile-widget.component.scss'] +}) +export class MyProfileWidgetComponent implements OnInit { + @Input() + profileImageRoot: string; + @Input() + profileImageFile: string; + + @Output() + openProfile = new EventEmitter< + UserInfo | UserInfoSS | UserInfoF | UserInfoDN + >(); + + constructor() {} + + ngOnInit() {} + + onClickOpenProfile( + event: MouseEvent, + userInfo: UserInfo | UserInfoSS | UserInfoF | UserInfoDN + ) { + event.preventDefault(); + event.stopPropagation(); + + this.openProfile.emit(userInfo); + } +} diff --git a/projects/ucap-webmessenger-ui-profile/src/lib/components/profile.component.html b/projects/ucap-webmessenger-ui-profile/src/lib/components/profile.component.html new file mode 100644 index 0000000..05eeed8 --- /dev/null +++ b/projects/ucap-webmessenger-ui-profile/src/lib/components/profile.component.html @@ -0,0 +1,262 @@ + + + + {{ userInfo.name }} + {{ userInfo.grade }} + + {{ userInfo.deptName }} + + + +
    + +
    + +
    + + + + + + + + + + + + + + + + + + + + + + + +
    + +
      +
    • + + + + {{ userInfo.intro }} +
    • +
    • + + + + + {{ userInfo.email }} +
    • +
    • + + + + {{ userInfo.lineNumber }} +
    • +
    • + + + + + {{ userInfo.hpNumber }} +
    • +
    +
    + + +
    + + + + + + + +
    +
    +
    diff --git a/projects/ucap-webmessenger-ui-profile/src/lib/components/profile.component.scss b/projects/ucap-webmessenger-ui-profile/src/lib/components/profile.component.scss new file mode 100644 index 0000000..6c941d9 --- /dev/null +++ b/projects/ucap-webmessenger-ui-profile/src/lib/components/profile.component.scss @@ -0,0 +1,104 @@ +@mixin ellipsis($row) { + overflow: hidden; + text-overflow: ellipsis; + @if $row == 1 { + display: block; + white-space: nowrap; + word-wrap: normal; + } @else if $row >= 2 { + display: -webkit-box; + -webkit-line-clamp: $row; + -webkit-box-orient: vertical; + word-wrap: break-word; + } +} +::ng-deep .mat-card-header-text{ + width:100%; + .mat-card-subtitle{ + color: rgb(256, 256, 256, 0.7) !important; + text-align:center; + margin-top:10px !important; + } +} + +.example-card { + width: 400px; + padding: 0 0 20px; + position: relative; + .mat-card-header{ + justify-content: center; + padding-bottom: 40px; + background: #76d9c5; + /*background: linear-gradient(to right, #345385, #ef4c73);*/ + color: #ffffff; + padding-top: 20px; + width:100%; + .mat-card-title{ + margin-bottom: 12px; + max-width: 100%; + justify-content: center; + display: flex; + margin:0 20px; + span{ + @include ellipsis(1); + } + } + } + .mat-card-content{ + margin-top:-40px; + .profile-img{ + display:flex; + height:80px; + justify-content: center; + margin-bottom:20px; + img{ + widows: 80px; + height: 80px; + border-radius: 50%; + background-color:#efefef; + border:2px solid #ffffff; + } + } + .profile-option{ + display:flex; + padding:0 20px; + color:#ffffff; + margin-top: -100px; + height: 120px; + .btn-favorite{ + cursor: pointer; + .on{ + fill:yellow; + } + } + .btn-groupadd{ + margin-left:auto; + cursor: pointer; + svg{ + display:none; + &.on{ + display:block; + } + } + } + } + ul{ + padding:0 20px; + display:flex; + flex-flow: column; + margin-top:-20px; + li{ + display:inline-flex; + height:30px; + align-items: center; + flex-flow:row; + margin-bottom:20px; + + svg{ + margin-right:10px; + color:#777777; + } + } + } + } +} diff --git a/projects/ucap-webmessenger-ui-profile/src/lib/components/profile.component.spec.ts b/projects/ucap-webmessenger-ui-profile/src/lib/components/profile.component.spec.ts new file mode 100644 index 0000000..9401c7a --- /dev/null +++ b/projects/ucap-webmessenger-ui-profile/src/lib/components/profile.component.spec.ts @@ -0,0 +1,28 @@ +/* tslint:disable:no-unused-variable */ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { By } from '@angular/platform-browser'; +import { DebugElement } from '@angular/core'; + +import { ProfileComponent } from './profile.component'; + +describe('ProfileComponent', () => { + let component: ProfileComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ ProfileComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ProfileComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/projects/ucap-webmessenger-ui-profile/src/lib/components/profile.component.ts b/projects/ucap-webmessenger-ui-profile/src/lib/components/profile.component.ts new file mode 100644 index 0000000..5a0c5fc --- /dev/null +++ b/projects/ucap-webmessenger-ui-profile/src/lib/components/profile.component.ts @@ -0,0 +1,76 @@ +import { Component, OnInit, Input, EventEmitter, Output } from '@angular/core'; + +import { UserInfo } from '@ucap-webmessenger/protocol-sync'; +import { + UserInfoSS, + UserInfoF, + UserInfoDN +} from '@ucap-webmessenger/protocol-query'; + +@Component({ + selector: 'ucap-profile-profile', + templateUrl: './profile.component.html', + styleUrls: ['./profile.component.scss'] +}) +export class ProfileComponent implements OnInit { + @Input() + profileImageRoot: string; + @Input() + isMe: boolean; + @Input() + isBuddy: boolean; + @Input() + isFavorit: boolean; + @Input() + userInfo: UserInfo | UserInfoF; + + @Output() + openChat = new EventEmitter(); + @Output() + toggleFavorit = new EventEmitter<{ + userInfo: UserInfo | UserInfoF; + isFavorit: boolean; + }>(); + @Output() + toggleBuddy = new EventEmitter<{ + userInfo: UserInfo | UserInfoF; + isBuddy: boolean; + }>(); + + constructor() {} + + ngOnInit() {} + + onClickOpenChat() { + this.openChat.emit(this.userInfo); + } + + onClickCall() {} + + onClickVideoConference() {} + + onClickMessage() {} + + onToggleFavorit() { + this.isFavorit = !this.isFavorit; + + this.toggleFavorit.emit({ + userInfo: this.userInfo, + isFavorit: this.isFavorit + }); + } + + onClickAddBuddy() { + this.toggleBuddy.emit({ + userInfo: this.userInfo, + isBuddy: true + }); + } + + onClickDelBuddy() { + this.toggleBuddy.emit({ + userInfo: this.userInfo, + isBuddy: false + }); + } +} diff --git a/projects/ucap-webmessenger-ui-profile/src/lib/components/user-list-item.component.html b/projects/ucap-webmessenger-ui-profile/src/lib/components/user-list-item.component.html index 45eb4a3..d258cc0 100644 --- a/projects/ucap-webmessenger-ui-profile/src/lib/components/user-list-item.component.html +++ b/projects/ucap-webmessenger-ui-profile/src/lib/components/user-list-item.component.html @@ -1,12 +1,7 @@ -
    +
    - {{ presence.pcStatus }} - +
    @@ -41,7 +37,7 @@ #checkbox [checked]="isChecked" (change)="onChangeCheck(checkbox.checked, userInfo)" - (click)="$event.stopPropagation()" + (click)="onClickCheck($event)" >
    diff --git a/projects/ucap-webmessenger-ui-profile/src/lib/components/user-list-item.component.ts b/projects/ucap-webmessenger-ui-profile/src/lib/components/user-list-item.component.ts index 0ba87db..be354e9 100644 --- a/projects/ucap-webmessenger-ui-profile/src/lib/components/user-list-item.component.ts +++ b/projects/ucap-webmessenger-ui-profile/src/lib/components/user-list-item.component.ts @@ -43,12 +43,19 @@ export class UserListItemComponent implements OnInit { @Input() /** 선택된 사용자의 리스트 */ selectedUserList?: (UserInfo | UserInfoSS | UserInfoF | UserInfoDN)[] = []; + @Input() + /** event bubbling fixed in custom tree */ + inTree = false; @Output() checkUser = new EventEmitter<{ isChecked: boolean; userInfo: UserInfo | UserInfoSS | UserInfoF | UserInfoDN; }>(); + @Output() + openProfile = new EventEmitter< + UserInfo | UserInfoSS | UserInfoF | UserInfoDN + >(); PresenceType = PresenceType; @@ -64,7 +71,7 @@ export class UserListItemComponent implements OnInit { let rtnClass = ''; switch (type) { case 'pc': - status = this.presence.pcStatus; + status = !!this.presence ? this.presence.pcStatus : undefined; break; } @@ -98,4 +105,19 @@ export class UserListItemComponent implements OnInit { userInfo }); } + onClickCheck(event: MouseEvent) { + if (!this.inTree) { + event.stopPropagation(); + } + } + + onClickOpenProfile( + event: MouseEvent, + userInfo: UserInfo | UserInfoSS | UserInfoF | UserInfoDN + ) { + event.preventDefault(); + event.stopPropagation(); + + this.openProfile.emit(userInfo); + } } diff --git a/projects/ucap-webmessenger-ui-profile/src/lib/ucap-ui-profile.module.ts b/projects/ucap-webmessenger-ui-profile/src/lib/ucap-ui-profile.module.ts index 6420bf4..3387eb5 100644 --- a/projects/ucap-webmessenger-ui-profile/src/lib/ucap-ui-profile.module.ts +++ b/projects/ucap-webmessenger-ui-profile/src/lib/ucap-ui-profile.module.ts @@ -1,3 +1,5 @@ +import { MatTooltipModule } from '@angular/material/tooltip'; +import { MatCardModule } from '@angular/material/card'; import { NgModule, ModuleWithProviders } from '@angular/core'; import { CommonModule } from '@angular/common'; @@ -11,8 +13,15 @@ import { UCapUiModule } from '@ucap-webmessenger/ui'; import { ListItemComponent } from './components/list-item.component'; import { UserListItemComponent } from './components/user-list-item.component'; +import { ProfileComponent } from './components/profile.component'; +import { MyProfileWidgetComponent } from './components/my-profile-widget.component'; -const COMPONENTS = [ListItemComponent, UserListItemComponent]; +const COMPONENTS = [ + ListItemComponent, + UserListItemComponent, + ProfileComponent, + MyProfileWidgetComponent +]; const SERVICES = []; @@ -24,6 +33,8 @@ const SERVICES = []; MatIconModule, MatRippleModule, MatCheckboxModule, + MatCardModule, + MatTooltipModule, UCapUiModule ], diff --git a/projects/ucap-webmessenger-ui-room/src/lib/components/list-item.component.html b/projects/ucap-webmessenger-ui-room/src/lib/components/list-item.component.html index b8599af..a4ef3ac 100644 --- a/projects/ucap-webmessenger-ui-room/src/lib/components/list-item.component.html +++ b/projects/ucap-webmessenger-ui-room/src/lib/components/list-item.component.html @@ -11,13 +11,22 @@ timer +
    {{ getRoomName(roomInfo) }}
    {{ roomInfo.joinUserCount }}명 diff --git a/projects/ucap-webmessenger-ui-room/src/lib/components/list-item.component.scss b/projects/ucap-webmessenger-ui-room/src/lib/components/list-item.component.scss index ae9b222..423d768 100644 --- a/projects/ucap-webmessenger-ui-room/src/lib/components/list-item.component.scss +++ b/projects/ucap-webmessenger-ui-room/src/lib/components/list-item.component.scss @@ -25,19 +25,19 @@ $thumbnail-msize: 40px; } .badge-timer { - position:absolute; + position: absolute; background-color: #ffffff; width: 18px; height: 18px; border-radius: 50%; bottom: 14px; left: 46px; - text-align:center; - .mat-icon{ - font-size:14px; - width: 18px; + text-align: center; + .mat-icon { + font-size: 14px; + width: 18px; height: 18px; - line-height:18px; + line-height: 18px; } } @@ -107,7 +107,7 @@ $thumbnail-msize: 40px; display: flex; margin: 0; padding: 0; - width: calc(100% - 60px); + .detail { flex-direction: column; width: calc(100% - 80px); @@ -131,7 +131,7 @@ $thumbnail-msize: 40px; font-size: 16px; color: #666666; margin-left: 4px; - line-height:unset; + line-height: unset; } .num { font-size: 12px; @@ -139,7 +139,6 @@ $thumbnail-msize: 40px; margin-left: 6px; border-radius: 3px; padding: 1px 6px; - background-color: #ef4c73; color: #ffffff; } } @@ -159,6 +158,14 @@ $thumbnail-msize: 40px; @include ellipsis(1); } } + + .num { + font-size: 18px; + font: bold; + flex: none; + text-align: center; + background-color: rgba($color: #666666, $alpha: 0.5); + } } .list-item { diff --git a/projects/ucap-webmessenger-ui-settings/README.md b/projects/ucap-webmessenger-ui-settings/README.md new file mode 100644 index 0000000..402cb1d --- /dev/null +++ b/projects/ucap-webmessenger-ui-settings/README.md @@ -0,0 +1,24 @@ +# UcapWebmessengerUiSettings + +This library was generated with [Angular CLI](https://github.com/angular/angular-cli) version 8.2.11. + +## Code scaffolding + +Run `ng generate component component-name --project ucap-webmessenger-ui-settings` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module --project ucap-webmessenger-ui-settings`. +> Note: Don't forget to add `--project ucap-webmessenger-ui-settings` or else it will be added to the default project in your `angular.json` file. + +## Build + +Run `ng build ucap-webmessenger-ui-settings` to build the project. The build artifacts will be stored in the `dist/` directory. + +## Publishing + +After building your library with `ng build ucap-webmessenger-ui-settings`, go to the dist folder `cd dist/ucap-webmessenger-ui-settings` and run `npm publish`. + +## Running unit tests + +Run `ng test ucap-webmessenger-ui-settings` to execute the unit tests via [Karma](https://karma-runner.github.io). + +## Further help + +To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md). diff --git a/projects/ucap-webmessenger-electron-notification/karma.conf.js b/projects/ucap-webmessenger-ui-settings/karma.conf.js similarity index 96% rename from projects/ucap-webmessenger-electron-notification/karma.conf.js rename to projects/ucap-webmessenger-ui-settings/karma.conf.js index 2b6d790..44e8601 100644 --- a/projects/ucap-webmessenger-electron-notification/karma.conf.js +++ b/projects/ucap-webmessenger-ui-settings/karma.conf.js @@ -16,7 +16,7 @@ module.exports = function (config) { clearContext: false // leave Jasmine Spec Runner output visible in browser }, coverageIstanbulReporter: { - dir: require('path').join(__dirname, '../../coverage/ucap-webmessenger-electron-notification'), + dir: require('path').join(__dirname, '../../coverage/ucap-webmessenger-ui-settings'), reports: ['html', 'lcovonly', 'text-summary'], fixWebpackSourcePaths: true }, diff --git a/projects/ucap-webmessenger-electron-notification/ng-package.json b/projects/ucap-webmessenger-ui-settings/ng-package.json similarity index 65% rename from projects/ucap-webmessenger-electron-notification/ng-package.json rename to projects/ucap-webmessenger-ui-settings/ng-package.json index fc7aaf1..6fbd226 100644 --- a/projects/ucap-webmessenger-electron-notification/ng-package.json +++ b/projects/ucap-webmessenger-ui-settings/ng-package.json @@ -1,6 +1,6 @@ { "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", - "dest": "../../dist/ucap-webmessenger-electron-notification", + "dest": "../../dist/ucap-webmessenger-ui-settings", "lib": { "entryFile": "src/public-api.ts" } diff --git a/projects/ucap-webmessenger-ui-settings/package.json b/projects/ucap-webmessenger-ui-settings/package.json new file mode 100644 index 0000000..d871cee --- /dev/null +++ b/projects/ucap-webmessenger-ui-settings/package.json @@ -0,0 +1,8 @@ +{ + "name": "@ucap-webmessenger/ui-settings", + "version": "0.0.1", + "peerDependencies": { + "@angular/common": "^8.2.11", + "@angular/core": "^8.2.11" + } +} diff --git a/projects/ucap-webmessenger-ui-settings/src/assets/timezone/en.json b/projects/ucap-webmessenger-ui-settings/src/assets/timezone/en.json new file mode 100644 index 0000000..f0d28cc --- /dev/null +++ b/projects/ucap-webmessenger-ui-settings/src/assets/timezone/en.json @@ -0,0 +1,1247 @@ +[ + { + "value": "Dateline Standard Time", + "abbr": "DST", + "offset": -12, + "isdst": false, + "text": "(UTC-12:00) International Date Line West", + "utc": ["Etc/GMT+12"] + }, + { + "value": "UTC-11", + "abbr": "U", + "offset": -11, + "isdst": false, + "text": "(UTC-11:00) Coordinated Universal Time-11", + "utc": ["Etc/GMT+11", "Pacific/Midway", "Pacific/Niue", "Pacific/Pago_Pago"] + }, + { + "value": "Hawaiian Standard Time", + "abbr": "HST", + "offset": -10, + "isdst": false, + "text": "(UTC-10:00) Hawaii", + "utc": [ + "Etc/GMT+10", + "Pacific/Honolulu", + "Pacific/Johnston", + "Pacific/Rarotonga", + "Pacific/Tahiti" + ] + }, + { + "value": "Alaskan Standard Time", + "abbr": "AKDT", + "offset": -8, + "isdst": true, + "text": "(UTC-09:00) Alaska", + "utc": [ + "America/Anchorage", + "America/Juneau", + "America/Nome", + "America/Sitka", + "America/Yakutat" + ] + }, + { + "value": "Pacific Standard Time (Mexico)", + "abbr": "PDT", + "offset": -7, + "isdst": true, + "text": "(UTC-08:00) Baja California", + "utc": ["America/Santa_Isabel"] + }, + { + "value": "Pacific Daylight Time", + "abbr": "PDT", + "offset": -7, + "isdst": true, + "text": "(UTC-07:00) Pacific Time (US & Canada)", + "utc": [ + "America/Dawson", + "America/Los_Angeles", + "America/Tijuana", + "America/Vancouver", + "America/Whitehorse" + ] + }, + { + "value": "Pacific Standard Time", + "abbr": "PST", + "offset": -8, + "isdst": false, + "text": "(UTC-08:00) Pacific Time (US & Canada)", + "utc": [ + "America/Dawson", + "America/Los_Angeles", + "America/Tijuana", + "America/Vancouver", + "America/Whitehorse", + "PST8PDT" + ] + }, + { + "value": "US Mountain Standard Time", + "abbr": "UMST", + "offset": -7, + "isdst": false, + "text": "(UTC-07:00) Arizona", + "utc": [ + "America/Creston", + "America/Dawson_Creek", + "America/Hermosillo", + "America/Phoenix", + "Etc/GMT+7" + ] + }, + { + "value": "Mountain Standard Time (Mexico)", + "abbr": "MDT", + "offset": -6, + "isdst": true, + "text": "(UTC-07:00) Chihuahua, La Paz, Mazatlan", + "utc": ["America/Chihuahua", "America/Mazatlan"] + }, + { + "value": "Mountain Standard Time", + "abbr": "MDT", + "offset": -6, + "isdst": true, + "text": "(UTC-07:00) Mountain Time (US & Canada)", + "utc": [ + "America/Boise", + "America/Cambridge_Bay", + "America/Denver", + "America/Edmonton", + "America/Inuvik", + "America/Ojinaga", + "America/Yellowknife", + "MST7MDT" + ] + }, + { + "value": "Central America Standard Time", + "abbr": "CAST", + "offset": -6, + "isdst": false, + "text": "(UTC-06:00) Central America", + "utc": [ + "America/Belize", + "America/Costa_Rica", + "America/El_Salvador", + "America/Guatemala", + "America/Managua", + "America/Tegucigalpa", + "Etc/GMT+6", + "Pacific/Galapagos" + ] + }, + { + "value": "Central Standard Time", + "abbr": "CDT", + "offset": -5, + "isdst": true, + "text": "(UTC-06:00) Central Time (US & Canada)", + "utc": [ + "America/Chicago", + "America/Indiana/Knox", + "America/Indiana/Tell_City", + "America/Matamoros", + "America/Menominee", + "America/North_Dakota/Beulah", + "America/North_Dakota/Center", + "America/North_Dakota/New_Salem", + "America/Rainy_River", + "America/Rankin_Inlet", + "America/Resolute", + "America/Winnipeg", + "CST6CDT" + ] + }, + { + "value": "Central Standard Time (Mexico)", + "abbr": "CDT", + "offset": -5, + "isdst": true, + "text": "(UTC-06:00) Guadalajara, Mexico City, Monterrey", + "utc": [ + "America/Bahia_Banderas", + "America/Cancun", + "America/Merida", + "America/Mexico_City", + "America/Monterrey" + ] + }, + { + "value": "Canada Central Standard Time", + "abbr": "CCST", + "offset": -6, + "isdst": false, + "text": "(UTC-06:00) Saskatchewan", + "utc": ["America/Regina", "America/Swift_Current"] + }, + { + "value": "SA Pacific Standard Time", + "abbr": "SPST", + "offset": -5, + "isdst": false, + "text": "(UTC-05:00) Bogota, Lima, Quito", + "utc": [ + "America/Bogota", + "America/Cayman", + "America/Coral_Harbour", + "America/Eirunepe", + "America/Guayaquil", + "America/Jamaica", + "America/Lima", + "America/Panama", + "America/Rio_Branco", + "Etc/GMT+5" + ] + }, + { + "value": "Eastern Standard Time", + "abbr": "EDT", + "offset": -4, + "isdst": true, + "text": "(UTC-05:00) Eastern Time (US & Canada)", + "utc": [ + "America/Detroit", + "America/Havana", + "America/Indiana/Petersburg", + "America/Indiana/Vincennes", + "America/Indiana/Winamac", + "America/Iqaluit", + "America/Kentucky/Monticello", + "America/Louisville", + "America/Montreal", + "America/Nassau", + "America/New_York", + "America/Nipigon", + "America/Pangnirtung", + "America/Port-au-Prince", + "America/Thunder_Bay", + "America/Toronto", + "EST5EDT" + ] + }, + { + "value": "US Eastern Standard Time", + "abbr": "UEDT", + "offset": -4, + "isdst": true, + "text": "(UTC-05:00) Indiana (East)", + "utc": [ + "America/Indiana/Marengo", + "America/Indiana/Vevay", + "America/Indianapolis" + ] + }, + { + "value": "Venezuela Standard Time", + "abbr": "VST", + "offset": -4.5, + "isdst": false, + "text": "(UTC-04:30) Caracas", + "utc": ["America/Caracas"] + }, + { + "value": "Paraguay Standard Time", + "abbr": "PYT", + "offset": -4, + "isdst": false, + "text": "(UTC-04:00) Asuncion", + "utc": ["America/Asuncion"] + }, + { + "value": "Atlantic Standard Time", + "abbr": "ADT", + "offset": -3, + "isdst": true, + "text": "(UTC-04:00) Atlantic Time (Canada)", + "utc": [ + "America/Glace_Bay", + "America/Goose_Bay", + "America/Halifax", + "America/Moncton", + "America/Thule", + "Atlantic/Bermuda" + ] + }, + { + "value": "Central Brazilian Standard Time", + "abbr": "CBST", + "offset": -4, + "isdst": false, + "text": "(UTC-04:00) Cuiaba", + "utc": ["America/Campo_Grande", "America/Cuiaba"] + }, + { + "value": "SA Western Standard Time", + "abbr": "SWST", + "offset": -4, + "isdst": false, + "text": "(UTC-04:00) Georgetown, La Paz, Manaus, San Juan", + "utc": [ + "America/Anguilla", + "America/Antigua", + "America/Aruba", + "America/Barbados", + "America/Blanc-Sablon", + "America/Boa_Vista", + "America/Curacao", + "America/Dominica", + "America/Grand_Turk", + "America/Grenada", + "America/Guadeloupe", + "America/Guyana", + "America/Kralendijk", + "America/La_Paz", + "America/Lower_Princes", + "America/Manaus", + "America/Marigot", + "America/Martinique", + "America/Montserrat", + "America/Port_of_Spain", + "America/Porto_Velho", + "America/Puerto_Rico", + "America/Santo_Domingo", + "America/St_Barthelemy", + "America/St_Kitts", + "America/St_Lucia", + "America/St_Thomas", + "America/St_Vincent", + "America/Tortola", + "Etc/GMT+4" + ] + }, + { + "value": "Pacific SA Standard Time", + "abbr": "PSST", + "offset": -4, + "isdst": false, + "text": "(UTC-04:00) Santiago", + "utc": ["America/Santiago", "Antarctica/Palmer"] + }, + { + "value": "Newfoundland Standard Time", + "abbr": "NDT", + "offset": -2.5, + "isdst": true, + "text": "(UTC-03:30) Newfoundland", + "utc": ["America/St_Johns"] + }, + { + "value": "E. South America Standard Time", + "abbr": "ESAST", + "offset": -3, + "isdst": false, + "text": "(UTC-03:00) Brasilia", + "utc": ["America/Sao_Paulo"] + }, + { + "value": "Argentina Standard Time", + "abbr": "AST", + "offset": -3, + "isdst": false, + "text": "(UTC-03:00) Buenos Aires", + "utc": [ + "America/Argentina/La_Rioja", + "America/Argentina/Rio_Gallegos", + "America/Argentina/Salta", + "America/Argentina/San_Juan", + "America/Argentina/San_Luis", + "America/Argentina/Tucuman", + "America/Argentina/Ushuaia", + "America/Buenos_Aires", + "America/Catamarca", + "America/Cordoba", + "America/Jujuy", + "America/Mendoza" + ] + }, + { + "value": "SA Eastern Standard Time", + "abbr": "SEST", + "offset": -3, + "isdst": false, + "text": "(UTC-03:00) Cayenne, Fortaleza", + "utc": [ + "America/Araguaina", + "America/Belem", + "America/Cayenne", + "America/Fortaleza", + "America/Maceio", + "America/Paramaribo", + "America/Recife", + "America/Santarem", + "Antarctica/Rothera", + "Atlantic/Stanley", + "Etc/GMT+3" + ] + }, + { + "value": "Greenland Standard Time", + "abbr": "GDT", + "offset": -3, + "isdst": true, + "text": "(UTC-03:00) Greenland", + "utc": ["America/Godthab"] + }, + { + "value": "Montevideo Standard Time", + "abbr": "MST", + "offset": -3, + "isdst": false, + "text": "(UTC-03:00) Montevideo", + "utc": ["America/Montevideo"] + }, + { + "value": "Bahia Standard Time", + "abbr": "BST", + "offset": -3, + "isdst": false, + "text": "(UTC-03:00) Salvador", + "utc": ["America/Bahia"] + }, + { + "value": "UTC-02", + "abbr": "U", + "offset": -2, + "isdst": false, + "text": "(UTC-02:00) Coordinated Universal Time-02", + "utc": ["America/Noronha", "Atlantic/South_Georgia", "Etc/GMT+2"] + }, + { + "value": "Mid-Atlantic Standard Time", + "abbr": "MDT", + "offset": -1, + "isdst": true, + "text": "(UTC-02:00) Mid-Atlantic - Old", + "utc": [] + }, + { + "value": "Azores Standard Time", + "abbr": "ADT", + "offset": 0, + "isdst": true, + "text": "(UTC-01:00) Azores", + "utc": ["America/Scoresbysund", "Atlantic/Azores"] + }, + { + "value": "Cape Verde Standard Time", + "abbr": "CVST", + "offset": -1, + "isdst": false, + "text": "(UTC-01:00) Cape Verde Is.", + "utc": ["Atlantic/Cape_Verde", "Etc/GMT+1"] + }, + { + "value": "Morocco Standard Time", + "abbr": "MDT", + "offset": 1, + "isdst": true, + "text": "(UTC) Casablanca", + "utc": ["Africa/Casablanca", "Africa/El_Aaiun"] + }, + { + "value": "UTC", + "abbr": "UTC", + "offset": 0, + "isdst": false, + "text": "(UTC) Coordinated Universal Time", + "utc": ["America/Danmarkshavn", "Etc/GMT"] + }, + { + "value": "GMT Standard Time", + "abbr": "GMT", + "offset": 0, + "isdst": false, + "text": "(UTC) Edinburgh, London", + "utc": [ + "Europe/Isle_of_Man", + "Europe/Guernsey", + "Europe/Jersey", + "Europe/London" + ] + }, + { + "value": "British Summer Time", + "abbr": "BST", + "offset": 1, + "isdst": true, + "text": "(UTC+01:00) Edinburgh, London", + "utc": [ + "Europe/Isle_of_Man", + "Europe/Guernsey", + "Europe/Jersey", + "Europe/London" + ] + }, + { + "value": "GMT Standard Time", + "abbr": "GDT", + "offset": 1, + "isdst": true, + "text": "(UTC) Dublin, Lisbon", + "utc": [ + "Atlantic/Canary", + "Atlantic/Faeroe", + "Atlantic/Madeira", + "Europe/Dublin", + "Europe/Lisbon" + ] + }, + { + "value": "Greenwich Standard Time", + "abbr": "GST", + "offset": 0, + "isdst": false, + "text": "(UTC) Monrovia, Reykjavik", + "utc": [ + "Africa/Abidjan", + "Africa/Accra", + "Africa/Bamako", + "Africa/Banjul", + "Africa/Bissau", + "Africa/Conakry", + "Africa/Dakar", + "Africa/Freetown", + "Africa/Lome", + "Africa/Monrovia", + "Africa/Nouakchott", + "Africa/Ouagadougou", + "Africa/Sao_Tome", + "Atlantic/Reykjavik", + "Atlantic/St_Helena" + ] + }, + { + "value": "W. Europe Standard Time", + "abbr": "WEDT", + "offset": 2, + "isdst": true, + "text": "(UTC+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna", + "utc": [ + "Arctic/Longyearbyen", + "Europe/Amsterdam", + "Europe/Andorra", + "Europe/Berlin", + "Europe/Busingen", + "Europe/Gibraltar", + "Europe/Luxembourg", + "Europe/Malta", + "Europe/Monaco", + "Europe/Oslo", + "Europe/Rome", + "Europe/San_Marino", + "Europe/Stockholm", + "Europe/Vaduz", + "Europe/Vatican", + "Europe/Vienna", + "Europe/Zurich" + ] + }, + { + "value": "Central Europe Standard Time", + "abbr": "CEDT", + "offset": 2, + "isdst": true, + "text": "(UTC+01:00) Belgrade, Bratislava, Budapest, Ljubljana, Prague", + "utc": [ + "Europe/Belgrade", + "Europe/Bratislava", + "Europe/Budapest", + "Europe/Ljubljana", + "Europe/Podgorica", + "Europe/Prague", + "Europe/Tirane" + ] + }, + { + "value": "Romance Standard Time", + "abbr": "RDT", + "offset": 2, + "isdst": true, + "text": "(UTC+01:00) Brussels, Copenhagen, Madrid, Paris", + "utc": [ + "Africa/Ceuta", + "Europe/Brussels", + "Europe/Copenhagen", + "Europe/Madrid", + "Europe/Paris" + ] + }, + { + "value": "Central European Standard Time", + "abbr": "CEDT", + "offset": 2, + "isdst": true, + "text": "(UTC+01:00) Sarajevo, Skopje, Warsaw, Zagreb", + "utc": [ + "Europe/Sarajevo", + "Europe/Skopje", + "Europe/Warsaw", + "Europe/Zagreb" + ] + }, + { + "value": "W. Central Africa Standard Time", + "abbr": "WCAST", + "offset": 1, + "isdst": false, + "text": "(UTC+01:00) West Central Africa", + "utc": [ + "Africa/Algiers", + "Africa/Bangui", + "Africa/Brazzaville", + "Africa/Douala", + "Africa/Kinshasa", + "Africa/Lagos", + "Africa/Libreville", + "Africa/Luanda", + "Africa/Malabo", + "Africa/Ndjamena", + "Africa/Niamey", + "Africa/Porto-Novo", + "Africa/Tunis", + "Etc/GMT-1" + ] + }, + { + "value": "Namibia Standard Time", + "abbr": "NST", + "offset": 1, + "isdst": false, + "text": "(UTC+01:00) Windhoek", + "utc": ["Africa/Windhoek"] + }, + { + "value": "GTB Standard Time", + "abbr": "GDT", + "offset": 3, + "isdst": true, + "text": "(UTC+02:00) Athens, Bucharest", + "utc": [ + "Asia/Nicosia", + "Europe/Athens", + "Europe/Bucharest", + "Europe/Chisinau" + ] + }, + { + "value": "Middle East Standard Time", + "abbr": "MEDT", + "offset": 3, + "isdst": true, + "text": "(UTC+02:00) Beirut", + "utc": ["Asia/Beirut"] + }, + { + "value": "Egypt Standard Time", + "abbr": "EST", + "offset": 2, + "isdst": false, + "text": "(UTC+02:00) Cairo", + "utc": ["Africa/Cairo"] + }, + { + "value": "Syria Standard Time", + "abbr": "SDT", + "offset": 3, + "isdst": true, + "text": "(UTC+02:00) Damascus", + "utc": ["Asia/Damascus"] + }, + { + "value": "E. Europe Standard Time", + "abbr": "EEDT", + "offset": 3, + "isdst": true, + "text": "(UTC+02:00) E. Europe", + "utc": [ + "Asia/Nicosia", + "Europe/Athens", + "Europe/Bucharest", + "Europe/Chisinau", + "Europe/Helsinki", + "Europe/Kiev", + "Europe/Mariehamn", + "Europe/Nicosia", + "Europe/Riga", + "Europe/Sofia", + "Europe/Tallinn", + "Europe/Uzhgorod", + "Europe/Vilnius", + "Europe/Zaporozhye" + ] + }, + { + "value": "South Africa Standard Time", + "abbr": "SAST", + "offset": 2, + "isdst": false, + "text": "(UTC+02:00) Harare, Pretoria", + "utc": [ + "Africa/Blantyre", + "Africa/Bujumbura", + "Africa/Gaborone", + "Africa/Harare", + "Africa/Johannesburg", + "Africa/Kigali", + "Africa/Lubumbashi", + "Africa/Lusaka", + "Africa/Maputo", + "Africa/Maseru", + "Africa/Mbabane", + "Etc/GMT-2" + ] + }, + { + "value": "FLE Standard Time", + "abbr": "FDT", + "offset": 3, + "isdst": true, + "text": "(UTC+02:00) Helsinki, Kyiv, Riga, Sofia, Tallinn, Vilnius", + "utc": [ + "Europe/Helsinki", + "Europe/Kiev", + "Europe/Mariehamn", + "Europe/Riga", + "Europe/Sofia", + "Europe/Tallinn", + "Europe/Uzhgorod", + "Europe/Vilnius", + "Europe/Zaporozhye" + ] + }, + { + "value": "Turkey Standard Time", + "abbr": "TDT", + "offset": 3, + "isdst": false, + "text": "(UTC+03:00) Istanbul", + "utc": ["Europe/Istanbul"] + }, + { + "value": "Israel Standard Time", + "abbr": "JDT", + "offset": 3, + "isdst": true, + "text": "(UTC+02:00) Jerusalem", + "utc": ["Asia/Jerusalem"] + }, + { + "value": "Libya Standard Time", + "abbr": "LST", + "offset": 2, + "isdst": false, + "text": "(UTC+02:00) Tripoli", + "utc": ["Africa/Tripoli"] + }, + { + "value": "Jordan Standard Time", + "abbr": "JST", + "offset": 3, + "isdst": false, + "text": "(UTC+03:00) Amman", + "utc": ["Asia/Amman"] + }, + { + "value": "Arabic Standard Time", + "abbr": "AST", + "offset": 3, + "isdst": false, + "text": "(UTC+03:00) Baghdad", + "utc": ["Asia/Baghdad"] + }, + { + "value": "Kaliningrad Standard Time", + "abbr": "KST", + "offset": 3, + "isdst": false, + "text": "(UTC+03:00) Kaliningrad, Minsk", + "utc": ["Europe/Kaliningrad", "Europe/Minsk"] + }, + { + "value": "Arab Standard Time", + "abbr": "AST", + "offset": 3, + "isdst": false, + "text": "(UTC+03:00) Kuwait, Riyadh", + "utc": [ + "Asia/Aden", + "Asia/Bahrain", + "Asia/Kuwait", + "Asia/Qatar", + "Asia/Riyadh" + ] + }, + { + "value": "E. Africa Standard Time", + "abbr": "EAST", + "offset": 3, + "isdst": false, + "text": "(UTC+03:00) Nairobi", + "utc": [ + "Africa/Addis_Ababa", + "Africa/Asmera", + "Africa/Dar_es_Salaam", + "Africa/Djibouti", + "Africa/Juba", + "Africa/Kampala", + "Africa/Khartoum", + "Africa/Mogadishu", + "Africa/Nairobi", + "Antarctica/Syowa", + "Etc/GMT-3", + "Indian/Antananarivo", + "Indian/Comoro", + "Indian/Mayotte" + ] + }, + { + "value": "Moscow Standard Time", + "abbr": "MSK", + "offset": 3, + "isdst": false, + "text": "(UTC+03:00) Moscow, St. Petersburg, Volgograd", + "utc": [ + "Europe/Kirov", + "Europe/Moscow", + "Europe/Simferopol", + "Europe/Volgograd" + ] + }, + { + "value": "Samara Time", + "abbr": "SAMT", + "offset": 4, + "isdst": false, + "text": "(UTC+04:00) Samara, Ulyanovsk, Saratov", + "utc": ["Europe/Astrakhan", "Europe/Samara", "Europe/Ulyanovsk"] + }, + { + "value": "Iran Standard Time", + "abbr": "IDT", + "offset": 4.5, + "isdst": true, + "text": "(UTC+03:30) Tehran", + "utc": ["Asia/Tehran"] + }, + { + "value": "Arabian Standard Time", + "abbr": "AST", + "offset": 4, + "isdst": false, + "text": "(UTC+04:00) Abu Dhabi, Muscat", + "utc": ["Asia/Dubai", "Asia/Muscat", "Etc/GMT-4"] + }, + { + "value": "Azerbaijan Standard Time", + "abbr": "ADT", + "offset": 5, + "isdst": true, + "text": "(UTC+04:00) Baku", + "utc": ["Asia/Baku"] + }, + { + "value": "Mauritius Standard Time", + "abbr": "MST", + "offset": 4, + "isdst": false, + "text": "(UTC+04:00) Port Louis", + "utc": ["Indian/Mahe", "Indian/Mauritius", "Indian/Reunion"] + }, + { + "value": "Georgian Standard Time", + "abbr": "GET", + "offset": 4, + "isdst": false, + "text": "(UTC+04:00) Tbilisi", + "utc": ["Asia/Tbilisi"] + }, + { + "value": "Caucasus Standard Time", + "abbr": "CST", + "offset": 4, + "isdst": false, + "text": "(UTC+04:00) Yerevan", + "utc": ["Asia/Yerevan"] + }, + { + "value": "Afghanistan Standard Time", + "abbr": "AST", + "offset": 4.5, + "isdst": false, + "text": "(UTC+04:30) Kabul", + "utc": ["Asia/Kabul"] + }, + { + "value": "West Asia Standard Time", + "abbr": "WAST", + "offset": 5, + "isdst": false, + "text": "(UTC+05:00) Ashgabat, Tashkent", + "utc": [ + "Antarctica/Mawson", + "Asia/Aqtau", + "Asia/Aqtobe", + "Asia/Ashgabat", + "Asia/Dushanbe", + "Asia/Oral", + "Asia/Samarkand", + "Asia/Tashkent", + "Etc/GMT-5", + "Indian/Kerguelen", + "Indian/Maldives" + ] + }, + { + "value": "Yekaterinburg Time", + "abbr": "YEKT", + "offset": 5, + "isdst": false, + "text": "(UTC+05:00) Yekaterinburg", + "utc": ["Asia/Yekaterinburg"] + }, + { + "value": "Pakistan Standard Time", + "abbr": "PKT", + "offset": 5, + "isdst": false, + "text": "(UTC+05:00) Islamabad, Karachi", + "utc": ["Asia/Karachi"] + }, + { + "value": "India Standard Time", + "abbr": "IST", + "offset": 5.5, + "isdst": false, + "text": "(UTC+05:30) Chennai, Kolkata, Mumbai, New Delhi", + "utc": ["Asia/Kolkata"] + }, + { + "value": "Sri Lanka Standard Time", + "abbr": "SLST", + "offset": 5.5, + "isdst": false, + "text": "(UTC+05:30) Sri Jayawardenepura", + "utc": ["Asia/Colombo"] + }, + { + "value": "Nepal Standard Time", + "abbr": "NST", + "offset": 5.75, + "isdst": false, + "text": "(UTC+05:45) Kathmandu", + "utc": ["Asia/Kathmandu"] + }, + { + "value": "Central Asia Standard Time", + "abbr": "CAST", + "offset": 6, + "isdst": false, + "text": "(UTC+06:00) Astana", + "utc": [ + "Antarctica/Vostok", + "Asia/Almaty", + "Asia/Bishkek", + "Asia/Qyzylorda", + "Asia/Urumqi", + "Etc/GMT-6", + "Indian/Chagos" + ] + }, + { + "value": "Bangladesh Standard Time", + "abbr": "BST", + "offset": 6, + "isdst": false, + "text": "(UTC+06:00) Dhaka", + "utc": ["Asia/Dhaka", "Asia/Thimphu"] + }, + { + "value": "Myanmar Standard Time", + "abbr": "MST", + "offset": 6.5, + "isdst": false, + "text": "(UTC+06:30) Yangon (Rangoon)", + "utc": ["Asia/Rangoon", "Indian/Cocos"] + }, + { + "value": "SE Asia Standard Time", + "abbr": "SAST", + "offset": 7, + "isdst": false, + "text": "(UTC+07:00) Bangkok, Hanoi, Jakarta", + "utc": [ + "Antarctica/Davis", + "Asia/Bangkok", + "Asia/Hovd", + "Asia/Jakarta", + "Asia/Phnom_Penh", + "Asia/Pontianak", + "Asia/Saigon", + "Asia/Vientiane", + "Etc/GMT-7", + "Indian/Christmas" + ] + }, + { + "value": "N. Central Asia Standard Time", + "abbr": "NCAST", + "offset": 7, + "isdst": false, + "text": "(UTC+07:00) Novosibirsk", + "utc": ["Asia/Novokuznetsk", "Asia/Novosibirsk", "Asia/Omsk"] + }, + { + "value": "China Standard Time", + "abbr": "CST", + "offset": 8, + "isdst": false, + "text": "(UTC+08:00) Beijing, Chongqing, Hong Kong, Urumqi", + "utc": ["Asia/Hong_Kong", "Asia/Macau", "Asia/Shanghai"] + }, + { + "value": "North Asia Standard Time", + "abbr": "NAST", + "offset": 8, + "isdst": false, + "text": "(UTC+08:00) Krasnoyarsk", + "utc": ["Asia/Krasnoyarsk"] + }, + { + "value": "Singapore Standard Time", + "abbr": "MPST", + "offset": 8, + "isdst": false, + "text": "(UTC+08:00) Kuala Lumpur, Singapore", + "utc": [ + "Asia/Brunei", + "Asia/Kuala_Lumpur", + "Asia/Kuching", + "Asia/Makassar", + "Asia/Manila", + "Asia/Singapore", + "Etc/GMT-8" + ] + }, + { + "value": "W. Australia Standard Time", + "abbr": "WAST", + "offset": 8, + "isdst": false, + "text": "(UTC+08:00) Perth", + "utc": ["Antarctica/Casey", "Australia/Perth"] + }, + { + "value": "Taipei Standard Time", + "abbr": "TST", + "offset": 8, + "isdst": false, + "text": "(UTC+08:00) Taipei", + "utc": ["Asia/Taipei"] + }, + { + "value": "Ulaanbaatar Standard Time", + "abbr": "UST", + "offset": 8, + "isdst": false, + "text": "(UTC+08:00) Ulaanbaatar", + "utc": ["Asia/Choibalsan", "Asia/Ulaanbaatar"] + }, + { + "value": "North Asia East Standard Time", + "abbr": "NAEST", + "offset": 8, + "isdst": false, + "text": "(UTC+08:00) Irkutsk", + "utc": ["Asia/Irkutsk"] + }, + { + "value": "Japan Standard Time", + "abbr": "JST", + "offset": 9, + "isdst": false, + "text": "(UTC+09:00) Osaka, Sapporo, Tokyo", + "utc": [ + "Asia/Dili", + "Asia/Jayapura", + "Asia/Tokyo", + "Etc/GMT-9", + "Pacific/Palau" + ] + }, + { + "value": "Korea Standard Time", + "abbr": "KST", + "offset": 9, + "isdst": false, + "text": "(UTC+09:00) Seoul", + "utc": ["Asia/Pyongyang", "Asia/Seoul"] + }, + { + "value": "Cen. Australia Standard Time", + "abbr": "CAST", + "offset": 9.5, + "isdst": false, + "text": "(UTC+09:30) Adelaide", + "utc": ["Australia/Adelaide", "Australia/Broken_Hill"] + }, + { + "value": "AUS Central Standard Time", + "abbr": "ACST", + "offset": 9.5, + "isdst": false, + "text": "(UTC+09:30) Darwin", + "utc": ["Australia/Darwin"] + }, + { + "value": "E. Australia Standard Time", + "abbr": "EAST", + "offset": 10, + "isdst": false, + "text": "(UTC+10:00) Brisbane", + "utc": ["Australia/Brisbane", "Australia/Lindeman"] + }, + { + "value": "AUS Eastern Standard Time", + "abbr": "AEST", + "offset": 10, + "isdst": false, + "text": "(UTC+10:00) Canberra, Melbourne, Sydney", + "utc": ["Australia/Melbourne", "Australia/Sydney"] + }, + { + "value": "West Pacific Standard Time", + "abbr": "WPST", + "offset": 10, + "isdst": false, + "text": "(UTC+10:00) Guam, Port Moresby", + "utc": [ + "Antarctica/DumontDUrville", + "Etc/GMT-10", + "Pacific/Guam", + "Pacific/Port_Moresby", + "Pacific/Saipan", + "Pacific/Truk" + ] + }, + { + "value": "Tasmania Standard Time", + "abbr": "TST", + "offset": 10, + "isdst": false, + "text": "(UTC+10:00) Hobart", + "utc": ["Australia/Currie", "Australia/Hobart"] + }, + { + "value": "Yakutsk Standard Time", + "abbr": "YST", + "offset": 9, + "isdst": false, + "text": "(UTC+09:00) Yakutsk", + "utc": ["Asia/Chita", "Asia/Khandyga", "Asia/Yakutsk"] + }, + { + "value": "Central Pacific Standard Time", + "abbr": "CPST", + "offset": 11, + "isdst": false, + "text": "(UTC+11:00) Solomon Is., New Caledonia", + "utc": [ + "Antarctica/Macquarie", + "Etc/GMT-11", + "Pacific/Efate", + "Pacific/Guadalcanal", + "Pacific/Kosrae", + "Pacific/Noumea", + "Pacific/Ponape" + ] + }, + { + "value": "Vladivostok Standard Time", + "abbr": "VST", + "offset": 11, + "isdst": false, + "text": "(UTC+11:00) Vladivostok", + "utc": ["Asia/Sakhalin", "Asia/Ust-Nera", "Asia/Vladivostok"] + }, + { + "value": "New Zealand Standard Time", + "abbr": "NZST", + "offset": 12, + "isdst": false, + "text": "(UTC+12:00) Auckland, Wellington", + "utc": ["Antarctica/McMurdo", "Pacific/Auckland"] + }, + { + "value": "UTC+12", + "abbr": "U", + "offset": 12, + "isdst": false, + "text": "(UTC+12:00) Coordinated Universal Time+12", + "utc": [ + "Etc/GMT-12", + "Pacific/Funafuti", + "Pacific/Kwajalein", + "Pacific/Majuro", + "Pacific/Nauru", + "Pacific/Tarawa", + "Pacific/Wake", + "Pacific/Wallis" + ] + }, + { + "value": "Fiji Standard Time", + "abbr": "FST", + "offset": 12, + "isdst": false, + "text": "(UTC+12:00) Fiji", + "utc": ["Pacific/Fiji"] + }, + { + "value": "Magadan Standard Time", + "abbr": "MST", + "offset": 12, + "isdst": false, + "text": "(UTC+12:00) Magadan", + "utc": [ + "Asia/Anadyr", + "Asia/Kamchatka", + "Asia/Magadan", + "Asia/Srednekolymsk" + ] + }, + { + "value": "Kamchatka Standard Time", + "abbr": "KDT", + "offset": 13, + "isdst": true, + "text": "(UTC+12:00) Petropavlovsk-Kamchatsky - Old", + "utc": ["Asia/Kamchatka"] + }, + { + "value": "Tonga Standard Time", + "abbr": "TST", + "offset": 13, + "isdst": false, + "text": "(UTC+13:00) Nuku'alofa", + "utc": [ + "Etc/GMT-13", + "Pacific/Enderbury", + "Pacific/Fakaofo", + "Pacific/Tongatapu" + ] + }, + { + "value": "Samoa Standard Time", + "abbr": "SST", + "offset": 13, + "isdst": false, + "text": "(UTC+13:00) Samoa", + "utc": ["Pacific/Apia"] + } +] diff --git a/projects/ucap-webmessenger-ui-settings/src/assets/timezone/ko.json b/projects/ucap-webmessenger-ui-settings/src/assets/timezone/ko.json new file mode 100644 index 0000000..f0d28cc --- /dev/null +++ b/projects/ucap-webmessenger-ui-settings/src/assets/timezone/ko.json @@ -0,0 +1,1247 @@ +[ + { + "value": "Dateline Standard Time", + "abbr": "DST", + "offset": -12, + "isdst": false, + "text": "(UTC-12:00) International Date Line West", + "utc": ["Etc/GMT+12"] + }, + { + "value": "UTC-11", + "abbr": "U", + "offset": -11, + "isdst": false, + "text": "(UTC-11:00) Coordinated Universal Time-11", + "utc": ["Etc/GMT+11", "Pacific/Midway", "Pacific/Niue", "Pacific/Pago_Pago"] + }, + { + "value": "Hawaiian Standard Time", + "abbr": "HST", + "offset": -10, + "isdst": false, + "text": "(UTC-10:00) Hawaii", + "utc": [ + "Etc/GMT+10", + "Pacific/Honolulu", + "Pacific/Johnston", + "Pacific/Rarotonga", + "Pacific/Tahiti" + ] + }, + { + "value": "Alaskan Standard Time", + "abbr": "AKDT", + "offset": -8, + "isdst": true, + "text": "(UTC-09:00) Alaska", + "utc": [ + "America/Anchorage", + "America/Juneau", + "America/Nome", + "America/Sitka", + "America/Yakutat" + ] + }, + { + "value": "Pacific Standard Time (Mexico)", + "abbr": "PDT", + "offset": -7, + "isdst": true, + "text": "(UTC-08:00) Baja California", + "utc": ["America/Santa_Isabel"] + }, + { + "value": "Pacific Daylight Time", + "abbr": "PDT", + "offset": -7, + "isdst": true, + "text": "(UTC-07:00) Pacific Time (US & Canada)", + "utc": [ + "America/Dawson", + "America/Los_Angeles", + "America/Tijuana", + "America/Vancouver", + "America/Whitehorse" + ] + }, + { + "value": "Pacific Standard Time", + "abbr": "PST", + "offset": -8, + "isdst": false, + "text": "(UTC-08:00) Pacific Time (US & Canada)", + "utc": [ + "America/Dawson", + "America/Los_Angeles", + "America/Tijuana", + "America/Vancouver", + "America/Whitehorse", + "PST8PDT" + ] + }, + { + "value": "US Mountain Standard Time", + "abbr": "UMST", + "offset": -7, + "isdst": false, + "text": "(UTC-07:00) Arizona", + "utc": [ + "America/Creston", + "America/Dawson_Creek", + "America/Hermosillo", + "America/Phoenix", + "Etc/GMT+7" + ] + }, + { + "value": "Mountain Standard Time (Mexico)", + "abbr": "MDT", + "offset": -6, + "isdst": true, + "text": "(UTC-07:00) Chihuahua, La Paz, Mazatlan", + "utc": ["America/Chihuahua", "America/Mazatlan"] + }, + { + "value": "Mountain Standard Time", + "abbr": "MDT", + "offset": -6, + "isdst": true, + "text": "(UTC-07:00) Mountain Time (US & Canada)", + "utc": [ + "America/Boise", + "America/Cambridge_Bay", + "America/Denver", + "America/Edmonton", + "America/Inuvik", + "America/Ojinaga", + "America/Yellowknife", + "MST7MDT" + ] + }, + { + "value": "Central America Standard Time", + "abbr": "CAST", + "offset": -6, + "isdst": false, + "text": "(UTC-06:00) Central America", + "utc": [ + "America/Belize", + "America/Costa_Rica", + "America/El_Salvador", + "America/Guatemala", + "America/Managua", + "America/Tegucigalpa", + "Etc/GMT+6", + "Pacific/Galapagos" + ] + }, + { + "value": "Central Standard Time", + "abbr": "CDT", + "offset": -5, + "isdst": true, + "text": "(UTC-06:00) Central Time (US & Canada)", + "utc": [ + "America/Chicago", + "America/Indiana/Knox", + "America/Indiana/Tell_City", + "America/Matamoros", + "America/Menominee", + "America/North_Dakota/Beulah", + "America/North_Dakota/Center", + "America/North_Dakota/New_Salem", + "America/Rainy_River", + "America/Rankin_Inlet", + "America/Resolute", + "America/Winnipeg", + "CST6CDT" + ] + }, + { + "value": "Central Standard Time (Mexico)", + "abbr": "CDT", + "offset": -5, + "isdst": true, + "text": "(UTC-06:00) Guadalajara, Mexico City, Monterrey", + "utc": [ + "America/Bahia_Banderas", + "America/Cancun", + "America/Merida", + "America/Mexico_City", + "America/Monterrey" + ] + }, + { + "value": "Canada Central Standard Time", + "abbr": "CCST", + "offset": -6, + "isdst": false, + "text": "(UTC-06:00) Saskatchewan", + "utc": ["America/Regina", "America/Swift_Current"] + }, + { + "value": "SA Pacific Standard Time", + "abbr": "SPST", + "offset": -5, + "isdst": false, + "text": "(UTC-05:00) Bogota, Lima, Quito", + "utc": [ + "America/Bogota", + "America/Cayman", + "America/Coral_Harbour", + "America/Eirunepe", + "America/Guayaquil", + "America/Jamaica", + "America/Lima", + "America/Panama", + "America/Rio_Branco", + "Etc/GMT+5" + ] + }, + { + "value": "Eastern Standard Time", + "abbr": "EDT", + "offset": -4, + "isdst": true, + "text": "(UTC-05:00) Eastern Time (US & Canada)", + "utc": [ + "America/Detroit", + "America/Havana", + "America/Indiana/Petersburg", + "America/Indiana/Vincennes", + "America/Indiana/Winamac", + "America/Iqaluit", + "America/Kentucky/Monticello", + "America/Louisville", + "America/Montreal", + "America/Nassau", + "America/New_York", + "America/Nipigon", + "America/Pangnirtung", + "America/Port-au-Prince", + "America/Thunder_Bay", + "America/Toronto", + "EST5EDT" + ] + }, + { + "value": "US Eastern Standard Time", + "abbr": "UEDT", + "offset": -4, + "isdst": true, + "text": "(UTC-05:00) Indiana (East)", + "utc": [ + "America/Indiana/Marengo", + "America/Indiana/Vevay", + "America/Indianapolis" + ] + }, + { + "value": "Venezuela Standard Time", + "abbr": "VST", + "offset": -4.5, + "isdst": false, + "text": "(UTC-04:30) Caracas", + "utc": ["America/Caracas"] + }, + { + "value": "Paraguay Standard Time", + "abbr": "PYT", + "offset": -4, + "isdst": false, + "text": "(UTC-04:00) Asuncion", + "utc": ["America/Asuncion"] + }, + { + "value": "Atlantic Standard Time", + "abbr": "ADT", + "offset": -3, + "isdst": true, + "text": "(UTC-04:00) Atlantic Time (Canada)", + "utc": [ + "America/Glace_Bay", + "America/Goose_Bay", + "America/Halifax", + "America/Moncton", + "America/Thule", + "Atlantic/Bermuda" + ] + }, + { + "value": "Central Brazilian Standard Time", + "abbr": "CBST", + "offset": -4, + "isdst": false, + "text": "(UTC-04:00) Cuiaba", + "utc": ["America/Campo_Grande", "America/Cuiaba"] + }, + { + "value": "SA Western Standard Time", + "abbr": "SWST", + "offset": -4, + "isdst": false, + "text": "(UTC-04:00) Georgetown, La Paz, Manaus, San Juan", + "utc": [ + "America/Anguilla", + "America/Antigua", + "America/Aruba", + "America/Barbados", + "America/Blanc-Sablon", + "America/Boa_Vista", + "America/Curacao", + "America/Dominica", + "America/Grand_Turk", + "America/Grenada", + "America/Guadeloupe", + "America/Guyana", + "America/Kralendijk", + "America/La_Paz", + "America/Lower_Princes", + "America/Manaus", + "America/Marigot", + "America/Martinique", + "America/Montserrat", + "America/Port_of_Spain", + "America/Porto_Velho", + "America/Puerto_Rico", + "America/Santo_Domingo", + "America/St_Barthelemy", + "America/St_Kitts", + "America/St_Lucia", + "America/St_Thomas", + "America/St_Vincent", + "America/Tortola", + "Etc/GMT+4" + ] + }, + { + "value": "Pacific SA Standard Time", + "abbr": "PSST", + "offset": -4, + "isdst": false, + "text": "(UTC-04:00) Santiago", + "utc": ["America/Santiago", "Antarctica/Palmer"] + }, + { + "value": "Newfoundland Standard Time", + "abbr": "NDT", + "offset": -2.5, + "isdst": true, + "text": "(UTC-03:30) Newfoundland", + "utc": ["America/St_Johns"] + }, + { + "value": "E. South America Standard Time", + "abbr": "ESAST", + "offset": -3, + "isdst": false, + "text": "(UTC-03:00) Brasilia", + "utc": ["America/Sao_Paulo"] + }, + { + "value": "Argentina Standard Time", + "abbr": "AST", + "offset": -3, + "isdst": false, + "text": "(UTC-03:00) Buenos Aires", + "utc": [ + "America/Argentina/La_Rioja", + "America/Argentina/Rio_Gallegos", + "America/Argentina/Salta", + "America/Argentina/San_Juan", + "America/Argentina/San_Luis", + "America/Argentina/Tucuman", + "America/Argentina/Ushuaia", + "America/Buenos_Aires", + "America/Catamarca", + "America/Cordoba", + "America/Jujuy", + "America/Mendoza" + ] + }, + { + "value": "SA Eastern Standard Time", + "abbr": "SEST", + "offset": -3, + "isdst": false, + "text": "(UTC-03:00) Cayenne, Fortaleza", + "utc": [ + "America/Araguaina", + "America/Belem", + "America/Cayenne", + "America/Fortaleza", + "America/Maceio", + "America/Paramaribo", + "America/Recife", + "America/Santarem", + "Antarctica/Rothera", + "Atlantic/Stanley", + "Etc/GMT+3" + ] + }, + { + "value": "Greenland Standard Time", + "abbr": "GDT", + "offset": -3, + "isdst": true, + "text": "(UTC-03:00) Greenland", + "utc": ["America/Godthab"] + }, + { + "value": "Montevideo Standard Time", + "abbr": "MST", + "offset": -3, + "isdst": false, + "text": "(UTC-03:00) Montevideo", + "utc": ["America/Montevideo"] + }, + { + "value": "Bahia Standard Time", + "abbr": "BST", + "offset": -3, + "isdst": false, + "text": "(UTC-03:00) Salvador", + "utc": ["America/Bahia"] + }, + { + "value": "UTC-02", + "abbr": "U", + "offset": -2, + "isdst": false, + "text": "(UTC-02:00) Coordinated Universal Time-02", + "utc": ["America/Noronha", "Atlantic/South_Georgia", "Etc/GMT+2"] + }, + { + "value": "Mid-Atlantic Standard Time", + "abbr": "MDT", + "offset": -1, + "isdst": true, + "text": "(UTC-02:00) Mid-Atlantic - Old", + "utc": [] + }, + { + "value": "Azores Standard Time", + "abbr": "ADT", + "offset": 0, + "isdst": true, + "text": "(UTC-01:00) Azores", + "utc": ["America/Scoresbysund", "Atlantic/Azores"] + }, + { + "value": "Cape Verde Standard Time", + "abbr": "CVST", + "offset": -1, + "isdst": false, + "text": "(UTC-01:00) Cape Verde Is.", + "utc": ["Atlantic/Cape_Verde", "Etc/GMT+1"] + }, + { + "value": "Morocco Standard Time", + "abbr": "MDT", + "offset": 1, + "isdst": true, + "text": "(UTC) Casablanca", + "utc": ["Africa/Casablanca", "Africa/El_Aaiun"] + }, + { + "value": "UTC", + "abbr": "UTC", + "offset": 0, + "isdst": false, + "text": "(UTC) Coordinated Universal Time", + "utc": ["America/Danmarkshavn", "Etc/GMT"] + }, + { + "value": "GMT Standard Time", + "abbr": "GMT", + "offset": 0, + "isdst": false, + "text": "(UTC) Edinburgh, London", + "utc": [ + "Europe/Isle_of_Man", + "Europe/Guernsey", + "Europe/Jersey", + "Europe/London" + ] + }, + { + "value": "British Summer Time", + "abbr": "BST", + "offset": 1, + "isdst": true, + "text": "(UTC+01:00) Edinburgh, London", + "utc": [ + "Europe/Isle_of_Man", + "Europe/Guernsey", + "Europe/Jersey", + "Europe/London" + ] + }, + { + "value": "GMT Standard Time", + "abbr": "GDT", + "offset": 1, + "isdst": true, + "text": "(UTC) Dublin, Lisbon", + "utc": [ + "Atlantic/Canary", + "Atlantic/Faeroe", + "Atlantic/Madeira", + "Europe/Dublin", + "Europe/Lisbon" + ] + }, + { + "value": "Greenwich Standard Time", + "abbr": "GST", + "offset": 0, + "isdst": false, + "text": "(UTC) Monrovia, Reykjavik", + "utc": [ + "Africa/Abidjan", + "Africa/Accra", + "Africa/Bamako", + "Africa/Banjul", + "Africa/Bissau", + "Africa/Conakry", + "Africa/Dakar", + "Africa/Freetown", + "Africa/Lome", + "Africa/Monrovia", + "Africa/Nouakchott", + "Africa/Ouagadougou", + "Africa/Sao_Tome", + "Atlantic/Reykjavik", + "Atlantic/St_Helena" + ] + }, + { + "value": "W. Europe Standard Time", + "abbr": "WEDT", + "offset": 2, + "isdst": true, + "text": "(UTC+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna", + "utc": [ + "Arctic/Longyearbyen", + "Europe/Amsterdam", + "Europe/Andorra", + "Europe/Berlin", + "Europe/Busingen", + "Europe/Gibraltar", + "Europe/Luxembourg", + "Europe/Malta", + "Europe/Monaco", + "Europe/Oslo", + "Europe/Rome", + "Europe/San_Marino", + "Europe/Stockholm", + "Europe/Vaduz", + "Europe/Vatican", + "Europe/Vienna", + "Europe/Zurich" + ] + }, + { + "value": "Central Europe Standard Time", + "abbr": "CEDT", + "offset": 2, + "isdst": true, + "text": "(UTC+01:00) Belgrade, Bratislava, Budapest, Ljubljana, Prague", + "utc": [ + "Europe/Belgrade", + "Europe/Bratislava", + "Europe/Budapest", + "Europe/Ljubljana", + "Europe/Podgorica", + "Europe/Prague", + "Europe/Tirane" + ] + }, + { + "value": "Romance Standard Time", + "abbr": "RDT", + "offset": 2, + "isdst": true, + "text": "(UTC+01:00) Brussels, Copenhagen, Madrid, Paris", + "utc": [ + "Africa/Ceuta", + "Europe/Brussels", + "Europe/Copenhagen", + "Europe/Madrid", + "Europe/Paris" + ] + }, + { + "value": "Central European Standard Time", + "abbr": "CEDT", + "offset": 2, + "isdst": true, + "text": "(UTC+01:00) Sarajevo, Skopje, Warsaw, Zagreb", + "utc": [ + "Europe/Sarajevo", + "Europe/Skopje", + "Europe/Warsaw", + "Europe/Zagreb" + ] + }, + { + "value": "W. Central Africa Standard Time", + "abbr": "WCAST", + "offset": 1, + "isdst": false, + "text": "(UTC+01:00) West Central Africa", + "utc": [ + "Africa/Algiers", + "Africa/Bangui", + "Africa/Brazzaville", + "Africa/Douala", + "Africa/Kinshasa", + "Africa/Lagos", + "Africa/Libreville", + "Africa/Luanda", + "Africa/Malabo", + "Africa/Ndjamena", + "Africa/Niamey", + "Africa/Porto-Novo", + "Africa/Tunis", + "Etc/GMT-1" + ] + }, + { + "value": "Namibia Standard Time", + "abbr": "NST", + "offset": 1, + "isdst": false, + "text": "(UTC+01:00) Windhoek", + "utc": ["Africa/Windhoek"] + }, + { + "value": "GTB Standard Time", + "abbr": "GDT", + "offset": 3, + "isdst": true, + "text": "(UTC+02:00) Athens, Bucharest", + "utc": [ + "Asia/Nicosia", + "Europe/Athens", + "Europe/Bucharest", + "Europe/Chisinau" + ] + }, + { + "value": "Middle East Standard Time", + "abbr": "MEDT", + "offset": 3, + "isdst": true, + "text": "(UTC+02:00) Beirut", + "utc": ["Asia/Beirut"] + }, + { + "value": "Egypt Standard Time", + "abbr": "EST", + "offset": 2, + "isdst": false, + "text": "(UTC+02:00) Cairo", + "utc": ["Africa/Cairo"] + }, + { + "value": "Syria Standard Time", + "abbr": "SDT", + "offset": 3, + "isdst": true, + "text": "(UTC+02:00) Damascus", + "utc": ["Asia/Damascus"] + }, + { + "value": "E. Europe Standard Time", + "abbr": "EEDT", + "offset": 3, + "isdst": true, + "text": "(UTC+02:00) E. Europe", + "utc": [ + "Asia/Nicosia", + "Europe/Athens", + "Europe/Bucharest", + "Europe/Chisinau", + "Europe/Helsinki", + "Europe/Kiev", + "Europe/Mariehamn", + "Europe/Nicosia", + "Europe/Riga", + "Europe/Sofia", + "Europe/Tallinn", + "Europe/Uzhgorod", + "Europe/Vilnius", + "Europe/Zaporozhye" + ] + }, + { + "value": "South Africa Standard Time", + "abbr": "SAST", + "offset": 2, + "isdst": false, + "text": "(UTC+02:00) Harare, Pretoria", + "utc": [ + "Africa/Blantyre", + "Africa/Bujumbura", + "Africa/Gaborone", + "Africa/Harare", + "Africa/Johannesburg", + "Africa/Kigali", + "Africa/Lubumbashi", + "Africa/Lusaka", + "Africa/Maputo", + "Africa/Maseru", + "Africa/Mbabane", + "Etc/GMT-2" + ] + }, + { + "value": "FLE Standard Time", + "abbr": "FDT", + "offset": 3, + "isdst": true, + "text": "(UTC+02:00) Helsinki, Kyiv, Riga, Sofia, Tallinn, Vilnius", + "utc": [ + "Europe/Helsinki", + "Europe/Kiev", + "Europe/Mariehamn", + "Europe/Riga", + "Europe/Sofia", + "Europe/Tallinn", + "Europe/Uzhgorod", + "Europe/Vilnius", + "Europe/Zaporozhye" + ] + }, + { + "value": "Turkey Standard Time", + "abbr": "TDT", + "offset": 3, + "isdst": false, + "text": "(UTC+03:00) Istanbul", + "utc": ["Europe/Istanbul"] + }, + { + "value": "Israel Standard Time", + "abbr": "JDT", + "offset": 3, + "isdst": true, + "text": "(UTC+02:00) Jerusalem", + "utc": ["Asia/Jerusalem"] + }, + { + "value": "Libya Standard Time", + "abbr": "LST", + "offset": 2, + "isdst": false, + "text": "(UTC+02:00) Tripoli", + "utc": ["Africa/Tripoli"] + }, + { + "value": "Jordan Standard Time", + "abbr": "JST", + "offset": 3, + "isdst": false, + "text": "(UTC+03:00) Amman", + "utc": ["Asia/Amman"] + }, + { + "value": "Arabic Standard Time", + "abbr": "AST", + "offset": 3, + "isdst": false, + "text": "(UTC+03:00) Baghdad", + "utc": ["Asia/Baghdad"] + }, + { + "value": "Kaliningrad Standard Time", + "abbr": "KST", + "offset": 3, + "isdst": false, + "text": "(UTC+03:00) Kaliningrad, Minsk", + "utc": ["Europe/Kaliningrad", "Europe/Minsk"] + }, + { + "value": "Arab Standard Time", + "abbr": "AST", + "offset": 3, + "isdst": false, + "text": "(UTC+03:00) Kuwait, Riyadh", + "utc": [ + "Asia/Aden", + "Asia/Bahrain", + "Asia/Kuwait", + "Asia/Qatar", + "Asia/Riyadh" + ] + }, + { + "value": "E. Africa Standard Time", + "abbr": "EAST", + "offset": 3, + "isdst": false, + "text": "(UTC+03:00) Nairobi", + "utc": [ + "Africa/Addis_Ababa", + "Africa/Asmera", + "Africa/Dar_es_Salaam", + "Africa/Djibouti", + "Africa/Juba", + "Africa/Kampala", + "Africa/Khartoum", + "Africa/Mogadishu", + "Africa/Nairobi", + "Antarctica/Syowa", + "Etc/GMT-3", + "Indian/Antananarivo", + "Indian/Comoro", + "Indian/Mayotte" + ] + }, + { + "value": "Moscow Standard Time", + "abbr": "MSK", + "offset": 3, + "isdst": false, + "text": "(UTC+03:00) Moscow, St. Petersburg, Volgograd", + "utc": [ + "Europe/Kirov", + "Europe/Moscow", + "Europe/Simferopol", + "Europe/Volgograd" + ] + }, + { + "value": "Samara Time", + "abbr": "SAMT", + "offset": 4, + "isdst": false, + "text": "(UTC+04:00) Samara, Ulyanovsk, Saratov", + "utc": ["Europe/Astrakhan", "Europe/Samara", "Europe/Ulyanovsk"] + }, + { + "value": "Iran Standard Time", + "abbr": "IDT", + "offset": 4.5, + "isdst": true, + "text": "(UTC+03:30) Tehran", + "utc": ["Asia/Tehran"] + }, + { + "value": "Arabian Standard Time", + "abbr": "AST", + "offset": 4, + "isdst": false, + "text": "(UTC+04:00) Abu Dhabi, Muscat", + "utc": ["Asia/Dubai", "Asia/Muscat", "Etc/GMT-4"] + }, + { + "value": "Azerbaijan Standard Time", + "abbr": "ADT", + "offset": 5, + "isdst": true, + "text": "(UTC+04:00) Baku", + "utc": ["Asia/Baku"] + }, + { + "value": "Mauritius Standard Time", + "abbr": "MST", + "offset": 4, + "isdst": false, + "text": "(UTC+04:00) Port Louis", + "utc": ["Indian/Mahe", "Indian/Mauritius", "Indian/Reunion"] + }, + { + "value": "Georgian Standard Time", + "abbr": "GET", + "offset": 4, + "isdst": false, + "text": "(UTC+04:00) Tbilisi", + "utc": ["Asia/Tbilisi"] + }, + { + "value": "Caucasus Standard Time", + "abbr": "CST", + "offset": 4, + "isdst": false, + "text": "(UTC+04:00) Yerevan", + "utc": ["Asia/Yerevan"] + }, + { + "value": "Afghanistan Standard Time", + "abbr": "AST", + "offset": 4.5, + "isdst": false, + "text": "(UTC+04:30) Kabul", + "utc": ["Asia/Kabul"] + }, + { + "value": "West Asia Standard Time", + "abbr": "WAST", + "offset": 5, + "isdst": false, + "text": "(UTC+05:00) Ashgabat, Tashkent", + "utc": [ + "Antarctica/Mawson", + "Asia/Aqtau", + "Asia/Aqtobe", + "Asia/Ashgabat", + "Asia/Dushanbe", + "Asia/Oral", + "Asia/Samarkand", + "Asia/Tashkent", + "Etc/GMT-5", + "Indian/Kerguelen", + "Indian/Maldives" + ] + }, + { + "value": "Yekaterinburg Time", + "abbr": "YEKT", + "offset": 5, + "isdst": false, + "text": "(UTC+05:00) Yekaterinburg", + "utc": ["Asia/Yekaterinburg"] + }, + { + "value": "Pakistan Standard Time", + "abbr": "PKT", + "offset": 5, + "isdst": false, + "text": "(UTC+05:00) Islamabad, Karachi", + "utc": ["Asia/Karachi"] + }, + { + "value": "India Standard Time", + "abbr": "IST", + "offset": 5.5, + "isdst": false, + "text": "(UTC+05:30) Chennai, Kolkata, Mumbai, New Delhi", + "utc": ["Asia/Kolkata"] + }, + { + "value": "Sri Lanka Standard Time", + "abbr": "SLST", + "offset": 5.5, + "isdst": false, + "text": "(UTC+05:30) Sri Jayawardenepura", + "utc": ["Asia/Colombo"] + }, + { + "value": "Nepal Standard Time", + "abbr": "NST", + "offset": 5.75, + "isdst": false, + "text": "(UTC+05:45) Kathmandu", + "utc": ["Asia/Kathmandu"] + }, + { + "value": "Central Asia Standard Time", + "abbr": "CAST", + "offset": 6, + "isdst": false, + "text": "(UTC+06:00) Astana", + "utc": [ + "Antarctica/Vostok", + "Asia/Almaty", + "Asia/Bishkek", + "Asia/Qyzylorda", + "Asia/Urumqi", + "Etc/GMT-6", + "Indian/Chagos" + ] + }, + { + "value": "Bangladesh Standard Time", + "abbr": "BST", + "offset": 6, + "isdst": false, + "text": "(UTC+06:00) Dhaka", + "utc": ["Asia/Dhaka", "Asia/Thimphu"] + }, + { + "value": "Myanmar Standard Time", + "abbr": "MST", + "offset": 6.5, + "isdst": false, + "text": "(UTC+06:30) Yangon (Rangoon)", + "utc": ["Asia/Rangoon", "Indian/Cocos"] + }, + { + "value": "SE Asia Standard Time", + "abbr": "SAST", + "offset": 7, + "isdst": false, + "text": "(UTC+07:00) Bangkok, Hanoi, Jakarta", + "utc": [ + "Antarctica/Davis", + "Asia/Bangkok", + "Asia/Hovd", + "Asia/Jakarta", + "Asia/Phnom_Penh", + "Asia/Pontianak", + "Asia/Saigon", + "Asia/Vientiane", + "Etc/GMT-7", + "Indian/Christmas" + ] + }, + { + "value": "N. Central Asia Standard Time", + "abbr": "NCAST", + "offset": 7, + "isdst": false, + "text": "(UTC+07:00) Novosibirsk", + "utc": ["Asia/Novokuznetsk", "Asia/Novosibirsk", "Asia/Omsk"] + }, + { + "value": "China Standard Time", + "abbr": "CST", + "offset": 8, + "isdst": false, + "text": "(UTC+08:00) Beijing, Chongqing, Hong Kong, Urumqi", + "utc": ["Asia/Hong_Kong", "Asia/Macau", "Asia/Shanghai"] + }, + { + "value": "North Asia Standard Time", + "abbr": "NAST", + "offset": 8, + "isdst": false, + "text": "(UTC+08:00) Krasnoyarsk", + "utc": ["Asia/Krasnoyarsk"] + }, + { + "value": "Singapore Standard Time", + "abbr": "MPST", + "offset": 8, + "isdst": false, + "text": "(UTC+08:00) Kuala Lumpur, Singapore", + "utc": [ + "Asia/Brunei", + "Asia/Kuala_Lumpur", + "Asia/Kuching", + "Asia/Makassar", + "Asia/Manila", + "Asia/Singapore", + "Etc/GMT-8" + ] + }, + { + "value": "W. Australia Standard Time", + "abbr": "WAST", + "offset": 8, + "isdst": false, + "text": "(UTC+08:00) Perth", + "utc": ["Antarctica/Casey", "Australia/Perth"] + }, + { + "value": "Taipei Standard Time", + "abbr": "TST", + "offset": 8, + "isdst": false, + "text": "(UTC+08:00) Taipei", + "utc": ["Asia/Taipei"] + }, + { + "value": "Ulaanbaatar Standard Time", + "abbr": "UST", + "offset": 8, + "isdst": false, + "text": "(UTC+08:00) Ulaanbaatar", + "utc": ["Asia/Choibalsan", "Asia/Ulaanbaatar"] + }, + { + "value": "North Asia East Standard Time", + "abbr": "NAEST", + "offset": 8, + "isdst": false, + "text": "(UTC+08:00) Irkutsk", + "utc": ["Asia/Irkutsk"] + }, + { + "value": "Japan Standard Time", + "abbr": "JST", + "offset": 9, + "isdst": false, + "text": "(UTC+09:00) Osaka, Sapporo, Tokyo", + "utc": [ + "Asia/Dili", + "Asia/Jayapura", + "Asia/Tokyo", + "Etc/GMT-9", + "Pacific/Palau" + ] + }, + { + "value": "Korea Standard Time", + "abbr": "KST", + "offset": 9, + "isdst": false, + "text": "(UTC+09:00) Seoul", + "utc": ["Asia/Pyongyang", "Asia/Seoul"] + }, + { + "value": "Cen. Australia Standard Time", + "abbr": "CAST", + "offset": 9.5, + "isdst": false, + "text": "(UTC+09:30) Adelaide", + "utc": ["Australia/Adelaide", "Australia/Broken_Hill"] + }, + { + "value": "AUS Central Standard Time", + "abbr": "ACST", + "offset": 9.5, + "isdst": false, + "text": "(UTC+09:30) Darwin", + "utc": ["Australia/Darwin"] + }, + { + "value": "E. Australia Standard Time", + "abbr": "EAST", + "offset": 10, + "isdst": false, + "text": "(UTC+10:00) Brisbane", + "utc": ["Australia/Brisbane", "Australia/Lindeman"] + }, + { + "value": "AUS Eastern Standard Time", + "abbr": "AEST", + "offset": 10, + "isdst": false, + "text": "(UTC+10:00) Canberra, Melbourne, Sydney", + "utc": ["Australia/Melbourne", "Australia/Sydney"] + }, + { + "value": "West Pacific Standard Time", + "abbr": "WPST", + "offset": 10, + "isdst": false, + "text": "(UTC+10:00) Guam, Port Moresby", + "utc": [ + "Antarctica/DumontDUrville", + "Etc/GMT-10", + "Pacific/Guam", + "Pacific/Port_Moresby", + "Pacific/Saipan", + "Pacific/Truk" + ] + }, + { + "value": "Tasmania Standard Time", + "abbr": "TST", + "offset": 10, + "isdst": false, + "text": "(UTC+10:00) Hobart", + "utc": ["Australia/Currie", "Australia/Hobart"] + }, + { + "value": "Yakutsk Standard Time", + "abbr": "YST", + "offset": 9, + "isdst": false, + "text": "(UTC+09:00) Yakutsk", + "utc": ["Asia/Chita", "Asia/Khandyga", "Asia/Yakutsk"] + }, + { + "value": "Central Pacific Standard Time", + "abbr": "CPST", + "offset": 11, + "isdst": false, + "text": "(UTC+11:00) Solomon Is., New Caledonia", + "utc": [ + "Antarctica/Macquarie", + "Etc/GMT-11", + "Pacific/Efate", + "Pacific/Guadalcanal", + "Pacific/Kosrae", + "Pacific/Noumea", + "Pacific/Ponape" + ] + }, + { + "value": "Vladivostok Standard Time", + "abbr": "VST", + "offset": 11, + "isdst": false, + "text": "(UTC+11:00) Vladivostok", + "utc": ["Asia/Sakhalin", "Asia/Ust-Nera", "Asia/Vladivostok"] + }, + { + "value": "New Zealand Standard Time", + "abbr": "NZST", + "offset": 12, + "isdst": false, + "text": "(UTC+12:00) Auckland, Wellington", + "utc": ["Antarctica/McMurdo", "Pacific/Auckland"] + }, + { + "value": "UTC+12", + "abbr": "U", + "offset": 12, + "isdst": false, + "text": "(UTC+12:00) Coordinated Universal Time+12", + "utc": [ + "Etc/GMT-12", + "Pacific/Funafuti", + "Pacific/Kwajalein", + "Pacific/Majuro", + "Pacific/Nauru", + "Pacific/Tarawa", + "Pacific/Wake", + "Pacific/Wallis" + ] + }, + { + "value": "Fiji Standard Time", + "abbr": "FST", + "offset": 12, + "isdst": false, + "text": "(UTC+12:00) Fiji", + "utc": ["Pacific/Fiji"] + }, + { + "value": "Magadan Standard Time", + "abbr": "MST", + "offset": 12, + "isdst": false, + "text": "(UTC+12:00) Magadan", + "utc": [ + "Asia/Anadyr", + "Asia/Kamchatka", + "Asia/Magadan", + "Asia/Srednekolymsk" + ] + }, + { + "value": "Kamchatka Standard Time", + "abbr": "KDT", + "offset": 13, + "isdst": true, + "text": "(UTC+12:00) Petropavlovsk-Kamchatsky - Old", + "utc": ["Asia/Kamchatka"] + }, + { + "value": "Tonga Standard Time", + "abbr": "TST", + "offset": 13, + "isdst": false, + "text": "(UTC+13:00) Nuku'alofa", + "utc": [ + "Etc/GMT-13", + "Pacific/Enderbury", + "Pacific/Fakaofo", + "Pacific/Tongatapu" + ] + }, + { + "value": "Samoa Standard Time", + "abbr": "SST", + "offset": 13, + "isdst": false, + "text": "(UTC+13:00) Samoa", + "utc": ["Pacific/Apia"] + } +] diff --git a/projects/ucap-webmessenger-ui-settings/src/lib/components/call.component.html b/projects/ucap-webmessenger-ui-settings/src/lib/components/call.component.html new file mode 100644 index 0000000..938018d --- /dev/null +++ b/projects/ucap-webmessenger-ui-settings/src/lib/components/call.component.html @@ -0,0 +1,63 @@ +
    + +

    오디오 장치

    + + 발표자 + + + + + 배너 및 전자 메일 + + + 배너 + + + 끔 + + + + + + + 마이크 + + + + + 배너 및 전자 메일 + + + 배너 + + + 끔 + + + + + + + + +

    카메라

    + + 알림 소리 + + + + + 모두 + + + 통화, 채팅 + + + 끔 + + + + + +
    +
    diff --git a/projects/ucap-webmessenger-ui-settings/src/lib/components/call.component.scss b/projects/ucap-webmessenger-ui-settings/src/lib/components/call.component.scss new file mode 100644 index 0000000..bd43512 --- /dev/null +++ b/projects/ucap-webmessenger-ui-settings/src/lib/components/call.component.scss @@ -0,0 +1,7 @@ +.item-title { + width: 25rem; +} + +.item-input { + width: 20rem; +} diff --git a/projects/ucap-webmessenger-ui-settings/src/lib/components/call.component.spec.ts b/projects/ucap-webmessenger-ui-settings/src/lib/components/call.component.spec.ts new file mode 100644 index 0000000..0e3c6d9 --- /dev/null +++ b/projects/ucap-webmessenger-ui-settings/src/lib/components/call.component.spec.ts @@ -0,0 +1,24 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { CallComponent } from './call.component'; + +describe('Settings::CallComponent', () => { + let component: CallComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [CallComponent] + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(CallComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/projects/ucap-webmessenger-ui-settings/src/lib/components/call.component.ts b/projects/ucap-webmessenger-ui-settings/src/lib/components/call.component.ts new file mode 100644 index 0000000..3802393 --- /dev/null +++ b/projects/ucap-webmessenger-ui-settings/src/lib/components/call.component.ts @@ -0,0 +1,20 @@ +import { Component, OnInit, ChangeDetectorRef } from '@angular/core'; +import { FormGroup, FormBuilder } from '@angular/forms'; + +@Component({ + selector: 'ucap-settings-call', + templateUrl: './call.component.html', + styleUrls: ['./call.component.scss'] +}) +export class CallComponent implements OnInit { + loginForm: FormGroup; + + constructor( + private formBuilder: FormBuilder, + private changeDetectorRef: ChangeDetectorRef + ) {} + + ngOnInit() {} + + onClickLogin() {} +} diff --git a/projects/ucap-webmessenger-ui-settings/src/lib/components/device.component.html b/projects/ucap-webmessenger-ui-settings/src/lib/components/device.component.html new file mode 100644 index 0000000..938018d --- /dev/null +++ b/projects/ucap-webmessenger-ui-settings/src/lib/components/device.component.html @@ -0,0 +1,63 @@ +
    + +

    오디오 장치

    + + 발표자 + + + + + 배너 및 전자 메일 + + + 배너 + + + 끔 + + + + + + + 마이크 + + + + + 배너 및 전자 메일 + + + 배너 + + + 끔 + + + + + + + + +

    카메라

    + + 알림 소리 + + + + + 모두 + + + 통화, 채팅 + + + 끔 + + + + + +
    +
    diff --git a/projects/ucap-webmessenger-ui-settings/src/lib/components/device.component.scss b/projects/ucap-webmessenger-ui-settings/src/lib/components/device.component.scss new file mode 100644 index 0000000..bd43512 --- /dev/null +++ b/projects/ucap-webmessenger-ui-settings/src/lib/components/device.component.scss @@ -0,0 +1,7 @@ +.item-title { + width: 25rem; +} + +.item-input { + width: 20rem; +} diff --git a/projects/ucap-webmessenger-ui-settings/src/lib/components/device.component.spec.ts b/projects/ucap-webmessenger-ui-settings/src/lib/components/device.component.spec.ts new file mode 100644 index 0000000..e5caf65 --- /dev/null +++ b/projects/ucap-webmessenger-ui-settings/src/lib/components/device.component.spec.ts @@ -0,0 +1,24 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { DeviceComponent } from './device.component'; + +describe('Settings::DeviceComponent', () => { + let component: DeviceComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [DeviceComponent] + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(DeviceComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/projects/ucap-webmessenger-ui-settings/src/lib/components/device.component.ts b/projects/ucap-webmessenger-ui-settings/src/lib/components/device.component.ts new file mode 100644 index 0000000..d47c430 --- /dev/null +++ b/projects/ucap-webmessenger-ui-settings/src/lib/components/device.component.ts @@ -0,0 +1,20 @@ +import { Component, OnInit, ChangeDetectorRef } from '@angular/core'; +import { FormGroup, FormBuilder } from '@angular/forms'; + +@Component({ + selector: 'ucap-settings-device', + templateUrl: './device.component.html', + styleUrls: ['./device.component.scss'] +}) +export class DeviceComponent implements OnInit { + loginForm: FormGroup; + + constructor( + private formBuilder: FormBuilder, + private changeDetectorRef: ChangeDetectorRef + ) {} + + ngOnInit() {} + + onClickLogin() {} +} diff --git a/projects/ucap-webmessenger-ui-settings/src/lib/components/general.component.html b/projects/ucap-webmessenger-ui-settings/src/lib/components/general.component.html new file mode 100644 index 0000000..4535a16 --- /dev/null +++ b/projects/ucap-webmessenger-ui-settings/src/lib/components/general.component.html @@ -0,0 +1,89 @@ +
    + +

    테마

    + + + + + +
    +
    기본값
    +
    +
    + + +
    +
    LG Red
    +
    +
    +
    +
    + + + +

    응용 프로그램

    + + 응용 프로그램 자동 시작 + + + 백그라운드에서 응용 프로그램 열기 + + + 닫을 시 응용 프로그램을 계속 실행 + + + + +

    로그인

    + + 실행 시 자동 로그인 + + + + +

    언어

    + + + + + 한국어 (대한민국) + + + 영어 (미국) + + + + + + + +

    시간대

    + + + + + 한국어 (대한민국) + + + 영어 (미국) + + + + +
    +
    diff --git a/projects/ucap-webmessenger-ui-settings/src/lib/components/general.component.scss b/projects/ucap-webmessenger-ui-settings/src/lib/components/general.component.scss new file mode 100644 index 0000000..ae9410d --- /dev/null +++ b/projects/ucap-webmessenger-ui-settings/src/lib/components/general.component.scss @@ -0,0 +1,72 @@ +::ng-deep .mat-list-base { + .theme-select.mat-list-item { + position: relative; + height: 160px; + .mat-list-item-content { + flex-direction: column; + } + } +} + +::ng-deep .theme-list { + position: relative; + display: flex; + flex-flow: row; + width: 100%; + margin: 10px; + height: auto !important; + .mat-tab-header { + position: relative; + width: 100% !important; + .mat-tab-labels { + position: relative; + width: 100%; + flex-flow: row !important; + .mat-tab-label { + width: 140px; + height: 120px; + .mat-tab-label-content { + position: relative; + flex-flow: column; + width: 100%; + } + } + } + .mat-ink-bar { + display: none; + } + } + .mat-tab-body-wrapper { + border-left: none; + } +} + +.theme-item { + width: 140px; + height: 98px; + margin-right: 10px; + border: 1px solid #dddddd; + background-size: 100% auto; + background-repeat: no-repeat; + opacity: 0.7; +} +.theme-box { + width: 140px; + height: 98px; + border: 1px solid #dddddd; + background-size: 100% auto; + background-repeat: no-repeat; + &.default-theme { + background-image: url('../../../../ucap-webmessenger-app/src/assets/images/theme/theme-default.png'); + } + &.lg-red-theme { + background-image: url('../../../../ucap-webmessenger-app/src/assets/images/theme/theme-lgRed.png'); + } +} + +.brightness { + position: relative; + width: 100%; + display: flex; + flex-direction: row; +} diff --git a/projects/ucap-webmessenger-ui-settings/src/lib/components/general.component.spec.ts b/projects/ucap-webmessenger-ui-settings/src/lib/components/general.component.spec.ts new file mode 100644 index 0000000..d6a5178 --- /dev/null +++ b/projects/ucap-webmessenger-ui-settings/src/lib/components/general.component.spec.ts @@ -0,0 +1,24 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { GeneralComponent } from './general.component'; + +describe('Settings::GeneralComponent', () => { + let component: GeneralComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [GeneralComponent] + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(GeneralComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/projects/ucap-webmessenger-ui-settings/src/lib/components/general.component.ts b/projects/ucap-webmessenger-ui-settings/src/lib/components/general.component.ts new file mode 100644 index 0000000..409cf3c --- /dev/null +++ b/projects/ucap-webmessenger-ui-settings/src/lib/components/general.component.ts @@ -0,0 +1,46 @@ +import { + Component, + OnInit, + ChangeDetectorRef, + Output, + EventEmitter +} from '@angular/core'; +import { FormGroup, FormBuilder } from '@angular/forms'; +import { MatTabChangeEvent } from '@angular/material'; + +@Component({ + selector: 'ucap-settings-general', + templateUrl: './general.component.html', + styleUrls: ['./general.component.scss'] +}) +export class GeneralComponent implements OnInit { + loginForm: FormGroup; + + @Output() + selectTheme = new EventEmitter(); + + constructor( + private formBuilder: FormBuilder, + private changeDetectorRef: ChangeDetectorRef + ) {} + + ngOnInit() {} + + onSelectedTabChange(e: MatTabChangeEvent) { + let theme = 'theme-default'; + switch (e.index) { + case 0: + theme = 'theme-default'; + break; + case 1: + theme = 'theme-lgRed'; + break; + default: + break; + } + + this.selectTheme.emit(theme); + } + + onClickLogin() {} +} diff --git a/projects/ucap-webmessenger-ui-settings/src/lib/components/notification.component.html b/projects/ucap-webmessenger-ui-settings/src/lib/components/notification.component.html new file mode 100644 index 0000000..8da42f0 --- /dev/null +++ b/projects/ucap-webmessenger-ui-settings/src/lib/components/notification.component.html @@ -0,0 +1,81 @@ +
    + +

    메시지

    + + 채팅 메시지 + + + + + 배너 및 전자 메일 + + + 배너 + + + 끔 + + + + + + + 내가 시작한 대화에 답장 + + + + + 배너 및 전자 메일 + + + 배너 + + + 끔 + + + + + + + + +

    기타

    + + 알림 소리 + + + + + 모두 + + + 통화, 채팅 + + + 끔 + + + + + + + 부재 중 활동 전자 메일 + + + + + 배너 및 전자 메일 + + + 배너 + + + 끔 + + + + + +
    +
    diff --git a/projects/ucap-webmessenger-ui-settings/src/lib/components/notification.component.scss b/projects/ucap-webmessenger-ui-settings/src/lib/components/notification.component.scss new file mode 100644 index 0000000..bd43512 --- /dev/null +++ b/projects/ucap-webmessenger-ui-settings/src/lib/components/notification.component.scss @@ -0,0 +1,7 @@ +.item-title { + width: 25rem; +} + +.item-input { + width: 20rem; +} diff --git a/projects/ucap-webmessenger-ui-settings/src/lib/components/notification.component.spec.ts b/projects/ucap-webmessenger-ui-settings/src/lib/components/notification.component.spec.ts new file mode 100644 index 0000000..e398431 --- /dev/null +++ b/projects/ucap-webmessenger-ui-settings/src/lib/components/notification.component.spec.ts @@ -0,0 +1,24 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { NotificationComponent } from './notification.component'; + +describe('Settings::NotificationComponent', () => { + let component: NotificationComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [NotificationComponent] + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(NotificationComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/projects/ucap-webmessenger-ui-settings/src/lib/components/notification.component.ts b/projects/ucap-webmessenger-ui-settings/src/lib/components/notification.component.ts new file mode 100644 index 0000000..e3674d6 --- /dev/null +++ b/projects/ucap-webmessenger-ui-settings/src/lib/components/notification.component.ts @@ -0,0 +1,20 @@ +import { Component, OnInit, ChangeDetectorRef } from '@angular/core'; +import { FormGroup, FormBuilder } from '@angular/forms'; + +@Component({ + selector: 'ucap-settings-notification', + templateUrl: './notification.component.html', + styleUrls: ['./notification.component.scss'] +}) +export class NotificationComponent implements OnInit { + loginForm: FormGroup; + + constructor( + private formBuilder: FormBuilder, + private changeDetectorRef: ChangeDetectorRef + ) {} + + ngOnInit() {} + + onClickLogin() {} +} diff --git a/projects/ucap-webmessenger-ui-settings/src/lib/components/permission.component.html b/projects/ucap-webmessenger-ui-settings/src/lib/components/permission.component.html new file mode 100644 index 0000000..689a772 --- /dev/null +++ b/projects/ucap-webmessenger-ui-settings/src/lib/components/permission.component.html @@ -0,0 +1,27 @@ +
    + +

    사용 권한

    + + 미디어 + + + + + + 마이크 + + + + + + + +

    카메라

    + + 알림 소리 + + + + +
    +
    diff --git a/projects/ucap-webmessenger-ui-settings/src/lib/components/permission.component.scss b/projects/ucap-webmessenger-ui-settings/src/lib/components/permission.component.scss new file mode 100644 index 0000000..bd43512 --- /dev/null +++ b/projects/ucap-webmessenger-ui-settings/src/lib/components/permission.component.scss @@ -0,0 +1,7 @@ +.item-title { + width: 25rem; +} + +.item-input { + width: 20rem; +} diff --git a/projects/ucap-webmessenger-ui-settings/src/lib/components/permission.component.spec.ts b/projects/ucap-webmessenger-ui-settings/src/lib/components/permission.component.spec.ts new file mode 100644 index 0000000..54ef1ee --- /dev/null +++ b/projects/ucap-webmessenger-ui-settings/src/lib/components/permission.component.spec.ts @@ -0,0 +1,24 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { PermissionComponent } from './permission.component'; + +describe('Settings::PermissionComponent', () => { + let component: PermissionComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [PermissionComponent] + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(PermissionComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/projects/ucap-webmessenger-ui-settings/src/lib/components/permission.component.ts b/projects/ucap-webmessenger-ui-settings/src/lib/components/permission.component.ts new file mode 100644 index 0000000..38da4c1 --- /dev/null +++ b/projects/ucap-webmessenger-ui-settings/src/lib/components/permission.component.ts @@ -0,0 +1,20 @@ +import { Component, OnInit, ChangeDetectorRef } from '@angular/core'; +import { FormGroup, FormBuilder } from '@angular/forms'; + +@Component({ + selector: 'ucap-settings-permission', + templateUrl: './permission.component.html', + styleUrls: ['./permission.component.scss'] +}) +export class PermissionComponent implements OnInit { + loginForm: FormGroup; + + constructor( + private formBuilder: FormBuilder, + private changeDetectorRef: ChangeDetectorRef + ) {} + + ngOnInit() {} + + onClickLogin() {} +} diff --git a/projects/ucap-webmessenger-ui-settings/src/lib/components/privacy.component.html b/projects/ucap-webmessenger-ui-settings/src/lib/components/privacy.component.html new file mode 100644 index 0000000..5a8a9dd --- /dev/null +++ b/projects/ucap-webmessenger-ui-settings/src/lib/components/privacy.component.html @@ -0,0 +1,15 @@ +
    + +

    방해 금지

    + + 상태를 방해 금지로 바꿉니다. + + + + +

    응용 프로그램

    + + 에러 자동 보고 + +
    +
    diff --git a/projects/ucap-webmessenger-ui-settings/src/lib/components/privacy.component.scss b/projects/ucap-webmessenger-ui-settings/src/lib/components/privacy.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/projects/ucap-webmessenger-ui-settings/src/lib/components/privacy.component.spec.ts b/projects/ucap-webmessenger-ui-settings/src/lib/components/privacy.component.spec.ts new file mode 100644 index 0000000..bddbec4 --- /dev/null +++ b/projects/ucap-webmessenger-ui-settings/src/lib/components/privacy.component.spec.ts @@ -0,0 +1,24 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { PrivacyComponent } from './privacy.component'; + +describe('Settings::PrivacyComponent', () => { + let component: PrivacyComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [PrivacyComponent] + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(PrivacyComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/projects/ucap-webmessenger-ui-settings/src/lib/components/privacy.component.ts b/projects/ucap-webmessenger-ui-settings/src/lib/components/privacy.component.ts new file mode 100644 index 0000000..d7de28d --- /dev/null +++ b/projects/ucap-webmessenger-ui-settings/src/lib/components/privacy.component.ts @@ -0,0 +1,20 @@ +import { Component, OnInit, ChangeDetectorRef } from '@angular/core'; +import { FormGroup, FormBuilder } from '@angular/forms'; + +@Component({ + selector: 'ucap-settings-privacy', + templateUrl: './privacy.component.html', + styleUrls: ['./privacy.component.scss'] +}) +export class PrivacyComponent implements OnInit { + loginForm: FormGroup; + + constructor( + private formBuilder: FormBuilder, + private changeDetectorRef: ChangeDetectorRef + ) {} + + ngOnInit() {} + + onClickLogin() {} +} diff --git a/projects/ucap-webmessenger-ui-settings/src/lib/ucap-ui-settings.module.ts b/projects/ucap-webmessenger-ui-settings/src/lib/ucap-ui-settings.module.ts new file mode 100644 index 0000000..285eda3 --- /dev/null +++ b/projects/ucap-webmessenger-ui-settings/src/lib/ucap-ui-settings.module.ts @@ -0,0 +1,53 @@ +import { NgModule, ModuleWithProviders } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { ReactiveFormsModule } from '@angular/forms'; + +import { FlexLayoutModule } from '@angular/flex-layout'; + +import { MatCheckboxModule } from '@angular/material/checkbox'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatListModule } from '@angular/material/list'; +import { MatSelectModule } from '@angular/material/select'; +import { MatSlideToggleModule } from '@angular/material/slide-toggle'; +import { MatTabsModule } from '@angular/material/tabs'; + +import { CallComponent } from './components/call.component'; +import { DeviceComponent } from './components/device.component'; +import { GeneralComponent } from './components/general.component'; +import { NotificationComponent } from './components/notification.component'; +import { PermissionComponent } from './components/permission.component'; +import { PrivacyComponent } from './components/privacy.component'; + +const COMPONENTS = [ + CallComponent, + DeviceComponent, + GeneralComponent, + NotificationComponent, + PermissionComponent, + PrivacyComponent +]; +const SERVICES = []; + +@NgModule({ + imports: [ + CommonModule, + ReactiveFormsModule, + FlexLayoutModule, + MatCheckboxModule, + MatFormFieldModule, + MatListModule, + MatSelectModule, + MatSlideToggleModule, + MatTabsModule + ], + exports: [...COMPONENTS], + declarations: [...COMPONENTS] +}) +export class UCapUiSettingsModule { + public static forRoot(): ModuleWithProviders { + return { + ngModule: UCapUiSettingsModule, + providers: [...SERVICES] + }; + } +} diff --git a/projects/ucap-webmessenger-ui-settings/src/public-api.ts b/projects/ucap-webmessenger-ui-settings/src/public-api.ts new file mode 100644 index 0000000..93dc960 --- /dev/null +++ b/projects/ucap-webmessenger-ui-settings/src/public-api.ts @@ -0,0 +1,7 @@ +/* + * Public API Surface of ucap-webmessenger-ui-settings + */ + +export * from './lib/components/general.component'; + +export * from './lib/ucap-ui-settings.module'; diff --git a/projects/ucap-webmessenger-ui-settings/src/test.ts b/projects/ucap-webmessenger-ui-settings/src/test.ts new file mode 100644 index 0000000..978c64f --- /dev/null +++ b/projects/ucap-webmessenger-ui-settings/src/test.ts @@ -0,0 +1,21 @@ +// This file is required by karma.conf.js and loads recursively all the .spec and framework files + +import 'zone.js/dist/zone'; +import 'zone.js/dist/zone-testing'; +import { getTestBed } from '@angular/core/testing'; +import { + BrowserDynamicTestingModule, + platformBrowserDynamicTesting +} from '@angular/platform-browser-dynamic/testing'; + +declare const require: any; + +// First, initialize the Angular testing environment. +getTestBed().initTestEnvironment( + BrowserDynamicTestingModule, + platformBrowserDynamicTesting() +); +// Then we find all the tests. +const context = require.context('./', true, /\.spec\.ts$/); +// And load the modules. +context.keys().map(context); diff --git a/projects/ucap-webmessenger-electron-notification/tsconfig.lib.json b/projects/ucap-webmessenger-ui-settings/tsconfig.lib.json similarity index 100% rename from projects/ucap-webmessenger-electron-notification/tsconfig.lib.json rename to projects/ucap-webmessenger-ui-settings/tsconfig.lib.json diff --git a/projects/ucap-webmessenger-ui-settings/tsconfig.spec.json b/projects/ucap-webmessenger-ui-settings/tsconfig.spec.json new file mode 100644 index 0000000..16da33d --- /dev/null +++ b/projects/ucap-webmessenger-ui-settings/tsconfig.spec.json @@ -0,0 +1,17 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "../../out-tsc/spec", + "types": [ + "jasmine", + "node" + ] + }, + "files": [ + "src/test.ts" + ], + "include": [ + "**/*.spec.ts", + "**/*.d.ts" + ] +} diff --git a/projects/ucap-webmessenger-ui-settings/tslint.json b/projects/ucap-webmessenger-ui-settings/tslint.json new file mode 100644 index 0000000..a91f162 --- /dev/null +++ b/projects/ucap-webmessenger-ui-settings/tslint.json @@ -0,0 +1,17 @@ +{ + "extends": "../../tslint.json", + "rules": { + "directive-selector": [ + true, + "attribute", + "ucapSettings", + "camelCase" + ], + "component-selector": [ + true, + "element", + "ucap-settings", + "kebab-case" + ] + } +} diff --git a/projects/ucap-webmessenger-ui/src/assets/scss/partials/_general.scss b/projects/ucap-webmessenger-ui/src/assets/scss/partials/_general.scss index c643d73..6f6a662 100644 --- a/projects/ucap-webmessenger-ui/src/assets/scss/partials/_general.scss +++ b/projects/ucap-webmessenger-ui/src/assets/scss/partials/_general.scss @@ -1,70 +1,74 @@ -// ----------------------------------------------------------------------------------------------------- -// @ Body scroll lock -// ----------------------------------------------------------------------------------------------------- -html, -body { - display: flex; - flex: 1 0 auto; - width: 100%; - height: 100%; - max-height: 100%; - min-height: 100%; - margin: 0; - padding: 0; - overflow: hidden; -} - -// ----------------------------------------------------------------------------------------------------- -// @ Boxed body -// ----------------------------------------------------------------------------------------------------- -body { - // Boxed - &.boxed { - max-width: 1200px; - margin: 0 auto; - - @include mat-elevation(8); - } -} - -/*----------------------------------------------------------------*/ -/* @ Text rendering & box sizing -/*----------------------------------------------------------------*/ -* { - text-rendering: optimizeLegibility; - -o-text-rendering: optimizeLegibility; - -ms-text-rendering: optimizeLegibility; - -moz-text-rendering: optimizeLegibility; - -webkit-text-rendering: optimizeLegibility; - -webkit-tap-highlight-color: transparent; - box-sizing: border-box; - - &:before, - &:after { - box-sizing: border-box; - } - - // Remove focus outline - &:focus { - outline: none; - } -} - -// ----------------------------------------------------------------------------------------------------- -// @ Responsive images -// ----------------------------------------------------------------------------------------------------- - -img { - max-width: 100%; - height: auto; - vertical-align: top; - border: none; -} - -// ----------------------------------------------------------------------------------------------------- -// @ Input -// ----------------------------------------------------------------------------------------------------- -input { - border: none; - padding: 0 16px; -} +// ----------------------------------------------------------------------------------------------------- +// @ Body scroll lock +// ----------------------------------------------------------------------------------------------------- +html, +body { + display: flex; + flex: 1 0 auto; + width: 100%; + height: 100%; + max-height: 100%; + min-height: 100%; + margin: 0; + padding: 0; + overflow: hidden; +} + +// ----------------------------------------------------------------------------------------------------- +// @ Boxed body +// ----------------------------------------------------------------------------------------------------- +body { + // Boxed + &.boxed { + max-width: 1200px; + margin: 0 auto; + + @include mat-elevation(8); + } +} + +/*----------------------------------------------------------------*/ +/* @ Text rendering & box sizing +/*----------------------------------------------------------------*/ +* { + text-rendering: optimizeLegibility; + -o-text-rendering: optimizeLegibility; + -ms-text-rendering: optimizeLegibility; + -moz-text-rendering: optimizeLegibility; + -webkit-text-rendering: optimizeLegibility; + -webkit-tap-highlight-color: transparent; + box-sizing: border-box; + + &:before, + &:after { + box-sizing: border-box; + } + + // Remove focus outline + &:focus { + outline: none; + } +} + +// ----------------------------------------------------------------------------------------------------- +// @ Responsive images +// ----------------------------------------------------------------------------------------------------- + +img { + max-width: 100%; + height: auto; + vertical-align: top; + border: none; +} + +// ----------------------------------------------------------------------------------------------------- +// @ Input +// ----------------------------------------------------------------------------------------------------- +input { + border: none; + padding: 0 16px; +} + +a { + cursor: pointer; +} diff --git a/projects/ucap-webmessenger-ui/src/assets/scss/partials/_presence.scss b/projects/ucap-webmessenger-ui/src/assets/scss/partials/_presence.scss index 2b3edec..f7f8677 100644 --- a/projects/ucap-webmessenger-ui/src/assets/scss/partials/_presence.scss +++ b/projects/ucap-webmessenger-ui/src/assets/scss/partials/_presence.scss @@ -8,7 +8,7 @@ margin-right: 4px; border-radius: 100px; } -.presence{ +.presence { @extend %presence; &.pcOn { background-color: #28ad66; @@ -16,7 +16,7 @@ &.pcOut { background-color: #f35b5b; } - & .pcOff { + &.pcOff { background-color: #a29f9f; } &.pcOther { diff --git a/projects/ucap-webmessenger-ui/src/lib/animations/index.ts b/projects/ucap-webmessenger-ui/src/lib/animations/index.ts index 84f249b..ba6c095 100644 --- a/projects/ucap-webmessenger-ui/src/lib/animations/index.ts +++ b/projects/ucap-webmessenger-ui/src/lib/animations/index.ts @@ -47,15 +47,21 @@ export const ucapAnimations = [ transition( 'void => 50', - query('@*', [stagger('50ms', [animateChild()])], { optional: true }) + query('@*', [stagger('50ms', [animateChild()])], { + optional: true + }) ), transition( 'void => 100', - query('@*', [stagger('100ms', [animateChild()])], { optional: true }) + query('@*', [stagger('100ms', [animateChild()])], { + optional: true + }) ), transition( 'void => 200', - query('@*', [stagger('200ms', [animateChild()])], { optional: true }) + query('@*', [stagger('200ms', [animateChild()])], { + optional: true + }) ) ]), @@ -551,6 +557,24 @@ export const ucapAnimations = [ ) ]), + trigger('treeToggler', [ + state( + '0, void', + style({ + opacity: 0 + }) + ), + state( + '1, *', + style({ + opacity: 1 + }) + ), + transition('1 => 0', animate('300ms ease-out')), + transition('0 => 1', animate('300ms ease-in')), + transition('void <=> *', animate('300ms ease-in')) + ]), + /** * Floating ACtion Button Animation. */ diff --git a/projects/ucap-webmessenger-ui/src/lib/components/expansion-panel.component.html b/projects/ucap-webmessenger-ui/src/lib/components/expansion-panel.component.html new file mode 100644 index 0000000..8c675cd --- /dev/null +++ b/projects/ucap-webmessenger-ui/src/lib/components/expansion-panel.component.html @@ -0,0 +1 @@ +
    diff --git a/projects/ucap-webmessenger-ui/src/lib/components/expansion-panel.component.scss b/projects/ucap-webmessenger-ui/src/lib/components/expansion-panel.component.scss new file mode 100644 index 0000000..5719c2a --- /dev/null +++ b/projects/ucap-webmessenger-ui/src/lib/components/expansion-panel.component.scss @@ -0,0 +1,4 @@ +.ucap-expansion-panel-container { + width: 100%; + height: 100%; +} diff --git a/projects/ucap-webmessenger-ui/src/lib/components/expansion-panel.component.spec.ts b/projects/ucap-webmessenger-ui/src/lib/components/expansion-panel.component.spec.ts new file mode 100644 index 0000000..48e6712 --- /dev/null +++ b/projects/ucap-webmessenger-ui/src/lib/components/expansion-panel.component.spec.ts @@ -0,0 +1,27 @@ +/* tslint:disable:no-unused-variable */ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { By } from '@angular/platform-browser'; +import { DebugElement } from '@angular/core'; + +import { ExpansionPanelComponent } from './expansion-panel.component'; + +describe('ExpansionPanelComponent', () => { + let component: ExpansionPanelComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ExpansionPanelComponent] + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ExpansionPanelComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/projects/ucap-webmessenger-ui/src/lib/components/expansion-panel.component.ts b/projects/ucap-webmessenger-ui/src/lib/components/expansion-panel.component.ts new file mode 100644 index 0000000..66fa997 --- /dev/null +++ b/projects/ucap-webmessenger-ui/src/lib/components/expansion-panel.component.ts @@ -0,0 +1,12 @@ +import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core'; + +@Component({ + selector: 'ucap-expansion-panel', + templateUrl: './expansion-panel.component.html', + styleUrls: ['./expansion-panel.component.scss'] +}) +export class ExpansionPanelComponent implements OnInit { + constructor() {} + + ngOnInit() {} +} diff --git a/projects/ucap-webmessenger-ui/src/lib/components/file-upload-queue.component.html b/projects/ucap-webmessenger-ui/src/lib/components/file-upload-queue.component.html index edf37d5..765e886 100644 --- a/projects/ucap-webmessenger-ui/src/lib/components/file-upload-queue.component.html +++ b/projects/ucap-webmessenger-ui/src/lib/components/file-upload-queue.component.html @@ -1,25 +1,23 @@
    -
    -
    -
    - image -
    -
    {{ fileUploadItem.file.name }}
    +
    +
    + + + + + + + +
    {{ fileUploadItem.file.name }}
    -
    - + +
    +
    @@ -28,4 +26,4 @@
    여기에 파일을 Drop하시면 업로드 됩니다.
    -
    +
    \ No newline at end of file diff --git a/projects/ucap-webmessenger-ui/src/lib/components/file-upload-queue.component.scss b/projects/ucap-webmessenger-ui/src/lib/components/file-upload-queue.component.scss index ae2e16e..ad4f3f5 100644 --- a/projects/ucap-webmessenger-ui/src/lib/components/file-upload-queue.component.scss +++ b/projects/ucap-webmessenger-ui/src/lib/components/file-upload-queue.component.scss @@ -1,4 +1,41 @@ +@mixin ellipsis($row) { + overflow: hidden; + text-overflow: ellipsis; + @if $row == 1 { + display: block; + white-space: nowrap; + word-wrap: normal; + } @else if $row >= 2 { + display: -webkit-box; + -webkit-line-clamp: $row; + -webkit-box-orient: vertical; + word-wrap: break-word; + } +} + .ucap-file-upload-queue-container { width: 100%; height: 100%; + .file-upload-item { + min-width: 200px; + margin: 0 1%; + margin-bottom: 10px; + width: 48%; + border-radius: 3px; + background-color:#f9f9f9; + border:1px solid #dddddd; + .file-upload-info { + padding: 10px; + svg { + margin-right: 6px; + } + .file-upload-name { + height: 20px; + @include ellipsis(2); + } + } + .file-upload-progress { + padding: 6px 10px; + } + } } diff --git a/projects/ucap-webmessenger-ui/src/lib/components/file-viewer.component.html b/projects/ucap-webmessenger-ui/src/lib/components/file-viewer.component.html index 4d38e2f..772d4c8 100644 --- a/projects/ucap-webmessenger-ui/src/lib/components/file-viewer.component.html +++ b/projects/ucap-webmessenger-ui/src/lib/components/file-viewer.component.html @@ -6,35 +6,35 @@ *ngSwitchCase="FileViewerType.Document" [fileInfo]="fileInfo" [fileDownloadUrl]="fileDownloadUrl" - (download)="onDownload()" + (download)="onDownload($event)" (closed)="onClosedViewer()" >
    diff --git a/projects/ucap-webmessenger-ui/src/lib/components/file-viewer.component.ts b/projects/ucap-webmessenger-ui/src/lib/components/file-viewer.component.ts index 51ce68c..246c72b 100644 --- a/projects/ucap-webmessenger-ui/src/lib/components/file-viewer.component.ts +++ b/projects/ucap-webmessenger-ui/src/lib/components/file-viewer.component.ts @@ -3,12 +3,13 @@ import { ucapAnimations } from '../animations'; import { FileEventJson } from '@ucap-webmessenger/protocol-event'; import { FileViewerType } from '../types/file-viewer.type'; import { FileType } from '@ucap-webmessenger/protocol-file'; +import { FileDownloadItem } from '@ucap-webmessenger/api-common'; @Component({ selector: 'ucap-file-viewer', templateUrl: './file-viewer.component.html', styleUrls: ['./file-viewer.component.scss'], - animations: ucapAnimations + animations: ucapAnimations, }) export class FileViewerComponent implements OnInit { @Input() @@ -18,7 +19,7 @@ export class FileViewerComponent implements OnInit { fileDownloadUrl: string; @Output() - download = new EventEmitter(); + download = new EventEmitter(); @Output() closed = new EventEmitter(); @@ -44,8 +45,8 @@ export class FileViewerComponent implements OnInit { return FileViewerType.Binary; } } - onDownload(): void { - this.download.emit(); + onDownload(fileDownloadItem: FileDownloadItem): void { + this.download.emit(fileDownloadItem); } onClosedViewer(): void { diff --git a/projects/ucap-webmessenger-ui/src/lib/components/file-viewer/binary-viewer.component.html b/projects/ucap-webmessenger-ui/src/lib/components/file-viewer/binary-viewer.component.html index 5474125..0ded2cb 100644 --- a/projects/ucap-webmessenger-ui/src/lib/components/file-viewer/binary-viewer.component.html +++ b/projects/ucap-webmessenger-ui/src/lib/components/file-viewer/binary-viewer.component.html @@ -1,6 +1,22 @@
    - - attachment + + + + + {{ fileInfo.fileName }} @@ -12,18 +28,56 @@ aria-label="" (click)="onClickDownload()" > - get_app + + + - + +
    +
    + +
    +
    -
    -
    +
    +
    +
    +
    -
    +
    미리보기를 지원하지 않는 파일입니다.
    - + +
    +
    + +
    +
    (); @Output() - download = new EventEmitter(); + download = new EventEmitter(); + + fileDownloadItem: FileDownloadItem; constructor() {} ngOnInit() {} onClickDownload(): void { - this.download.emit(); + this.fileDownloadItem = new FileDownloadItem(); + this.download.emit(this.fileDownloadItem); } onClickClose(): void { diff --git a/projects/ucap-webmessenger-ui/src/lib/components/file-viewer/sound-viewer.component.html b/projects/ucap-webmessenger-ui/src/lib/components/file-viewer/sound-viewer.component.html index fbe086a..20aae80 100644 --- a/projects/ucap-webmessenger-ui/src/lib/components/file-viewer/sound-viewer.component.html +++ b/projects/ucap-webmessenger-ui/src/lib/components/file-viewer/sound-viewer.component.html @@ -5,23 +5,43 @@ + +
    +
    + +
    +
    - music_note -
    -
    - - -
    -
    -
    - {{ currentTime | ucapSecondsToMinutes }} +
    + music_note
    - - - -
    - {{ duration | ucapSecondsToMinutes }} + + +
    +
    +
    + {{ currentTime | ucapSecondsToMinutes }} +
    + + + +
    + {{ duration | ucapSecondsToMinutes }} +
    -
    +
    \ No newline at end of file diff --git a/projects/ucap-webmessenger-ui/src/lib/components/file-viewer/sound-viewer.component.scss b/projects/ucap-webmessenger-ui/src/lib/components/file-viewer/sound-viewer.component.scss index 31b6cb6..22290b5 100644 --- a/projects/ucap-webmessenger-ui/src/lib/components/file-viewer/sound-viewer.component.scss +++ b/projects/ucap-webmessenger-ui/src/lib/components/file-viewer/sound-viewer.component.scss @@ -4,40 +4,73 @@ .ucap-sound-viewer-header { width: 100%; - height: 50px; + height: 60px; + box-shadow: 0 3px 6px rgba(0, 0, 0, 0.6); + background-color: #333333; .ucap-sound-viewer-icon { + margin-right: 10px; } .ucap-sound-viewer-title { + font-size:16px; + } + .stroke-bar { + width: 1px; + height: 30px; + background-color: rgba(256, 256, 256, 0.3); + margin: 0 10px; + } + .ucap-sound-viewer-action { + &:hover { + opacity: 0.7; + } } } .ucap-sound-viewer-body { position: relative; - background-color: white; width: 100%; - height: 100%; - padding-bottom: 70px; + height: calc(100% - 60px); - .ucap-sound-viewer-sound-icon { - width: 100%; - height: calc(100% - 60px); - } - .ucap-sound-viewer-sound-time { - width: 100%; - height: 30px; - } - .ucap-sound-viewer-sound-controls { - width: 100%; - height: 30px; - - .ucap-sound-viewer-sound-time-current { - padding-left: 30px; + .circle-box{ + display:flex; + width:140px; + height:140px; + border-radius:50%; + justify-content: center; + align-items: center; + border:2px solid #ffffff; + background-color:rgba(256, 256, 256, 0.7); + .mat-icon{ + font-size: 100px; + height: 100px; + width: 100px; } + } - .ucap-sound-viewer-sound-time-total { - padding-right: 30px; + .ucap-sound-viewer-sound-icon{ + width: 100%; + height: calc(100% - 80px); + } + .viewer-bottom{ + background-color: #212121; + color:#ffffff; + .ucap-sound-viewer-sound-time { + width: 100%; + height: 30px; + } + .ucap-sound-viewer-sound-controls { + width: 100%; + height: 50px; + + .ucap-sound-viewer-sound-time-current { + padding-left: 30px; + } + + .ucap-sound-viewer-sound-time-total { + padding-right: 30px; + } } } } @@ -45,9 +78,22 @@ flex: 1 1 auto; } .ucap-sound-viewer-action { + .mat-icon{ + font-size: 40px; + width: 100%; + height: 100%; + line-height:40px; + } } } mat-slider { - width: 95%; + width: 94%; } + +::ng-deep .mat-slider-horizontal .mat-slider-track-background{ + background-color: #999999 !important; +} +::ng-deep .mat-slider-min-value:not(.mat-slider-thumb-label-showing) .mat-slider-thumb{ + border-color: #999999 !important; +} \ No newline at end of file diff --git a/projects/ucap-webmessenger-ui/src/lib/components/file-viewer/sound-viewer.component.ts b/projects/ucap-webmessenger-ui/src/lib/components/file-viewer/sound-viewer.component.ts index c2ae0ff..ab1ed25 100644 --- a/projects/ucap-webmessenger-ui/src/lib/components/file-viewer/sound-viewer.component.ts +++ b/projects/ucap-webmessenger-ui/src/lib/components/file-viewer/sound-viewer.component.ts @@ -5,17 +5,18 @@ import { Output, EventEmitter, ViewChild, - ElementRef + ElementRef, } from '@angular/core'; import { ucapAnimations } from '../../animations'; import { FileEventJson } from '@ucap-webmessenger/protocol-event'; import { MatSlider, MatSliderChange } from '@angular/material'; +import { FileDownloadItem } from '@ucap-webmessenger/api-common'; @Component({ selector: 'ucap-sound-viewer', templateUrl: './sound-viewer.component.html', styleUrls: ['./sound-viewer.component.scss'], - animations: ucapAnimations + animations: ucapAnimations, }) export class SoundViewerComponent implements OnInit { @Input() @@ -25,7 +26,7 @@ export class SoundViewerComponent implements OnInit { fileDownloadUrl: string; @Output() - download = new EventEmitter(); + download = new EventEmitter(); @Output() closed = new EventEmitter(); @@ -41,6 +42,7 @@ export class SoundViewerComponent implements OnInit { currentTime = 0; volume = 0.1; loading = false; + fileDownloadItem: FileDownloadItem; constructor() {} @@ -90,7 +92,8 @@ export class SoundViewerComponent implements OnInit { } onClickDownload(): void { - this.download.emit(); + this.fileDownloadItem = new FileDownloadItem(); + this.download.emit(this.fileDownloadItem); } onClickClose(): void { diff --git a/projects/ucap-webmessenger-ui/src/lib/components/file-viewer/video-viewer.component.html b/projects/ucap-webmessenger-ui/src/lib/components/file-viewer/video-viewer.component.html index 28ea7bb..9c1e66e 100644 --- a/projects/ucap-webmessenger-ui/src/lib/components/file-viewer/video-viewer.component.html +++ b/projects/ucap-webmessenger-ui/src/lib/components/file-viewer/video-viewer.component.html @@ -1,6 +1,27 @@
    - video_label + + + + + + + + + + + {{ fileInfo.fileName }} - + +
    +
    + +
    +
    -
    - +
    - -
    -
    -
    - {{ currentTime | ucapSecondsToMinutes }} + +
    - - - -
    - {{ duration | ucapSecondsToMinutes }} +
    + {{ currentTime | ucapSecondsToMinutes }} +
    + + + +
    + {{ duration | ucapSecondsToMinutes }} +
    diff --git a/projects/ucap-webmessenger-ui/src/lib/components/file-viewer/video-viewer.component.scss b/projects/ucap-webmessenger-ui/src/lib/components/file-viewer/video-viewer.component.scss index 8f7be76..e77e2bb 100644 --- a/projects/ucap-webmessenger-ui/src/lib/components/file-viewer/video-viewer.component.scss +++ b/projects/ucap-webmessenger-ui/src/lib/components/file-viewer/video-viewer.component.scss @@ -4,40 +4,57 @@ .ucap-video-viewer-header { width: 100%; - height: 50px; + height: 60px; + box-shadow: 0 3px 6px rgba(0, 0, 0, 0.6); + background-color: #333333; .ucap-video-viewer-icon { + margin-right: 10px; } .ucap-video-viewer-title { + font-size:16px; + } + .stroke-bar { + width: 1px; + height: 30px; + background-color: rgba(256, 256, 256, 0.3); + margin: 0 10px; + } + .ucap-image-viewer-action { + &:hover { + opacity: 0.7; + } } } .ucap-video-viewer-body { position: relative; - background-color: white; width: 100%; - height: 100%; - padding-bottom: 70px; + height: calc(100% - 60px); .ucap-video-viewer-video-icon { width: 100%; - height: calc(100% - 60px); + height: calc(100% - 80px); } - .ucap-video-viewer-video-time { - width: 100%; - height: 30px; - } - .ucap-video-viewer-video-controls { - width: 100%; - height: 30px; - - .ucap-video-viewer-video-time-current { - padding-left: 30px; + .viewer-bottom{ + background-color: #212121; + color:#ffffff; + .ucap-video-viewer-video-time { + width: 100%; + height: 30px; } + .ucap-video-viewer-video-controls { + width: 100%; + height: 50px; - .ucap-video-viewer-video-time-total { - padding-right: 30px; + .ucap-video-viewer-video-time-current { + padding-left: 30px; + } + + .ucap-video-viewer-video-time-total { + padding-right: 30px; + } } } } @@ -45,9 +62,22 @@ flex: 1 1 auto; } .ucap-video-viewer-action { + .mat-icon{ + font-size: 40px; + width: 100%; + height: 100%; + line-height:40px; + } } } mat-slider { - width: 95%; + width: 94%; } + +::ng-deep .mat-slider-horizontal .mat-slider-track-background{ + background-color: #999999 !important; +} +::ng-deep .mat-slider-min-value:not(.mat-slider-thumb-label-showing) .mat-slider-thumb{ + border-color: #999999 !important; +} \ No newline at end of file diff --git a/projects/ucap-webmessenger-ui/src/lib/components/file-viewer/video-viewer.component.ts b/projects/ucap-webmessenger-ui/src/lib/components/file-viewer/video-viewer.component.ts index 2259508..9eed043 100644 --- a/projects/ucap-webmessenger-ui/src/lib/components/file-viewer/video-viewer.component.ts +++ b/projects/ucap-webmessenger-ui/src/lib/components/file-viewer/video-viewer.component.ts @@ -5,17 +5,18 @@ import { Output, EventEmitter, ViewChild, - ElementRef + ElementRef, } from '@angular/core'; import { ucapAnimations } from '../../animations'; import { FileEventJson } from '@ucap-webmessenger/protocol-event'; import { MatSlider, MatSliderChange } from '@angular/material'; +import { FileDownloadItem } from '@ucap-webmessenger/api-common'; @Component({ selector: 'ucap-video-viewer', templateUrl: './video-viewer.component.html', styleUrls: ['./video-viewer.component.scss'], - animations: ucapAnimations + animations: ucapAnimations, }) export class VideoViewerComponent implements OnInit { @Input() @@ -25,7 +26,7 @@ export class VideoViewerComponent implements OnInit { fileDownloadUrl: string; @Output() - download = new EventEmitter(); + download = new EventEmitter(); @Output() closed = new EventEmitter(); @@ -41,6 +42,7 @@ export class VideoViewerComponent implements OnInit { currentTime = 0; volume = 0.1; loading = false; + fileDownloadItem: FileDownloadItem; constructor() {} @@ -90,7 +92,8 @@ export class VideoViewerComponent implements OnInit { } onClickDownload(): void { - this.download.emit(); + this.fileDownloadItem = new FileDownloadItem(); + this.download.emit(this.fileDownloadItem); } onClickClose(): void { diff --git a/projects/ucap-webmessenger-ui/src/lib/components/float-action-button.component.html b/projects/ucap-webmessenger-ui/src/lib/components/float-action-button.component.html index 9811169..9d812f2 100644 --- a/projects/ucap-webmessenger-ui/src/lib/components/float-action-button.component.html +++ b/projects/ucap-webmessenger-ui/src/lib/components/float-action-button.component.html @@ -3,7 +3,7 @@ *ngIf="fabTogglerState === 'active'" (click)="onToggleFab()" >
    -
    +
    diff --git a/projects/ucap-webmessenger-ui/src/lib/components/float-action-button.component.scss b/projects/ucap-webmessenger-ui/src/lib/components/float-action-button.component.scss index 164c51e..068e4aa 100644 --- a/projects/ucap-webmessenger-ui/src/lib/components/float-action-button.component.scss +++ b/projects/ucap-webmessenger-ui/src/lib/components/float-action-button.component.scss @@ -1,7 +1,7 @@ .fab-container { position: absolute; bottom: 15px; - right: 15px; + right: 40px; z-index: 100; display: flex; flex-direction: column-reverse; diff --git a/projects/ucap-webmessenger-ui/src/lib/components/sticker-selector.component.html b/projects/ucap-webmessenger-ui/src/lib/components/sticker-selector.component.html new file mode 100644 index 0000000..604c287 --- /dev/null +++ b/projects/ucap-webmessenger-ui/src/lib/components/sticker-selector.component.html @@ -0,0 +1,39 @@ +
    + +
    +
    + + + + + +
    +
    + +
    +
    +
    +
    +
    diff --git a/projects/ucap-webmessenger-ui/src/lib/components/sticker-selector.component.scss b/projects/ucap-webmessenger-ui/src/lib/components/sticker-selector.component.scss new file mode 100644 index 0000000..b85f3e7 --- /dev/null +++ b/projects/ucap-webmessenger-ui/src/lib/components/sticker-selector.component.scss @@ -0,0 +1,8 @@ +.sticker-selector { + height: 200px; + overflow: auto; +} + +.sticker-item { + cursor: pointer; +} diff --git a/projects/ucap-webmessenger-ui/src/lib/components/sticker-selector.component.spec.ts b/projects/ucap-webmessenger-ui/src/lib/components/sticker-selector.component.spec.ts new file mode 100644 index 0000000..a5b654b --- /dev/null +++ b/projects/ucap-webmessenger-ui/src/lib/components/sticker-selector.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { StickerSelectorComponent } from './sticker-selector.component'; + +describe('StickerSelectorComponent', () => { + let component: StickerSelectorComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ StickerSelectorComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(StickerSelectorComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/projects/ucap-webmessenger-ui/src/lib/components/sticker-selector.component.ts b/projects/ucap-webmessenger-ui/src/lib/components/sticker-selector.component.ts new file mode 100644 index 0000000..2f3ce9f --- /dev/null +++ b/projects/ucap-webmessenger-ui/src/lib/components/sticker-selector.component.ts @@ -0,0 +1,91 @@ +import { Component, OnInit, Output, EventEmitter } from '@angular/core'; +import { + StickerInfo, + StickerFilesInfo, + StickerUtil, + StickerMap +} from '@ucap-webmessenger/core'; +import { LocalStorageService } from '@ucap-webmessenger/web-storage'; +import { KEY_STICKER_HISTORY } from '@app/types'; + +@Component({ + selector: 'ucap-sticker-selector', + templateUrl: './sticker-selector.component.html', + styleUrls: ['./sticker-selector.component.scss'] +}) +export class StickerSelectorComponent implements OnInit { + @Output() + selectedSticker = new EventEmitter(); + + stickerBasePath = 'assets/sticker/'; + stickerInfoList: StickerInfo[] = []; + stickerFileInfoList: StickerFilesInfo[] = []; + currentSticker: StickerFilesInfo; + + currentTabIndex: number; + + constructor(private localStorageService: LocalStorageService) {} + + ngOnInit() { + this.stickerInfoList = StickerUtil.getStickerInfoList(); + this.currentTabIndex = 0; + } + + onSelectedIndexChange(value: number) { + this.currentTabIndex = value; + } + + getStickerTitleImage( + stickerInfo: StickerInfo, + hover: boolean, + tabIndex: number + ) { + if (this.currentTabIndex === tabIndex) { + return this.stickerBasePath + stickerInfo.iconPathOn; + } + + if (!hover) { + return this.stickerBasePath + stickerInfo.iconPath; + } else { + return this.stickerBasePath + stickerInfo.iconPathOn; + } + } + + getStickerInfos(stickerInfo: StickerInfo) { + if (stickerInfo.index === '00') { + const rtnArray: StickerFilesInfo[] = []; + const history = this.localStorageService.get( + KEY_STICKER_HISTORY + ); + if (!!history && history.length > 0) { + history.forEach(sticker => { + const arr: string[] = sticker.split('_'); + if (arr.length === 2) { + const sInfo: StickerInfo[] = StickerMap.filter( + s => s.index === arr[0] + ); + if (!!sInfo && sInfo.length > 0) { + rtnArray.push( + ...sInfo[0].fileInfos.filter( + fileInfo => fileInfo.index === sticker + ) + ); + } + } + }); + } + return rtnArray; + } else { + return stickerInfo.fileInfos; + } + } + + getStickerContentsImage(sticker: StickerFilesInfo) { + return this.stickerBasePath + sticker.path; + } + + onClickSelectSticker(sticker: StickerFilesInfo) { + this.currentSticker = sticker; + this.selectedSticker.emit(sticker); + } +} diff --git a/projects/ucap-webmessenger-ui/src/lib/data-source/virtual-scroll-tree-flat.data-source.ts b/projects/ucap-webmessenger-ui/src/lib/data-source/virtual-scroll-tree-flat.data-source.ts new file mode 100644 index 0000000..d73a138 --- /dev/null +++ b/projects/ucap-webmessenger-ui/src/lib/data-source/virtual-scroll-tree-flat.data-source.ts @@ -0,0 +1,132 @@ +import { CollectionViewer, DataSource } from '@angular/cdk/collections'; +import { FlatTreeControl } from '@angular/cdk/tree'; +import { + BehaviorSubject, + merge, + Observable, + Subject, + Subscription +} from 'rxjs'; +import { map, share } from 'rxjs/operators'; +import { MatTreeFlattener } from '@angular/material'; +import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling'; + +export class VirtualScrollTreeFlatDataSource extends DataSource { + private flattenedDataSubject = new BehaviorSubject([]); + + expandedDataSubject = new BehaviorSubject([]); + expandedData$: Observable; + + private connectSubject: Subject; + private dataChangeSubscription: Subscription; + private rangeChangeSubscription: Subscription; + + private rangeSubject: BehaviorSubject<{ + start: number; + end: number; + }>; + + // tslint:disable-next-line: variable-name + private _cdkVirtualScrollViewport: CdkVirtualScrollViewport; + set cdkVirtualScrollViewport( + cdkVirtualScrollViewport: CdkVirtualScrollViewport + ) { + if (!cdkVirtualScrollViewport) { + return; + } + this._cdkVirtualScrollViewport = cdkVirtualScrollViewport; + this.rangeSubject = new BehaviorSubject<{ start: number; end: number }>({ + start: 0, + end: 1 + }); + + this.rangeChangeSubscription = cdkVirtualScrollViewport.renderedRangeStream.subscribe( + range => { + this.rangeSubject.next({ + start: range.start, + end: range.end + }); + if (!!this.connectSubject) { + this.connectSubject.next( + this.expandedDataSubject.value.slice( + this.rangeSubject.value.start, + this.rangeSubject.value.end + ) + ); + } + } + ); + } + + // tslint:disable-next-line: variable-name + private _data: BehaviorSubject; + get data() { + return this._data.value; + } + set data(value: T[]) { + this._data.next(value); + this.flattenedDataSubject.next(this.treeFlattener.flattenNodes(this.data)); + this.treeControl.dataNodes = this.flattenedDataSubject.value; + } + + constructor( + private treeControl: FlatTreeControl, + private treeFlattener: MatTreeFlattener, + initialData: T[] = [] + ) { + super(); + this._data = new BehaviorSubject(initialData); + this.expandedData$ = this.expandedDataSubject.asObservable().pipe(share()); + } + + connect(collectionViewer: CollectionViewer): Observable { + this.connectSubject = new Subject(); + + this.dataChangeSubscription = merge( + collectionViewer.viewChange, + this.treeControl.expansionModel.changed, + this.flattenedDataSubject + ) + .pipe( + map(() => { + this.expandedDataSubject.next( + this.treeFlattener.expandFlattenedNodes( + this.flattenedDataSubject.value, + this.treeControl + ) + ); + + return !this.rangeSubject + ? this.expandedDataSubject.value + : this.expandedDataSubject.value.slice( + this.rangeSubject.value.start, + this.rangeSubject.value.end + ); + }) + ) + .subscribe(datas => { + this.connectSubject.next(datas); + if (!!this._cdkVirtualScrollViewport) { + this._cdkVirtualScrollViewport.checkViewportSize(); + } + }); + + return this.connectSubject.asObservable(); + } + + disconnect() { + console.log('VirtualScrollTreeFlatDataSource disconnect'); + if (!!this.connectSubject) { + this.connectSubject.next(); + this.connectSubject.unsubscribe(); + } + + if (!!this.dataChangeSubscription) { + this.dataChangeSubscription.unsubscribe(); + } + + if (!!this.rangeChangeSubscription) { + this.rangeChangeSubscription.unsubscribe(); + } + } +} diff --git a/projects/ucap-webmessenger-ui/src/lib/dialogs/alert.dialog.component.html b/projects/ucap-webmessenger-ui/src/lib/dialogs/alert.dialog.component.html index 0ab17f2..9973e3b 100644 --- a/projects/ucap-webmessenger-ui/src/lib/dialogs/alert.dialog.component.html +++ b/projects/ucap-webmessenger-ui/src/lib/dialogs/alert.dialog.component.html @@ -9,7 +9,7 @@
    - diff --git a/projects/ucap-webmessenger-ui/src/lib/directives/cdk-virtual-scroll-viewport-patch.directive.ts b/projects/ucap-webmessenger-ui/src/lib/directives/cdk-virtual-scroll-viewport-patch.directive.ts new file mode 100644 index 0000000..bc45b33 --- /dev/null +++ b/projects/ucap-webmessenger-ui/src/lib/directives/cdk-virtual-scroll-viewport-patch.directive.ts @@ -0,0 +1,30 @@ +import { Directive, OnInit, OnDestroy, Self, Inject } from '@angular/core'; +import { Subject, fromEvent } from 'rxjs'; +import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling'; +import { debounceTime, takeUntil } from 'rxjs/operators'; + +@Directive({ + // tslint:disable-next-line: directive-selector + selector: 'cdk-virtual-scroll-viewport' +}) +export class CdkVirtualScrollViewportPatchDirective + implements OnInit, OnDestroy { + protected readonly destroySubject = new Subject(); + + constructor( + @Self() + @Inject(CdkVirtualScrollViewport) + private readonly viewportComponent: CdkVirtualScrollViewport + ) {} + + ngOnInit() { + fromEvent(window, 'resize') + .pipe(debounceTime(10), takeUntil(this.destroySubject)) + .subscribe(() => this.viewportComponent.checkViewportSize()); + } + + ngOnDestroy() { + this.destroySubject.next(); + this.destroySubject.complete(); + } +} diff --git a/projects/ucap-webmessenger-ui/src/lib/directives/file-upload-for.directive.ts b/projects/ucap-webmessenger-ui/src/lib/directives/file-upload-for.directive.ts index df56bee..072f145 100644 --- a/projects/ucap-webmessenger-ui/src/lib/directives/file-upload-for.directive.ts +++ b/projects/ucap-webmessenger-ui/src/lib/directives/file-upload-for.directive.ts @@ -15,6 +15,12 @@ import { CommonApiService } from '@ucap-webmessenger/api-common'; import { FileUtil } from '@ucap-webmessenger/core'; +import { DialogService } from '../services/dialog.service'; +import { + AlertDialogComponent, + AlertDialogResult, + AlertDialogData +} from '../dialogs/alert.dialog.component'; @Directive({ selector: 'input[ucapFileUploadFor], div[ucapFileUploadFor]' @@ -40,7 +46,8 @@ export class FileUploadForDirective implements AfterViewInit { constructor( private commonApiService: CommonApiService, private elementRef: ElementRef, - private logger: NGXLogger + private logger: NGXLogger, + private dialogService: DialogService ) {} ngAfterViewInit(): void {} @@ -66,11 +73,11 @@ export class FileUploadForDirective implements AfterViewInit { return; } - if (this.fileUploadQueue.isEventInElement(event)) { - event.dataTransfer.dropEffect = 'copy'; - } else { - event.dataTransfer.dropEffect = 'none'; - } + // if (this.fileUploadQueue.isEventInElement(event)) { + // event.dataTransfer.dropEffect = 'copy'; + // } else { + // event.dataTransfer.dropEffect = 'none'; + // } event.preventDefault(); } @@ -103,17 +110,32 @@ export class FileUploadForDirective implements AfterViewInit { } const files: FileList = event.dataTransfer.files; - if ( - !this.commonApiService.acceptableExtensionForFileTalk( - FileUtil.getExtensions(files) - ) - ) { + const checkExt = this.commonApiService.acceptableExtensionForFileTalk( + FileUtil.getExtensions(files) + ); + if (!checkExt.accept) { this.logger.debug('window:drop not acceptable'); if (!!this.fileUploadQueue) { this.fileUploadQueue.onDragLeave(); } this.elementRef.nativeElement.value = ''; this.dragOver = false; + + this.dialogService.open< + AlertDialogComponent, + AlertDialogData, + AlertDialogResult + >(AlertDialogComponent, { + data: { + title: 'Alert', + html: `지원하지 않는 파일형식입니다.${ + checkExt.reject.length > 0 + ? '
    (' + checkExt.reject.join(',') + ')' + : '' + }` + } + }); + return; } diff --git a/projects/ucap-webmessenger-ui/src/lib/pipes/dates.pipe.spec.ts b/projects/ucap-webmessenger-ui/src/lib/pipes/dates.pipe.spec.ts deleted file mode 100644 index 8104bdc..0000000 --- a/projects/ucap-webmessenger-ui/src/lib/pipes/dates.pipe.spec.ts +++ /dev/null @@ -1,11 +0,0 @@ -/* tslint:disable:no-unused-variable */ - -import { TestBed, async } from '@angular/core/testing'; -import { DatesPipe } from './dates.pipe'; - -describe('Pipe: Datese', () => { - it('create an instance', () => { - let pipe = new DatesPipe(); - expect(pipe).toBeTruthy(); - }); -}); diff --git a/projects/ucap-webmessenger-ui/src/lib/pipes/dates.pipe.ts b/projects/ucap-webmessenger-ui/src/lib/pipes/dates.pipe.ts index ba604ac..cf90efd 100644 --- a/projects/ucap-webmessenger-ui/src/lib/pipes/dates.pipe.ts +++ b/projects/ucap-webmessenger-ui/src/lib/pipes/dates.pipe.ts @@ -1,8 +1,9 @@ import { Pipe, PipeTransform } from '@angular/core'; import { StringUtil } from '../utils/string.util'; +import moment from 'moment'; @Pipe({ - name: 'dateToStringChatList' + name: 'dateToStringChatList', }) export class DateToStringForChatRoomListPipe implements PipeTransform { transform(value: any): string { @@ -10,7 +11,7 @@ export class DateToStringForChatRoomListPipe implements PipeTransform { const yesterDate = new Date(curDate.getTime() - 1 * 24 * 60 * 60 * 1000); let date: Date; if (typeof value === 'string') { - date = new Date(value.toString()); + date = moment(value.toString()).toDate(); } else if (value instanceof Date) { date = value; } else { @@ -36,3 +37,18 @@ export class DateToStringForChatRoomListPipe implements PipeTransform { } } } + +@Pipe({ + name: 'dateToStringFormat', +}) +export class DateToStringFormatPipe implements PipeTransform { + transform(value: any, format?: string): string { + const date = moment(value.toString()).toDate(); + + if (!!format) { + return StringUtil.dateFormat(date, format); + } else { + return StringUtil.dateFormat(date, 'YYYY.MM.DD'); + } + } +} diff --git a/projects/ucap-webmessenger-ui/src/lib/pipes/linefeed.pipe.spec.ts b/projects/ucap-webmessenger-ui/src/lib/pipes/linefeed.pipe.spec.ts deleted file mode 100644 index 69dabff..0000000 --- a/projects/ucap-webmessenger-ui/src/lib/pipes/linefeed.pipe.spec.ts +++ /dev/null @@ -1,11 +0,0 @@ -/* tslint:disable:no-unused-variable */ - -import { TestBed, async } from '@angular/core/testing'; -import { LinefeedPipe } from './linefeed.pipe'; - -describe('Pipe: Linefeede', () => { - it('create an instance', () => { - let pipe = new LinefeedPipe(); - expect(pipe).toBeTruthy(); - }); -}); diff --git a/projects/ucap-webmessenger-ui/src/lib/pipes/linky.pipe.ts b/projects/ucap-webmessenger-ui/src/lib/pipes/linky.pipe.ts new file mode 100644 index 0000000..02c9421 --- /dev/null +++ b/projects/ucap-webmessenger-ui/src/lib/pipes/linky.pipe.ts @@ -0,0 +1,22 @@ +import { Pipe, PipeTransform } from '@angular/core'; +import { Autolinker, AutolinkerConfig } from 'autolinker'; + +@Pipe({ name: 'linky' }) +export class LinkyPipe implements PipeTransform { + transform(value: string, options?: AutolinkerConfig): string { + options = { + email: false, + phone: false, + stripPrefix: false, + // newWindow: false, + replaceFn: match => { + if (match.getType() === 'url') { + return '' + match.getAnchorHref() + ''; + // return true; + } + }, + ...options + }; + return Autolinker.link(value, options); + } +} diff --git a/projects/ucap-webmessenger-ui/src/lib/services/splash-screen.service.spec.ts b/projects/ucap-webmessenger-ui/src/lib/services/splash-screen.service.spec.ts new file mode 100644 index 0000000..97fb5eb --- /dev/null +++ b/projects/ucap-webmessenger-ui/src/lib/services/splash-screen.service.spec.ts @@ -0,0 +1,12 @@ +import { TestBed } from '@angular/core/testing'; + +import { BottomSheetService } from './bottom-sheet.service'; + +describe('ui::BottomSheetService', () => { + beforeEach(() => TestBed.configureTestingModule({})); + + it('should be created', () => { + const service: BottomSheetService = TestBed.get(BottomSheetService); + expect(service).toBeTruthy(); + }); +}); diff --git a/projects/ucap-webmessenger-ui/src/lib/services/splash-screen.service.ts b/projects/ucap-webmessenger-ui/src/lib/services/splash-screen.service.ts new file mode 100644 index 0000000..70307ef --- /dev/null +++ b/projects/ucap-webmessenger-ui/src/lib/services/splash-screen.service.ts @@ -0,0 +1,88 @@ +import { Injectable, Inject } from '@angular/core'; +import { + animate, + AnimationBuilder, + AnimationPlayer, + style +} from '@angular/animations'; +import { DOCUMENT } from '@angular/common'; +import { Router, NavigationEnd } from '@angular/router'; +import { filter, take } from 'rxjs/operators'; + +@Injectable({ + providedIn: 'root' +}) +export class SplashScreenService { + splashScreenEl: any; + player: AnimationPlayer; + + public constructor( + private animationBuilder: AnimationBuilder, + @Inject(DOCUMENT) private document: Document, + private router: Router + ) { + this.init(); + } + + private init(): void { + // Get the splash screen element + this.splashScreenEl = this.document.body.querySelector('#splash-screen'); + + // If the splash screen element exists... + if (this.splashScreenEl) { + // Hide it on the first NavigationEnd event + this.router.events + .pipe( + filter(event => event instanceof NavigationEnd), + take(1) + ) + .subscribe(() => { + setTimeout(() => { + this.hide(); + }); + }); + } + } + + /** + * Show the splash screen + */ + show(): void { + this.player = this.animationBuilder + .build([ + style({ + opacity: '0', + zIndex: '99999' + }), + animate('400ms ease', style({ opacity: '1' })) + ]) + .create(this.splashScreenEl); + + setTimeout(() => { + this.player.play(); + }, 0); + } + + /** + * Hide the splash screen + */ + hide(): void { + this.player = this.animationBuilder + .build([ + style({ opacity: '1' }), + animate( + '700ms ease', + style({ + opacity: '0', + display: 'none', + zIndex: '-10' + }) + ) + ]) + .create(this.splashScreenEl); + + setTimeout(() => { + this.player.play(); + }, 0); + } +} diff --git a/projects/ucap-webmessenger-ui/src/lib/ucap-ui.module.ts b/projects/ucap-webmessenger-ui/src/lib/ucap-ui.module.ts index 6fa1b1a..e8485cc 100644 --- a/projects/ucap-webmessenger-ui/src/lib/ucap-ui.module.ts +++ b/projects/ucap-webmessenger-ui/src/lib/ucap-ui.module.ts @@ -18,6 +18,7 @@ import { DragDropModule } from '@angular/cdk/drag-drop'; import { FileUploadQueueComponent } from './components/file-upload-queue.component'; import { FloatActionButtonComponent } from './components/float-action-button.component'; import { FileViewerComponent } from './components/file-viewer.component'; +import { ExpansionPanelComponent } from './components/expansion-panel.component'; import { BinaryViewerComponent } from './components/file-viewer/binary-viewer.component'; import { DocumentViewerComponent } from './components/file-viewer/document-viewer.component'; @@ -29,23 +30,33 @@ import { BottomSheetService } from './services/bottom-sheet.service'; import { ClipboardService } from './services/clipboard.service'; import { DialogService } from './services/dialog.service'; import { SnackBarService } from './services/snack-bar.service'; +import { SplashScreenService } from './services/splash-screen.service'; import { ClickOutsideDirective } from './directives/click-outside.directive'; import { FileUploadForDirective } from './directives/file-upload-for.directive'; import { ImageDirective } from './directives/image.directive'; +import { CdkVirtualScrollViewportPatchDirective } from './directives/cdk-virtual-scroll-viewport-patch.directive'; import { AlertDialogComponent } from './dialogs/alert.dialog.component'; import { ConfirmDialogComponent } from './dialogs/confirm.dialog.component'; import { BytesPipe } from './pipes/bytes.pipe'; import { LinefeedToHtmlPipe, HtmlToLinefeedPipe } from './pipes/linefeed.pipe'; -import { DateToStringForChatRoomListPipe } from './pipes/dates.pipe'; +import { + DateToStringForChatRoomListPipe, + DateToStringFormatPipe +} from './pipes/dates.pipe'; import { SecondsToMinutesPipe } from './pipes/seconds-to-minutes.pipe'; +import { LinkyPipe } from './pipes/linky.pipe'; +import { StickerSelectorComponent } from './components/sticker-selector.component'; +import { MatTabsModule } from '@angular/material'; const COMPONENTS = [ FileUploadQueueComponent, FloatActionButtonComponent, FileViewerComponent, + ExpansionPanelComponent, + StickerSelectorComponent, BinaryViewerComponent, DocumentViewerComponent, @@ -57,20 +68,24 @@ const DIALOGS = [AlertDialogComponent, ConfirmDialogComponent]; const DIRECTIVES = [ ClickOutsideDirective, FileUploadForDirective, - ImageDirective + ImageDirective, + CdkVirtualScrollViewportPatchDirective ]; const PIPES = [ BytesPipe, LinefeedToHtmlPipe, HtmlToLinefeedPipe, DateToStringForChatRoomListPipe, - SecondsToMinutesPipe + DateToStringFormatPipe, + SecondsToMinutesPipe, + LinkyPipe ]; const SERVICES = [ BottomSheetService, ClipboardService, DialogService, - SnackBarService + SnackBarService, + SplashScreenService ]; @NgModule({ @@ -86,6 +101,7 @@ const SERVICES = [ MatSnackBarModule, MatToolbarModule, MatTooltipModule, + MatTabsModule, DragDropModule ], exports: [...COMPONENTS, ...DIRECTIVES, ...PIPES], diff --git a/projects/ucap-webmessenger-ui/src/lib/utils/string.util.ts b/projects/ucap-webmessenger-ui/src/lib/utils/string.util.ts index 6318164..dceff78 100644 --- a/projects/ucap-webmessenger-ui/src/lib/utils/string.util.ts +++ b/projects/ucap-webmessenger-ui/src/lib/utils/string.util.ts @@ -2,7 +2,7 @@ import { EventType, EventJson, FileEventJson, - MassTextEventJson + MassTextEventJson, } from '@ucap-webmessenger/protocol-event'; import { FileType } from '@ucap-webmessenger/protocol-file'; @@ -79,7 +79,7 @@ export class StringUtil { '수요일', '목요일', '금요일', - '토요일' + '토요일', ]; const weekKorShortName = ['일', '월', '화', '수', '목', '금', '토']; @@ -91,56 +91,62 @@ export class StringUtil { 'Wednesday', 'Thursday', 'Friday', - 'Saturday' + 'Saturday', ]; const weekEngShortName = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']; - return f.replace(/(yyyy|yy|MM|dd|KS|KL|ES|EL|HH|hh|mm|ss|a\/p)/gi, $1 => { - switch ($1) { - case 'yyyy': - return date.getFullYear().toString(); // 년 (4자리) + return f.replace( + /(YYYY|yyyy|YY|yy|MM|DD|dd|KS|KL|ES|EL|HH|hh|mm|ss|a\/p)/gi, + $1 => { + switch ($1) { + case 'YYYY': + case 'yyyy': + return date.getFullYear().toString(); // 년 (4자리) - case 'yy': - return StringUtil.zeroFill(date.getFullYear() % 1000, 2); // 년 (2자리) + case 'YY': + case 'yy': + return StringUtil.zeroFill(date.getFullYear() % 1000, 2); // 년 (2자리) - case 'MM': - return StringUtil.zeroFill(date.getMonth() + 1, 2); // 월 (2자리) + case 'MM': + return StringUtil.zeroFill(date.getMonth() + 1, 2); // 월 (2자리) - case 'dd': - return StringUtil.zeroFill(date.getDate(), 2); // 일 (2자리) + case 'DD': + case 'dd': + return StringUtil.zeroFill(date.getDate(), 2); // 일 (2자리) - case 'KS': - return weekKorShortName[date.getDay()]; // 요일 (짧은 한글) + case 'KS': + return weekKorShortName[date.getDay()]; // 요일 (짧은 한글) - case 'KL': - return weekKorName[date.getDay()]; // 요일 (긴 한글) + case 'KL': + return weekKorName[date.getDay()]; // 요일 (긴 한글) - case 'ES': - return weekEngShortName[date.getDay()]; // 요일 (짧은 영어) + case 'ES': + return weekEngShortName[date.getDay()]; // 요일 (짧은 영어) - case 'EL': - return weekEngName[date.getDay()]; // 요일 (긴 영어) + case 'EL': + return weekEngName[date.getDay()]; // 요일 (긴 영어) - case 'HH': - return StringUtil.zeroFill(date.getHours(), 2); // 시간 (24시간 기준, 2자리) + case 'HH': + return StringUtil.zeroFill(date.getHours(), 2); // 시간 (24시간 기준, 2자리) - case 'hh': - return StringUtil.zeroFill(date.getHours() % 12, 2); // 시간 (12시간 기준, 2자리) + case 'hh': + return StringUtil.zeroFill(date.getHours() % 12, 2); // 시간 (12시간 기준, 2자리) - case 'mm': - return StringUtil.zeroFill(date.getMinutes(), 2); // 분 (2자리) + case 'mm': + return StringUtil.zeroFill(date.getMinutes(), 2); // 분 (2자리) - case 'ss': - return StringUtil.zeroFill(date.getSeconds(), 2); // 초 (2자리) + case 'ss': + return StringUtil.zeroFill(date.getSeconds(), 2); // 초 (2자리) - case 'a/p': - return date.getHours() < 12 ? '오전' : '오후'; // 오전/오후 구분 + case 'a/p': + return date.getHours() < 12 ? '오전' : '오후'; // 오전/오후 구분 - default: - return $1; + default: + return $1; + } } - }); + ); } public static convertFinalEventMessage( diff --git a/projects/ucap-webmessenger-ui/src/public-api.ts b/projects/ucap-webmessenger-ui/src/public-api.ts index db8b49e..6ffe333 100644 --- a/projects/ucap-webmessenger-ui/src/public-api.ts +++ b/projects/ucap-webmessenger-ui/src/public-api.ts @@ -13,6 +13,8 @@ export * from './lib/components/file-upload-queue.component'; export * from './lib/components/file-viewer.component'; export * from './lib/components/float-action-button.component'; +export * from './lib/data-source/virtual-scroll-tree-flat.data-source'; + export * from './lib/dialogs/alert.dialog.component'; export * from './lib/dialogs/confirm.dialog.component'; @@ -24,6 +26,7 @@ export * from './lib/services/bottom-sheet.service'; export * from './lib/services/clipboard.service'; export * from './lib/services/dialog.service'; export * from './lib/services/snack-bar.service'; +export * from './lib/services/splash-screen.service'; export * from './lib/types/file-viewer.type'; diff --git a/tsconfig.json b/tsconfig.json index 18fcd29..db8767b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -11,11 +11,10 @@ "module": "esnext", "moduleResolution": "node", "importHelpers": true, - "target": "es5", + "target": "es2015", "typeRoots": ["node_modules/@types"], "lib": ["es2018", "dom"], "paths": { - "@app/*": ["projects/ucap-webmessenger-app/src/app/*"], "@ucap-webmessenger/core": [ "projects/ucap-webmessenger-core/src/public-api" ], @@ -31,6 +30,9 @@ "@ucap-webmessenger/api-external": [ "projects/ucap-webmessenger-api-external/src/public-api" ], + "@ucap-webmessenger/api-message": [ + "projects/ucap-webmessenger-api-message/src/public-api" + ], "@ucap-webmessenger/pi": ["projects/ucap-webmessenger-pi/src/public-api"], "@ucap-webmessenger/ui": ["projects/ucap-webmessenger-ui/src/public-api"], "@ucap-webmessenger/ui-account": [ @@ -54,6 +56,9 @@ "@ucap-webmessenger/ui-profile": [ "projects/ucap-webmessenger-ui-profile/src/public-api" ], + "@ucap-webmessenger/ui-settings": [ + "projects/ucap-webmessenger-ui-settings/src/public-api" + ], "@ucap-webmessenger/protocol": [ "projects/ucap-webmessenger-protocol/src/public-api" ], @@ -126,11 +131,15 @@ "@ucap-webmessenger/util": [ "projects/ucap-webmessenger-util/src/public-api" ], + "@app/*": ["projects/ucap-webmessenger-app/src/app/*"], "@ucap-webmessenger/electron-core": [ - "projects/ucap-webmessenger-electron-core/src/public-api" + "electron-projects/ucap-webmessenger-electron-core/src/public-api" ], "@ucap-webmessenger/electron-notification": [ - "projects/ucap-webmessenger-electron-notification/src/public-api" + "electron-projects/ucap-webmessenger-electron-notification/src/public-api" + ], + "@ucap-webmessenger/electron": [ + "electron-projects/ucap-webmessenger-electron/src/public-api" ] } },