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))
+ ])
]);
}
}