diff --git a/.gitignore b/.gitignore index 6704566..a7eda93 100644 --- a/.gitignore +++ b/.gitignore @@ -102,3 +102,10 @@ dist # TernJS port file .tern-port + +# Editor configuration files +.idea/ +.vscode/ + +# Mac OS +.DS_Store diff --git a/css/main.css b/css/main.css index ab66b10..6a31ba9 100644 --- a/css/main.css +++ b/css/main.css @@ -1,255 +1,334 @@ :root { - --navy-50: #f6f8fc; - --navy-100: #dee2f2; - --navy-200: #c9d0e6; - --navy-900: #212242; - --white: #ffffff; - --purple-300: #825eeb; - --green-300: #1fcca1; - --green-400: #00998c; - --red-300: #f24d6b; - --red-400: #d92148; - --mute-gray: rgba(255, 255, 255, 0.2); + --navy-50: #f6f8fc; + --navy-100: #dee2f2; + --navy-200: #c9d0e6; + --navy-400: #8A92BA; + --navy-600: #595e8a; + --navy-900: #212242; + --white: #ffffff; + --purple-300: #825eeb; + --purple-400: #6440C4; + --green-300: #1fcca1; + --green-400: #00998c; + --red-300: #f24d6b; + --red-400: #d92148; + --mute-gray: rgba(255, 255, 255, 0.2); +} + +input:-moz-read-only { /* For Firefox */ + color: var(--navy-600); +} + +input:read-only { + color: var(--navy-600); } body { - margin: 0 auto; + margin: 0 auto; + overflow: hidden; + font-family: Avenir Next; + font-size: 14px; + font-weight: normal; + font-stretch: normal; + font-style: normal; + line-height: 1.43; + color: var(--navy-900); + letter-spacing: normal; + text-rendering: optimizeLegibility; + -moz-osx-font-smoothing: grayscale; + -webkit-font-smoothing: antialiased; } .center { - align-items: center; - justify-content: center; + align-items: center; + justify-content: center; } .column { - display: flex; - flex-direction: column; + display: flex; + flex-direction: column; } .row { - display: flex; - flex-direction: row; + display: flex; + flex-direction: row; } .row-reverse { - display:flex; - flex-direction: row-reverse; + display:flex; + flex-direction: row-reverse; +} + +.margin-top-24 { + margin-top: 24px; +} + +.margin-right-24 { + margin-right: 24px; +} + +.font-weight-demi { + font-weight: 600; } .btn { - display: flex; - cursor: pointer; - align-items: center; - justify-content: center; - text-decoration: none; - border-radius: 4px; - font-family: Avenir Next; - font-size: 14px; - font-weight: normal; - font-stretch: normal; - font-style: normal; - line-height: 1.43; - letter-spacing: normal; - text-align: center; - height: 40px; + display: flex; + cursor: pointer; + align-items: center; + justify-content: center; + text-decoration: none; + border-radius: 4px; + text-align: center; + height: 40px; } .btn-primary { - background-color: var(--purple-300); - color: var(--white); + background-color: var(--purple-300); + color: var(--white); } .btn.btn-big { - width: 180px; - height: 50px; - margin-left: 16px; - margin-right: 16px; + width: 180px; + height: 50px; + margin-left: 16px; + margin-right: 16px; } .btn.btn-mid { - width: 80px; - height: 40px; - margin-bottom: 23px; - margin-left: 10px; + width: 80px; + height: 40px; + margin-bottom: 23px; + margin-left: 10px; } .btn-block { - width: 100%; + width: 100%; +} + +.title-choice-type { + width: 174px; + height: 20px; + letter-spacing: -0.1px; + color: var(--navy-600); +} + +.choice-item { + display: inline-block; + width: 260px; + height: 158px; + border-radius: 4px; + border: solid 1px #dee2f2; + background-color: var(--white); + cursor: pointer; +} + +.choice-item:hover { + border: solid 1px var(--purple-300); } +.title-choice-item { + margin: 24px 24px 8px 24px; + width: 88px; + height: 24px; + font-size: 18px; + font-weight: 500; + line-height: 1.33; + letter-spacing: -0.25px; + color: var(--navy-900); +} + +.desc-choice-item { + width: 212px; + height: 60px; + margin-left: 24px; + letter-spacing: -0.1px; + color: var(--navy-600); +} + + .title { - height: 32px; - font-family: Avenir Next; - font-size: 24px; - font-weight: normal; - font-stretch: normal; - font-style: normal; - line-height: 1.33; - letter-spacing: -0.25px; - color: inherit; + height: 32px; + font-size: 24px; + line-height: 1.33; + letter-spacing: -0.25px; + color: inherit; } .input-title { - display: inline-block; - margin-top: 6px; - height: 12px; - font-family: Avenir Next; - font-size: 12px; - font-weight: 500; - font-stretch: normal; - font-style: normal; - line-height: 1; - letter-spacing: normal; - color: var(--navy-900); - margin-bottom: 6px; + display: inline-block; + margin-top: 6px; + height: 12px; + font-size: 12px; + line-height: 1; + color: var(--navy-900); + margin-bottom: 6px; } .input-title:first-of-type { - margin-top: 38px; + margin-top: 38px; } .desc { - font-family: Avenir Next; - font-size: 14px; - font-weight: 500; - font-stretch: normal; - font-style: normal; - line-height: 1.43; - letter-spacing: -0.1px; - color: inherit; + letter-spacing: -0.1px; + color: var(--navy-600); } .desc-light { - font-family: Avenir Next; - font-size: 14px; - font-weight: normal; - font-stretch: normal; - font-style: normal; - line-height: 1.43; - letter-spacing: normal; - text-align: center; - color: var(--white); + width: 100%; + height: 20px; + text-align: center; + letter-spacing: -0.1px; + color: var(--navy-600); + margin-bottom: 24px; } .smile-face { - margin-top: 32px; - margin-bottom: 16px; - width: 40px; - height: 40px; - font-family: AppleColorEmoji; - font-size: 40px; - font-weight: normal; - font-stretch: normal; - font-style: normal; - line-height: 1; - letter-spacing: normal; - color: var(--white); + margin-top: 32px; + margin-bottom: 16px; + width: 40px; + height: 40px; + font-family: AppleColorEmoji; + font-size: 40px; + line-height: 1; + color: var(--white); } .wrapper { - display: flex; - width: 100vw; - height: 100vh; + display: flex; + width: 100vw; + height: 100vh; + overflow: hidden; } .container { - display: flex; - width: 100%; - height: 100%; + display: flex; + width: 100%; + height: 100%; } .backdrop { - -webkit-backdrop-filter: blur(2px); - backdrop-filter: blur(2px); + -webkit-backdrop-filter: blur(2px); + backdrop-filter: blur(2px); } .bg-white { - background-color: var(--white); - color: var(--navy-900); + background-color: var(--white); + color: var(--navy-900); } .bg-light { - background-color: var(--navy-50); - color: var(--navy-900); + background-color: var(--navy-50); + color: var(--navy-900); } .bg-dark { - background-color: var(--navy-900); - color: var(--white); + background-color: var(--navy-900); + color: var(--white); } .bg-white .desc { - color: #595e8a; + color: var(--navy-600); } .bg-light .desc { - color: #595e8a; + color: var(--navy-600); } .bg-dark .desc { - color: var(--white); + color: var(--white); } .hidden { - display: none; + display: none; } .invisible { - visibility: hidden; + visibility: hidden; } button label { - cursor: inherit; + cursor: inherit; } -.ic-logo-horizontal-purple-300 { - display: block; - width: 180px; - height: 40px; - background: url("../img/ic-logo-horizontal-purple-300.svg"); - margin-bottom: 24px; +.ic-logo-horizontal { + display: block; + width: 180px; + height: 40px; + background: url("../img/ic-logo-horizontal.svg"); + margin-bottom: 24px; } .voice-sample-page { - height: 32px; - margin-bottom: 20px; - text-align: center; + height: 32px; + margin-bottom: 16px; + text-align: center; } .voice-sample-desc { - width: 280px; - height: 40px; - margin-bottom: 28px; - text-align: center; + width: 308px; + height: 40px; + margin-bottom: 28px; + text-align: center; } .ic-arrow-down-right-24 { - display: block; - width: 32px; - height: 32px; - object-fit: contain; + display: block; + width: 32px; + height: 32px; + object-fit: contain; } .widget-tooltip { - position: absolute; - right: 24px; - bottom: 88px; - width: 296px; - height: 124px; - border-radius: 4px; + position: absolute; + right: 18px; + bottom: 112px; + width: 296px; + height: 108px; + border-radius: 4px; + box-shadow: 0 3px 5px -3px rgba(33, 34, 66, 0.04), 0 3px 14px 2px rgba(33, 34, 66, 0.08), 0 8px 10px 1px rgba(33, 34, 66, 0.12); + border: solid 1px var(--navy-200); } .img-tooltip-tail-down { - position: absolute; - right: 16px; - bottom: -8px; - width: 12px; - height: 8px; - object-fit: contain; + position: absolute; + right: 16px; + bottom: -8px; + width: 0; + height: 0; + border-left: 7px solid transparent; + border-right: 7px solid transparent; + border-top: 8px solid white; } .widget { - position: absolute; - right: 16px; - bottom: 16px; + position: absolute; + right: 16px; + bottom: 16px; +} + +::-webkit-scrollbar { + width: 5px; /* 세로축 스크롤바 길이 */ +} +::-webkit-scrollbar-track { + background-color: transparent; +} +::-webkit-scrollbar-track-piece { + background-color: transparent; +} +::-webkit-scrollbar-thumb { + border-radius: 8px; + background-color: gray; +} + +::-webkit-input-placeholder { /* Chrome/Opera/Safari */ + color: var(--navy-400); +} +::-moz-placeholder { /* Firefox 19+ */ + color: var(--navy-400); +} +:-ms-input-placeholder { /* IE 10+ */ + color: var(--navy-400); +} +:-moz-placeholder { /* Firefox 18- */ + color: var(--navy-400); } diff --git a/img/ic-logo-horizontal-purple-300.svg b/img/ic-logo-horizontal-purple-300.svg deleted file mode 100644 index 78d907c..0000000 --- a/img/ic-logo-horizontal-purple-300.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/img/ic-logo-horizontal.svg b/img/ic-logo-horizontal.svg new file mode 100644 index 0000000..1c3c7d4 --- /dev/null +++ b/img/ic-logo-horizontal.svg @@ -0,0 +1,3 @@ + + + diff --git a/js/main.js b/js/main.js index b4e8c80..1394175 100644 --- a/js/main.js +++ b/js/main.js @@ -3,6 +3,7 @@ import MainApp from "../lib/components/MainApp"; import DialView from "../lib/views/DialView"; import CallView from "../lib/views/CallView"; import LoginView from "../lib/views/LoginView"; +import CallLogView from "../lib/views/CallLogView"; import { ACCESS_TOKEN, IS_ACCESS_TOKEN_NEEDED, TEST_APP_ID, USER_ID } from "../envs"; function onLoadedHandler() { @@ -12,7 +13,8 @@ function onLoadedHandler() { 'index': LoginView, 'login_view': LoginView, 'dial_view': DialView, - 'call_view': CallView + 'call_view': CallView, + 'calllog_view': CallLogView }, styles: {}, args: { diff --git a/js/widget.js b/js/widget.js index fcfc1c7..3773281 100644 --- a/js/widget.js +++ b/js/widget.js @@ -2,6 +2,7 @@ import "../css/main.css"; import LoginView from "../lib/views/LoginView"; import CallView from "../lib/views/CallView"; import DialView from "../lib/views/DialView"; +import CallLogView from "../lib/views/CallLogView"; import { TEST_APP_ID, USER_ID, ACCESS_TOKEN, IS_ACCESS_TOKEN_NEEDED } from "../envs.js"; import WidgetApp from "../lib/components/WidgetApp"; @@ -13,7 +14,8 @@ function onLoadedHandler() { 'index': LoginView, 'login_view': LoginView, 'dial_view': DialView, - 'call_view': CallView + 'call_view': CallView, + 'calllog_view': CallLogView }, styles: { }, diff --git a/lib/assets/ic-call-filled-active.svg b/lib/assets/ic-call-filled-active.svg new file mode 100644 index 0000000..e875484 --- /dev/null +++ b/lib/assets/ic-call-filled-active.svg @@ -0,0 +1,3 @@ + + + diff --git a/lib/assets/ic-call-filled-deactive.svg b/lib/assets/ic-call-filled-deactive.svg new file mode 100644 index 0000000..115f2a7 --- /dev/null +++ b/lib/assets/ic-call-filled-deactive.svg @@ -0,0 +1,3 @@ + + + diff --git a/lib/assets/ic-call-filled-purple.svg b/lib/assets/ic-call-filled-purple.svg new file mode 100644 index 0000000..e875484 --- /dev/null +++ b/lib/assets/ic-call-filled-purple.svg @@ -0,0 +1,3 @@ + + + diff --git a/lib/assets/ic-call-white.svg b/lib/assets/ic-call-white.svg new file mode 100644 index 0000000..f35280c --- /dev/null +++ b/lib/assets/ic-call-white.svg @@ -0,0 +1,3 @@ + + + diff --git a/lib/assets/ic-copy.svg b/lib/assets/ic-copy.svg new file mode 100644 index 0000000..59a010d --- /dev/null +++ b/lib/assets/ic-copy.svg @@ -0,0 +1,3 @@ + + + diff --git a/lib/assets/ic-layout-default-active.svg b/lib/assets/ic-layout-default-active.svg new file mode 100644 index 0000000..78a47c9 --- /dev/null +++ b/lib/assets/ic-layout-default-active.svg @@ -0,0 +1,3 @@ + + + diff --git a/lib/assets/ic-layout-default-deactive.svg b/lib/assets/ic-layout-default-deactive.svg new file mode 100644 index 0000000..5f4b5bd --- /dev/null +++ b/lib/assets/ic-layout-default-deactive.svg @@ -0,0 +1,3 @@ + + + diff --git a/lib/assets/ic-layout-default.svg b/lib/assets/ic-layout-default.svg new file mode 100644 index 0000000..da2789d --- /dev/null +++ b/lib/assets/ic-layout-default.svg @@ -0,0 +1,3 @@ + + + diff --git a/lib/assets/ic-logo-black-01.svg b/lib/assets/ic-logo-black-01.svg index ddf7ac9..06a28d8 100644 --- a/lib/assets/ic-logo-black-01.svg +++ b/lib/assets/ic-logo-black-01.svg @@ -1,3 +1,3 @@ - - + + diff --git a/lib/assets/ic-logo-horizontal-inverse-01.svg b/lib/assets/ic-logo-horizontal-inverse-01.svg new file mode 100644 index 0000000..f574caf --- /dev/null +++ b/lib/assets/ic-logo-horizontal-inverse-01.svg @@ -0,0 +1,3 @@ + + + diff --git a/lib/assets/ic-logo-horizontal-purple-300.svg b/lib/assets/ic-logo-horizontal-purple-300.svg index 78d907c..1c3c7d4 100644 --- a/lib/assets/ic-logo-horizontal-purple-300.svg +++ b/lib/assets/ic-logo-horizontal-purple-300.svg @@ -1,3 +1,3 @@ - + diff --git a/lib/assets/ic-logo-horizontal.svg b/lib/assets/ic-logo-horizontal.svg new file mode 100644 index 0000000..1c3c7d4 --- /dev/null +++ b/lib/assets/ic-logo-horizontal.svg @@ -0,0 +1,3 @@ + + + diff --git a/lib/assets/ic-logo-inverse-01.svg b/lib/assets/ic-logo-inverse-01.svg index cf5a385..173f883 100644 --- a/lib/assets/ic-logo-inverse-01.svg +++ b/lib/assets/ic-logo-inverse-01.svg @@ -1,7 +1,3 @@ - - - - - + diff --git a/lib/assets/ic-video-thumbnail-filled.svg b/lib/assets/ic-video-thumbnail-filled.svg new file mode 100644 index 0000000..677fc23 --- /dev/null +++ b/lib/assets/ic-video-thumbnail-filled.svg @@ -0,0 +1,3 @@ + + + diff --git a/lib/assets/icon-avatar.jpg b/lib/assets/icon-avatar.jpg new file mode 100644 index 0000000..f30ff57 Binary files /dev/null and b/lib/assets/icon-avatar.jpg differ diff --git a/lib/assets/icon-call-video-incoming-filled.svg b/lib/assets/icon-call-video-incoming-filled.svg new file mode 100644 index 0000000..a408dd3 --- /dev/null +++ b/lib/assets/icon-call-video-incoming-filled.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/lib/assets/icon-call-video-outgoing-filled.svg b/lib/assets/icon-call-video-outgoing-filled.svg new file mode 100644 index 0000000..380a106 --- /dev/null +++ b/lib/assets/icon-call-video-outgoing-filled.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/lib/assets/icon-call-voice-incoming-filled.svg b/lib/assets/icon-call-voice-incoming-filled.svg new file mode 100644 index 0000000..f0cd9cc --- /dev/null +++ b/lib/assets/icon-call-voice-incoming-filled.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/lib/assets/icon-call-voice-outgoing-filled.svg b/lib/assets/icon-call-voice-outgoing-filled.svg new file mode 100644 index 0000000..9bb0429 --- /dev/null +++ b/lib/assets/icon-call-voice-outgoing-filled.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/lib/assets/icon-camera-off.svg b/lib/assets/icon-camera-off.svg new file mode 100644 index 0000000..28bd930 --- /dev/null +++ b/lib/assets/icon-camera-off.svg @@ -0,0 +1,3 @@ + + + diff --git a/lib/components/App.js b/lib/components/App.js index b05beac..7d140b7 100644 --- a/lib/components/App.js +++ b/lib/components/App.js @@ -30,7 +30,13 @@ export default class App extends BaseElement { route(pageName, opt = {}) { for (let i = this.children.length - 1 ; i >= 0 ; i--) { const child = this.children[i]; - child.remove(); + if(child.element.id === 'header' || child.element.id === 'tabtoolbar') { + if(opt.isRemoveHeader) { + child.remove(); + } + } else { + child.remove(); + } } const _pageName = this.pages[pageName] ? pageName : 'index'; @@ -38,7 +44,7 @@ export default class App extends BaseElement { const pageClass = this.pages[_pageName]; const view = new pageClass(opt); view.appendToBaseElement(this); - + if (this.onPageChange) this.onPageChange(_pageName); } } \ No newline at end of file diff --git a/lib/components/Header.js b/lib/components/Header.js new file mode 100644 index 0000000..90cbc9a --- /dev/null +++ b/lib/components/Header.js @@ -0,0 +1,99 @@ +import SendBirdCall from "sendbird-calls"; +import BaseElement from "./BaseElement"; +import { createDiv } from "../utils/domUtil"; +import Menu from "../components/Menu"; +import { sheet, classes } from "../css/styles"; + +export default class Header extends BaseElement { + constructor({ id, className, parent, element, args } = {}) { + super({ id, className, parent, element, args }); + this.element = element; + this.parent = parent; + + this.settingItems = [ + { + 'label': 'Device settings', + 'callback': () => { this.sendToParent('show_settings') } + }, + { + 'label': 'Application information', + 'callback': () => { this.sendToParent('show_app_info') } + }, + { + 'label': 'Sign out', + 'callback': () => { + SendBirdCall.deauthenticate(); + this.sendToParent('deauthenticate'); + } + } + ]; + + this.parent = parent; + } + + build() { + const userDiv = createDiv({ + id: 'header_user_div', + className: `${classes['userDiv']} ${classes['center']}` + }); + + let profileImg; + if (this.args.user && this.args.user.profileUrl) { + sheet.update({ profileUrl: this.args.user.profileUrl }); + profileImg = createDiv({ id: 'header_profile_img', className: classes['profileSmall'] }); + } else { + profileImg = createDiv({ id: 'header_avatar', className: `${classes['avatar']}` }); + } + + const headerInfo = createDiv({ id: 'header_info', className: `${classes['headerInfo']}` }); + const userId = createDiv({ + id: 'header_user_id', + className: `${classes['headerUserId']} ${classes['fontMidBig']} ${classes['fontDemi']}`, + innerText: this.args.user.userId || '' + }); + const nickname = createDiv({ + id: 'header_nickname', + className: `${classes['headerNickname']} ${classes['fontSmall']}`, + innerText: this.args.user.nickname || 'no nickname' + }); + headerInfo.appendChild(userId); + headerInfo.appendChild(nickname); + + userDiv.appendChild(profileImg); + userDiv.appendChild(headerInfo); + + const headerButtons = createDiv({ + id: 'header_buttons', + className: `${classes['headerButtons']} ${classes['row']} ${classes['center']}` + }); + const settingsButton = new Menu({ + id: 'settings_button', + element: createDiv({ className: `${classes['settingsButton']}` }), + items: this.settingItems + }); + + const closeButton = createDiv({ + id: 'close_button', + className: `${classes['closeButton']}` + }); + closeButton.onclick = () => { + this.parent.sendToParent('widgetclose'); + }; + settingsButton.appendToHTML(headerButtons); + headerButtons.appendChild(closeButton); + + const divider = createDiv({ + id: 'header_divider', + className: classes['headerDivider'] + }); + + this.element.appendChild(userDiv); + this.element.appendChild(divider); + this.element.appendChild(headerButtons); + + if(!this.args.isWidget){ + const headerLogo = createDiv({ id: 'header_logo', className: `${classes['headerLogo']}`}); + this.element.appendChild(headerLogo); + } + } +} \ No newline at end of file diff --git a/lib/components/MainApp.js b/lib/components/MainApp.js index cc31ae0..ec85415 100644 --- a/lib/components/MainApp.js +++ b/lib/components/MainApp.js @@ -5,6 +5,10 @@ import { classes } from "../css/styles.js"; import { getCallOption } from "../utils/util"; import { Toast } from "./Toast"; import Settings from "../views/Settings"; +import Header from "../components/Header"; +import TabToolBar from "../components/TabToolBar"; +import { createDiv } from "../utils/domUtil"; +import AppInfoView from "../views/AppInfoView"; export default class MainApp extends App { constructor({ id, className, pages, styles, args }) { @@ -13,7 +17,8 @@ export default class MainApp extends App { this.onLoginSuccess = null; this.onLoginFailure = null; - + this.settings = null; + this.appInfoView = null; this.init(); } @@ -44,6 +49,18 @@ export default class MainApp extends App { } } + showAppInfo() { + this.appInfoView = new AppInfoView({}); + this.appInfoView.appendToBaseElement(this); + } + + closeAppInfo() { + if (this.appInfoView) { + this.appInfoView.remove(); + this.appInfoView = null; + } + } + async setAppId(appId) { await this._setArgs({ appId: appId }); } @@ -97,6 +114,9 @@ export default class MainApp extends App { this.args.user = user; if (this.onLoginSuccess) this.onLoginSuccess(); + + this.createHeaderNTab(); + this.route('dial_view', {}); } catch (e) { if (this.onLoginFailure) this.onLoginFailure(e); @@ -137,7 +157,7 @@ export default class MainApp extends App { return undefined; } - this.route('call_view', { call: call, state: state }); + this.route('call_view', { call: call, state: state, isRemoveHeader: true }); } isBusy() { @@ -170,9 +190,21 @@ export default class MainApp extends App { case 'close_settings': this.recvCloseSettings(); break; + case 'show_app_info': + this.recvShowAppInfo(); + break; + case 'close_app_info': + this.recvCloseAppInfo(); + break; case 'close': this.recvClose(value); break; + case 'show_calllog': + this.route('calllog_view', {args: this.args}); + break; + case 'show_dial': + this.route('dial_view', {}); + break; default: break; } @@ -190,7 +222,7 @@ export default class MainApp extends App { this.args.user = undefined; this.args.userId = undefined; this.args.accessToken = undefined; - this.route('login_view', {}); + this.route('login_view', { isRemoveHeader: true }); } recvShowSettings() { @@ -201,7 +233,24 @@ export default class MainApp extends App { this.closeSettings(); } + recvShowAppInfo() { + this.showAppInfo(); + } + + recvCloseAppInfo() { + this.closeAppInfo(); + } + recvClose() { + this.createHeaderNTab(); this.route('dial_view', {}); } + + createHeaderNTab() { + const HeaderItem = new Header({parent: this, element: createDiv({ id: 'header', className: `${classes['widgetHeader']}`})}); + const tabToolBar = new TabToolBar({ parent: this, element: createDiv({id: 'tabtoolbar', className: `${classes['tabToolBar']}`}), args: this.args }); + + HeaderItem.appendToBaseElement(this); + tabToolBar.appendToBaseElement(this); + } } \ No newline at end of file diff --git a/lib/components/Menu.js b/lib/components/Menu.js index 347dafa..5a5deba 100644 --- a/lib/components/Menu.js +++ b/lib/components/Menu.js @@ -11,6 +11,14 @@ export default class Menu extends BaseElement { } build() { + this.element.addEventListener('mouseenter', function(){ + this.style.backgroundColor = '#6440c4'; + }); + + this.element.addEventListener('mouseout', function(){ + this.style.backgroundColor = ''; + }); + this.menuItems = createDiv({ className: `${classes['menuItems']} ${classes['hidden']}` }); this.items.forEach((item) => { diff --git a/lib/components/TabToolBar.js b/lib/components/TabToolBar.js new file mode 100644 index 0000000..d982f9e --- /dev/null +++ b/lib/components/TabToolBar.js @@ -0,0 +1,64 @@ +import BaseElement from "./BaseElement"; +import { createDiv, createParagraph, replaceClassName, hasClassName } from "../utils/domUtil"; +import { classes } from "../css/styles"; + +export default class TabToolBar extends BaseElement { + constructor({ id, className, parent, element, args } = {}) { + super({ id, className, parent, element }); + this.element = element; + } + + build() { + // dial tab + if(this.args.isWidget) { + this.element.classList.add(classes['tabToolBarWidget']); + } + const btnDial = createDiv({ id: 'btn_tab_dial', className: `${classes['btnTab']}` }); + const icoTabDial = createDiv({id: 'ico_tab_dial', className: `${classes['tabIco']} ${classes['dialActive']}`}); + const btnDialCaption = createParagraph({id: 'btn_dial_caption', innerText: 'Dial', className: `${classes['fontSmall']} ${classes['fontHeavy']} ${classes['btnTabCaption']} ${classes['btnTabActive']}`}); + + btnDial.appendChild(icoTabDial); + btnDial.appendChild(btnDialCaption); + + btnDial.onclick = (ev) => { + if(!hasClassName(icoTabDial, classes['dialActive'])) { + replaceClassName(icoTabDial, classes['dialDeactive'], classes['dialActive']); + replaceClassName(btnDialCaption, classes['btnTabDeactive'], classes['btnTabActive']); + + replaceClassName(icoCallLog, classes['callLogActive'], classes['callLogDeactive']); + replaceClassName(btnCalllogCaption, classes['btnTabActive'], classes['btnTabDeactive']); + this.sendToParent('show_dial'); + } + }; + + if(this.args.isWidget) { + btnDial.classList.add(classes['btnTabWidget']); + } + + const btnCallLog = createDiv({id: 'btn_tab_calllog', className: `${classes['btnTab']}`}); + const icoCallLog = createDiv({id: 'ico_tab_callog', className: `${classes['tabIco']} ${classes['callLogDeactive']}`}); + const btnCalllogCaption = createParagraph({id: 'btn_calllog_caption', innerText: 'History', className: `${classes['fontSmall']} ${classes['fontHeavy']} ${classes['btnTabCaption']} ${classes['btnTabDeactive']}`}); + + btnCallLog.appendChild(icoCallLog); + btnCallLog.appendChild(btnCalllogCaption); + + btnCallLog.onclick = (ev) => { + if(!hasClassName(icoCallLog, classes['callLogActive'])) { + replaceClassName(icoCallLog, classes['callLogDeactive'], classes['callLogActive']); + replaceClassName(btnCalllogCaption, classes['btnTabDeactive'], classes['btnTabActive']); + + replaceClassName(icoTabDial, classes['dialActive'], classes['dialDeactive']); + replaceClassName(btnDialCaption, classes['btnTabActive'], classes['btnTabDeactive']); + + this.sendToParent('show_calllog'); + } + }; + + if(this.args.isWidget) { + btnCallLog.classList.add(classes['btnTabWidget']); + } + + this.element.appendChild(btnDial); + this.element.appendChild(btnCallLog); + } +} \ No newline at end of file diff --git a/lib/components/WidgetApp.js b/lib/components/WidgetApp.js index 97b15ee..12070b0 100644 --- a/lib/components/WidgetApp.js +++ b/lib/components/WidgetApp.js @@ -63,6 +63,7 @@ export default class WidgetApp extends BaseElement { build() { if (!this.widgetIcon) this.widgetIcon = createDiv({ id: 'widget_icon', className: classes['widgetIcon'] }); + this.args.isWidget = true; this.mainApp = new MainApp({ className: `${classes['widgetDiv']} ${classes['hidden']}`, pages: this.pages, diff --git a/lib/css/styles.js b/lib/css/styles.js index 63ad272..7de439c 100644 --- a/lib/css/styles.js +++ b/lib/css/styles.js @@ -13,17 +13,30 @@ import videoWhiteIcon from '../assets/ic-video-thumbnail-white.svg'; import videoBlackIcon from '../assets/ic-video-thumbnail-black.svg'; import audioOffBlack from '../assets/ic-callkit-audio-off-black.svg'; import audioOffWhite from '../assets/ic-callkit-audio-off-white.svg'; +import cameraOff from '../assets/icon-camera-off.svg'; import declineWhiteIcon from '../assets/ic-callkit-decline.svg'; import declineBlackIcon from '../assets/ic-decline-black.svg'; import endIcon from '../assets/ic-callkit-end.svg'; import toastErrorIcon from '../assets/ic-error-20.svg'; import toastCloseBtn from '../assets/ic-close-20.svg'; -import widgetIcon from '../assets/ic-call-logs-filled.svg'; +import widgetIcon from '../assets/ic-call-white.svg'; import settingsIcon from '../assets/ic-settings.svg'; import settingsCloseIcon from '../assets/ic-close-black-20.svg'; import widgetCloseIcon from '../assets/ic-close-24.svg'; import arrowDownIcon from '../assets/ic-input-arrow-down.svg'; import avatarIcon from '../assets/icon-avatar.svg'; +import dialIconActive from '../assets/ic-call-filled-active.svg'; +import dialIconDeactive from '../assets/ic-call-filled-deactive.svg'; +import callhistoryIconDeactive from '../assets/ic-layout-default-deactive.svg'; +import callhistoryIconActive from '../assets/ic-layout-default-active.svg'; +import headerLogo from '../assets/ic-logo-horizontal-inverse-01.svg'; +import thumbnailVideo from '../assets/ic-video-thumbnail-filled.svg'; +import thumbnailVoice from '../assets/ic-call-filled-purple.svg'; +import callLogEmpty from '../assets/ic-layout-default.svg'; +import logoHorizon from '../assets/ic-logo-horizontal.svg'; +import icCopy from '../assets/ic-copy.svg'; + + const option = Object.assign( {}, @@ -39,15 +52,21 @@ jss.setup(option); const colors = { navy50: '#f6f8fc', + navy80: '#eef2fa', navy100: '#dee2f2', navy200: '#c9d0e6', navy300: '#b6bdd7', + navy400: '#8a92ba', + navy600: '#595e8a', navy800: '#353761', navy900: '#212242', white: '#ffffff', + purple50: '#ededff', purple300: '#825eeb', + purple400: '#6440c4', green300: '#1fcca1', green400: '#00998c', + green500: '#007a7a', red300: '#f24d6b', red400: '#d92148', mutegray: 'rgba(168, 168, 168, 0.38)' @@ -109,7 +128,8 @@ const styles = { height: '40px', '& label': { cursor: 'inherit' - } + }, + backgroundColor: colors.white }, btnPrimary: { @@ -137,10 +157,13 @@ const styles = { display: 'flex', width: '100%', height: '100%', + flexDirection: 'column', + justifyContent: 'flex-start', + alignItems: 'flex-start', }, hidden: { - display: 'none' + display: 'none !important' }, invisible: { @@ -160,8 +183,8 @@ const styles = { }, profileSmall: { - width: '32px', - height: '32px', + width: '40px', + height: '40px', borderRadius: '50%', marginLeft: '24px', marginRight: '16px', @@ -190,17 +213,28 @@ const styles = { marginBottom: '24px' }, + headerLogo: { + display: 'block', + width: '100%', + height: '24px', + backgroundImage: `url(${headerLogo})`, + backgroundRepeat: 'no-repeat', + marginLeft: '16px' + }, + /*** views ***/ view: { boxSizing: 'border-box', - width: '100vw', + // width: '100vw', + width: '100%', height: '100%', padding: '24px', display: 'relative' }, viewDial: { + height: 'calc(100% - 110px)', color: colors.navy900, '& $content': { marginTop: 'auto' @@ -225,6 +259,8 @@ const styles = { viewSettings: { position: 'absolute', + left: '0px', + top: '0px', width: '100%', height: '100%', zIndex: '100', @@ -233,7 +269,7 @@ const styles = { }, settingsCloseButton: { - marginLeft: 'auto', + display: 'inline-flex', width: '32px', height: '32px', cursor: 'pointer', @@ -256,14 +292,83 @@ const styles = { maxWidth: '480px', paddingTop: '16px', paddingBottom: '24px', - paddingLeft: '32px', - paddingRight: '32px', + paddingLeft: '24px', + paddingRight: '24px', zIndex: '1', boxShadow: '0 6px 10px -5px rgba(33, 34, 66, 0.04), 0 6px 30px 5px rgba(33, 34, 66, 0.08), 0 16px 24px 2px rgba(33, 34, 66, 0.12)', borderRadius: '4px', backgroundColor: colors.white }, + popupHeader: { + display: 'flex', + alignItems: 'center', + width: '100%' + }, + + popupTitle: { + display: 'inline-flex', + width: 'calc(100% - 20px)' + }, + + popupItemLabel: { + display: 'inline-block', + width: '100%', + height: '12px', + marginTop: '22px', + marginBottom: '6px', + lineHeight: '12px !important' + }, + + widgetpopup: { + width: '312px', + marginBottom: '2px' + }, + + appInfoLabelWrap: { + display: 'flex', + width: '100%', + height: '40px', + borderRadius: '4px', + alignItems: 'center', + justifyContent: 'space-between', + backgroundColor: colors.navy80, + }, + + appInfoLabel: { + display: 'inline-flex', + width: 'calc(100% - 60px)', + height: '40px', + borderRadius: '4px', + paddingLeft: '16px', + lineHeight: '40px !important', + textOverflow: 'clip', + overflow: 'hidden', + backgroundColor: colors.navy80, + color: colors.nany600, + }, + + appName: { + width: 'calc(100% - 16px) !important' + }, + + appInfoIdLabel: { + textOverflow: 'clip', + overflow: 'hidden', + whiteSpace: 'pre' + }, + + appInfoIdCopy: { + display: 'inline-flex', + width: '32px', + height: '32px', + borderRadius: '4px', + marginRight: '4px', + backgroundImage: `url(${icCopy})`, + backgroundRepeat: 'no-repeat', + backgroundPosition: 'center', + cursor: 'pointer' + }, /*** fonts ***/ fontSmall: { @@ -272,6 +377,7 @@ const styles = { fontWeight: 'normal', fontStretch: 'normal', fontStyle: 'normal', + color: colors.navy900, lineHeight: 'normal', letterSpacing: 'normal', textRendering: 'optimizelegibility', @@ -285,6 +391,7 @@ const styles = { fontWeight: 'normal', fontStretch: 'normal', fontStyle: 'normal', + color: colors.navy900, lineHeight: '1.43', letterSpacing: 'normal', textRendering: 'optimizelegibility', @@ -298,6 +405,7 @@ const styles = { fontWeight: 'normal', fontStretch: 'normal', fontStyle: 'normal', + color: colors.navy900, lineHeight: '1.33', letterSpacing: '-0.25px', textRendering: 'optimizelegibility', @@ -305,6 +413,20 @@ const styles = { '-moz-osx-font-smoothing': 'grayscale' }, + font16: { + fontFamily: 'Avenir Next', + fontSize: '16px', + fontWeight: 'normal', + fontStretch: 'normal', + fontStyle: 'normal', + color: colors.navy900, + lineHeight: '1.25', + letterSpacing: '-0.15px', + textRendering: 'optimizelegibility', + '-webkit-font-smoothing': 'antialiased', + '-moz-osx-font-smoothing': 'grayscale' + }, + font20: { fontFamily: 'Avenir Next', fontSize: '20px', @@ -317,6 +439,20 @@ const styles = { '-moz-osx-font-smoothing': 'grayscale' }, + font24: { + fontFamily: 'Avenir Next', + fontSize: '24px', + fontWeight: 'normal', + fontStretch: 'normal', + fontStyle: 'normal', + color: colors.navy900, + lineHeight: '1.33', + letterSpacing: '-0.25px', + textRendering: 'optimizelegibility', + '-webkit-font-smoothing': 'antialiased', + '-moz-osx-font-smoothing': 'grayscale' + }, + fontBig: { fontFamily: 'Avenir Next', height: '32px', @@ -339,6 +475,92 @@ const styles = { fontWeight: 600 }, + fontColorWhite: { + color: colors.white + }, + + fontReadOnlyColor: { + color: colors.navy600 + }, + + /*** tab ***/ + tabToolBar: { + position: 'relative', + display: 'inline-flex', + justifyContent: 'center', + width: '100%', + height: '55px', + backgroundColor: colors.white, + textAlign: 'center', + borderTop: 'solid 1px #dee2f2', + borderBottom: 'solid 1px #dee2f2' + }, + + tabToolBarWidget: { + position: 'absolute', + display: 'inline-block', + bottom: '0px', + left: '0px', + borderBottomLeftRadius: '8px', + borderBottomRightRadius: '8px' + }, + + btnTab: { + display: 'inline-block', + width: '100px', + height: '55px', + cursor: 'pointer' + }, + + btnTabWidget: { + width: '156px', + }, + + tabIco: { + width: '100%', + height: '32px', + backgroundRepeat: 'no-repeat', + backgroundPosition: 'center' + }, + + dialActive: { + backgroundImage: `url(${dialIconActive})` + }, + + dialDeactive: { + backgroundImage: `url(${dialIconDeactive})` + }, + + btnTabCaption: { + width: '100%', + height: '12px', + margin: 0, + lineHeight: 1, + textAlign: 'center' + }, + + icoTabCallLog: { + width: '100%', + height: '32px', + backgroundRepeat: 'no-repeat', + backgroundPosition: 'center', + }, + + callLogActive: { + backgroundImage: `url(${callhistoryIconActive})`, + }, + + callLogDeactive: { + backgroundImage: `url(${callhistoryIconDeactive})`, + }, + + btnTabActive: { + color: colors.purple300 + }, + + btnTabDeactive: { + color: colors.navy600 + }, /*** components ***/ formContainer: { @@ -365,6 +587,10 @@ const styles = { marginBottom: '16px' }, + dialField: { + width: '312px' + }, + fieldInvalid: { border: `solid 1px ${colors.red300}` }, @@ -379,6 +605,9 @@ const styles = { } }, + dialTitle: { + marginBottom: '16px' + }, /*** buttons ***/ loginButton: { @@ -415,14 +644,18 @@ const styles = { remoteProfile: ` display: block; - width: 120px; - height: 120px; + width: 80px; + height: 80px; border-radius: 50%; object-fit: contain; margin-bottom: 24px; `, peerName: ` + min-height: 32px; + height: auto; + text-align: center; + word-break: break-all; margin-bottom: 4px; `, @@ -449,21 +682,41 @@ const styles = { display: block; `, + peerVideoMuteIcon: ` + width: 40px; + height: 40px; + background-image: url(${cameraOff}); + background-repeat: no-repeat; + background-position: center; + margin-bottom: 16px; + `, + + peerVideoMuteLabel: ` + display: block; + `, + + callButtons: { position: 'relative' }, + callButtonsWidget: { + position: 'absolute', + bottom: '40px' + }, + btnCircle: { width: '64px', height: '64px', - border: 'none', borderRadius: '50%', + backgroundColor: colors.white, + borderColor: 'transparent', cursor: 'pointer', '&::before': { content: '', display: 'block', - width: '64px', - height: '64px', + width: '56px', + height: '56px', borderRadius: '50%' }, 'btn-circle:hover::before': { @@ -472,58 +725,79 @@ const styles = { }, btnCall: { - marginLeft: '16px', - marginRight: '16px', - marginBottom: '8px', + marginLeft: '10px', + marginRight: '10px' }, btnVideoAccept: { - backgroundColor: colors.green300, + backgroundColor: '#2eba9f', backgroundImage: `url(${videoIcon})`, backgroundRepeat: 'no-repeat', - backgroundPosition: 'center' + backgroundPosition: 'center', + '&:hover': { + backgroundColor: colors.green500 + } }, btnAccept: { - backgroundColor: colors.green300, + backgroundColor: '#2eba9f', backgroundImage: `url(${audioIcon})`, backgroundRepeat: 'no-repeat', - backgroundPosition: 'center' + backgroundPosition: 'center', + '&:hover': { + backgroundColor: colors.green500 + } }, btnMute: { backgroundColor: colors.mutegray, backgroundImage: `url(${audioOffWhite})`, backgroundRepeat: 'no-repeat', - backgroundPosition: 'center' + backgroundPosition: 'center', + '&:hover': { + backgroundColor: 'rgba(168, 168, 168, 0.5)' + } }, btnUnmute: { backgroundColor: colors.white, backgroundImage: `url(${audioOffBlack})`, backgroundRepeat: 'no-repeat', - backgroundPosition: 'center' + backgroundPosition: 'center', + '&:hover': { + backgroundColor: 'rgba(168, 168, 168, 0.5)' + } }, btnStopVideo: { backgroundColor: colors.mutegray, backgroundImage: `url(${videoWhiteIcon})`, backgroundRepeat: 'no-repeat', - backgroundPosition: 'center' + backgroundPosition: 'center', + '&:hover': { + backgroundColor: 'rgba(168, 168, 168, 0.5)' + } }, btnStartVideo: { backgroundColor: colors.white, backgroundImage: `url(${videoBlackIcon})`, backgroundRepeat: 'no-repeat', - backgroundPosition: 'center' + backgroundPosition: 'center', + '&:hover': { + backgroundColor: 'rgba(168, 168, 168, 0.5)' + } }, btnEnd: { - backgroundColor: colors.red300, + backgroundColor: '#e53157', backgroundImage: `url(${endIcon})`, backgroundRepeat: 'no-repeat', - backgroundPosition: 'center' + backgroundSize: '32px', + backgroundPosition: 'center', + '&:hover' : { + backgroundColor: '#a30e2d', + } }, videoView: { @@ -557,6 +831,8 @@ const styles = { left: '16px', width: '200px', height: '150px', + borderRadius: '8px', + backgroundColor: colors.navy300, transition: 'all 1s' }, @@ -573,22 +849,29 @@ const styles = { overflow: 'hidden' }, + callBackgroundWidget: { + borderRadius: '8px' + }, + callForeground: { position: 'relative' }, btnDecline: { - backgroundColor: colors.red300, + backgroundColor: 'rgba(0, 0, 0, 0.12)', backgroundImage: `url(${declineWhiteIcon})`, backgroundRepeat: 'no-repeat', - backgroundPosition: 'center' + backgroundPosition: 'center', + '&:hover' : { + backgroundColor: 'rgba(0, 0, 0, 0.38)', + } }, closeDiv: {}, btnClose: { width: '248px', - height: '64px', + height: '48px', border: 'none', borderRadius: '4px', marginBottom: '28px', @@ -606,7 +889,7 @@ const styles = { opacity: 0 }, to: { - bottom: '30px', + bottom: '24px', opacity: 1 } }, @@ -623,9 +906,10 @@ const styles = { toast: { visibility: 'hidden', + display: 'inline-flex', position: 'absolute', left: '32px', - bottom: '32px', + bottom: '24px', width: '33%', padding: '14px 16px', zIndex: '200', @@ -748,11 +1032,14 @@ const styles = { } }, '& $viewDial': { + height: 'calc(100% - 136px)', '& $content': { - marginTop: '144px' + // marginTop: '144px' + margin: 'auto' }, '& $versionInfo': { - display: 'flex' + // display: 'flex' + display: 'none' } }, '& $widgetHeader': { @@ -761,7 +1048,7 @@ const styles = { borderTopLeftRadius: '8px', borderTopRightRadius: '8px', '& $closeButton:hover': { - backgroundColor: colors.navy900 + backgroundColor: colors.purple400 } }, '& $userDiv': { @@ -790,7 +1077,7 @@ const styles = { display: 'block' }, '& $headerNickname': { - display: 'block' + display: 'block', }, '& $formContainer': { boxSizing: 'border-box', @@ -804,9 +1091,8 @@ const styles = { borderRadius: '8px' }, '& $toast': { - left: 'auto', - width: '80%', - margin: 'auto' + left: 'calc((100% - 80% - 32px) / 2)', + width: '80%' } }, @@ -818,29 +1104,30 @@ const styles = { borderRadius: '8px', boxShadow: '0 9px 15px -7px rgba(33, 34, 66, 0.04), 0 9px 46px 8px rgba(33, 34, 66, 0.08), 0 24px 38px 3px rgba(33, 34, 66, 0.12)', backgroundColor: colors.white, - overflow: 'hidden' + overflow: 'hidden', }, widgetIcon: { cursor: 'pointer', - width: '56px', - height: '56px', - borderRadius: '32px', + width: '48px', + height: '48px', + borderRadius: '24px', backgroundColor: colors.purple300, backgroundImage: `url(${widgetIcon})`, backgroundRepeat: 'no-repeat', - backgroundPosition: 'center' + backgroundPosition: 'center', + boxShadow: '0 5px 8px -4px rgba(33, 34, 66, 0.04), 0 5px 22px 4px rgba(33, 34, 66, 0.08), 0 12px 17px 2px rgba(33, 34, 66, 0.12)', + marginBottom: '32px' }, widgetHeader: { - position: 'absolute', - top: '0', - display: 'flex', + display: 'inline-flex', + position: 'relative', flexDirection: 'row-reverse', alignItems: 'center', width: '100%', height: '48px', - backgroundColor: colors.navy800, + backgroundColor: colors.purple300, }, userDiv: { @@ -860,7 +1147,7 @@ const styles = { headerNickname: { display: 'none', - color: colors.navy200 + color: colors.purple50 }, menuItems: { @@ -874,7 +1161,8 @@ const styles = { width: '200px', borderRadius: '4px', boxShadow: '0 3px 5px -3px rgba(33, 34, 66, 0.04), 0 3px 14px 2px rgba(33, 34, 66, 0.08), 0 8px 10px 1px rgba(33, 34, 66, 0.12)', - backgroundColor: colors.white + backgroundColor: colors.white, + zIndex: '1' }, menuItem: { @@ -885,6 +1173,7 @@ const styles = { paddingLeft: '16px', paddingRight: '16px', width: '100%', + height: '32px !important', textAlign: 'left', '&:hover': { backgroundColor: colors.navy50 @@ -896,7 +1185,7 @@ const styles = { height: '20px', marginLeft: '8px', marginRight: '16px', - backgroundColor: colors.navy900 + backgroundColor: colors.purple400 }, headerButtons: { @@ -907,16 +1196,14 @@ const styles = { position: 'relative', marginLeft: '2px', marginRight: '2px', - width: '40px', - height: '40px', + width: '48px', + height: '48px', borderRadius: '4px', cursor: 'pointer', backgroundImage: `url(${settingsIcon})`, backgroundRepeat: 'no-repeat', backgroundPosition: 'center', - '&:hover': { - backgroundColor: colors.navy900 - } + zIndex: 10, }, closeButton: { @@ -942,7 +1229,12 @@ const styles = { }, dialDesc: { - marginBottom: '32px' + width: '312px', + height: '40px', + textAlign: 'center', + color: colors.navy400, + marginBottom: '32px', + letterSpacing: '-0.1px' }, btnVideo: { @@ -959,6 +1251,216 @@ const styles = { backgroundColor: colors.purple300 }, + /* call Log View */ + callLogListContainer: { + display: 'inline-block', + position: 'relative', + height: '100%', + overflowY: 'auto', + listStyle: 'none', + margin: '0', + padding: '0', + borderRight: 'solid 1px #dee2f2', + 'li': { + '&:first-child': { + borderTop: 'unset' + } + } + }, + + callLogListDesc: { + display: 'inline-flex', + position: 'absolute', + width: 'calc(100% - 382px)', + height: 'calc(100% - 103px)', + margin: '0', + padding: '0', + right: '0px', + justifyContent: 'center', + alignItems: 'center', + flexDirection: 'column' + }, + + callLogDescLogo: { + display: 'block', + position: 'relative', + width: '100%', + height: '40px', + backgroundImage: `url(${logoHorizon})`, + backgroundPosition: 'center', + backgroundRepeat: 'no-repeat' + }, + + callLogDescTitle: { + position: 'relative', + width: '100%', + height: '32px', + textAlign: 'center', + marginTop: '24px', + color: colors.navy900 + }, + + callLogDescLabel: { + position: 'relative', + width: '275px', + height: '40px', + textAlign: 'center', + marginTop: '16px', + color: colors.navy600 + }, + + widgetCallLog: { + height: '100%', + width: '100%', + overflowX: 'hidden', + }, + + /* call Log item */ + callLogItemWrap: { + display: 'flex', + flexDirection: 'row', + justifyContent: 'flex-start', + position: 'relative', + width: '376px', + minHeight: '88px', + height: 'auto', + borderTop: 'solid 1px #dee2f2', + paddingBottom: '5px', + }, + + callLogEmptyWrap: { + position: 'relative', + width: '312px', + height: '116px', + marginTop: '158px', + marginLeft: '32px', + }, + + icoCallLogEmpty: { + display: 'inline-block', + width: '100%', + height: '64px', + backgroundImage: `url(${callLogEmpty})`, + backgroundPosition: 'center', + backgroundRepeat: 'no-repeat' + }, + + labelCallLogEmpty: { + display: 'inline-block', + width: '100%', + height: '20px', + marginTop: '12px', + textAlign: 'center', + color: colors.navy600 + }, + + callLogTypeDiv: { + display: 'inline-flex', + width: '44px', + height: '100%' + }, + + callLogProfileDiv: { + display: 'inline-flex', + width: '40px', + height: '100%' + }, + + callLogInfoDiv: { + display: 'inline-flex', + flexDirection: 'column', + justifyContent: 'flex-start', + width: '156px', + height: '100%' + }, + + callLogActionDiv: { + display: 'inline-flex', + flexDirection: 'column', + justifyContent: 'flex-start', + alignItems: 'right', + width: '136px', + height: '100%' + }, + + callLogItemType: { + display: 'block', + width: '20px', + height: '20px', + marginLeft: '18px', + marginTop: '26px', + }, + + callLogProfileImg: { + display: 'block', + width: '40px', + height: '40px', + marginTop: '16px', + borderRadius: '50%' + }, + + callLogDisplayName: { + display: 'inline-flex', + width: '140px', + height: 'auto', + marginTop: '16px', + marginLeft: '12px', + wordBreak: 'break-all', + }, + + callLogDuration: { + display: 'inline-flex', + width: '100%', + height: '16px', + marginLeft: '12px', + marginTop: '4px', + color: colors.navy600 + }, + + callLogStartTime: { + display: 'block', + width: '120px', + height: '16px', + textAlign: 'right', + marginTop: '16px', + marginRight: '16px', + color: colors.navy600 + }, + + callLogActionBtnWrap: { + display: 'inline-flex', + flexDirection: 'row', + justifyContent: 'flex-end', + width: '136px', + height: '56px' + }, + + callLogVideoActionBtn: { + width: '32px', + height: '32px', + borderRadius: '50%', + backgroundImage: `url(${thumbnailVideo})`, + backgroundPosition: 'center', + backgroundRepeat: 'no-repeat', + marginRight: '12px', + marginTop: '12px', + backgroundColor: colors.navy80, + cursor: 'pointer' + }, + + callLogVoiceActionBtn: { + width: '32px', + height: '32px', + borderRadius: '50%', + backgroundImage: `url(${thumbnailVoice})`, + backgroundPosition: 'center', + backgroundRepeat: 'no-repeat', + marginTop: '12px', + marginRight: '16px', + backgroundColor: colors.navy80, + cursor: 'pointer' + }, + welcomeDiv: {} }; diff --git a/lib/utils/domUtil.js b/lib/utils/domUtil.js index 509bb37..1491ae6 100644 --- a/lib/utils/domUtil.js +++ b/lib/utils/domUtil.js @@ -111,12 +111,12 @@ export function createButton({ id, className, innerText } = {}) { return _createElement({ tagName: 'button', id: id, className: className, innerText: innerText }); } -export function createImg({ id, className, src, onerror } = {}) { +export function createImg({ id, className, src, alt, onerror } = {}) { const element = _createElement({ tagName: 'img', id: id, className: className, - attrs: { src: src } + attrs: { src, alt } }); element.onerror = onerror; @@ -124,6 +124,22 @@ export function createImg({ id, className, src, onerror } = {}) { return element; } +export function createParagraph({ id, className, innerText } = {}) { + return _createElement({ tagName: 'p', id: id, className: className, innerText: innerText }); +} + +export function createList({ id, className } = {}) { + return _createElement({ tagName: 'ul', id: id, className: className }); +} + +export function createListItem({ id, className } = {}) { + return _createElement({ tagName: 'li', id: id, className: className }); +} + export function replaceClassName(element, searchValue, newValue) { element.classList.replace(searchValue, newValue); +} + +export function hasClassName(element, searchValue) { + return (element.classList.value.indexOf(searchValue) !== -1); } \ No newline at end of file diff --git a/lib/views/AppInfoView.js b/lib/views/AppInfoView.js new file mode 100644 index 0000000..7e22a52 --- /dev/null +++ b/lib/views/AppInfoView.js @@ -0,0 +1,108 @@ +import SendBirdCall from "sendbird-calls"; +import BaseElement from "../components/BaseElement"; +import { classes } from "../css/styles"; +import { createDiv, createLabel, createOption, createSelect } from "../utils/domUtil"; + +export default class AppInfoView extends BaseElement { + constructor({ args }) { + super({ + id: 'appinfo_view', + className: `${classes['viewSettings']} ${classes['column']} ${classes['center']}`, + args + }); + } + + onLoaded() { + + } + + onRemoved() { + + } + + build() { + const cover = createDiv({ id: 'cover', className: classes['cover'] }); + cover.onclick = () => { + this.remove(); + }; + let popup; + if (this.args.isWidget) { + popup = createDiv({ id: 'settings_popup', className: `${classes['popup']} ${classes['widgetpopup']}` }); + } else { + popup = createDiv({ id: 'settings_popup', className: classes['popup'] }); + } + + const popupHeader = createDiv({ + id: 'settings_popup_header', + className: `${classes['popupHeader']}` + }); + + const popupTitle = createDiv({ + id: 'settings_popup_title', + className: `${classes['popupTitle']} ${classes['font20']} ${classes['fontDemi']}`, + innerText: 'Application information' + }); + const closeButton = createDiv({ + id: 'popup_close_button', + className: `${classes['settingsCloseButton']}` + }); + closeButton.onclick = () => { + this.remove(); + }; + popupHeader.appendChild(popupTitle); + popupHeader.appendChild(closeButton); + + const applicationInfoContainer = createDiv({ id: 'select_container', className: classes['selectContainer'] }); + const applicationNameLabel = createLabel({ + id: 'app_name_label', + innerText: 'Application name', + className: `${classes['popupItemLabel']} ${classes['fontSmall']} ${classes['fontHeavy']}` + }); + const applicationName = createLabel({ + id: 'app_name', + innerText: 'Voice & Video', + className: `${classes['appInfoLabel']} ${classes['appName']} ${classes['fontNormal']} ${classes['fontReadOnlyColor']}` + }); + + applicationInfoContainer.appendChild(applicationNameLabel); + applicationInfoContainer.appendChild(applicationName); + + const applicationIDLabel = createLabel({ + id: 'app_id_label', + innerText: 'Application ID', + className: `${classes['popupItemLabel']} ${classes['fontSmall']} ${classes['fontHeavy']}` + }); + + const applicationIdWrap = createDiv({ + id: 'app_id_wrap', + className: `${classes['appInfoLabelWrap']}` + }); + + const applicationID = createLabel({ + id: 'app_id', + innerText: this.args.appId, + className: `${classes['appInfoLabel']} ${classes['appInfoIdLabel']} ${classes['fontNormal']} ${classes['fontReadOnlyColor']}` + }); + + const appInfoIdCopy = createDiv({ + id: 'btn_app_id_copy', + className: `${classes['appInfoIdCopy']}` + }); + + appInfoIdCopy.onclick = () => { + navigator.clipboard.writeText(this.args.appId); + }; + + applicationIdWrap.appendChild(applicationID); + applicationIdWrap.appendChild(appInfoIdCopy); + + applicationInfoContainer.appendChild(applicationIDLabel); + applicationInfoContainer.appendChild(applicationIdWrap); + + popup.appendChild(popupHeader); + popup.appendChild(applicationInfoContainer); + + this.element.appendChild(cover); + this.element.appendChild(popup); + } +} \ No newline at end of file diff --git a/lib/views/CallButtons.js b/lib/views/CallButtons.js index 1575ab3..3d1e719 100644 --- a/lib/views/CallButtons.js +++ b/lib/views/CallButtons.js @@ -19,7 +19,13 @@ export default class CallButtons extends BaseElement { } build() { - const element = createDiv({ className: `${classes['row']} ${classes['center']} ${classes['callButtons']}` }); + let element = null; + if (this.args.isWidget) { + element = createDiv({ className: `${classes['row']} ${classes['center']} ${classes['callButtonsWidget']}` }); + } else { + element = createDiv({ className: `${classes['row']} ${classes['center']} ${classes['callButtons']}` }); + } + this.element = element; if (this.call.isVideoCall) { @@ -128,8 +134,6 @@ export default class CallButtons extends BaseElement { setAccepting() { this.hideActiveButtons(); this.showButtons(this.endBtn); - - replaceClassName(this.endBtn.icon, classes['btnDecline'], classes['btnEnd']); } setDialing() { @@ -140,8 +144,6 @@ export default class CallButtons extends BaseElement { setRinging() { this.hideActiveButtons(); this.showButtons(this.muteBtn, this.videoBtn, this.acceptBtn, this.endBtn); - - replaceClassName(this.endBtn.icon, classes['btnEnd'], classes['btnDecline']); } setConnected() { diff --git a/lib/views/CallLogItem.js b/lib/views/CallLogItem.js new file mode 100644 index 0000000..14f0916 --- /dev/null +++ b/lib/views/CallLogItem.js @@ -0,0 +1,147 @@ +import outgoingVideo from '../assets/icon-call-video-outgoing-filled.svg'; +import incomingVideo from '../assets/icon-call-video-incoming-filled.svg'; +import outgoingVoice from '../assets/icon-call-voice-outgoing-filled.svg'; +import incomingVoice from '../assets/icon-call-voice-incoming-filled.svg'; +import avatarIcon from '../assets/icon-avatar.svg'; + +import { createListItem, createDiv, createImg, createLabel } from "../utils/domUtil"; +import { classes } from "../css/styles"; + +export class CallLogItem { + constructor({ callLogInfo, className }) { + if(callLogInfo) { + const wrapper = createListItem({ id: callLogInfo.callId, className: className }); + let callType = null; + let callTypeAlt = ''; + if(callLogInfo.isVideoCall){ + if(callLogInfo.userRole === 'dc_caller'){ + callType = outgoingVideo; + callTypeAlt = 'Outgoing call history'; + } else { + callType = incomingVideo; + callTypeAlt = 'Incoming call history'; + } + } else { + if(callLogInfo.userRole === 'dc_caller'){ + callType = outgoingVoice; + callTypeAlt = 'Outgoing call history'; + } else { + callType = incomingVoice; + callTypeAlt = 'Incoming call history'; + } + } + + let profileImage = null; + let displayName = ""; + if(callLogInfo.userRole === 'dc_caller'){ + profileImage = callLogInfo.callee.profileUrl; + displayName = callLogInfo.callee.userId; + } else { + profileImage = callLogInfo.caller.profileUrl; + displayName = callLogInfo.caller.userId; + } + + const icoCallType = createImg({ className: `${classes['callLogItemType']}`, src: callType, alt: callTypeAlt }); + const callTypeDiv = createDiv({ className: `${classes['callLogTypeDiv']}` }); + callTypeDiv.appendChild(icoCallType); + + const profileImg = createImg({ + className: `${classes['callLogProfileImg']}`, + src: profileImage, + alt: 'Opponent profile photo of call history', + onerror: (error) => { + error.currentTarget.src = avatarIcon; + } + }); + + const profileDiv = createDiv({ className: `${classes['callLogProfileDiv']}` }); + profileDiv.appendChild(profileImg); + + //duration + let callDurationTime = ''; + if(callLogInfo.duration > 0){ + let tempDuration = Math.ceil(callLogInfo.duration / 1000); + let hour = parseInt(tempDuration / 3600); + let min = parseInt((tempDuration - (hour * 3600)) / 60); + let sec = tempDuration - (hour * 3600) - (min * 60); + if(hour > 0){ + callDurationTime = hour + 'h '; + } + if(min > 0){ + callDurationTime += (min + 'm '); + } + callDurationTime += (sec + 's'); + } else { + callDurationTime = '0s'; + } + + const displayNameLabel = createLabel({ className: `${classes['callLogDisplayName']} ${classes['fontNormal']} ${classes['fontHeavy']}`, innerText: displayName }); + const callDuration = createLabel({ className: `${classes['callLogDuration']} ${classes['fontSmall']}`, innerText: callDurationTime }); + const callEndReason = createLabel({ className: `${classes['callLogDuration']} ${classes['fontSmall']}`, innerText: callLogInfo.endResult }); + + + const callLogInfoDiv = createDiv({ className: `${classes['callLogInfoDiv']}` }); + callLogInfoDiv.appendChild(displayNameLabel); + callLogInfoDiv.appendChild(callDuration); + callLogInfoDiv.appendChild(callEndReason); + + + let callStartTime = new Date(callLogInfo.startedAt); + let callStartTimeLabel = `${callStartTime.getFullYear()}/${callStartTime.toLocaleString(['en-US'], {month: '2-digit'})}/${callStartTime.toLocaleString(['en-US'], {day: '2-digit'})} ${this.formatAMPM(callStartTime)}`; + + const callLogStartTime = createLabel({ className: `${classes['callLogStartTime']} ${classes['fontSmall']}`, innerText: callStartTimeLabel}); + const callActionBtnWrap = createDiv({ className: `${classes['callLogActionBtnWrap']}` }); + const btnCallVideo = createDiv({ className: `${classes['callLogVideoActionBtn']}`}); + const btnCallVoice = createDiv({ className: `${classes['callLogVoiceActionBtn']}`}); + callActionBtnWrap.appendChild(btnCallVoice); + callActionBtnWrap.appendChild(btnCallVideo); + + + const callLogActionDiv = createDiv({ className: `${classes['callLogActionDiv']}` }); + callLogActionDiv.appendChild(callLogStartTime); + callLogActionDiv.appendChild(callActionBtnWrap); + + + wrapper.appendChild(callTypeDiv); + wrapper.appendChild(profileDiv); + wrapper.appendChild(callLogInfoDiv); + wrapper.appendChild(callLogActionDiv); + + this.element = wrapper; + this.btnCallVideo = btnCallVideo; + this.btnCallVoice = btnCallVoice; + this.destPeerID = displayName; + } else { + const wrapper = createDiv({ id: 'empty_calllog', className: className }); + const icoCallLogEmpty = createDiv({ className: `${classes['icoCallLogEmpty']}` }); + const labelCallLogEmpty = createDiv({ innerText: 'No call history', className: `${classes['labelCallLogEmpty']} ${classes['font16']} ${classes['fontHeavy']}` }); + wrapper.appendChild(icoCallLogEmpty); + wrapper.appendChild(labelCallLogEmpty); + + this.element = wrapper; + } + } + + formatAMPM(date) { + let hours = date.getHours(); + let minutes = date.getMinutes(); + let ampm = hours >= 12 ? 'pm' : 'am'; + hours = hours % 12; + hours = hours || 12; // the hour '0' should be '12' + minutes = minutes < 10 ? '0'+minutes : minutes; + let strTime = hours + ':' + minutes + ampm; + return strTime; + } + + /** + * @param {(event: any, args: any) => void} eventhandler + */ + set onclick(eventhandler) { + this.btnCallVideo.onclick = (event) => { + eventhandler(event, {peerId: this.destPeerID, isVideoCall: true, callOption: null}); + }; + this.btnCallVoice.onclick = (event) => { + eventhandler(event, {peerId: this.destPeerID, isVideoCall: false, callOption: null}); + }; + } +} diff --git a/lib/views/CallLogView.js b/lib/views/CallLogView.js new file mode 100644 index 0000000..a4f52bf --- /dev/null +++ b/lib/views/CallLogView.js @@ -0,0 +1,66 @@ +import SendBirdCall from "sendbird-calls"; +import BaseElement from "../components/BaseElement"; +import { classes } from "../css/styles.js"; +import { createList, createDiv } from "../utils/domUtil"; +import { CallLogItem } from "./CallLogItem"; + +export default class CallLogView extends BaseElement{ + constructor({ args }) { + super({ + id: 'calllog_view', + className: `${classes['container']} ${classes['center']} ${classes['viewDial']}`, + args + }); + + this.callLogQuery = null; + this.callLogQueryData = []; + } + + build() { + const callLogList = createList({ id: 'call_log_list', className: `${classes['callLogListContainer']}` }); + this.callLogQuery = SendBirdCall.createDirectCallLogListQuery({ limit: 30 }); + this.getCallLogs(callLogList); + callLogList.onscroll = (e) => { + let scrollposition = e.target.scrollHeight - e.target.clientHeight; + if(scrollposition === e.target.scrollTop) { + this.getCallLogs(callLogList); + } + }; + + if(this.args.isWidget) { + callLogList.classList.add(classes['widgetCallLog']); + this.element.appendChild(callLogList); + } else { + const callLogDescription = createDiv({ id: 'call_log_desc', className: `${classes['callLogListDesc']}` }); + const callLogDescLogo = createDiv({ className: `${classes['callLogDescLogo']}` }); + const callLogDescTitle = createDiv({ innerText: 'Sendbird Calls Quickstart', className: `${classes['callLogDescTitle']} ${classes['font24']} ${classes['fontDemi']}` }); + const callLogDescLabel = createDiv({ innerText: 'This is the Sendbird Calls Quickstart page. Lorem ipsum', className: `${classes['callLogDescLabel']} ${classes['fontNormal']} ${classes['fontHeavy']}` }); + callLogDescription.appendChild(callLogDescLogo); + callLogDescription.appendChild(callLogDescTitle); + callLogDescription.appendChild(callLogDescLabel); + + this.element.appendChild(callLogList); + this.element.appendChild(callLogDescription); + } + } + + getCallLogs(element){ + if (!this.callLogQuery.hasNext || this.callLogQuery.isLoading) return; + this.callLogQuery.next((directCallLog) => { + if( directCallLog.length > 0 ) { + for(let callLogItem of directCallLog) { + const callItem = new CallLogItem({ callLogInfo: callLogItem, className: `${classes['callLogItemWrap']}` }); + callItem.onclick = (event, args) => { + this.sendToParent('dial', args); + }; + + element.appendChild(callItem.element); + } + } else { + // empty call log + const emptyCallLog = new CallLogItem({ className: `${classes['callLogEmptyWrap']}` }); + element.appendChild(emptyCallLog.element); + } + }); + } +} diff --git a/lib/views/CallView.js b/lib/views/CallView.js index f809b8c..504b503 100644 --- a/lib/views/CallView.js +++ b/lib/views/CallView.js @@ -1,6 +1,7 @@ import BaseElement from "../components/BaseElement"; import { createAudio, createDiv, createImg, createLabel, createVideo } from "../utils/domUtil"; import { getCallOption } from "../utils/util"; +import { hasClassName } from "../utils/domUtil"; import CallButtons from "./CallButtons"; import { classes } from "../css/styles.js"; import PeriodicJob from "../modules/periodicJob"; @@ -12,6 +13,7 @@ export default class CallView extends BaseElement { this.call = call; this.state = state; this.connected = false; + this.addCallListeners(call); } @@ -24,10 +26,11 @@ export default class CallView extends BaseElement { this.peerProfile = createImg({ src: remoteUser.profileUrl, + alt: 'Sendbird voice & video call opponent profile photo', className: classes['remoteProfile'], onerror: (e) => e.target.style.visibility = 'hidden' }); - this.peerName = createLabel({ id: 'peer_name', innerText: remoteUser.userId, className: `${classes['peerName']} ${classes['fontBig']}` }); + this.peerName = createLabel({ id: 'peer_name', innerText: (remoteUser.nickname ? remoteUser.nickname : remoteUser.userId), className: `${classes['peerName']} ${classes['fontBig']} ${classes['fontDemi']}` }); let connectionText; if (this.call.isVideoCall) { @@ -37,13 +40,13 @@ export default class CallView extends BaseElement { } this.connectionInfo = createLabel({ id: 'conn_info_label', - className: `${classes['connectionInfo']} ${classes['fontNormal']}`, + className: `${classes['connectionInfo']} ${classes['fontNormal']} ${classes['fontColorWhite']}`, innerText: connectionText }); const peerStateDiv = createDiv({ id: 'peer_state', className: `${classes['column']} ${classes['peerStateDiv']} ${classes['invisible']}` }); - const peerMuteIcon = createDiv({ id: 'peer_mute_icon', className: classes['peerMuteIcon'] }); - const peerMuteLabel = createLabel({ id: 'peer_mute_label', className: `${classes['peerMuteLabel']} ${classes['fontSmall']}`, innerText: `${remoteUser.userId} muted this call` }); + const peerMuteIcon = createDiv({ id: 'peer_mute_icon', className: `${classes['peerMuteIcon']}` }); + const peerMuteLabel = createLabel({ id: 'peer_mute_label', className: `${classes['peerMuteLabel']} ${classes['fontSmall']} ${classes['fontColorWhite']}`, innerText: `${remoteUser.userId} audio muted this call` }); peerStateDiv.appendChild(peerMuteIcon); peerStateDiv.appendChild(peerMuteLabel); @@ -52,12 +55,13 @@ export default class CallView extends BaseElement { }); if (this.call.isVideoCall) { - this.localMediaView = createVideo({ className: `${classes['videoView']}`, autoplay: true, muted: true }); + this.localMediaView = createVideo({ className: `${classes['videoView']}`, autoplay: true, muted: true, style: "background-color: #fff;" }); this.remoteMediaView = createVideo({ className: `${classes['videoView']}`, autoplay: true, muted: false, remote: true }); } else { this.localMediaView = createAudio({ autoplay: true, muted: true }); this.remoteMediaView = createAudio({ autoplay: true, muted: false, remote: true }); } + this.localMediaViewDiv = createDiv({ className: `${classes['videoViewDiv']} ${classes['videoFull']}` }); this.remoteMediaViewDiv = createDiv({ className: `${classes['videoViewDiv']} ${classes['videoHidden']}` }); this.localMediaViewDiv.appendChild(this.localMediaView); @@ -66,7 +70,12 @@ export default class CallView extends BaseElement { this.call.setLocalMediaView(this.localMediaView); this.call.setRemoteMediaView(this.remoteMediaView); - const background = createDiv({ className: classes['callBackground'] }); + let background = null; + if (this.args.isWidget) { + background = createDiv({ className: `${classes['callBackground']} ${classes['callBackgroundWidget']}` }); + } else { + background = createDiv({ className: classes['callBackground'] }); + } const foreground = createDiv({ className: `${classes['column']} ${classes['center']} ${classes['callForeground']}` }); background.appendChild(this.remoteMediaViewDiv); @@ -104,14 +113,15 @@ export default class CallView extends BaseElement { this.sendToChildren('connected'); }; - call.onEnded = () => { + call.onEnded = (endedCall) => { this.drawEndResult(); - if (this.call.isVideoCall) { + if (endedCall.isVideoCall) { this.localMediaViewDiv.classList.add(classes['videoHidden']); this.remoteMediaViewDiv.classList.add(classes['videoHidden']); this.showSecondaryInfo(); } + this.sendToChildren('ended'); }; @@ -124,11 +134,15 @@ export default class CallView extends BaseElement { }; call.onRemoteAudioSettingsChanged = (call) => { - this.onRemoteMuted(call.isRemoteAudioEnabled); + this.onRemoteAudioMuted(call.isRemoteAudioEnabled); + }; + + call.onRemoteVideoSettingsChanged = (call) => { + this.onRemoteVideoMuted(call.isRemoteVideoEnabled); }; } - onRemoteMuted(isEnabled) { + onRemoteAudioMuted(isEnabled) { const peerStateDiv = this.element.querySelector('#peer_state'); if (isEnabled) { peerStateDiv.classList.add(classes['invisible']); @@ -137,6 +151,14 @@ export default class CallView extends BaseElement { } } + onRemoteVideoMuted(isEnabled) { + if (isEnabled) { + this.remoteMediaViewDiv.classList.remove(classes['videoHidden']); + } else { + this.remoteMediaViewDiv.classList.add(classes['videoHidden']); + } + } + recvMessage(name, value) { switch (name) { case 'click_accept': @@ -185,7 +207,9 @@ export default class CallView extends BaseElement { toggleVideo() { if (this.call.isLocalVideoEnabled) { this.call.stopVideo(); + this.localMediaView.classList.add(classes['videoHidden']); } else { + this.localMediaView.classList.remove(classes['videoHidden']); this.call.startVideo(); } } diff --git a/lib/views/DialView.js b/lib/views/DialView.js index da4bb2d..12a27d5 100644 --- a/lib/views/DialView.js +++ b/lib/views/DialView.js @@ -1,9 +1,9 @@ import SendBirdCall from "sendbird-calls"; import BaseElement from "../components/BaseElement"; -import { sheet, classes } from "../css/styles.js"; +import { classes } from "../css/styles.js"; import { createButton, createDiv, createInput, createLabel } from "../utils/domUtil"; -import Menu from "../components/Menu"; + import pack from "../../package"; export default class DialView extends BaseElement { @@ -13,108 +13,32 @@ export default class DialView extends BaseElement { className: `${classes['container']} ${classes['column']} ${classes['center']} ${classes['view']} ${classes['viewDial']}`, args }); - - this.settingItems = [ - { - 'label': 'Device settings', - 'callback': () => { this.sendToParent('show_settings') } - }, - { - 'label': 'Sign out', - 'callback': () => { this.deauthenticate(); } - } - ]; } onLoaded() { } build() { - const header = createDiv({ id: 'header', className: classes['widgetHeader'] }); - const content = createDiv({ id: 'content', className: `${classes['content']} ${classes['column']} ${classes['center']}` }); - - const userDiv = createDiv({ - id: 'header_user_div', - className: `${classes['userDiv']} ${classes['center']}` - }); - - let profileImg; - if (this.args.user && this.args.user.profileUrl) { - sheet.update({ profileUrl: this.args.user.profileUrl }); - profileImg = createDiv({ id: 'header_profile_img', className: classes['profileSmall'] }); - } else { - profileImg = createDiv({ id: 'header_avatar', className: `${classes['avatar']}` }); - } - - const headerInfo = createDiv({ id: 'header_info', className: `${classes['headerInfo']}` }); - const userId = createDiv({ - id: 'header_user_id', - className: `${classes['headerUserId']} ${classes['fontNormal']} ${classes['fontHeavy']}`, - innerText: this.args.user.userId || '' - }); - const nickname = createDiv({ - id: 'header_nickname', - className: `${classes['headerNickname']} ${classes['fontSmall']}`, - innerText: this.args.user.nickname || 'no nickname' - }); - headerInfo.appendChild(userId); - headerInfo.appendChild(nickname); - - userDiv.appendChild(profileImg); - userDiv.appendChild(headerInfo); - - const headerButtons = createDiv({ - id: 'header_buttons', - className: `${classes['headerButtons']} ${classes['row']} ${classes['center']}` - }); - const settingsButton = new Menu({ - id: 'settings_button', - element: createDiv({ className: `${classes['settingsButton']}` }), - items: this.settingItems - }); - - const closeButton = createDiv({ - id: 'close_button', - className: `${classes['closeButton']}` - }); - closeButton.onclick = () => { - this.sendToParent('widgetclose'); - }; - settingsButton.appendToHTML(headerButtons); - headerButtons.appendChild(closeButton); - - const divider = createDiv({ - id: 'header_divider', - className: classes['headerDivider'] - }); - - header.appendChild(userDiv); - header.appendChild(divider); - header.appendChild(headerButtons); - + const formContainer = createDiv({ className: `${classes['formContainer']} ${classes['column']} ${classes['center']}` }); - const logoMid = createDiv({ id: 'logo_oval', className: `${classes['logoMidBlack']}` }); - const welcomeDiv = createDiv({ - id: 'welcome_div', - className: `${classes['fontBig']} ${classes['fontHeavy']}`, - innerText: 'SendBird Calls' - }); - const descDiv = createDiv({ - id: 'desc_div', - className: `${classes['dialDesc']} ${classes['fontNormal']}`, - innerText: 'Make a call' + const dialTitleDiv = createDiv({ id: 'title', className: `${classes['fontBig']} ${classes['fontDemi']} ${classes['dialTitle']}`, innerText: 'Make a call'}); + const descDiv = createDiv({id: 'desc_div', + innerText: 'Enter the user ID of the user you wish to call, then press one of the video or voice call buttons', + className: `${classes['fontNormal']} ${classes['dialDesc']} ${classes['fontHeavy']}` }); + const peerId = createInput({ id: 'peer_id', - className: `${classes['field']} ${classes['fontNormal']}`, - placeholder: 'User ID' + className: `${classes['field']} ${classes['fontNormal']} ${classes['dialField']}`, + placeholder: 'Enter user ID' }); + const btns = createDiv({ id: 'buttons', className: `${classes['row']} ${classes['center']} ${classes['btns']}` }); const btnVideo = createButton({ @@ -129,14 +53,15 @@ export default class DialView extends BaseElement { btns.appendChild(btnVideo); btns.appendChild(btnAudio); - formContainer.appendChild(logoMid); - formContainer.appendChild(welcomeDiv); + formContainer.appendChild(dialTitleDiv); formContainer.appendChild(descDiv); formContainer.appendChild(peerId); formContainer.appendChild(btns); content.appendChild(formContainer); + this.element.appendChild(content); + const sampleVersion = pack.version; const sdkVersion = SendBirdCall.sdkVersion; const versionInfo = createDiv({ @@ -156,8 +81,7 @@ export default class DialView extends BaseElement { versionInfo.appendChild(sampleVersionLabel); versionInfo.appendChild(sdkVersionLabel); - this.element.appendChild(header); - this.element.appendChild(content); + this.element.appendChild(versionInfo); btnVideo.onclick = () => { @@ -181,4 +105,4 @@ export default class DialView extends BaseElement { SendBirdCall.deauthenticate(); this.sendToParent('deauthenticate'); } -} \ No newline at end of file +} diff --git a/lib/views/LoginView.js b/lib/views/LoginView.js index 1bde781..32baeeb 100644 --- a/lib/views/LoginView.js +++ b/lib/views/LoginView.js @@ -49,7 +49,7 @@ export default class LoginView extends BaseElement { const inputAccessToken = createInput({ id: 'input_access_token', className: `${classes['field']} ${classes['fontNormal']}` }); const btnLogin = createButton({ id: 'btn_login', className: `${classes['btn']} ${classes['btnPrimary']} ${classes['btnMid']} ${classes['loginButton']} ${classes['fontNormal']}`, }); - const loginLabel = createLabel({ id: 'login_label', innerText: 'Sign in' }); + const loginLabel = createLabel({ id: 'login_label', className: `${classes['fontNormal']} ${classes['fontColorWhite']} ${classes['fontDemi']}`, innerText: 'Sign in' }); btnLogin.appendChild(loginLabel); btnLogin.onclick = () => { const appId = inputAppId.value; diff --git a/lib/views/Settings.js b/lib/views/Settings.js index 8e9161a..66ea4fb 100644 --- a/lib/views/Settings.js +++ b/lib/views/Settings.js @@ -40,11 +40,16 @@ export default class Settings extends BaseElement { cover.onclick = () => { this.remove(); }; - const popup = createDiv({ id: 'settings_popup', className: classes['popup'] }); + let popup; + if (this.args.isWidget) { + popup = createDiv({ id: 'settings_popup', className: `${classes['popup']} ${classes['widgetpopup']}` }); + } else { + popup = createDiv({ id: 'settings_popup', className: classes['popup'] }); + } const popupHeader = createDiv({ id: 'settings_popup_header', - className: `${classes['popupHeader']}, ${classes['row']} ${classes['center']}` + className: `${classes['popupHeader']}` }); const popupTitle = createDiv({ diff --git a/package-lock.json b/package-lock.json index 6e347f8..81c2241 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "calls-quickstart", - "version": "1.0.1", + "version": "1.1.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -1158,9 +1158,9 @@ } }, "acorn": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.0.tgz", - "integrity": "sha512-gac8OEcQ2Li1dxIEWGZzsp2BitJxwkwcOm0zHAJLcPJaVvm58FRnk6RkuLRpU1EujipU2ZFODv2P9DLMfnV8mw==", + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.1.tgz", + "integrity": "sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==", "dev": true }, "ajv": { @@ -1688,9 +1688,9 @@ "dev": true }, "cacache": { - "version": "12.0.3", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.3.tgz", - "integrity": "sha512-kqdmfXEGFepesTuROHMs3MpFLWrPkSSpRqOw80RCflZXy/khxaArvFrQ7uJxSUduzAufc6G0g1VUCOZXxWavPw==", + "version": "12.0.4", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.4.tgz", + "integrity": "sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==", "dev": true, "requires": { "bluebird": "^3.5.5", @@ -1781,9 +1781,9 @@ } }, "chownr": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.3.tgz", - "integrity": "sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", "dev": true }, "chrome-trace-event": { @@ -2869,9 +2869,9 @@ } }, "figgy-pudding": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.1.tgz", - "integrity": "sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.2.tgz", + "integrity": "sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw==", "dev": true }, "file-loader": { @@ -4592,9 +4592,9 @@ "dev": true }, "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", "dev": true }, "lcid": { @@ -4856,9 +4856,9 @@ } }, "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", "dev": true }, "mississippi": { @@ -4901,20 +4901,12 @@ } }, "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", "dev": true, "requires": { - "minimist": "0.0.8" - }, - "dependencies": { - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true - } + "minimist": "^1.2.5" } }, "move-concurrently": { @@ -5472,9 +5464,9 @@ } }, "portfinder": { - "version": "1.0.25", - "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.25.tgz", - "integrity": "sha512-6ElJnHBbxVA1XSLgBp7G1FiCkQdlqGzuF7DswL5tcea+E8UpuvPU7beVAjjRwCioTS9ZluNbu+ZyRvgTsmqEBg==", + "version": "1.0.26", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.26.tgz", + "integrity": "sha512-Xi7mKxJHHMI3rIUrnm/jjUgwhbYMkp/XKEcZX3aG4BrumLpq3nmoQMX+ClYnDZnZ/New7IatC1no5RX0zo1vXQ==", "dev": true, "requires": { "async": "^2.6.2", @@ -6129,9 +6121,9 @@ } }, "sendbird-calls": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/sendbird-calls/-/sendbird-calls-1.0.1.tgz", - "integrity": "sha512-f2EK+1dvaUnYrHYuyItpApi4RhQ3hEDnj2B3FuXlvF2K2BAVpr6F9DhaDs0Mqce8liiMEbx6+KLZ9zw1wNcxJA==" + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sendbird-calls/-/sendbird-calls-1.1.3.tgz", + "integrity": "sha512-bYKg8eFCciQQM3Pj5auIsX51JK9xhyrzL7zZpKZMQTJneA981BhxG0scNmHr+5DovdcnZHqgUoxUkCrIxFwVjw==" }, "serialize-javascript": { "version": "2.1.2", @@ -6460,9 +6452,9 @@ } }, "source-map-support": { - "version": "0.5.16", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.16.tgz", - "integrity": "sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ==", + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", + "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", "dev": true, "requires": { "buffer-from": "^1.0.0", @@ -6633,9 +6625,9 @@ } }, "stream-shift": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz", - "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", + "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==", "dev": true }, "strict-uri-encode": { @@ -6742,9 +6734,9 @@ "dev": true }, "terser": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/terser/-/terser-4.4.2.tgz", - "integrity": "sha512-Uufrsvhj9O1ikwgITGsZ5EZS6qPokUOkCegS7fYOdGTv+OA90vndUbU6PEjr5ePqHfNUbGyMO7xyIZv2MhsALQ==", + "version": "4.6.13", + "resolved": "https://registry.npmjs.org/terser/-/terser-4.6.13.tgz", + "integrity": "sha512-wMvqukYgVpQlymbnNbabVZbtM6PN63AzqexpwJL8tbh/mRT9LE5o+ruVduAGL7D6Fpjl+Q+06U5I9Ul82odAhw==", "dev": true, "requires": { "commander": "^2.20.0", @@ -6767,16 +6759,16 @@ } }, "terser-webpack-plugin": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.2.tgz", - "integrity": "sha512-fdEb91kR2l+BVgES77N/NTXWZlpX6vX+pYPjnX5grcDYBF2CMnzJiXX4NNlna4l04lvCW39lZ+O/jSvUhHH/ew==", + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.3.tgz", + "integrity": "sha512-QMxecFz/gHQwteWwSo5nTc6UaICqN1bMedC5sMtUc7y3Ha3Q8y6ZO0iCR8pq4RJC8Hjf0FEPEHZqcMB/+DFCrA==", "dev": true, "requires": { "cacache": "^12.0.2", "find-cache-dir": "^2.1.0", "is-wsl": "^1.1.0", "schema-utils": "^1.0.0", - "serialize-javascript": "^2.1.1", + "serialize-javascript": "^2.1.2", "source-map": "^0.6.1", "terser": "^4.1.2", "webpack-sources": "^1.4.0", @@ -7665,9 +7657,9 @@ } }, "yargs-parser": { - "version": "13.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz", - "integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==", + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", "dev": true, "requires": { "camelcase": "^5.0.0", diff --git a/package.json b/package.json index b3c7269..20ae896 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "calls-quickstart", - "version": "1.0.1", + "version": "1.1.0", "description": "", "main": "app.js", "scripts": { @@ -34,7 +34,7 @@ "express": "^4.17.1", "jss": "^10.0.3", "jss-preset-default": "^10.0.3", - "sendbird-calls": "^1.0.1", + "sendbird-calls": "^1.1.3", "uuid": "^3.3.3" } } diff --git a/views/index.html b/views/index.html index f88d1f7..20fbfdb 100644 --- a/views/index.html +++ b/views/index.html @@ -1,33 +1,44 @@ - + Sample - + - +
-
- -
- Calls Sample +
+ Sendbird horizontal logo svg icon +
+ Sendbird Calls Quickstart +
+
+ Choose an application type +
+
+
+
+ Full-screen
-
- Choose Sample Type +
+ The full-screen application type provides an example of a standalone phone call web app. +
+
+ +
+
+ Widget
-
- - - - - - +
+ The widget application type showcases how a phone call may be overlaid on top of an existing web application.
+
+
- + - \ No newline at end of file + diff --git a/views/widget.html b/views/widget.html index 8b6c3ad..453514b 100644 --- a/views/widget.html +++ b/views/widget.html @@ -10,28 +10,28 @@
- -
- Voice Sample Login + Sendbird horizontal logo svg icon +
+ Sendbird Calls Quickstart
- This is the Voice Sample page. To try our - Voice widget, please click the button below. + This is the Sendbird Calls Quickstart page.
+ To try our widget, please click the button below.
- + arrow-down-right
-
+
😀
- + tooltip-tail-down
- \ No newline at end of file +