move "looks like not installed" in client view, and open & install stage
This commit is contained in:
parent
c1bc2546fd
commit
64657c196a
9 changed files with 141 additions and 53 deletions
38
css/main.css
38
css/main.css
|
@ -144,19 +144,19 @@ button.text:hover {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ClientView .header {
|
.ListedClientView .header {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ClientView .description {
|
.ListedClientView .description {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ClientView h3 {
|
.ListedClientView h3 {
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ClientView .icon {
|
.ListedClientView .icon {
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
|
@ -165,23 +165,39 @@ button.text:hover {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.ClientView .icon.element-io {
|
.ListedClientView .icon.element-io {
|
||||||
background-image: url('../images/client-icons/element.svg');
|
background-image: url('../images/client-icons/element.svg');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
button.primary, a.primary, button.secondary, a.secondary {
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: bold;
|
||||||
|
text-align: center;
|
||||||
|
padding: 8px;
|
||||||
|
margin: 8px 0;
|
||||||
|
line-height: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.secondary, a.secondary {
|
||||||
|
background: var(--background);
|
||||||
|
color: var(--link);
|
||||||
|
}
|
||||||
|
|
||||||
button.primary, a.primary {
|
button.primary, a.primary {
|
||||||
background: var(--link);
|
background: var(--link);
|
||||||
color: var(--background);
|
color: var(--background);
|
||||||
border-radius: 32px;
|
border-radius: 32px;
|
||||||
text-decoration: none;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.ClientView .actions a {
|
button.primary, button.secondary {
|
||||||
|
border: none;
|
||||||
|
width: 100%;
|
||||||
|
font-size: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.PreviewView .primary, .PreviewView .secondary {
|
||||||
display: block;
|
display: block;
|
||||||
text-align: center;
|
|
||||||
padding: 8px;
|
|
||||||
margin: 8px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.ClientView .actions a.badge {
|
.ClientView .actions a.badge {
|
||||||
|
|
|
@ -40,6 +40,15 @@ function asPrefix(identifierKind) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getLabelForLinkKind(kind) {
|
||||||
|
switch (kind) {
|
||||||
|
case LinkKind.User: return "Start chat";
|
||||||
|
case LinkKind.Room: return "View room";
|
||||||
|
case LinkKind.Group: return "View community";
|
||||||
|
case LinkKind.Event: return "View message";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const LinkKind = createEnum(
|
export const LinkKind = createEnum(
|
||||||
"Room",
|
"Room",
|
||||||
"User",
|
"User",
|
||||||
|
|
|
@ -15,11 +15,11 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {TemplateView} from "../utils/TemplateView.js";
|
import {TemplateView} from "../utils/TemplateView.js";
|
||||||
import {ClientView} from "./ClientView.js";
|
import {ListedClientView} from "./ClientView.js";
|
||||||
|
|
||||||
export class ClientListView extends TemplateView {
|
export class ClientListView extends TemplateView {
|
||||||
render(t, vm) {
|
render(t, vm) {
|
||||||
const clients = vm.clients.map(clientViewModel => t.view(new ClientView(clientViewModel)));
|
const clients = vm.clients.map(clientViewModel => t.view(new ListedClientView(clientViewModel)));
|
||||||
return t.div({className: "ClientListView"}, [
|
return t.div({className: "ClientListView"}, [
|
||||||
t.h3("You need an app to continue"),
|
t.h3("You need an app to continue"),
|
||||||
t.div({className: "ClientListView"}, clients)
|
t.div({className: "ClientListView"}, clients)
|
||||||
|
|
|
@ -16,9 +16,9 @@ limitations under the License.
|
||||||
|
|
||||||
import {TemplateView} from "../utils/TemplateView.js";
|
import {TemplateView} from "../utils/TemplateView.js";
|
||||||
|
|
||||||
export class ClientView extends TemplateView {
|
export class ListedClientView extends TemplateView {
|
||||||
render(t, vm) {
|
render(t, vm) {
|
||||||
return t.div({className: "ClientView"}, [
|
return t.div({className: "ListedClientView"}, [
|
||||||
t.div({className: "header"}, [
|
t.div({className: "header"}, [
|
||||||
t.div({className: "description"}, [
|
t.div({className: "description"}, [
|
||||||
t.h3(vm.name),
|
t.h3(vm.name),
|
||||||
|
@ -26,6 +26,41 @@ export class ClientView extends TemplateView {
|
||||||
]),
|
]),
|
||||||
t.div({className: `icon ${vm.clientId}`})
|
t.div({className: `icon ${vm.clientId}`})
|
||||||
]),
|
]),
|
||||||
|
t.view(new ClientView(vm))
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ClientView extends TemplateView {
|
||||||
|
render(t, vm) {
|
||||||
|
return t.div({className: "ClientView"}, [
|
||||||
|
t.mapView(vm => vm.stage, stage => {
|
||||||
|
switch (stage) {
|
||||||
|
case "open": return new OpenClientView(vm);
|
||||||
|
case "install": return new InstallClientView(vm);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class OpenClientView extends TemplateView {
|
||||||
|
render(t, vm) {
|
||||||
|
return t.div({className: "OpenClientView"}, [
|
||||||
|
t.a({
|
||||||
|
className: "primary",
|
||||||
|
href: vm.deepLink,
|
||||||
|
rel: "noopener noreferrer",
|
||||||
|
onClick: () => vm.deepLinkActivated(),
|
||||||
|
}, vm.deepLinkLabel)
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class InstallClientView extends TemplateView {
|
||||||
|
render(t, vm) {
|
||||||
|
return t.div({className: "InstallClientView"}, [
|
||||||
|
t.h3(`Looks like you don't have ${vm.name} installed.`),
|
||||||
t.div({className: "actions"}, vm.actions.map(a => {
|
t.div({className: "actions"}, vm.actions.map(a => {
|
||||||
let badgeUrl;
|
let badgeUrl;
|
||||||
switch (a.kind) {
|
switch (a.kind) {
|
||||||
|
|
|
@ -16,20 +16,28 @@ limitations under the License.
|
||||||
|
|
||||||
import {isWebPlatform, Platform} from "./Platform.js";
|
import {isWebPlatform, Platform} from "./Platform.js";
|
||||||
import {ViewModel} from "../utils/ViewModel.js";
|
import {ViewModel} from "../utils/ViewModel.js";
|
||||||
|
import {getLabelForLinkKind} from "../Link.js";
|
||||||
|
|
||||||
export class ClientViewModel extends ViewModel {
|
export class ClientViewModel extends ViewModel {
|
||||||
constructor(options) {
|
constructor(options) {
|
||||||
super(options);
|
super(options);
|
||||||
const {client, link} = options;
|
const {client, link} = options;
|
||||||
this._client = client;
|
this._client = client;
|
||||||
|
this._link = link;
|
||||||
|
|
||||||
const supportedPlatforms = client.platforms;
|
const supportedPlatforms = client.platforms;
|
||||||
const matchingPlatforms = this.platforms.filter(p => {
|
const matchingPlatforms = this.platforms.filter(p => {
|
||||||
return supportedPlatforms.includes(p);
|
return supportedPlatforms.includes(p);
|
||||||
});
|
});
|
||||||
const nativePlatform = matchingPlatforms.find(p => !isWebPlatform(p));
|
const nativePlatform = matchingPlatforms.find(p => !isWebPlatform(p));
|
||||||
const webPlatform = this.platforms.find(p => isWebPlatform(p));
|
const webPlatform = this.platforms.find(p => isWebPlatform(p));
|
||||||
|
|
||||||
|
this._showOpen = nativePlatform && !client.canInterceptMatrixToLinks(nativePlatform);
|
||||||
|
this._proposedPlatform = this.preferences.platform || nativePlatform || webPlatform;
|
||||||
|
|
||||||
this.actions = this._createActions(client, link, nativePlatform, webPlatform);
|
this.actions = this._createActions(client, link, nativePlatform, webPlatform);
|
||||||
this.name = this._client.getName(nativePlatform || webPlatform);
|
this.name = this._client.getName(this._proposedPlatform);
|
||||||
|
this.deepLink = this._client.getDeepLink(this._proposedPlatform, this._link);
|
||||||
}
|
}
|
||||||
|
|
||||||
_createActions(client, link, nativePlatform, webPlatform) {
|
_createActions(client, link, nativePlatform, webPlatform) {
|
||||||
|
@ -64,4 +72,34 @@ export class ClientViewModel extends ViewModel {
|
||||||
get clientId() {
|
get clientId() {
|
||||||
return this._client.id;
|
return this._client.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get stage() {
|
||||||
|
return this._showOpen ? "open" : "install";
|
||||||
|
}
|
||||||
|
|
||||||
|
get deepLinkLabel() {
|
||||||
|
return getLabelForLinkKind(this._link.kind);
|
||||||
|
}
|
||||||
|
|
||||||
|
deepLinkActivated() {
|
||||||
|
this.preferences.setClient(this._client.id, this._proposedPlatform);
|
||||||
|
if (this._showOpen) {
|
||||||
|
this._showOpen = false;
|
||||||
|
this.emitChange();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
if (this._preferredClient.getLinkSupport(this.preferences.platform, this._link)) {
|
||||||
|
const deepLink = this._preferredClient.getDeepLink(this.preferences.platform, this._link);
|
||||||
|
this.openLink(deepLink);
|
||||||
|
const protocol = new URL(deepLink).protocol;
|
||||||
|
const isWebProtocol = protocol === "http:" || protocol === "https:";
|
||||||
|
if (!isWebProtocol) {
|
||||||
|
this.missingClientViewModel = new ClientViewModel(this.childOptions({client: this._preferredClient, link: this._link}));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.acceptInstructions = this._preferredClient.getLinkInstructions(this.preferences.platform, this._link);
|
||||||
|
}
|
||||||
|
*/
|
|
@ -29,6 +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];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isWebPlatform(p) {
|
export function isWebPlatform(p) {
|
||||||
|
|
|
@ -78,4 +78,8 @@ export class Element {
|
||||||
default: return [new WebsiteLink("https://element.io/get-started")];
|
default: return [new WebsiteLink("https://element.io/get-started")];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
canInterceptMatrixToLinks(platform) {
|
||||||
|
return platform === Platform.iOS || platform === Platform.Android;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -31,19 +31,14 @@ export class PreviewView extends TemplateView {
|
||||||
t.p(["Preview from ", vm => vm.previewDomain]),
|
t.p(["Preview from ", vm => vm.previewDomain]),
|
||||||
]),
|
]),
|
||||||
]),
|
]),
|
||||||
t.p({hidden: vm => !!vm.clientsViewModel}, t.button({onClick: () => vm.accept()}, vm => vm.acceptLabel)),
|
t.p({className: {hidden: vm => !vm.canShowClients}}, t.button({
|
||||||
|
className: "primary",
|
||||||
|
onClick: () => vm.showClients()
|
||||||
|
}, vm => vm.showClientsLabel)),
|
||||||
t.mapView(vm => vm.clientsViewModel, childVM => childVM ? new ClientListView(childVM) : null),
|
t.mapView(vm => vm.clientsViewModel, childVM => childVM ? new ClientListView(childVM) : null),
|
||||||
t.mapView(vm => vm.missingClientViewModel, childVM => childVM ? new MissingClientView(childVM) : null),
|
t.mapView(vm => vm.preferredClientViewModel, childVM => childVM ? new ClientView(childVM) : null),
|
||||||
|
t.p({className: {hidden: vm => !vm.preferredClientViewModel}}, vm => `This will open in ${vm.preferredClientViewModel?.name}`),
|
||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class MissingClientView extends TemplateView {
|
|
||||||
render(t, vm) {
|
|
||||||
return t.div({className: "MissingClientView"}, [
|
|
||||||
t.h3(`It looks like you don't have ${vm.name} installed.`),
|
|
||||||
t.view(new ClientView(vm)),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {LinkKind} from "../Link.js";
|
import {LinkKind, getLabelForLinkKind} 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 "../client/ClientListViewModel.js";
|
||||||
|
@ -35,7 +35,10 @@ export class PreviewViewModel extends ViewModel {
|
||||||
this.previewDomain = null;
|
this.previewDomain = null;
|
||||||
this.clientsViewModel = null;
|
this.clientsViewModel = null;
|
||||||
this.acceptInstructions = null;
|
this.acceptInstructions = null;
|
||||||
this.missingClientViewModel = null;
|
this.preferredClientViewModel = this._preferredClient ? new ClientViewModel(this.childOptions({
|
||||||
|
client: this._preferredClient,
|
||||||
|
link: this._link
|
||||||
|
})) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
async load() {
|
async load() {
|
||||||
|
@ -71,31 +74,18 @@ export class PreviewViewModel extends ViewModel {
|
||||||
return this._link.identifier;
|
return this._link.identifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
get acceptLabel() {
|
get canShowClients() {
|
||||||
if (this._preferredClient) {
|
return !(this.preferredClientViewModel || this.clientsViewModel);
|
||||||
return `Open in ${this._preferredClient.getName(this.preferences.platform)}`;
|
|
||||||
} else {
|
|
||||||
return "Choose app";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
accept() {
|
get showClientsLabel() {
|
||||||
if (this._preferredClient) {
|
return getLabelForLinkKind(this._link.kind);
|
||||||
if (this._preferredClient.getLinkSupport(this.preferences.platform, this._link)) {
|
|
||||||
const deepLink = this._preferredClient.getDeepLink(this.preferences.platform, this._link);
|
|
||||||
this.openLink(deepLink);
|
|
||||||
const protocol = new URL(deepLink).protocol;
|
|
||||||
const isWebProtocol = protocol === "http:" || protocol === "https:";
|
|
||||||
if (!isWebProtocol) {
|
|
||||||
this.missingClientViewModel = new ClientViewModel(this.childOptions({client: this._preferredClient, link: this._link}));
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
this.acceptInstructions = this._preferredClient.getLinkInstructions(this.preferences.platform, this._link);
|
showClients() {
|
||||||
}
|
if (!this._preferredClient) {
|
||||||
} else {
|
|
||||||
this.clientsViewModel = new ClientListViewModel(this.childOptions({clients: this._clients, link: this._link}));
|
this.clientsViewModel = new ClientListViewModel(this.childOptions({clients: this._clients, link: this._link}));
|
||||||
// show client list
|
|
||||||
}
|
|
||||||
this.emitChange();
|
this.emitChange();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Reference in a new issue