make preview useable without client list, to reuse in create link view

This commit is contained in:
Bruno Windels 2020-12-02 15:36:54 +01:00
parent 60b280bbf9
commit 5d40d01360
17 changed files with 165 additions and 66 deletions

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import {createEnum} from "../utils/enum.js"; import {createEnum} from "./utils/enum.js";
export const Platform = createEnum( export const Platform = createEnum(
"DesktopWeb", "DesktopWeb",
@ -29,7 +29,7 @@ export const Platform = createEnum(
export function guessApplicablePlatforms(userAgent) { export function guessApplicablePlatforms(userAgent) {
// use https://github.com/faisalman/ua-parser-js to guess, and pass as RootVM options // use https://github.com/faisalman/ua-parser-js to guess, and pass as RootVM options
return [Platform.DesktopWeb, Platform.Linux]; return [Platform.DesktopWeb, Platform.Linux];
//return [Platform.MobileWeb, Platform.iOS]; // return [Platform.MobileWeb, Platform.Android];
} }
export function isWebPlatform(p) { export function isWebPlatform(p) {

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import {Platform} from "./client/Platform.js"; import {Platform} from "./Platform.js";
export class Preferences { export class Preferences {
constructor(localStorage) { constructor(localStorage) {

View file

@ -15,12 +15,14 @@ limitations under the License.
*/ */
import {TemplateView} from "./utils/TemplateView.js"; import {TemplateView} from "./utils/TemplateView.js";
import {PreviewView} from "./preview/PreviewView.js"; import {OpenLinkView} from "./open/OpenLinkView.js";
import {CreateLinkView} from "./create/CreateLinkView.js";
export class RootView extends TemplateView { export class RootView extends TemplateView {
render(t, vm) { render(t, vm) {
return t.div({className: "RootView"}, [ return t.div({className: "RootView"}, [
t.mapView(vm => vm.previewViewModel, vm => vm ? new PreviewView(vm) : null), t.mapView(vm => vm.openLinkViewModel, vm => vm ? new OpenLinkView(vm) : null),
t.mapView(vm => vm.createLinkViewModel, vm => vm ? new CreateLinkView(vm) : null),
t.div({className: "footer"}, [ t.div({className: "footer"}, [
t.p(t.img({src: "images/matrix-logo.svg"})), t.p(t.img({src: "images/matrix-logo.svg"})),
t.p(["This invite uses ", externalLink(t, "https://matrix.org", "Matrix"), ", an open network for secure, decentralized communication."]), t.p(["This invite uses ", externalLink(t, "https://matrix.org", "Matrix"), ", an open network for secure, decentralized communication."]),

View file

@ -16,27 +16,29 @@ 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 {OpenLinkViewModel} from "./open/OpenLinkViewModel.js";
import {Element} from "./client/clients/Element.js"; import {createClients} from "./open/clients/index.js";
import {Weechat} from "./client/clients/Weechat.js"; import {CreateLinkViewModel} from "./create/CreateLinkViewModel.js";
import {Platform} from "./client/Platform.js"; import {Platform} from "./Platform.js";
export class RootViewModel extends ViewModel { export class RootViewModel extends ViewModel {
constructor(options) { constructor(options) {
super(options); super(options);
this.link = null; this.link = null;
this.previewViewModel = null; this.openLinkViewModel = null;
this.createLinkViewModel = null;
} }
_updateChildVMs(oldLink) { _updateChildVMs(oldLink) {
if (this.link) { if (this.link) {
this.createLinkViewModel = null;
if (!oldLink || !oldLink.equals(this.link)) { if (!oldLink || !oldLink.equals(this.link)) {
this.previewViewModel = new PreviewViewModel(this.childOptions({ this.openLinkViewModel = new OpenLinkViewModel(this.childOptions({
link: this.link, link: this.link,
consentedServers: this.link.servers, consentedServers: this.link.servers,
clients: [new Element(), new Weechat()] clients: createClients()
})); }));
this.previewViewModel.load(); this.openLinkViewModel.load();
} }
} else { } else {
this.previewViewModel = null; this.previewViewModel = null;
@ -50,13 +52,6 @@ export class RootViewModel extends ViewModel {
this._updateChildVMs(oldLink); this._updateChildVMs(oldLink);
} }
get activeSection() {
if (this.previewViewModel) {
return "preview";
}
return "";
}
clearPreferences() { clearPreferences() {
this.preferences.clear(); this.preferences.clear();
this._updateChildVMs(); this._updateChildVMs();

View file

@ -2,7 +2,7 @@ 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 {Preferences} from "./Preferences.js"; import {Preferences} from "./Preferences.js";
import {guessApplicablePlatforms} from "./client/Platform.js"; import {guessApplicablePlatforms} from "./Platform.js";
export async function main(container) { export async function main(container) {
const vm = new RootViewModel({ const vm = new RootViewModel({
@ -10,6 +10,7 @@ export async function main(container) {
openLink: url => location.href = url, openLink: url => location.href = url,
platforms: guessApplicablePlatforms(navigator.userAgent), platforms: guessApplicablePlatforms(navigator.userAgent),
preferences: new Preferences(window.localStorage), preferences: new Preferences(window.localStorage),
origin: location.origin,
}); });
vm.updateHash(location.hash); vm.updateHash(location.hash);
window.__rootvm = vm; window.__rootvm = vm;

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import {isWebPlatform, Platform} from "./Platform.js"; import {isWebPlatform, Platform} from "../Platform.js";
import {Maturity} from "./types.js"; import {Maturity} from "./types.js";
import {ClientViewModel} from "./ClientViewModel.js"; import {ClientViewModel} from "./ClientViewModel.js";
import {ViewModel} from "../utils/ViewModel.js"; import {ViewModel} from "../utils/ViewModel.js";

View file

@ -99,6 +99,7 @@ class InstallClientView extends TemplateView {
const deepLink = t.a({ const deepLink = t.a({
rel: "noopener noreferrer", rel: "noopener noreferrer",
href: vm.deepLink, href: vm.deepLink,
onClick: () => vm.deepLinkActivated(),
}, "open it here"); }, "open it here");
children.push(t.p([`If you already have ${vm.name} installed, you can `, deepLink, "."])) children.push(t.p([`If you already have ${vm.name} installed, you can `, deepLink, "."]))
} }

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import {isWebPlatform, isDesktopPlatform, Platform} from "./Platform.js"; import {isWebPlatform, isDesktopPlatform, Platform} from "../Platform.js";
import {ViewModel} from "../utils/ViewModel.js"; import {ViewModel} from "../utils/ViewModel.js";
export class ClientViewModel extends ViewModel { export class ClientViewModel extends ViewModel {
@ -44,7 +44,7 @@ export class ClientViewModel extends ViewModel {
_createActions(client, link, nativePlatform, webPlatform) { _createActions(client, link, nativePlatform, webPlatform) {
let actions = []; let actions = [];
if (nativePlatform) { if (nativePlatform) {
const nativeActions = client.getInstallLinks(nativePlatform).map(installLink => { const nativeActions = (client.getInstallLinks(nativePlatform) || []).map(installLink => {
return { return {
label: installLink.description, label: installLink.description,
url: installLink.createInstallURL(link), url: installLink.createInstallURL(link),
@ -56,13 +56,16 @@ export class ClientViewModel extends ViewModel {
actions.push(...nativeActions); actions.push(...nativeActions);
} }
if (webPlatform) { if (webPlatform) {
const webDeepLink = client.getDeepLink(webPlatform, link);
if (webDeepLink) {
actions.push({ actions.push({
label: `Or open in ${client.getName(webPlatform)}`, label: `Or open in ${client.getName(webPlatform)}`,
url: client.getDeepLink(webPlatform, link), url: webDeepLink,
kind: "open-in-web", kind: "open-in-web",
activated: () => this.preferences.setClient(client.id, webPlatform), activated: () => this.preferences.setClient(client.id, webPlatform),
}); });
} }
}
if (actions.length === 0) { if (actions.length === 0) {
actions.push({ actions.push({
label: `Visit app homepage`, label: `Visit app homepage`,

35
src/open/OpenLinkView.js Normal file
View file

@ -0,0 +1,35 @@
/*
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 {ClientListView} from "./ClientListView.js";
import {PreviewView} from "../preview/PreviewView.js";
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]),
])
]);
}
}

View file

@ -0,0 +1,64 @@
/*
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 {ViewModel} from "../utils/ViewModel.js";
import {ClientListViewModel} from "./ClientListViewModel.js";
import {ClientViewModel} from "./ClientViewModel.js";
import {PreviewViewModel} from "../preview/PreviewViewModel.js";
import {getLabelForLinkKind} from "../Link.js";
export class OpenLinkViewModel extends ViewModel {
constructor(options) {
super(options);
const {clients, link, consentedServers} = options;
this._link = link;
this._clients = clients;
this.previewViewModel = new PreviewViewModel(this.childOptions({link, consentedServers}));
this.previewLoading = false;
const preferredClient = this.preferences.clientId ? clients.find(c => c.id === this.preferences.clientId) : null;
this.clientsViewModel = preferredClient ? new ClientListViewModel(this.childOptions({
clients,
link,
client: preferredClient,
})) : null;
}
async load() {
this.previewLoading = true;
this.emitChange();
await this.previewViewModel.load();
this.previewLoading = false;
this.emitChange();
}
get previewDomain() {
return this.previewViewModel.previewDomain;
}
get showClientsLabel() {
return getLabelForLinkKind(this._link.kind);
}
showClients() {
if (!this.clientsViewModel) {
this.clientsViewModel = new ClientListViewModel(this.childOptions({
clients: this._clients,
link: this._link
}));
this.emitChange();
}
}
}

View file

@ -28,7 +28,7 @@ export class Element {
return [ return [
Platform.Android, Platform.iOS, Platform.Android, Platform.iOS,
Platform.Windows, Platform.macOS, Platform.Linux, Platform.Windows, Platform.macOS, Platform.Linux,
Platform.DesktopWeb, Platform.MobileWeb Platform.DesktopWeb
]; ];
} }

25
src/open/clients/index.js Normal file
View file

@ -0,0 +1,25 @@
/*
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 {Element} from "./Element.js";
import {Weechat} from "./Weechat.js";
export function createClients() {
return [
new Element(),
new Weechat(),
];
}

View file

@ -17,7 +17,7 @@ limitations under the License.
import {createEnum} from "../utils/enum.js"; import {createEnum} from "../utils/enum.js";
export const Maturity = createEnum("Alpha", "Beta", "Stable"); export const Maturity = createEnum("Alpha", "Beta", "Stable");
export {LinkKind} from "../Link.js"; export {LinkKind} from "../Link.js";
export {Platform} from "./Platform.js"; export {Platform} from "../Platform.js";
export class AppleStoreLink { export class AppleStoreLink {
constructor(org, appId) { constructor(org, appId) {

View file

@ -15,12 +15,12 @@ limitations under the License.
*/ */
import {TemplateView} from "../utils/TemplateView.js"; import {TemplateView} from "../utils/TemplateView.js";
import {ClientListView} from "../client/ClientListView.js"; import {ClientListView} from "../open/ClientListView.js";
import {ClientView} from "../client/ClientView.js"; import {ClientView} from "../open/ClientView.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"}, [
t.h1({className: {hidden: vm => !vm.loading}}, "Loading preview…"), t.h1({className: {hidden: vm => !vm.loading}}, "Loading preview…"),
t.div({className: {hidden: vm => vm.loading}}, [ t.div({className: {hidden: vm => vm.loading}}, [
t.div({className: "preview"}, [ t.div({className: "preview"}, [
@ -30,12 +30,6 @@ export class PreviewView extends TemplateView {
t.p({className: {memberCount: true, hidden: vm => !vm.memberCount}}, [vm => vm.memberCount, " members"]), 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.p({className: {topic: true, hidden: vm => !vm.topic}}, [vm => vm.topic]),
]), ]),
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]),
]) ])
]); ]);
} }

View file

@ -14,21 +14,18 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import {LinkKind, IdentifierKind, getLabelForLinkKind} from "../Link.js"; import {LinkKind, IdentifierKind} 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"; import {ClientListViewModel} from "../open/ClientListViewModel.js";
import {ClientViewModel} from "../client/ClientViewModel.js"; import {ClientViewModel} from "../open/ClientViewModel.js";
export class PreviewViewModel extends ViewModel { export class PreviewViewModel extends ViewModel {
constructor(options) { constructor(options) {
super(options); super(options);
const { link, consentedServers, clients } = options; const { link, consentedServers } = options;
this._link = link; this._link = link;
this._consentedServers = consentedServers; this._consentedServers = consentedServers;
this._clients = clients;
this._preferredClient = this.preferences.clientId ? clients.find(c => c.id === this.preferences.clientId) : null;
this.loading = false; this.loading = false;
this.name = null; this.name = null;
this.avatarUrl = null; this.avatarUrl = null;
@ -36,13 +33,6 @@ export class PreviewViewModel extends ViewModel {
this.memberCount = null; this.memberCount = null;
this.topic = null; this.topic = null;
this.previewDomain = null; this.previewDomain = null;
this.clientsViewModel = null;
this.acceptInstructions = null;
this.clientsViewModel = this._preferredClient ? new ClientListViewModel(this.childOptions({
clients: this._clients,
client: this._preferredClient,
link: this._link,
})) : null;
} }
async load() { async load() {
@ -97,15 +87,4 @@ export class PreviewViewModel extends ViewModel {
this.topic = publicRoom?.topic; this.topic = publicRoom?.topic;
this.identifier = publicRoom?.canonical_alias || link.identifier; this.identifier = publicRoom?.canonical_alias || link.identifier;
} }
get showClientsLabel() {
return getLabelForLinkKind(this._link.kind);
}
showClients() {
if (!this.clientsViewModel) {
this.clientsViewModel = new ClientListViewModel(this.childOptions({clients: this._clients, link: this._link}));
this.emitChange();
}
}
} }