forked from mystiq/hydrogen-web
style session picker
This commit is contained in:
parent
19e5d310e0
commit
c12ecd6cc1
7 changed files with 172 additions and 26 deletions
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
6
src/ui/web/css/themes/element/element-logo.svg
Normal file
6
src/ui/web/css/themes/element/element-logo.svg
Normal file
|
@ -0,0 +1,6 @@
|
|||
<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M17.28 2.88C17.28 1.28942 18.5694 0 20.16 0C30.7639 0 39.36 8.59613 39.36 19.2C39.36 20.7906 38.0706 22.08 36.48 22.08C34.8894 22.08 33.6 20.7906 33.6 19.2C33.6 11.7773 27.5827 5.76 20.16 5.76C18.5694 5.76 17.28 4.47058 17.28 2.88Z" fill="#0DBD8B"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M30.72 45.12C30.72 46.7106 29.4306 48 27.84 48C17.2361 48 8.64 39.4039 8.64 28.8C8.64 27.2094 9.92942 25.92 11.52 25.92C13.1106 25.92 14.4 27.2094 14.4 28.8C14.4 36.2227 20.4173 42.24 27.84 42.24C29.4306 42.24 30.72 43.5294 30.72 45.12Z" fill="#0DBD8B"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M2.88 30.72C1.28942 30.72 -5.63623e-08 29.4306 -1.25889e-07 27.84C-5.89399e-07 17.2361 8.59613 8.63997 19.2 8.63997C20.7906 8.63997 22.08 9.92939 22.08 11.52C22.08 13.1106 20.7906 14.4 19.2 14.4C11.7773 14.4 5.76 20.4173 5.76 27.84C5.76 29.4306 4.47058 30.72 2.88 30.72Z" fill="#0DBD8B"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M45.12 17.28C46.7106 17.28 48 18.5694 48 20.16C48 30.7639 39.4039 39.36 28.8 39.36C27.2094 39.36 25.92 38.0706 25.92 36.48C25.92 34.8894 27.2094 33.6 28.8 33.6C36.2227 33.6 42.24 27.5827 42.24 20.16C42.24 18.5694 43.5294 17.28 45.12 17.28Z" fill="#0DBD8B"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.3 KiB |
3
src/ui/web/css/themes/element/icons/chevron-right.svg
Normal file
3
src/ui/web/css/themes/element/icons/chevron-right.svg
Normal file
|
@ -0,0 +1,3 @@
|
|||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M9 18L15 12L9 6" stroke="#8D99A5" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
After Width: | Height: | Size: 212 B |
|
@ -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;
|
||||
|
|
|
@ -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))
|
||||
])
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue