very basic client list code
This commit is contained in:
parent
eded08595a
commit
8659594c22
16 changed files with 482 additions and 15 deletions
30
css/main.css
30
css/main.css
|
@ -122,4 +122,34 @@ textarea {
|
||||||
|
|
||||||
a {
|
a {
|
||||||
color: var(--link);
|
color: var(--link);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ClientListView ul {
|
||||||
|
list-style: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.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');
|
||||||
}
|
}
|
6
images/client-icons/element.svg
Normal file
6
images/client-icons/element.svg
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M25.28 10.88C25.28 9.28942 26.5694 8 28.16 8C38.7639 8 47.36 16.5961 47.36 27.2C47.36 28.7906 46.0706 30.08 44.48 30.08C42.8894 30.08 41.6 28.7906 41.6 27.2C41.6 19.7773 35.5827 13.76 28.16 13.76C26.5694 13.76 25.28 12.4706 25.28 10.88Z" fill="#0DBD8B"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M38.72 53.12C38.72 54.7106 37.4306 56 35.84 56C25.2361 56 16.64 47.4039 16.64 36.8C16.64 35.2094 17.9294 33.92 19.52 33.92C21.1105 33.92 22.4 35.2094 22.4 36.8C22.4 44.2227 28.4173 50.24 35.84 50.24C37.4306 50.24 38.72 51.5294 38.72 53.12Z" fill="#0DBD8B"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.88 38.72C9.28942 38.72 8 37.4306 8 35.84C8 25.2361 16.5961 16.64 27.2 16.64C28.7906 16.64 30.08 17.9294 30.08 19.52C30.08 21.1105 28.7906 22.4 27.2 22.4C19.7773 22.4 13.76 28.4173 13.76 35.84C13.76 37.4306 12.4706 38.72 10.88 38.72Z" fill="#0DBD8B"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M53.12 25.28C54.7106 25.28 56 26.5694 56 28.16C56 38.7639 47.4039 47.36 36.8 47.36C35.2094 47.36 33.92 46.0706 33.92 44.48C33.92 42.8895 35.2094 41.6 36.8 41.6C44.2227 41.6 50.24 35.5827 50.24 28.16C50.24 26.5694 51.5294 25.28 53.12 25.28Z" fill="#0DBD8B"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.3 KiB |
10
images/matrix-logo.svg
Normal file
10
images/matrix-logo.svg
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<svg width="66" height="28" viewBox="0 0 66 28" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M0.975097 0.640961V27.359H2.89517V28H0.238281V0H2.89517V0.640961H0.975097Z" fill="#2D2D2D"/>
|
||||||
|
<path d="M8.37266 9.11071V10.4628H8.4111C8.7712 9.94812 9.20494 9.54849 9.71306 9.26518C10.2208 8.98235 10.8029 8.84036 11.4586 8.84036C12.0885 8.84036 12.664 8.96298 13.1846 9.2074C13.7054 9.45223 14.1009 9.88336 14.371 10.5015C14.6665 10.0638 15.0683 9.67744 15.5764 9.34266C16.0842 9.00804 16.6852 8.84036 17.3797 8.84036C17.9069 8.84036 18.3953 8.90487 18.8457 9.03365C19.2955 9.16242 19.6812 9.36843 20.0027 9.65166C20.3239 9.93515 20.5746 10.3053 20.755 10.7621C20.9349 11.2196 21.025 11.7698 21.025 12.4139V19.0966H18.2861V13.4373C18.2861 13.1027 18.2734 12.7872 18.2475 12.4908C18.2216 12.1949 18.1512 11.9375 18.0354 11.7183C17.9196 11.4996 17.7491 11.3256 17.5243 11.1967C17.2993 11.0684 16.9938 11.0037 16.6081 11.0037C16.2225 11.0037 15.9106 11.0782 15.6727 11.2257C15.4346 11.374 15.2483 11.5673 15.1134 11.8052C14.9784 12.0438 14.8884 12.314 14.8435 12.6168C14.7982 12.9192 14.7759 13.2252 14.7759 13.5342V19.0966H12.0372V13.4955C12.0372 13.1994 12.0305 12.9063 12.0181 12.6168C12.005 12.3269 11.9506 12.0598 11.8539 11.815C11.7575 11.5706 11.5967 11.374 11.3717 11.2257C11.1467 11.0782 10.8156 11.0037 10.3785 11.0037C10.2497 11.0037 10.0794 11.0327 9.86746 11.0908C9.65528 11.1487 9.44941 11.2584 9.25027 11.4191C9.05071 11.5802 8.88053 11.812 8.73908 12.1143C8.59754 12.4171 8.5269 12.8128 8.5269 13.3021V19.0966H5.78833V9.11071H8.37266Z" fill="#2D2D2D"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M23.8596 9.55506C23.4223 9.81286 23.0621 10.1539 22.7794 10.5789C22.4962 11.0036 22.3357 11.5382 22.2974 12.1818H25.036C25.0872 11.6412 25.2676 11.2547 25.5761 11.023C25.8847 10.7912 26.309 10.6752 26.8491 10.6752C27.0931 10.6752 27.3215 10.6917 27.5338 10.7234C27.7458 10.7558 27.9322 10.8202 28.093 10.9167C28.2537 11.0132 28.3823 11.1487 28.4787 11.3224C28.5752 11.4962 28.6233 11.7313 28.6233 12.0273C28.6359 12.3108 28.5523 12.5264 28.3726 12.6745C28.1924 12.8227 27.9483 12.9352 27.6397 13.0124C27.3311 13.0897 26.9774 13.1477 26.5789 13.1864C26.1802 13.225 25.7753 13.2766 25.3638 13.3408C24.9523 13.4056 24.5441 13.4923 24.1392 13.6016C23.734 13.711 23.374 13.8752 23.0592 14.094C22.7437 14.3131 22.4867 14.6059 22.2876 14.9731C22.0879 15.3398 21.9884 15.8067 21.9884 16.3731C21.9884 16.8879 22.0753 17.3326 22.2489 17.706C22.4225 18.0793 22.6635 18.3884 22.9722 18.6327C23.2807 18.8778 23.6406 19.0579 24.0522 19.1739C24.4636 19.2896 24.9072 19.3476 25.3831 19.3476C26.0003 19.3476 26.6046 19.2572 27.1963 19.0774C27.7873 18.897 28.3018 18.5815 28.739 18.1308C28.7517 18.2983 28.7741 18.4625 28.8065 18.6232C28.8385 18.7843 28.8804 18.9418 28.932 19.0965H31.7091C31.5805 18.8906 31.4903 18.5815 31.4393 18.1693C31.3877 17.7573 31.362 17.3264 31.362 16.8751V11.6798C31.362 11.0745 31.227 10.5883 30.957 10.2214C30.6868 9.85459 30.3398 9.56787 29.9155 9.36194C29.4911 9.15619 29.0217 9.0176 28.5074 8.94652C27.9931 8.87594 27.4854 8.84036 26.9838 8.84036C26.431 8.84036 25.8812 8.89531 25.3348 9.00463C24.7882 9.1142 24.2966 9.2976 23.8596 9.55506ZM27.6302 14.5965C27.8293 14.5578 28.0159 14.5096 28.1893 14.4518C28.363 14.3937 28.5076 14.3134 28.6235 14.21V15.2339C28.6235 15.3884 28.6072 15.5944 28.5754 15.8519C28.5431 16.1098 28.4562 16.3636 28.3149 16.6146C28.1732 16.8659 27.9548 17.0817 27.6592 17.2618C27.3632 17.4423 26.9455 17.5322 26.4055 17.5322C26.1868 17.5322 25.9747 17.5129 25.7692 17.4742C25.5632 17.4358 25.3833 17.368 25.2291 17.2715C25.0748 17.175 24.9525 17.0431 24.8625 16.8754C24.7724 16.7084 24.7275 16.502 24.7275 16.2576C24.7275 16.0001 24.7724 15.7876 24.8625 15.6201C24.9525 15.4531 25.0713 15.3145 25.2194 15.205C25.3671 15.0956 25.5407 15.0089 25.7402 14.9441C25.9393 14.88 26.1418 14.828 26.3476 14.7897C26.566 14.7511 26.7846 14.719 27.0034 14.693C27.2219 14.6674 27.4308 14.6352 27.6302 14.5965Z" fill="#2D2D2D"/>
|
||||||
|
<path d="M38.5753 9.11176V10.9467H36.5696V15.8914C36.5696 16.3547 36.6467 16.6639 36.8011 16.8183C36.9552 16.9728 37.264 17.05 37.7268 17.05C37.8812 17.05 38.0288 17.0437 38.1704 17.0307C38.3117 17.0181 38.4468 16.9985 38.5753 16.9729V19.0975C38.3439 19.1362 38.0866 19.1618 37.8039 19.1749C37.521 19.1873 37.2446 19.194 36.9746 19.194C36.5503 19.194 36.1484 19.1649 35.7692 19.1069C35.3897 19.0491 35.0555 18.9367 34.7663 18.7691C34.4769 18.602 34.2486 18.3635 34.0816 18.0544C33.9143 17.7457 33.8308 17.3399 33.8308 16.8375V10.9467H32.1722V9.11176H33.8308V6.11795H36.5696V9.11176H38.5753Z" fill="#2D2D2D"/>
|
||||||
|
<path d="M42.4905 9.11088V10.9652H42.5291C42.6575 10.6559 42.831 10.3697 43.0498 10.1055C43.2684 9.84179 43.519 9.61625 43.8019 9.42953C44.0845 9.24315 44.3869 9.09824 44.7086 8.99491C45.0297 8.89207 45.3642 8.84036 45.7115 8.84036C45.8914 8.84036 46.0905 8.87278 46.3093 8.93705V11.4868C46.1806 11.4608 46.0263 11.4382 45.8465 11.4191C45.6663 11.3997 45.4928 11.39 45.3256 11.39C44.8242 11.39 44.3999 11.474 44.0529 11.6411C43.7057 11.8086 43.4262 12.0369 43.2139 12.3267C43.0018 12.6166 42.8504 12.9544 42.7605 13.3408C42.6706 13.727 42.6256 14.1457 42.6256 14.5963V19.0966H39.8869V9.11088H42.4905Z" fill="#2D2D2D"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M47.467 5.3064V7.56622H50.2059V5.3064H47.467ZM50.2054 19.0974V9.11166H47.4665V19.0974H50.2054Z" fill="#2D2D2D"/>
|
||||||
|
<path d="M51.6319 9.1106H54.7563L56.5115 11.7181L58.2473 9.1106H61.2753L57.9966 13.7849L61.6805 19.0964H58.5559L56.4729 15.9482L54.3898 19.0964H51.3235L54.9107 13.843L51.6319 9.1106Z" fill="#2D2D2D"/>
|
||||||
|
<path d="M65.0246 27.359V0.640961H63.1046V0H65.7616V28H63.1046V27.359H65.0246Z" fill="#2D2D2D"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 5.5 KiB |
|
@ -1,8 +1,9 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title></title>
|
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
|
<title>matrix.to - you're invited to chat on matrix</title>
|
||||||
|
<meta name="description" content="You're invited to chat on matrix">
|
||||||
<meta name="viewport" content="width=device-width, user-scalable=no">
|
<meta name="viewport" content="width=device-width, user-scalable=no">
|
||||||
<link rel="stylesheet" type="text/css" href="css/main.css">
|
<link rel="stylesheet" type="text/css" href="css/main.css">
|
||||||
</head>
|
</head>
|
||||||
|
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
||||||
import {Link} from "./Link.js";
|
import {Link} from "./Link.js";
|
||||||
import {ViewModel} from "./utils/ViewModel.js";
|
import {ViewModel} from "./utils/ViewModel.js";
|
||||||
import {PreviewViewModel} from "./preview/PreviewViewModel.js";
|
import {PreviewViewModel} from "./preview/PreviewViewModel.js";
|
||||||
|
import {Element} from "./client/clients/Element.js";
|
||||||
|
|
||||||
export class RootViewModel extends ViewModel {
|
export class RootViewModel extends ViewModel {
|
||||||
constructor(options) {
|
constructor(options) {
|
||||||
|
@ -28,9 +29,13 @@ export class RootViewModel extends ViewModel {
|
||||||
_updateChildVMs(oldLink) {
|
_updateChildVMs(oldLink) {
|
||||||
if (this.link) {
|
if (this.link) {
|
||||||
if (!oldLink || !oldLink.equals(this.link)) {
|
if (!oldLink || !oldLink.equals(this.link)) {
|
||||||
|
const element = new Element();
|
||||||
this.previewViewModel = new PreviewViewModel(this.childOptions({
|
this.previewViewModel = new PreviewViewModel(this.childOptions({
|
||||||
link: this.link,
|
link: this.link,
|
||||||
consentedServers: this.link.servers
|
consentedServers: this.link.servers,
|
||||||
|
// preferredClient: element,
|
||||||
|
// preferredPlatform: this.platforms[0],
|
||||||
|
clients: [element]
|
||||||
}));
|
}));
|
||||||
this.previewViewModel.load();
|
this.previewViewModel.load();
|
||||||
}
|
}
|
||||||
|
|
28
src/client/ClientListView.js
Normal file
28
src/client/ClientListView.js
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
/*
|
||||||
|
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {TemplateView} from "../utils/TemplateView.js";
|
||||||
|
import {ClientView} from "./ClientView.js";
|
||||||
|
|
||||||
|
export class ClientListView extends TemplateView {
|
||||||
|
render(t, vm) {
|
||||||
|
const clients = vm.clients.map(clientViewModel => t.view(new ClientView(clientViewModel)));
|
||||||
|
return t.div({className: "ClientListView"}, [
|
||||||
|
t.h3("You need an app to continue"),
|
||||||
|
t.ul({className: "ClientListView"}, clients)
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
27
src/client/ClientListViewModel.js
Normal file
27
src/client/ClientListViewModel.js
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
/*
|
||||||
|
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {isWebPlatform, Platform} from "./Platform.js";
|
||||||
|
import {ClientViewModel} from "./ClientViewModel.js";
|
||||||
|
import {ViewModel} from "../utils/ViewModel.js";
|
||||||
|
|
||||||
|
export class ClientListViewModel extends ViewModel {
|
||||||
|
constructor(options) {
|
||||||
|
super(options);
|
||||||
|
const {clients, link} = options;
|
||||||
|
this.clients = clients.map(client => new ClientViewModel(this.childOptions({client, link})));
|
||||||
|
}
|
||||||
|
}
|
34
src/client/ClientView.js
Normal file
34
src/client/ClientView.js
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {TemplateView} from "../utils/TemplateView.js";
|
||||||
|
|
||||||
|
export class ClientView extends TemplateView {
|
||||||
|
render(t, vm) {
|
||||||
|
return t.li({className: "ClientView"}, [
|
||||||
|
t.div({className: "header"}, [
|
||||||
|
t.div({className: "description"}, [
|
||||||
|
t.h3(vm.name),
|
||||||
|
t.p(vm.description),
|
||||||
|
]),
|
||||||
|
t.div({className: `icon ${vm.clientId}`})
|
||||||
|
]),
|
||||||
|
t.div({className: "actions"}, vm.actions.map(a => {
|
||||||
|
return t.a({href: a.url, className: a.kind, rel: "noopener noreferrer", onClick: () => a.activated()}, a.label);
|
||||||
|
}))
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
66
src/client/ClientViewModel.js
Normal file
66
src/client/ClientViewModel.js
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
/*
|
||||||
|
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {isWebPlatform, Platform} from "./Platform.js";
|
||||||
|
import {ViewModel} from "../utils/ViewModel.js";
|
||||||
|
|
||||||
|
export class ClientViewModel extends ViewModel {
|
||||||
|
constructor(options) {
|
||||||
|
super(options);
|
||||||
|
const {client, link} = options;
|
||||||
|
this._client = client;
|
||||||
|
const supportedPlatforms = client.platforms;
|
||||||
|
const matchingPlatforms = this.platforms.filter(p => {
|
||||||
|
return supportedPlatforms.includes(p);
|
||||||
|
});
|
||||||
|
const nativePlatform = matchingPlatforms.find(p => !isWebPlatform(p));
|
||||||
|
const webPlatform = this.platforms.find(p => isWebPlatform(p));
|
||||||
|
this.actions = this._createActions(client, link, nativePlatform, webPlatform);
|
||||||
|
this.name = this._client.getName(nativePlatform || webPlatform);
|
||||||
|
}
|
||||||
|
|
||||||
|
_createActions(client, link, nativePlatform, webPlatform) {
|
||||||
|
let actions = [];
|
||||||
|
if (nativePlatform) {
|
||||||
|
const nativeActions = client.getInstallLinks(nativePlatform).map(installLink => {
|
||||||
|
return {
|
||||||
|
label: installLink.description,
|
||||||
|
url: installLink.createInstallURL(link),
|
||||||
|
kind: installLink.channelId,
|
||||||
|
activated() {},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
actions.push(...nativeActions);
|
||||||
|
}
|
||||||
|
if (webPlatform) {
|
||||||
|
actions.push({
|
||||||
|
label: `Or open in ${client.getName(webPlatform)}`,
|
||||||
|
url: client.getDeepLink(webPlatform, link),
|
||||||
|
kind: "open-in-web",
|
||||||
|
activated() {},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return actions;
|
||||||
|
}
|
||||||
|
|
||||||
|
get description() {
|
||||||
|
return this._client.description;
|
||||||
|
}
|
||||||
|
|
||||||
|
get clientId() {
|
||||||
|
return this._client.id;
|
||||||
|
}
|
||||||
|
}
|
36
src/client/Platform.js
Normal file
36
src/client/Platform.js
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {createEnum} from "../utils/enum.js";
|
||||||
|
|
||||||
|
export const Platform = createEnum(
|
||||||
|
"DesktopWeb",
|
||||||
|
"MobileWeb",
|
||||||
|
"Android",
|
||||||
|
"iOS",
|
||||||
|
"Windows",
|
||||||
|
"macOS",
|
||||||
|
"Linux"
|
||||||
|
);
|
||||||
|
|
||||||
|
export function guessApplicablePlatforms(userAgent) {
|
||||||
|
// use https://github.com/faisalman/ua-parser-js to guess, and pass as RootVM options
|
||||||
|
return [Platform.DesktopWeb, Platform.Linux];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isWebPlatform(p) {
|
||||||
|
return p === Platform.DesktopWeb || p === Platform.MobileWeb;
|
||||||
|
}
|
81
src/client/clients/Element.js
Normal file
81
src/client/clients/Element.js
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
/*
|
||||||
|
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {Maturity, Platform, LinkKind,
|
||||||
|
FDroidLink, AppleStoreLink, PlayStoreLink, WebsiteLink} from "../types.js";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Information on how to deep link to a given matrix client.
|
||||||
|
*/
|
||||||
|
export class Element {
|
||||||
|
/* should only contain alphanumerical and -_, no dots (needs to be usable as css class) */
|
||||||
|
get id() { return "element-io"; }
|
||||||
|
|
||||||
|
get platforms() {
|
||||||
|
return [
|
||||||
|
Platform.Android, Platform.iOS,
|
||||||
|
Platform.Windows, Platform.macOS, Platform.Linux,
|
||||||
|
Platform.DesktopWeb, Platform.MobileWeb
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
get description() { return 'Fully-featured Matrix client'; }
|
||||||
|
|
||||||
|
getMaturity(platform) { return Maturity.Stable; }
|
||||||
|
|
||||||
|
getLinkSupport(platform, link) { return true; }
|
||||||
|
|
||||||
|
getDeepLink(platform, link) {
|
||||||
|
let fragmentPath;
|
||||||
|
switch (link.kind) {
|
||||||
|
case LinkKind.User:
|
||||||
|
fragmentPath = `user/${link.identifier}`;
|
||||||
|
break;
|
||||||
|
case LinkKind.Room:
|
||||||
|
fragmentPath = `room/${link.identifier}`;
|
||||||
|
break;
|
||||||
|
case LinkKind.Group:
|
||||||
|
fragmentPath = `group/${link.identifier}`;
|
||||||
|
break;
|
||||||
|
case LinkKind.Event:
|
||||||
|
fragmentPath = `room/${link.identifier}/${link.eventId}`;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (platform === Platform.DesktopWeb || platform === Platform.MobileWeb || platform === Platform.iOS) {
|
||||||
|
return `https://app.element.io/#/${fragmentPath}`;
|
||||||
|
} else {
|
||||||
|
return `element://${fragmentPath}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getLinkInstructions(platform, link) {}
|
||||||
|
|
||||||
|
getName(platform) {
|
||||||
|
if (platform === Platform.DesktopWeb || platform === Platform.MobileWeb) {
|
||||||
|
return "Element Web";
|
||||||
|
} else {
|
||||||
|
return "Element";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getInstallLinks(platform) {
|
||||||
|
switch (platform) {
|
||||||
|
case Platform.iOS: return [new AppleStoreLink('vector', 'id1083446067')];
|
||||||
|
case Platform.Android: return [new PlayStoreLink('im.vector.app'), new FDroidLink('im.vector.app')];
|
||||||
|
default: return [new WebsiteLink("https://element.io/get-started")];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
94
src/client/types.js
Normal file
94
src/client/types.js
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
/*
|
||||||
|
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {createEnum} from "../utils/enum.js";
|
||||||
|
export const Maturity = createEnum("Alpha", "Beta", "Stable");
|
||||||
|
export {LinkKind} from "../Link.js";
|
||||||
|
export {Platform} from "./Platform.js";
|
||||||
|
|
||||||
|
export class AppleStoreLink {
|
||||||
|
constructor(org, appId) {
|
||||||
|
this._org = org;
|
||||||
|
this._appId = appId;
|
||||||
|
}
|
||||||
|
|
||||||
|
createInstallURL(link) {
|
||||||
|
return `https://apps.apple.com/app/${encodeURIComponent(this._org)}/${encodeURIComponent(this._appId)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
get channelId() {
|
||||||
|
return "apple-app-store";
|
||||||
|
}
|
||||||
|
|
||||||
|
get description() {
|
||||||
|
return "Download on the App Store";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PlayStoreLink {
|
||||||
|
constructor(appId) {
|
||||||
|
this._appId = appId;
|
||||||
|
}
|
||||||
|
|
||||||
|
createInstallURL(link) {
|
||||||
|
return `https://play.google.com/store/apps/details?id=${encodeURIComponent(this._appId)}&referrer=${encodeURIComponent(link.identifier)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
get channelId() {
|
||||||
|
return "play-store";
|
||||||
|
}
|
||||||
|
|
||||||
|
get description() {
|
||||||
|
return "Get it on Google Play";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class FDroidLink {
|
||||||
|
constructor(appId) {
|
||||||
|
this._appId = appId;
|
||||||
|
}
|
||||||
|
|
||||||
|
createInstallURL(link) {
|
||||||
|
return `https://f-droid.org/packages/${encodeURIComponent(this._appId)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
get channelId() {
|
||||||
|
return "fdroid";
|
||||||
|
}
|
||||||
|
|
||||||
|
get description() {
|
||||||
|
return "Get it on F-Droid";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export class WebsiteLink {
|
||||||
|
constructor(url) {
|
||||||
|
this._url = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
createInstallURL(link) {
|
||||||
|
return this._url;
|
||||||
|
}
|
||||||
|
|
||||||
|
get channelId() {
|
||||||
|
return "website";
|
||||||
|
}
|
||||||
|
|
||||||
|
get description() {
|
||||||
|
return `Download from ${new URL(this._url).hostname}`;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,9 +1,14 @@
|
||||||
import {xhrRequest} from "./utils/xhr.js";
|
import {xhrRequest} from "./utils/xhr.js";
|
||||||
import {RootViewModel} from "./RootViewModel.js";
|
import {RootViewModel} from "./RootViewModel.js";
|
||||||
import {RootView} from "./RootView.js";
|
import {RootView} from "./RootView.js";
|
||||||
|
import {guessApplicablePlatforms} from "./client/Platform.js";
|
||||||
|
|
||||||
export async function main(container) {
|
export async function main(container) {
|
||||||
const vm = new RootViewModel({request: xhrRequest});
|
const vm = new RootViewModel({
|
||||||
|
request: xhrRequest,
|
||||||
|
openLink: url => location.href = url,
|
||||||
|
platforms: guessApplicablePlatforms(navigator.userAgent),
|
||||||
|
});
|
||||||
vm.updateHash(location.hash);
|
vm.updateHash(location.hash);
|
||||||
window.__rootvm = vm;
|
window.__rootvm = vm;
|
||||||
const view = new RootView(vm);
|
const view = new RootView(vm);
|
||||||
|
|
|
@ -15,18 +15,23 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {TemplateView} from "../utils/TemplateView.js";
|
import {TemplateView} from "../utils/TemplateView.js";
|
||||||
|
import {ClientListView} from "../client/ClientListView.js";
|
||||||
|
|
||||||
export class PreviewView extends TemplateView {
|
export class PreviewView extends TemplateView {
|
||||||
render(t, vm) {
|
render(t, vm) {
|
||||||
return t.div({className: "PreviewView card"}, [
|
return t.div({className: "PreviewView card"}, [
|
||||||
t.h2({className: {hidden: vm => !vm.loading}}, "Loading preview…"),
|
t.h2({className: {hidden: vm => !vm.loading}}, "Loading preview…"),
|
||||||
t.div({className: {preview: true, hidden: vm => vm.loading}}, [
|
t.div({className: {hidden: vm => vm.loading}}, [
|
||||||
t.p(t.img({className: "avatar", src: vm => vm.avatarUrl})),
|
t.div({className: "preview"}, [
|
||||||
t.div({className: "profileInfo"}, [
|
t.p(t.img({className: "avatar", src: vm => vm.avatarUrl})),
|
||||||
t.h2(vm => vm.name),
|
t.div({className: "profileInfo"}, [
|
||||||
t.p(vm => vm.identifier),
|
t.h2(vm => vm.name),
|
||||||
t.p(["Preview from ", vm => vm.previewDomain]),
|
t.p(vm => vm.identifier),
|
||||||
])
|
t.p(["Preview from ", vm => vm.previewDomain]),
|
||||||
|
]),
|
||||||
|
]),
|
||||||
|
t.p({hidden: vm => !!vm.clientsViewModel}, t.button({onClick: () => vm.accept()}, vm => vm.acceptLabel)),
|
||||||
|
t.mapView(vm => vm.clientsViewModel, vm => vm ? new ClientListView(vm) : null)
|
||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,17 +17,28 @@ limitations under the License.
|
||||||
import {LinkKind} from "../Link.js";
|
import {LinkKind} from "../Link.js";
|
||||||
import {ViewModel} from "../utils/ViewModel.js";
|
import {ViewModel} from "../utils/ViewModel.js";
|
||||||
import {resolveServer} from "./HomeServer.js";
|
import {resolveServer} from "./HomeServer.js";
|
||||||
|
import {ClientListViewModel} from "../client/ClientListViewModel.js";
|
||||||
|
|
||||||
export class PreviewViewModel extends ViewModel {
|
export class PreviewViewModel extends ViewModel {
|
||||||
constructor(options) {
|
constructor(options) {
|
||||||
super(options);
|
super(options);
|
||||||
const {link, consentedServers} = options;
|
const {
|
||||||
|
link, consentedServers,
|
||||||
|
preferredClient, preferredPlatform, clients
|
||||||
|
} = options;
|
||||||
this._link = link;
|
this._link = link;
|
||||||
this._consentedServers = consentedServers;
|
this._consentedServers = consentedServers;
|
||||||
|
this._preferredClient = preferredClient;
|
||||||
|
// used to differentiate web from native if a client supports both
|
||||||
|
this._preferredPlatform = preferredPlatform;
|
||||||
|
this._clients = clients;
|
||||||
|
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
this.name = null;
|
this.name = null;
|
||||||
this.avatarUrl = null;
|
this.avatarUrl = null;
|
||||||
this.previewDomain = null;
|
this.previewDomain = null;
|
||||||
|
this.clientsViewModel = null;
|
||||||
|
this.acceptInstructions = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
async load() {
|
async load() {
|
||||||
|
@ -62,4 +73,28 @@ export class PreviewViewModel extends ViewModel {
|
||||||
get identifier() {
|
get identifier() {
|
||||||
return this._link.identifier;
|
return this._link.identifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get acceptLabel() {
|
||||||
|
if (this._preferredClient) {
|
||||||
|
return `Open in ${this._preferredClient.getName(this._preferredPlatform)}`;
|
||||||
|
} else {
|
||||||
|
return "Choose app";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
accept() {
|
||||||
|
if (this._preferredClient) {
|
||||||
|
if (this._preferredClient.getLinkSupport(this._preferredPlatform, this._link)) {
|
||||||
|
const deepLink = this._preferredClient.getDeepLink(this._preferredPlatform, this._link);
|
||||||
|
this.openLink(deepLink);
|
||||||
|
// show "looks like you don't have the native app installed"
|
||||||
|
} else {
|
||||||
|
this.acceptInstructions = this._preferredClient.getLinkInstructions(this._preferredPlatform, this._link);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.clientsViewModel = new ClientListViewModel(this.childOptions({clients: this._clients, link: this._link}));
|
||||||
|
// show client list
|
||||||
|
}
|
||||||
|
this.emitChange();
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -60,11 +60,15 @@ export class ViewModel extends EventEmitter {
|
||||||
this.emit("change");
|
this.emit("change");
|
||||||
}
|
}
|
||||||
|
|
||||||
get request() {
|
get request() { return this._options.request; }
|
||||||
return this._options.request;
|
get openLink() { return this._options.openLink; }
|
||||||
}
|
get platforms() { return this._options.platforms; }
|
||||||
|
|
||||||
childOptions(options = {}) {
|
childOptions(options = {}) {
|
||||||
return Object.assign({request: this.request}, options);
|
return Object.assign({
|
||||||
|
request: this.request,
|
||||||
|
openLink: this.openLink,
|
||||||
|
platforms: this.platforms,
|
||||||
|
}, options);
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in a new issue