diff --git a/src/domain/SessionPickerViewModel.js b/src/domain/SessionPickerViewModel.js index 52b2d5f7..37fbf767 100644 --- a/src/domain/SessionPickerViewModel.js +++ b/src/domain/SessionPickerViewModel.js @@ -17,6 +17,7 @@ limitations under the License. import {SortedArray} from "../observable/index.js"; import {SessionLoadViewModel} from "./SessionLoadViewModel.js"; import {ViewModel} from "./ViewModel.js"; +import {avatarInitials, getIdentifierColorNumber} from "./avatar.js"; class SessionItemViewModel extends ViewModel { constructor(sessionInfo, pickerVM) { @@ -112,6 +113,14 @@ class SessionItemViewModel extends ViewModel { this.emitChange("exportDataUrl"); } } + + get avatarColorNumber() { + return getIdentifierColorNumber(this._sessionInfo.userId); + } + + get avatarInitials() { + return avatarInitials(this._sessionInfo.userId); + } } diff --git a/src/ui/web/css/layout.css b/src/ui/web/css/layout.css index d48dfd98..95fe71da 100644 --- a/src/ui/web/css/layout.css +++ b/src/ui/web/css/layout.css @@ -19,6 +19,16 @@ html { height: 100%; } + +@media screen and (min-width: 600px) { + .PreSessionScreen { + width: 600px; + box-sizing: border-box; + margin: 0 auto; + margin-top: 50px; + } +} + .SessionView { display: flex; flex-direction: column; diff --git a/src/ui/web/css/login.css b/src/ui/web/css/login.css index d8387e73..4df1ac22 100644 --- a/src/ui/web/css/login.css +++ b/src/ui/web/css/login.css @@ -31,16 +31,18 @@ limitations under the License. padding: 0.5em; } -.SessionPickerView .sessionInfo { +.SessionPickerView .session-info { cursor: pointer; display: flex; + align-items: center; + gap: 10px; } -.SessionPickerView li span.userId { +.SessionPickerView li .user-id { flex: 1; } -.SessionPickerView li span.error { +.SessionPickerView li .error { margin: 0 20px; } diff --git a/src/ui/web/css/themes/element/element-logo.svg b/src/ui/web/css/themes/element/element-logo.svg new file mode 100644 index 00000000..7e6c50fb --- /dev/null +++ b/src/ui/web/css/themes/element/element-logo.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/ui/web/css/themes/element/icons/chevron-right.svg b/src/ui/web/css/themes/element/icons/chevron-right.svg new file mode 100644 index 00000000..a7b862aa --- /dev/null +++ b/src/ui/web/css/themes/element/icons/chevron-right.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/ui/web/css/themes/element/theme.css b/src/ui/web/css/themes/element/theme.css index a481db73..57b0da51 100644 --- a/src/ui/web/css/themes/element/theme.css +++ b/src/ui/web/css/themes/element/theme.css @@ -36,7 +36,6 @@ limitations under the License. .avatar { border-radius: 100%; background: #3D88FA; - color: white; } @@ -49,6 +48,76 @@ limitations under the License. .hydrogen .avatar.usercolor7 { background-color: var(--usercolor7); } .hydrogen .avatar.usercolor8 { background-color: var(--usercolor8); } +.logo { + height: 48px; + min-width: 48px; + background-image: url('element-logo.svg'); + background-repeat: no-repeat; + background-position: center; +} + +/** buttons */ +.button-row { + display: flex; +} +.button-row > * { + margin-right: 10px; +} +.button-row > *:last-child { + margin-right: 0px; +} + +.button-row button { + margin: 10px 0; + flex: 1 0 auto; +} + +button.styled.secondary { + color: #03B381; +} + +button.styled.primary { + background-color: #03B381; + border-radius: 8px; + color: white; +} + +button.styled.primary.destructive { + background-color: #FF4B55; +} + +button.styled.secondary.destructive { + color: #FF4B55; +} + +button.styled { + border: none; + padding: 10px; + background: none; + font-weight: 500; +} + +.PreSessionScreen { + padding: 30px; +} + +.PreSessionScreen h1 { + font-size: 16px; + text-align: center; +} + +@media screen and (min-width: 600px) { + .PreSessionScreen { + box-shadow: 0px 6px 32px rgba(0, 0, 0, 0.1); + border-radius: 8px; + } +} + +.PreSessionScreen .logo { + height: 48px; + min-width: 48px; +} + .LeftPanel { background: rgba(245, 245, 245, 0.90); } @@ -79,7 +148,7 @@ limitations under the License. } a { - color: white; + color: inherit; } .SessionStatusView { @@ -103,9 +172,41 @@ a { .SessionPickerView li { font-size: 1.2em; - background-color: grey; } +.SessionPickerView .session-info { + padding: 12px; + border: 1px solid rgba(141, 151, 165, 0.15); + border-radius: 8px; + background-image: url('icons/chevron-right.svg'); + background-position: center right 30px; + background-repeat: no-repeat; + font-weight: 500; +} + +.SessionPickerView .session-actions { + margin: 10px 0 20px 0; + display: flex; +} + +.SessionPickerView .session-actions > * { + margin-right: 10px; +} +.SessionPickerView .session-actions > *:last-child { + margin-right: 0px; +} + +.SessionPickerView .session-actions button { + border: none; + background: none; + color: inherit; +} + +.SessionPickerView button.destructive { + color: #FF4B55; +} + + .RoomHeader { background: rgba(245, 245, 245, 0.90); padding: 10px; diff --git a/src/ui/web/login/SessionPickerView.js b/src/ui/web/login/SessionPickerView.js index 41c37d15..8b051dcc 100644 --- a/src/ui/web/login/SessionPickerView.js +++ b/src/ui/web/login/SessionPickerView.js @@ -52,9 +52,10 @@ class SessionPickerItemView extends TemplateView { render(t, vm) { const deleteButton = t.button({ + className: "destructive", disabled: vm => vm.isDeleting, onClick: this._onDeleteClick.bind(this), - }, "Delete"); + }, "Sign Out"); const clearButton = t.button({ disabled: vm => vm.isClearing, onClick: () => vm.clear(), @@ -70,17 +71,20 @@ class SessionPickerItemView extends TemplateView { onClick: () => setTimeout(() => vm.clearExport(), 100), }, "Download"); })); - - const userName = t.span({className: "userId"}, vm => vm.label); - const errorMessage = t.if(vm => vm.error, t.createTemplate(t => t.span({className: "error"}, vm => vm.error))); - return t.li([t.div({className: "sessionInfo"}, [ - userName, - errorMessage, - downloadExport, - exportButton, - clearButton, - deleteButton, - ])]); + const errorMessage = t.if(vm => vm.error, t.createTemplate(t => t.p({className: "error"}, vm => vm.error))); + return t.li([ + t.div({className: "session-info"}, [ + t.div({className: `avatar usercolor${vm.avatarColorNumber}`}, vm => vm.avatarInitials), + t.div({className: "user-id"}, vm => vm.label), + ]), + t.div({className: "session-actions"}, [ + deleteButton, + exportButton, + downloadExport, + clearButton, + ]), + errorMessage + ]); } } @@ -89,7 +93,7 @@ export class SessionPickerView extends TemplateView { const sessionList = new ListView({ list: vm.sessions, onItemClick: (item, event) => { - if (event.target.closest(".userId")) { + if (event.target.closest(".session-info")) { vm.pick(item.value.id); } }, @@ -98,13 +102,24 @@ export class SessionPickerView extends TemplateView { return new SessionPickerItemView(sessionInfo); }); - return t.div({className: "SessionPickerView"}, [ - t.h1(["Pick a session"]), - t.view(sessionList), - t.p(t.button({onClick: () => vm.cancel()}, ["Log in to a new session instead"])), - t.p(t.button({onClick: async () => vm.import(await selectFileAsText("application/json"))}, "Import")), - t.if(vm => vm.loadViewModel, vm => new SessionLoadView(vm.loadViewModel)), - t.p(hydrogenGithubLink(t)) + return t.div({className: "PreSessionScreen"}, [ + t.div({className: "logo"}), + t.div({className: "SessionPickerView"}, [ + t.h1(["Continue as …"]), + t.view(sessionList), + t.div({className: "button-row"}, [ + t.button({ + className: "styled secondary", + onClick: async () => vm.import(await selectFileAsText("application/json")) + }, vm.i18n`Import a session`), + t.button({ + className: "styled primary", + onClick: () => vm.cancel() + }, vm.i18n`Sign In`) + ]), + t.if(vm => vm.loadViewModel, vm => new SessionLoadView(vm.loadViewModel)), + t.p(hydrogenGithubLink(t)) + ]) ]); } }