first round of styling for preview

This commit is contained in:
Bruno Windels 2020-12-03 17:11:46 +01:00
parent 822431ac14
commit db2c2b0b25
10 changed files with 267 additions and 87 deletions

48
css/client.css Normal file
View File

@ -0,0 +1,48 @@
.ClientListView .list {
padding: 16px 0;
}
.ClientView {
border: 1px solid #E6E6E6;
border-radius: 8px;
margin: 16px 0;
padding: 16px;
}
.ClientView .header {
display: flex;
}
.ClientView .description {
flex: 1;
}
.ClientView h3 {
margin-top: 0;
}
.ClientView .icon {
border-radius: 8px;
background-repeat: no-repeat;
background-size: cover;
width: 60px;
height: 60px;
}
.ClientView .icon.element-io {
background-image: url('../images/client-icons/element.svg');
}
.ClientView .icon.weechat {
background-image: url('../images/client-icons/weechat.svg');
}
.ClientView .actions a.badge {
display: inline-block;
height: 40px;
margin: 8px 16px 8px 0;
}
.ClientView .actions img {
height: 100%;
}

5
css/create.css Normal file
View File

@ -0,0 +1,5 @@
.CreateLinkView h2 {
padding: 0 40px;
word-break: break-all;
text-align: center;
}

View File

@ -14,6 +14,11 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
@import url('spinner.css');
@import url('client.css');
@import url('preview.css');
@import url('create.css');
:root {
--app-background: #f4f4f4;
--background: #ffffff;
@ -24,6 +29,8 @@ limitations under the License.
--error: #d6001c;
--link: #0098d4;
--borders: #f4f4f4;
--lightgrey: #E6E6E6;
--spinner-stroke-size: 2px;
}
html {
@ -70,14 +77,6 @@ textarea {
padding: 2rem;
}
.PreviewView .preview {
text-align: center;
}
.PreviewView .avatar {
border-radius: 100%;
}
.hidden {
display: none !important;
}
@ -134,45 +133,6 @@ button.text:hover {
cursor: pointer;
}
.ClientListView .list {
padding: 16px 0;
}
.ClientView {
border: 1px solid #E6E6E6;
border-radius: 8px;
margin: 16px 0;
padding: 16px;
}
.ClientView .header {
display: flex;
}
.ClientView .description {
flex: 1;
}
.ClientView h3 {
margin-top: 0;
}
.ClientView .icon {
border-radius: 8px;
background-repeat: no-repeat;
background-size: cover;
width: 60px;
height: 60px;
}
.ClientView .icon.element-io {
background-image: url('../images/client-icons/element.svg');
}
.ClientView .icon.weechat {
background-image: url('../images/client-icons/weechat.svg');
}
.primary, .secondary {
text-decoration: none;
font-weight: bold;
@ -213,19 +173,3 @@ input[type='text'].large {
width: 100%;
box-sizing: border-box;
}
.ClientView .actions a.badge {
display: inline-block;
height: 40px;
margin: 8px 16px 8px 0;
}
.ClientView .actions img {
height: 100%;
}
.CreateLinkView h2 {
padding: 0 40px;
word-break: break-all;
text-align: center;
}

117
css/preview.css Normal file
View File

@ -0,0 +1,117 @@
.PreviewView {
text-align: center;
margin-bottom: 32px;
}
.PreviewView h1 {
font-size: 24px;
line-height: 32px;
margin-bottom: 8px;
}
.PreviewView .avatarContainer {
display: flex;
justify-content: center;
margin: 0;
}
.PreviewView .avatar {
border-radius: 100%;
width: 64px;
height: 64px;
}
.PreviewView .spinner {
width: 32px;
height: 32px;
}
.PreviewView .avatar.loading {
border: 1px solid #eee;
display: flex;
align-items: center;
justify-content: center;
}
.PreviewView .identifier {
color: var(--grey);
font-size: 12px;
margin: 8px 0;
}
.PreviewView .identifier.placeholder {
height: 1em;
margin: 16px 30%;
}
.PreviewView .memberCount {
display: flex;
justify-content: center;
margin: 8px 0;
}
.PreviewView .memberCount p:not(.placeholder) {
background-image: url(../images/member-icon.svg);
background-repeat: no-repeat;
background-position: 2px center;
padding: 0 4px 0 24px;
}
.PreviewView .memberCount.loading {
margin: 16px 0;
}
.PreviewView .memberCount p {
background-color: var(--lightgrey);
padding: 0 4px;
border-radius: 8px;
font-size: 12px;
margin: 0;
}
.PreviewView .memberCount p.placeholder {
height: 1.5em;
width: 80px;
}
.PreviewView .topic {
font-size: 12px;
color: var(--grey);
margin: 32px;
}
.PreviewView .topic.loading {
display: block;
}
.PreviewView .topic.loading .placeholder {
height: 0.8em;
display: block;
margin: 12px 0;
}
.PreviewView .topic.loading .placeholder:nth-child(2) {
margin-left: 5%;
margin-right: 5%;
}
.placeholder {
border-radius: 1em;
--flash-bg: #ddd;
--flash-fg: #eee;
background: linear-gradient(120deg,
var(--flash-bg),
var(--flash-bg) 10%,
var(--flash-fg) calc(10% + 25px),
var(--flash-bg) calc(10% + 50px)
);
animation: flash 2s ease-in-out infinite;
background-size: 200%;
}
@keyframes flash {
0% { background-position-x: 0; }
50% { background-position-x: -80%; }
51% { background-position-x: 40%; }
100% { background-position-x: 0%; }
}

27
css/spinner.css Normal file
View File

@ -0,0 +1,27 @@
@keyframes rotate {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.spinner {
width: 40px;
height: 40px;
border-radius: 100%;
border: var(--spinner-stroke-size) solid var(--app-background);
box-sizing: border-box;
}
.spinner::before {
content: "";
display: block;
width: inherit;
height: inherit;
border-radius: 100%;
border-width: var(--spinner-stroke-size);
border-style: solid;
border-color: transparent;
border-top-color: var(--grey);
animation: rotate 0.8s linear infinite;
box-sizing: border-box;
margin: calc(-1 * var(--spinner-stroke-size));
}

4
images/chat-icon.svg Normal file
View File

@ -0,0 +1,4 @@
<svg width="36" height="38" viewBox="0 0 36 38" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M19.4184 32.9995C20.2122 31.5062 20.6613 29.8061 20.6613 28.0024C20.6613 22.0566 15.7808 17.2365 9.76039 17.2365C6.93884 17.2365 4.36767 18.2952 2.43193 20.0323C2.21698 18.9773 2.10413 17.8851 2.10413 16.7666C2.10413 7.78278 9.38427 0.5 18.3648 0.5C27.3453 0.5 34.6254 7.78278 34.6254 16.7666C34.6254 19.283 34.0542 21.6659 33.0345 23.7928L35.874 33.0245C36.1106 33.7938 35.3879 34.5131 34.6198 34.273L25.4569 31.4085C23.6119 32.3044 21.5722 32.8617 19.4184 32.9995Z" fill="black"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.8203 37.1184C13.4204 37.1184 17.1495 33.4167 17.1495 28.8504C17.1495 24.2842 13.4204 20.5825 8.8203 20.5825C4.22021 20.5825 0.491095 24.2842 0.491095 28.8504C0.491095 30.1291 0.783484 31.3399 1.30551 32.4206L0.126628 36.2238C-0.111642 36.9925 0.609435 37.7134 1.37807 37.475L5.18834 36.293C6.28601 36.8218 7.51826 37.1184 8.8203 37.1184Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

7
images/member-icon.svg Normal file
View File

@ -0,0 +1,7 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<mask id="path-1-inside-1" fill="white">
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.1332 16.3632C11.1709 16.7731 10.112 17 9 17C4.58172 17 1 13.4183 1 9C1 4.58172 4.58172 1 9 1C13.4183 1 17 4.58172 17 9C17 11.6173 15.7432 13.941 13.8001 15.4005V15.4005C13.4881 15.6349 13.1583 15.847 12.8133 16.0344C12.5926 16.1543 12.3657 16.2641 12.1332 16.3632ZM9.00004 9.39998C10.3255 9.39998 11.4 8.23592 11.4 6.79998C11.4 5.36404 10.3255 4.19998 9.00004 4.19998C7.67456 4.19998 6.60004 5.36404 6.60004 6.79998C6.60004 8.23592 7.67456 9.39998 9.00004 9.39998ZM8.99989 15.4001C10.7295 15.4001 12.2989 14.7139 13.4507 13.599C12.7384 11.8404 11.0141 10.6 9.00009 10.6C6.98597 10.6 5.26159 11.8406 4.54932 13.5992C5.7011 14.714 7.27038 15.4001 8.99989 15.4001Z"/>
</mask>
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.1332 16.3632C11.1709 16.7731 10.112 17 9 17C4.58172 17 1 13.4183 1 9C1 4.58172 4.58172 1 9 1C13.4183 1 17 4.58172 17 9C17 11.6173 15.7432 13.941 13.8001 15.4005V15.4005C13.4881 15.6349 13.1583 15.847 12.8133 16.0344C12.5926 16.1543 12.3657 16.2641 12.1332 16.3632ZM9.00004 9.39998C10.3255 9.39998 11.4 8.23592 11.4 6.79998C11.4 5.36404 10.3255 4.19998 9.00004 4.19998C7.67456 4.19998 6.60004 5.36404 6.60004 6.79998C6.60004 8.23592 7.67456 9.39998 9.00004 9.39998ZM8.99989 15.4001C10.7295 15.4001 12.2989 14.7139 13.4507 13.599C12.7384 11.8404 11.0141 10.6 9.00009 10.6C6.98597 10.6 5.26159 11.8406 4.54932 13.5992C5.7011 14.714 7.27038 15.4001 8.99989 15.4001Z" fill="#666666"/>
<path d="M12.1332 16.3632L11.3492 14.5232L12.1332 16.3632ZM13.8001 15.4005H11.8001V19.4042L15.0013 16.9996L13.8001 15.4005ZM13.8001 15.4005H15.8001V11.3967L12.5989 13.8014L13.8001 15.4005ZM12.8133 16.0344L13.768 17.7919L13.768 17.7919L12.8133 16.0344ZM13.4507 13.599L14.8418 15.036L15.8107 14.098L15.3044 12.8481L13.4507 13.599ZM4.54932 13.5992L2.69558 12.8485L2.18935 14.0984L3.15837 15.0363L4.54932 13.5992ZM9 19C10.3863 19 11.7115 18.7168 12.9171 18.2031L11.3492 14.5232C10.6303 14.8295 9.83767 15 9 15V19ZM-1 9C-1 14.5228 3.47715 19 9 19V15C5.68629 15 3 12.3137 3 9H-1ZM9 -1C3.47715 -1 -1 3.47715 -1 9H3C3 5.68629 5.68629 3 9 3V-1ZM19 9C19 3.47715 14.5228 -1 9 -1V3C12.3137 3 15 5.68629 15 9H19ZM15.0013 16.9996C17.4256 15.1786 19 12.2729 19 9H15C15 10.9617 14.0607 12.7034 12.5989 13.8014L15.0013 16.9996ZM15.8001 15.4005V15.4005H11.8001V15.4005H15.8001ZM12.5989 13.8014C12.3645 13.9774 12.1171 14.1365 11.8586 14.277L13.768 17.7919C14.1995 17.5574 14.6116 17.2923 15.0013 16.9996L12.5989 13.8014ZM12.9171 18.2031C13.2082 18.0791 13.4921 17.9418 13.768 17.7919L11.8586 14.277C11.6932 14.3668 11.5233 14.449 11.3492 14.5232L12.9171 18.2031ZM9.40004 6.79998C9.40004 7.01655 9.31985 7.18185 9.22749 7.2819C9.13763 7.37925 9.05614 7.39998 9.00004 7.39998V11.4C11.5778 11.4 13.4 9.18675 13.4 6.79998H9.40004ZM9.00004 6.19998C9.05614 6.19998 9.13763 6.22071 9.22749 6.31806C9.31985 6.41811 9.40004 6.58341 9.40004 6.79998H13.4C13.4 4.41321 11.5778 2.19998 9.00004 2.19998V6.19998ZM8.60004 6.79998C8.60004 6.58341 8.68024 6.41811 8.77259 6.31806C8.86246 6.22071 8.94395 6.19998 9.00004 6.19998V2.19998C6.42228 2.19998 4.60004 4.41321 4.60004 6.79998H8.60004ZM9.00004 7.39998C8.94395 7.39998 8.86246 7.37925 8.77259 7.28189C8.68024 7.18184 8.60004 7.01655 8.60004 6.79998H4.60004C4.60004 9.18675 6.42228 11.4 9.00004 11.4V7.39998ZM12.0597 12.162C11.2659 12.9304 10.1898 13.4001 8.99989 13.4001V17.4001C11.2693 17.4001 13.332 16.4975 14.8418 15.036L12.0597 12.162ZM9.00009 12.6C10.1718 12.6 11.18 13.3204 11.597 14.3498L15.3044 12.8481C14.2968 10.3605 11.8564 8.60004 9.00009 8.60004V12.6ZM6.40306 14.35C6.82004 13.3204 7.82833 12.6 9.00009 12.6V8.60004C6.1436 8.60004 3.70313 10.3607 2.69558 12.8485L6.40306 14.35ZM8.99989 13.4001C7.81008 13.4001 6.73407 12.9304 5.94027 12.1621L3.15837 15.0363C4.66812 16.4976 6.73068 17.4001 8.99989 17.4001V13.4001Z" fill="#666666" mask="url(#path-1-inside-1)"/>
</svg>

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

@ -22,14 +22,12 @@ export class OpenLinkView extends TemplateView {
render(t, vm) {
return t.div({className: "OpenLinkView card"}, [
t.view(new PreviewView(vm.previewViewModel)),
t.div({className: {hidden: vm => vm.previewLoading}}, [
t.p({className: {hidden: vm => vm.clientsViewModel}}, t.button({
className: "primary fullwidth",
onClick: () => vm.showClients()
}, vm => vm.showClientsLabel)),
t.mapView(vm => vm.clientsViewModel, childVM => childVM ? new ClientListView(childVM) : null),
t.p(["Preview provided by ", vm => vm.previewDomain]),
])
t.p({className: {accept: true, hidden: vm => vm.clientsViewModel}}, t.button({
className: "primary fullwidth",
onClick: () => vm.showClients()
}, vm => vm.showClientsLabel)),
t.mapView(vm => vm.clientsViewModel, childVM => childVM ? new ClientListView(childVM) : null),
t.p({className: {hidden: vm => !vm.previewDomain}}, ["Preview provided by ", vm => vm.previewDomain]),
]);
}
}
}

View File

@ -19,18 +19,35 @@ import {ClientListView} from "../open/ClientListView.js";
import {ClientView} from "../open/ClientView.js";
export class PreviewView extends TemplateView {
render(t, vm) {
return t.mapView(vm => vm.loading, loading => loading ? new LoadingPreviewView(vm) : new LoadedPreviewView(vm));
}
}
class LoadingPreviewView extends TemplateView {
render(t, vm) {
return t.div({className: "PreviewView"}, [
t.div({className: "avatarContainer"}, t.div({className: "avatar loading"}, t.div({className: "spinner"}))),
t.h1(vm => vm.identifier),
t.p({className: "identifier placeholder"}),
t.div({className: {memberCount: true, loading: true, hidden: !vm.hasMemberCount}}, t.p({className: "placeholder"})),
t.p({className: {topic: true, loading: true, hidden: !vm.hasTopic}}, [
t.div({className: "placeholder"}),
t.div({className: "placeholder"}),
t.div({className: "placeholder"}),
]),
]);
}
}
class LoadedPreviewView extends TemplateView {
render(t, vm) {
return t.div({className: "PreviewView"}, [
t.h1({className: {hidden: vm => !vm.loading}}, "Loading preview…"),
t.div({className: {hidden: vm => vm.loading}}, [
t.div({className: "preview"}, [
t.p(t.img({className: "avatar", src: vm => vm.avatarUrl})),
t.h1(vm => vm.name),
t.p({className: "identifier"}, vm => vm.identifier),
t.p({className: {memberCount: true, hidden: vm => !vm.memberCount}}, [vm => vm.memberCount, " members"]),
t.p({className: {topic: true, hidden: vm => !vm.topic}}, [vm => vm.topic]),
]),
])
t.div({className: "avatarContainer"}, t.img({className: "avatar", src: vm => vm.avatarUrl})),
t.h1(vm => vm.name),
t.p({className: {identifier: true, hidden: vm => !vm.identifier}}, vm => vm.identifier),
t.div({className: {memberCount: true, hidden: vm => !vm.memberCount}}, t.p([vm => vm.memberCount, " members"])),
t.p({className: {topic: true, hidden: vm => !vm.topic}}, [vm => vm.topic]),
]);
}
}

View File

@ -29,7 +29,7 @@ export class PreviewViewModel extends ViewModel {
this.loading = false;
this.name = null;
this.avatarUrl = null;
this.identifier = null;
this.identifier = this._link.identifier;
this.memberCount = null;
this.topic = null;
this.previewDomain = null;
@ -38,6 +38,7 @@ export class PreviewViewModel extends ViewModel {
async load() {
this.loading = true;
this.emitChange();
// await new Promise(r => setTimeout(r, 5000));
for (const server of this._consentedServers) {
try {
const homeserver = await resolveServer(this.request, server);
@ -51,15 +52,21 @@ export class PreviewViewModel extends ViewModel {
}
// assume we're done if nothing threw
this.previewDomain = server;
break;
this.loading = false;
this.emitChange();
return;
} catch (err) {
continue;
}
}
this.loading = false;
this.emitChange();
this._setNoPreview(this._link);
this.loading = false;
this.emitChange();
}
get hasTopic() { return this._link.kind === LinkKind.Room; }
get hasMemberCount() { return this.hasTopic; }
async _loadUserPreview(homeserver, userId) {
const profile = await homeserver.getUserProfile(userId);
this.name = profile.displayname || userId;
@ -87,4 +94,10 @@ export class PreviewViewModel extends ViewModel {
this.topic = publicRoom?.topic;
this.identifier = publicRoom?.canonical_alias || link.identifier;
}
}
_setNoPreview(link) {
this.name = link.identifier;
this.identifier = null;
this.avatarUrl = "images/chat-icon.svg";
}
}