Compare commits
65 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b86b69d171 | |||
| e009496885 | |||
| 690692b494 | |||
| 5df8bdf932 | |||
| 030966c506 | |||
| 5a633a0ebb | |||
| 50d03aad1d | |||
| 29b754c9f8 | |||
| 9c90ede9c4 | |||
| 35bdc7473f | |||
| 3255e2ec29 | |||
| a550178de7 | |||
| 18284ef0f2 | |||
| a6b30c1b19 | |||
| 4a41e3f114 | |||
| 106bece811 | |||
| ad022b9472 | |||
| da2f07783d | |||
| d11fd87696 | |||
| 7cf278beb1 | |||
| ab2b2c940a | |||
| 3ca9c00725 | |||
| edd095efc4 | |||
| 5832cd0449 | |||
| d821417a3f | |||
| 8a2bfff1d9 | |||
| e6c548611b | |||
| 47b834779a | |||
| eb0a7d4522 | |||
| 61ce605627 | |||
| cda2271e9e | |||
| ef43b377c1 | |||
| 5605404bae | |||
| 211b14db78 | |||
| 160ddead30 | |||
| 2a3f7f2dec | |||
| 52dbfeadda | |||
| 876b4c742c | |||
| 12040df452 | |||
| 80c28bc155 | |||
| dbae95dca4 | |||
| 45681069cd | |||
| de7992d60c | |||
| 88a7eb03a7 | |||
| 3474e341a5 | |||
| 8e82c4687e | |||
| e8fcc3d2b9 | |||
| 2b44055047 | |||
| f8c30eabde | |||
| 3e521505c0 | |||
| 442ed2f1d1 | |||
| 26020e3e95 | |||
| 28d77a898c | |||
| 5b4b57306b | |||
| 2e4ce08874 | |||
| b49724d83b | |||
| fe21222d33 | |||
| f1d33f3f63 | |||
| 1205705555 | |||
| 4259af79bd | |||
| 6dd9a0213c | |||
| d993157cfa | |||
| 06237b1b8b | |||
| 50a25dd04c | |||
| 22ad0e9289 |
@@ -1,9 +1,7 @@
|
||||
name: Build and Push Matrix-to Docker Image
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
workflow_dispatch: # Erlaubt den manuellen Start über das GitHub UI
|
||||
|
||||
jobs:
|
||||
build:
|
||||
@@ -25,5 +23,7 @@ jobs:
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
tags: domoel/matrix-to:latest
|
||||
no-cache: true
|
||||
|
||||
@@ -56,6 +56,8 @@ The matrix.to URL scheme is
|
||||
The #/ component is mandatory and exists to avoid leaking the target URL to the
|
||||
server hosting matrix.to.
|
||||
|
||||
There is no _Entity type_ for **Spaces**, as they are technically just rooms.
|
||||
|
||||
Note that linking to rooms by ID should only be used for rooms to which the
|
||||
target user has been invited: these links cannot be assumed to work for all
|
||||
visitors.
|
||||
@@ -93,17 +95,11 @@ You can discuss matrix.to in
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
web:
|
||||
matrix-to:
|
||||
container_name: Matrix-to
|
||||
image: domoel/matrix-to:latest
|
||||
ports:
|
||||
- "1336:80"
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
volumes:
|
||||
- ./nginx.conf:/etc/nginx/nginx.conf
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "--fail", "http://localhost:80"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
```
|
||||
|
||||
+2
-8
@@ -1,16 +1,10 @@
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
web:
|
||||
matrix-to:
|
||||
container_name: Matrix-to
|
||||
image: domoel/matrix-to:latest
|
||||
ports:
|
||||
- "1336:80"
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
volumes:
|
||||
- ./nginx.conf:/etc/nginx/nginx.conf
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "--fail", "http://localhost:80"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
|
||||
+106
-71
@@ -1,98 +1,133 @@
|
||||
.ClientListView h2 {
|
||||
text-align: center;
|
||||
margin: 18px 0;
|
||||
}
|
||||
|
||||
.ClientListView .filterOption {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 8px 0;
|
||||
margin: 24px 0;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.ClientView {
|
||||
border: 1px solid #E6E6E6;
|
||||
border-radius: 8px;
|
||||
background: var(--app-background);
|
||||
border: 1px solid var(--borders);
|
||||
border-radius: 4px;
|
||||
margin: 16px 0;
|
||||
padding: 16px;
|
||||
padding: 24px;
|
||||
transition: all 0.3s ease;
|
||||
border-left: 4px solid transparent;
|
||||
display: block;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.ClientView:hover {
|
||||
background: #2f313d !important;
|
||||
border-left-color: var(--ztfr-purple) !important;
|
||||
transform: translateX(5px);
|
||||
}
|
||||
|
||||
.ClientView.isPreferred {
|
||||
border: 3px solid var(--link);
|
||||
box-shadow: 0px 8px 4px rgba(0, 0, 0, 0.05);
|
||||
border-color: var(--ztfr-purple);
|
||||
background: rgba(189, 147, 249, 0.05);
|
||||
}
|
||||
|
||||
.ClientView .hostedBanner {
|
||||
text-align: center;
|
||||
margin-bottom: 29px;
|
||||
padding: 4px 0;
|
||||
line-height: 20px;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 20px;
|
||||
padding: 6px 0;
|
||||
border-radius: 4px;
|
||||
font-weight: bold;
|
||||
font-size: 16px;
|
||||
background-color: var(--lightgrey);
|
||||
font-size: 13px;
|
||||
background-color: var(--ztfr-purple);
|
||||
color: var(--app-background);
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.ClientView .header {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.ClientView .description {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.ClientView h3 {
|
||||
margin-top: 0;
|
||||
}
|
||||
.ClientView .header { display: flex; align-items: flex-start; }
|
||||
.ClientView .description { flex: 1; }
|
||||
.ClientView h3 { margin: 0 0 8px 0; font-size: 18px; }
|
||||
.ClientView .description p { margin: 0; font-size: 13px; color: var(--font); }
|
||||
|
||||
.ClientView .clientIcon {
|
||||
border-radius: 8px;
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
overflow: hidden;
|
||||
display: block;
|
||||
border-radius: 4px;
|
||||
background-color: #ffffff;
|
||||
padding: 4px;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
margin-left: 16px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.ClientView .platforms {
|
||||
background-image: url('../images/platform-icon.svg');
|
||||
background-repeat: no-repeat;
|
||||
background-position: 0 center;
|
||||
padding-left: 28px;
|
||||
}
|
||||
|
||||
.ClientView .actions a.badge {
|
||||
display: inline-block;
|
||||
height: 40px;
|
||||
margin: 8px 16px 8px 0;
|
||||
}
|
||||
|
||||
.ClientView .actions img {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.ClientView .back {
|
||||
margin-top: 22px;
|
||||
}
|
||||
|
||||
.InstallClientView .instructions button {
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
background-color: transparent;
|
||||
padding: 4px;
|
||||
border: none;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
margin: 8px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.InstallClientView .instructions button.copy {
|
||||
background-image: url('../images/copy.svg');
|
||||
margin-top: 12px;
|
||||
font-size: 12px;
|
||||
color: var(--grey);
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.InstallClientView .instructions button.tick {
|
||||
background-image: url('../images/tick-dark.svg');
|
||||
filter: invert(1); /* Macht schwarze Icons weiß */
|
||||
}
|
||||
|
||||
.ClientView .back {
|
||||
display: block;
|
||||
margin-top: 20px;
|
||||
font-size: 12px;
|
||||
color: var(--grey);
|
||||
}
|
||||
|
||||
.ClientView .actions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.ClientView .actions a.badge {
|
||||
height: auto !important;
|
||||
max-width: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.ClientView .actions img {
|
||||
max-width: 160px;
|
||||
height: auto;
|
||||
display: block;
|
||||
margin: 5px auto;
|
||||
}
|
||||
|
||||
/* --- ANPASSUNG FÜR DEN DEZENTEN LOOK --- */
|
||||
|
||||
.ClientView .actions .footer {
|
||||
display: block !important; /* Kein Flex mehr, damit Links wie Text fließen */
|
||||
text-align: center !important;
|
||||
margin-top: 20px !important;
|
||||
width: 100%;
|
||||
line-height: 1.8 !important; /* Gibt den Zeilen etwas Platz */
|
||||
color: var(--sub-text);
|
||||
}
|
||||
|
||||
/* Der "Use Custom Web Instance" Bereich bekommt eine eigene Zeile */
|
||||
.ClientView .actions .footer .custom {
|
||||
display: block !important;
|
||||
margin-top: 6px !important;
|
||||
}
|
||||
|
||||
/* Sicherstellen, dass der "Change" Button im Textfluss bleibt */
|
||||
.ClientView .actions .footer button.change {
|
||||
display: inline !important;
|
||||
margin: 0 4px !important;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 480px) {
|
||||
.ClientView {
|
||||
padding: 15px;
|
||||
margin: 8px 0;
|
||||
}
|
||||
|
||||
.ClientView .header {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.ClientView .clientIcon {
|
||||
margin: 0 0 15px 0;
|
||||
}
|
||||
}
|
||||
|
||||
+169
-158
@@ -1,17 +1,6 @@
|
||||
/*
|
||||
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.
|
||||
Modified 2026 for Zeitfresser Matrix Community Look & Mobile Fixes
|
||||
*/
|
||||
|
||||
@import url('spinner.css');
|
||||
@@ -20,57 +9,63 @@ limitations under the License.
|
||||
@import url('create.css');
|
||||
@import url('open.css');
|
||||
|
||||
/* Globaler Fix für Box-Berechnungen */
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
:root {
|
||||
--app-background: #f4f4f4;
|
||||
--background: #ffffff;
|
||||
--foreground: #000000;
|
||||
--font: #333333;
|
||||
--grey: #666666;
|
||||
--accent: #0098d4;
|
||||
--error: #d6001c;
|
||||
--link: #0098d4;
|
||||
--borders: #f4f4f4;
|
||||
--lightgrey: #E6E6E6;
|
||||
--app-background: #1e1f29; /* Tief-Anthrazit */
|
||||
--background: #282a36; /* Card Hintergrund */
|
||||
--foreground: #f7f7fa; /* Weiß */
|
||||
--font: #bdc3c7; /* Grau */
|
||||
--grey: #64748b; /* Dunkles Grau */
|
||||
--accent: #0dbd8b; /* Matrix-Green */
|
||||
--ztfr-purple: #bd93f9; /* Zeitfresser-Lila */
|
||||
--error: #ff5555;
|
||||
--link: #f7f7fa;
|
||||
--borders: #2f313d;
|
||||
--lightgrey: #383a59;
|
||||
--spinner-stroke-size: 2px;
|
||||
--sub-text: #bdc3c7;
|
||||
}
|
||||
|
||||
html {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 100%;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: var(--app-background);
|
||||
background-image: url('../images/background.svg');
|
||||
background-attachment: fixed;
|
||||
background-repeat: no-repeat;
|
||||
background-size: auto;
|
||||
background-position: center -50px;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
font-size: 14px;
|
||||
background-image: none !important;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
||||
color: var(--font);
|
||||
padding: 120px 0 0 0;
|
||||
padding: 80px 20px 0 20px;
|
||||
margin: 0;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
noscript {
|
||||
display: block;
|
||||
padding: 20px;
|
||||
h1, h2, h3 {
|
||||
color: var(--foreground);
|
||||
letter-spacing: -0.5px;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
p { line-height: 150%; }
|
||||
a { text-decoration: none; }
|
||||
p {
|
||||
line-height: 1.7;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
h1 { font-size: 24px; }
|
||||
h2 { font-size: 21px; }
|
||||
h3 { font-size: 16px; }
|
||||
a { text-decoration: none; color: var(--link); }
|
||||
|
||||
body,
|
||||
button,
|
||||
input,
|
||||
textarea {
|
||||
font-family: Helvetica Neue, Helvetica, Arial, sans-serif;
|
||||
body, button, input, textarea {
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@@ -78,13 +73,14 @@ button, input[type=submit] {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
button, input {
|
||||
font-size: inherit;
|
||||
font-weight: inherit;
|
||||
}
|
||||
|
||||
input[type="checkbox"], input[type="radio"] {
|
||||
margin: 0 8px 0 0;
|
||||
/* Die zentrale Kachel */
|
||||
.card {
|
||||
background-color: var(--background);
|
||||
border-radius: 4px;
|
||||
border-left: 4px solid var(--ztfr-purple);
|
||||
box-shadow: 0px 20px 40px rgba(0, 0, 0, 0.3);
|
||||
padding: 2.5rem;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.RootView {
|
||||
@@ -93,131 +89,146 @@ input[type="checkbox"], input[type="radio"] {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.card {
|
||||
background-color: var(--background);
|
||||
border-radius: 16px;
|
||||
box-shadow: 0px 18px 24px rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.card, .footer {
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
|
||||
@media screen and (max-width: 480px) {
|
||||
body {
|
||||
background-image: none;
|
||||
background-color: var(--background);
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.card {
|
||||
border-radius: unset;
|
||||
box-shadow: unset;
|
||||
}
|
||||
}
|
||||
|
||||
.footer .links li:not(:first-child) {
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
|
||||
.footer .links li:not(:first-child)::before {
|
||||
content: "·";
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
|
||||
.footer .links li {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.footer .links {
|
||||
font-size: 12px;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
a, button.text {
|
||||
color: var(--link);
|
||||
}
|
||||
|
||||
button.text {
|
||||
background: none;
|
||||
border: none;
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
font-size: inherit;
|
||||
padding: 8px 0;
|
||||
margin: -8px 0;
|
||||
}
|
||||
|
||||
button.text:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* --- GROSSE BUTTONS (z.B. Download, Continue) --- */
|
||||
.primary, .secondary {
|
||||
text-decoration: none;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
padding: 12px 8px;
|
||||
margin: 8px 0;
|
||||
}
|
||||
|
||||
.secondary {
|
||||
background: var(--background);
|
||||
color: var(--link);
|
||||
border: 1px solid var(--link);
|
||||
border-radius: 32px;
|
||||
padding: 14px 20px;
|
||||
margin: 4px 0 !important;
|
||||
display: block;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
width: 100%;
|
||||
|
||||
/* Simplifizierung: Nur Farbe, keine Bewegung */
|
||||
transition: background-color 0.2s ease, filter 0.2s ease;
|
||||
transform: none !important;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
.primary {
|
||||
background: var(--link);
|
||||
color: var(--background);
|
||||
border-radius: 32px;
|
||||
}
|
||||
|
||||
.primary.icon, .secondary.icon {
|
||||
background-repeat: no-repeat;
|
||||
background-position: 12px center;
|
||||
}
|
||||
|
||||
.icon.link { background-image: url('../images/link.svg'); }
|
||||
.icon.tick { background-image: url('../images/tick.svg'); }
|
||||
.icon.copy { background-image: url('../images/copy.svg'); }
|
||||
|
||||
button.primary, input[type='submit'].primary, button.secondary, input[type='submit'].secondary {
|
||||
background: var(--foreground);
|
||||
color: var(--app-background) !important;
|
||||
border-radius: 4px;
|
||||
border: none;
|
||||
font-size: inherit;
|
||||
}
|
||||
|
||||
.primary:hover {
|
||||
filter: brightness(0.9);
|
||||
}
|
||||
|
||||
.secondary {
|
||||
background: transparent;
|
||||
color: var(--foreground);
|
||||
border: 1px solid var(--foreground);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.secondary:hover {
|
||||
background: rgba(255,255,255,0.05);
|
||||
}
|
||||
|
||||
/* --- DEZENTE BUTTONS (Change, Custom Instance, Footer-Style) --- */
|
||||
.ClientListView button.change,
|
||||
.PreviewView button.change,
|
||||
.ClientView button.custom,
|
||||
.ClientView .footer button,
|
||||
.CustomInstanceView .actions button.secondary,
|
||||
.footer button,
|
||||
.footer a {
|
||||
background: none !important;
|
||||
border: none !important;
|
||||
color: var(--grey) !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
font-family: inherit !important;
|
||||
font-size: inherit !important;
|
||||
font-weight: normal !important;
|
||||
text-transform: none !important;
|
||||
letter-spacing: normal !important;
|
||||
cursor: pointer !important;
|
||||
display: inline !important;
|
||||
width: auto !important;
|
||||
border-bottom: 1px solid rgba(189, 195, 199, 0.3) !important;
|
||||
transition: all 0.3s ease !important;
|
||||
}
|
||||
|
||||
.ClientListView button.change:hover,
|
||||
.PreviewView button.change:hover,
|
||||
.ClientView button.custom:hover,
|
||||
.ClientView .footer button:hover,
|
||||
.CustomInstanceView .actions button.secondary:hover,
|
||||
.footer button:hover,
|
||||
.footer a:hover {
|
||||
color: var(--foreground) !important;
|
||||
border-bottom-color: var(--ztfr-purple) !important;
|
||||
}
|
||||
|
||||
/* Fix für Button-Container */
|
||||
.actions,
|
||||
.ClientView .actions,
|
||||
.ClientListView .actions,
|
||||
.CustomInstanceView .actions {
|
||||
display: flex !important;
|
||||
flex-direction: column !important;
|
||||
gap: 0px !important;
|
||||
}
|
||||
|
||||
.actions > *:last-child {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
/* Input Felder */
|
||||
input[type='text'].large {
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
background: var(--background);
|
||||
border: 1px solid var(--foreground);
|
||||
border-radius: 16px;
|
||||
padding: 14px;
|
||||
background: #1a1a1a !important;
|
||||
border: 1px solid var(--borders);
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.fullwidth {
|
||||
display: block;
|
||||
width: 100%;
|
||||
color: var(--foreground);
|
||||
box-sizing: border-box;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.LoadServerPolicyView {
|
||||
display: flex;
|
||||
.footer {
|
||||
margin-top: 60px;
|
||||
padding: 40px 20px;
|
||||
text-align: center;
|
||||
font-size: 0.85rem;
|
||||
color: var(--sub-text);
|
||||
opacity: 0.7;
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.05);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.LoadServerPolicyView .spinner {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
margin-right: 12px;
|
||||
.footer ul { list-style: none; padding: 0; margin: 0; }
|
||||
.footer li { display: inline; margin: 0; }
|
||||
|
||||
@media screen and (max-width: 480px) {
|
||||
body { padding: 40px 10px 0 10px; }
|
||||
.card { padding: 1.5rem; border-radius: 2px; }
|
||||
h1 { font-size: 1.5rem; }
|
||||
}
|
||||
|
||||
.LoadServerPolicyView h2 {
|
||||
margin-top: 0;
|
||||
/* --- FIX FÜR NUTZERPROFILE (null-Werte verstecken) --- */
|
||||
|
||||
.PreviewView .members[data-count="null"],
|
||||
.PreviewView .members:empty {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.PreviewView .topic:empty,
|
||||
.PreviewView .topic:contains("null") {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.PreviewView p:empty,
|
||||
.PreviewView span:empty {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.PreviewView h2 + p {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
+54
-89
@@ -4,130 +4,95 @@
|
||||
}
|
||||
|
||||
.PreviewView h1 {
|
||||
font-size: 24px;
|
||||
line-height: 32px;
|
||||
font-size: 26px;
|
||||
line-height: 1.2;
|
||||
margin-bottom: 8px;
|
||||
word-wrap: anywhere;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.PreviewView .avatarContainer {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin: 0;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.PreviewView .avatar {
|
||||
border-radius: 100%;
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
}
|
||||
|
||||
.PreviewView .mxSpace .avatar {
|
||||
border-radius: 12px;
|
||||
border-radius: 8px; /* Eckig */
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
border: 3px solid var(--borders);
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.PreviewView .defaultAvatar {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
border-radius: 8px;
|
||||
background-color: var(--lightgrey);
|
||||
background-image: url('../images/chat-icon.svg');
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
background-size: 85%;
|
||||
}
|
||||
|
||||
.PreviewView .spinner {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
.PreviewView .avatar.loading {
|
||||
border: 1px solid #eee;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-sizing: border-box;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.PreviewView .identifier {
|
||||
color: var(--grey);
|
||||
font-size: 12px;
|
||||
color: var(--ztfr-purple);
|
||||
font-family: monospace;
|
||||
font-size: 13px;
|
||||
margin: 8px 0;
|
||||
}
|
||||
|
||||
.PreviewView .identifier.placeholder {
|
||||
height: 1em;
|
||||
margin: 1em 30%;
|
||||
}
|
||||
|
||||
.PreviewView .memberCount {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin: 8px 0;
|
||||
}
|
||||
|
||||
.PreviewView .memberCount.loading {
|
||||
margin: 16px 0;
|
||||
}
|
||||
|
||||
.PreviewView .memberCount p {
|
||||
font-size: 12px;
|
||||
margin: 0;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.PreviewView .memberCount p:not(.placeholder) {
|
||||
padding: 4px 8px 4px 24px;
|
||||
border-radius: 14px;
|
||||
padding: 6px 12px 6px 28px;
|
||||
border-radius: 4px;
|
||||
color: var(--foreground);
|
||||
background-image: url(../images/member-icon.svg);
|
||||
background-repeat: no-repeat;
|
||||
background-position: 2px center;
|
||||
background-position: 8px center;
|
||||
background-color: var(--lightgrey);
|
||||
}
|
||||
|
||||
.PreviewView .memberCount p.placeholder {
|
||||
height: 1.5em;
|
||||
width: 100px;
|
||||
display: inline-block;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.PreviewView .topic {
|
||||
font-size: 12px;
|
||||
color: var(--grey);
|
||||
margin: 32px 0;
|
||||
}
|
||||
|
||||
.PreviewView .topic.loading {
|
||||
display: block;
|
||||
margin: 24px 12px;
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
.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%;
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
color: var(--font);
|
||||
margin: 24px 0;
|
||||
}
|
||||
|
||||
/* Dark Mode Placeholders */
|
||||
.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)
|
||||
);
|
||||
border-radius: 4px;
|
||||
--flash-bg: #2f313d;
|
||||
--flash-fg: #383a59;
|
||||
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 linear infinite;
|
||||
background-size: 200%;
|
||||
}
|
||||
|
||||
@keyframes flash {
|
||||
0% { background-position-x: 0; }
|
||||
50% { background-position-x: -80%; }
|
||||
51% { background-position-x: 80%; }
|
||||
100% { background-position-x: 0%; }
|
||||
100% { background-position-x: -200%; }
|
||||
}
|
||||
|
||||
.PreviewView h1,
|
||||
.PreviewView .identifier,
|
||||
.PreviewView .topic {
|
||||
word-wrap: break-word; /* Bricht extrem lange Wörter/IDs um */
|
||||
overflow-wrap: break-word;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 480px) {
|
||||
.PreviewView .avatar,
|
||||
.PreviewView .defaultAvatar {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
}
|
||||
|
||||
.PreviewView h1 {
|
||||
font-size: 20px; /* Titel etwas dezenter mobil */
|
||||
}
|
||||
}
|
||||
|
||||
+12
-5
@@ -18,7 +18,7 @@ import {createEnum} from "./utils/enum.js";
|
||||
import {orderedUnique} from "./utils/unique.js";
|
||||
|
||||
const ROOMALIAS_PATTERN = /^#([^:]*):(.+)$/;
|
||||
const ROOMID_PATTERN = /^!([^:]*):(.+)$/;
|
||||
const ROOMID_PATTERN = /^!([^:]*)(:(.+))?$/; // As of room version 12, room IDs don't have domains
|
||||
const USERID_PATTERN = /^@([^:]+):(.+)$/;
|
||||
const EVENTID_PATTERN = /^$([^:]+):(.+)$/;
|
||||
const GROUPID_PATTERN = /^\+([^:]+):(.+)$/;
|
||||
@@ -92,7 +92,7 @@ export class Link {
|
||||
static validateIdentifier(identifier) {
|
||||
return !!(
|
||||
USERID_PATTERN.exec(identifier) ||
|
||||
ROOMALIAS_PATTERN.exec(identifier) ||
|
||||
ROOMALIAS_PATTERN.exec(identifier) ||
|
||||
ROOMID_PATTERN.exec(identifier) ||
|
||||
GROUPID_PATTERN.exec(identifier)
|
||||
);
|
||||
@@ -152,7 +152,7 @@ export class Link {
|
||||
}
|
||||
matches = ROOMID_PATTERN.exec(identifier);
|
||||
if (matches) {
|
||||
const server = matches[2];
|
||||
const server = matches[3]; // group 2 is an optional over `:domain`, group 3 is just `domain`
|
||||
const localPart = matches[1];
|
||||
return new Link(clientId, viaServers, IdentifierKind.RoomId, localPart, server, webInstances, eventId);
|
||||
}
|
||||
@@ -166,12 +166,19 @@ export class Link {
|
||||
}
|
||||
|
||||
constructor(clientId, viaServers, identifierKind, localPart, server, webInstances, eventId) {
|
||||
const servers = [server];
|
||||
const servers = [];
|
||||
if (server !== undefined) {
|
||||
servers.push(server); // v12 rooms don't have domains, and therefore no server
|
||||
}
|
||||
servers.push(...viaServers);
|
||||
this.webInstances = webInstances;
|
||||
this.servers = orderedUnique(servers);
|
||||
this.identifierKind = identifierKind;
|
||||
this.identifier = `${asPrefix(identifierKind)}${localPart}:${server}`;
|
||||
if (identifierKind === IdentifierKind.RoomId && !server) {
|
||||
this.identifier = `${asPrefix(identifierKind)}${localPart}`;
|
||||
} else {
|
||||
this.identifier = `${asPrefix(identifierKind)}${localPart}:${server}`;
|
||||
}
|
||||
this.eventId = eventId;
|
||||
this.clientId = clientId;
|
||||
}
|
||||
|
||||
+18
-1
@@ -25,6 +25,7 @@ export class Preferences extends EventEmitter {
|
||||
// used to differentiate web from native if a client supports both
|
||||
this.platform = null;
|
||||
this.homeservers = null;
|
||||
this.customWebInstances = {};
|
||||
|
||||
const prefsStr = localStorage.getItem("preferred_client");
|
||||
if (prefsStr) {
|
||||
@@ -36,6 +37,10 @@ export class Preferences extends EventEmitter {
|
||||
if (serversStr) {
|
||||
this.homeservers = JSON.parse(serversStr);
|
||||
}
|
||||
const customWebInstancesStr = localStorage.getItem("custom_web_instances");
|
||||
if (customWebInstancesStr) {
|
||||
this.customWebInstances = JSON.parse(customWebInstancesStr);
|
||||
}
|
||||
}
|
||||
|
||||
setClient(id, platform) {
|
||||
@@ -54,15 +59,27 @@ export class Preferences extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
setCustomWebInstance(client_id, instance_url) {
|
||||
this.customWebInstances[client_id] = instance_url;
|
||||
this._localStorage.setItem("custom_web_instances", JSON.stringify(this.customWebInstances));
|
||||
this.emit("canClear");
|
||||
}
|
||||
|
||||
getCustomWebInstance(client_id) {
|
||||
return this.customWebInstances[client_id];
|
||||
}
|
||||
|
||||
clear() {
|
||||
this._localStorage.removeItem("preferred_client");
|
||||
this._localStorage.removeItem("consented_servers");
|
||||
this._localStorage.removeItem("custom_web_instances");
|
||||
this.clientId = null;
|
||||
this.platform = null;
|
||||
this.homeservers = null;
|
||||
this.customWebInstances = {};
|
||||
}
|
||||
|
||||
get canClear() {
|
||||
return !!this.clientId || !!this.platform || !!this.homeservers;
|
||||
return !!this.clientId || !!this.platform || !!this.homeservers || !!this.customWebInstances;
|
||||
}
|
||||
}
|
||||
|
||||
+8
-8
@@ -30,14 +30,14 @@ export class RootView extends TemplateView {
|
||||
t.mapView(vm => vm.createLinkViewModel, vm => vm ? new CreateLinkView(vm) : null),
|
||||
t.mapView(vm => vm.loadServerPolicyViewModel, vm => vm ? new LoadServerPolicyView(vm) : null),
|
||||
t.div({className: "footer"}, [
|
||||
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.ul({className: "links"}, [
|
||||
t.li(externalLink(t, "https://github.com/matrix-org/matrix.to", "GitHub project")),
|
||||
t.li(externalLink(t, "https://github.com/matrix-org/matrix.to/tree/main/src/open/clients", "Add your app")),
|
||||
t.li({className: {hidden: vm => !vm.hasPreferences}},
|
||||
t.button({className: "text", onClick: () => vm.clearPreferences()}, "Clear preferences")),
|
||||
t.li(t.a({href: "#/disclaimer/"}, "Disclaimer")),
|
||||
t.p([
|
||||
"© 2026 ",
|
||||
t.a({href: "https://ztfr.eu"}, "Zeitfresser"),
|
||||
" | Powered by ",
|
||||
externalLink(t, "https://github.com/matrix-org/matrix.to", "Matrix-to")
|
||||
]),
|
||||
t.p({className: {hidden: vm => !vm.hasPreferences}}, [
|
||||
t.button({className: "text", onClick: () => { vm.clearPreferences(); location.reload(); }}, "Clear preferences")
|
||||
])
|
||||
])
|
||||
]);
|
||||
|
||||
@@ -39,6 +39,14 @@ function renderInstructions(parts) {
|
||||
export class ClientView extends TemplateView {
|
||||
|
||||
render(t, vm) {
|
||||
return t.mapView(vm => vm.customWebInstanceFormOpen, open => {
|
||||
switch (open) {
|
||||
case true: return new SetCustomWebInstanceView(vm);
|
||||
case false: return new TemplateView(vm, t => this.renderContent(t, vm));
|
||||
}
|
||||
});
|
||||
}
|
||||
renderContent(t, vm) {
|
||||
return t.div({className: {"ClientView": true, "isPreferred": vm => vm.hasPreferredWebInstance}}, [
|
||||
... vm.hasPreferredWebInstance ? [t.div({className: "hostedBanner"}, vm.hostedByBannerLabel)] : [],
|
||||
t.div({className: "header"}, [
|
||||
@@ -112,10 +120,49 @@ class InstallClientView extends TemplateView {
|
||||
}
|
||||
}
|
||||
|
||||
export class SetCustomWebInstanceView extends TemplateView {
|
||||
render(t, vm) {
|
||||
return t.div({className: "SetCustomWebInstanceView"}, [
|
||||
t.p([
|
||||
"Use a custom web instance for the ", t.strong(vm.name), " client:",
|
||||
]),
|
||||
t.form({action: "#", id: "setCustomWebInstanceForm", onSubmit: evt => this._onSubmit(evt)}, [
|
||||
t.input({
|
||||
type: "text",
|
||||
className: "fullwidth large",
|
||||
placeholder: "chat.example.org",
|
||||
name: "instanceHostname",
|
||||
value: vm.preferredWebInstance || "",
|
||||
}),
|
||||
t.input({type: "submit", value: "Save", className: "primary fullwidth"}),
|
||||
t.input({type: "button", value: "Use Default Instance", className: "secondary fullwidth", onClick: evt => this._onReset(evt)}),
|
||||
])
|
||||
]);
|
||||
}
|
||||
|
||||
_onSubmit(evt) {
|
||||
evt.preventDefault();
|
||||
const form = evt.target;
|
||||
const {instanceHostname} = form.elements;
|
||||
this.value.setCustomWebInstance(instanceHostname.value);
|
||||
this.value.closeCustomWebInstanceForm();
|
||||
}
|
||||
|
||||
_onReset(evt) {
|
||||
this.value.setCustomWebInstance(undefined);
|
||||
this.value.closeCustomWebInstanceForm();
|
||||
}
|
||||
}
|
||||
|
||||
function showBack(t, vm) {
|
||||
return t.p({className: {caption: true, "back": true, hidden: vm => !vm.showBack}}, [
|
||||
`Continue with ${vm.name} · `,
|
||||
t.button({className: "text", onClick: () => vm.back()}, "Change"),
|
||||
t.span({hidden: vm => !vm.supportsCustomWebInstances}, [
|
||||
' · ',
|
||||
t.button({className: "text", onClick: () => vm.configureCustomWebInstance()}, "Use Custom Web Instance"),
|
||||
])
|
||||
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
+46
-13
@@ -35,6 +35,7 @@ export class ClientViewModel extends ViewModel {
|
||||
this._pickClient = pickClient;
|
||||
// to provide "choose other client" button after calling pick()
|
||||
this._clientListViewModel = null;
|
||||
this.customWebInstanceFormOpen = false;
|
||||
this._update();
|
||||
}
|
||||
|
||||
@@ -59,11 +60,11 @@ export class ClientViewModel extends ViewModel {
|
||||
if (this._proposedPlatform === this._nativePlatform) {
|
||||
deepLinkLabel = "Open in app";
|
||||
} else {
|
||||
deepLinkLabel = `Open on ${this._client.getPreferredWebInstance(this._link)}`;
|
||||
deepLinkLabel = `Open on ${this.preferredWebInstance}`;
|
||||
}
|
||||
}
|
||||
const actions = [];
|
||||
const proposedDeepLink = this._client.getDeepLink(this._proposedPlatform, this._link);
|
||||
const proposedDeepLink = this._client.getDeepLink(this._proposedPlatform, this._link, this.preferredWebInstance);
|
||||
if (proposedDeepLink) {
|
||||
actions.push({
|
||||
label: deepLinkLabel,
|
||||
@@ -83,8 +84,8 @@ export class ClientViewModel extends ViewModel {
|
||||
// show only if there is a preferred instance, and if we don't already link to it in the first button
|
||||
if (hasPreferredWebInstance && this._webPlatform && this._proposedPlatform !== this._webPlatform) {
|
||||
actions.push({
|
||||
label: `Open on ${this._client.getPreferredWebInstance(this._link)}`,
|
||||
url: this._client.getDeepLink(this._webPlatform, this._link),
|
||||
label: `Open on ${this.preferredWebInstance}`,
|
||||
url: this._client.getDeepLink(this._webPlatform, this._link, this.preferredWebInstance),
|
||||
kind: "open-in-web",
|
||||
activated: () => {} // don't persist this choice as we don't persist the preferred web instance, it's in the url
|
||||
});
|
||||
@@ -108,10 +109,10 @@ export class ClientViewModel extends ViewModel {
|
||||
actions.push(...nativeActions);
|
||||
}
|
||||
if (this._webPlatform) {
|
||||
const webDeepLink = this._client.getDeepLink(this._webPlatform, this._link);
|
||||
const webDeepLink = this._client.getDeepLink(this._webPlatform, this._link, this.preferredWebInstance);
|
||||
if (webDeepLink) {
|
||||
const webLabel = this.hasPreferredWebInstance ?
|
||||
`Open on ${this._client.getPreferredWebInstance(this._link)}` :
|
||||
`Open on ${this.preferredWebInstance}` :
|
||||
`Continue in your browser`;
|
||||
actions.push({
|
||||
label: webLabel,
|
||||
@@ -128,18 +129,26 @@ export class ClientViewModel extends ViewModel {
|
||||
return actions;
|
||||
}
|
||||
|
||||
get hasPreferredWebInstance() {
|
||||
get preferredWebInstance() {
|
||||
// also check there is a web platform that matches the platforms the user is on (mobile or desktop web)
|
||||
return this._webPlatform && typeof this._client.getPreferredWebInstance(this._link) === "string";
|
||||
if (!this._webPlatform) return undefined;
|
||||
return (
|
||||
this.preferences.getCustomWebInstance(this._client.id)
|
||||
|| this._client.getPreferredWebInstance(this._link)
|
||||
);
|
||||
}
|
||||
|
||||
get hasPreferredWebInstance() {
|
||||
return typeof this.preferredWebInstance === "string";
|
||||
}
|
||||
|
||||
get hostedByBannerLabel() {
|
||||
const preferredWebInstance = this._client.getPreferredWebInstance(this._link);
|
||||
if (this._webPlatform && preferredWebInstance) {
|
||||
if (this.hasPreferredWebInstance) {
|
||||
const preferredWebInstance = this.preferredWebInstance;
|
||||
let label = preferredWebInstance;
|
||||
const subDomainIdx = preferredWebInstance.lastIndexOf(".", preferredWebInstance.lastIndexOf("."));
|
||||
const subDomainIdx = preferredWebInstance.lastIndexOf(".", preferredWebInstance.lastIndexOf(".") - 1);
|
||||
if (subDomainIdx !== -1) {
|
||||
label = preferredWebInstance.slice(preferredWebInstance.length - subDomainIdx + 1);
|
||||
label = preferredWebInstance.slice(subDomainIdx + 1);
|
||||
}
|
||||
return `Hosted by ${label}`;
|
||||
}
|
||||
@@ -188,7 +197,7 @@ export class ClientViewModel extends ViewModel {
|
||||
|
||||
get showDeepLinkInInstall() {
|
||||
// we can assume this._nativePlatform as this._clientCanIntercept already checks it
|
||||
return this._clientCanIntercept && !!this._client.getDeepLink(this._nativePlatform, this._link);
|
||||
return this._clientCanIntercept && !!this._client.getDeepLink(this._nativePlatform, this._link, this.preferredWebInstance);
|
||||
}
|
||||
|
||||
get availableOnPlatformNames() {
|
||||
@@ -223,6 +232,10 @@ export class ClientViewModel extends ViewModel {
|
||||
return !!this._clientListViewModel;
|
||||
}
|
||||
|
||||
get supportsCustomWebInstances() {
|
||||
return !!this._client.supportsCustomInstances;
|
||||
}
|
||||
|
||||
back() {
|
||||
if (this._clientListViewModel) {
|
||||
const vm = this._clientListViewModel;
|
||||
@@ -231,9 +244,29 @@ export class ClientViewModel extends ViewModel {
|
||||
// in the list with all clients, and also if we refresh, we get the list with
|
||||
// all clients rather than having our "change client" click reverted.
|
||||
this.preferences.setClient(undefined, undefined);
|
||||
this.preferences.setCustomWebInstance(this._client.id, undefined);
|
||||
this._update();
|
||||
this.emitChange();
|
||||
vm.showAll();
|
||||
}
|
||||
}
|
||||
|
||||
configureCustomWebInstance() {
|
||||
this.customWebInstanceFormOpen = true;
|
||||
this.emitChange();
|
||||
}
|
||||
|
||||
closeCustomWebInstanceForm() {
|
||||
this.customWebInstanceFormOpen = false;
|
||||
this.emitChange();
|
||||
}
|
||||
|
||||
setCustomWebInstance(hostname) {
|
||||
if (hostname) {
|
||||
hostname = hostname.trim().replace(/^https:\/\//, '').replace(/\/.*$/, '');
|
||||
}
|
||||
this.preferences.setClient(this._client.id, hostname ? this._webPlatform : (this._nativePlatform || this._webPlatform));
|
||||
this.preferences.setCustomWebInstance(this._client.id, hostname || undefined);
|
||||
this._update();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,13 +18,15 @@ import {Maturity, Platform, LinkKind,
|
||||
FDroidLink, AppleStoreLink, PlayStoreLink, WebsiteLink} from "../types.js";
|
||||
|
||||
const trustedWebInstances = [
|
||||
"app.element.io", // first one is the default one
|
||||
"chat.ztfr.eu", // Zeitfresser ist der gesetzte Standard
|
||||
"app.element.io",
|
||||
"develop.element.io",
|
||||
"chat.fedoraproject.org",
|
||||
"chat.fosdem.org",
|
||||
"chat.mozilla.org",
|
||||
"webchat.kde.org",
|
||||
"app.gitter.im",
|
||||
"chat.blender.org",
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -56,8 +58,9 @@ export class Element {
|
||||
get homepage() { return "https://element.io"; }
|
||||
get author() { return "Element"; }
|
||||
getMaturity(platform) { return Maturity.Stable; }
|
||||
get supportsCustomInstances() { return true; }
|
||||
|
||||
getDeepLink(platform, link) {
|
||||
getDeepLink(platform, link, preferredWebInstance) {
|
||||
let fragmentPath;
|
||||
switch (link.kind) {
|
||||
case LinkKind.User:
|
||||
@@ -80,12 +83,15 @@ export class Element {
|
||||
|
||||
const isWebPlatform = platform === Platform.DesktopWeb || platform === Platform.MobileWeb;
|
||||
if (isWebPlatform || platform === Platform.iOS) {
|
||||
// Standardmäßig deine Instanz nehmen
|
||||
let instanceHost = trustedWebInstances[0];
|
||||
// we use app.element.io which iOS will intercept, but it likely won't intercept any other trusted instances
|
||||
// so only use a preferred web instance for true web links.
|
||||
if (isWebPlatform && trustedWebInstances.includes(link.webInstances[this.id])) {
|
||||
instanceHost = link.webInstances[this.id];
|
||||
|
||||
// Falls der Nutzer über den "Change"-Dialog eine bevorzugte Instanz
|
||||
// oder eine Custom-URL gewählt hat, nutzen wir diese:
|
||||
if (isWebPlatform && preferredWebInstance) {
|
||||
instanceHost = preferredWebInstance;
|
||||
}
|
||||
|
||||
return `https://${instanceHost}/#/${fragmentPath}`;
|
||||
} else if (platform === Platform.Linux || platform === Platform.Windows || platform === Platform.macOS) {
|
||||
return `element://vector/webapp/#/${fragmentPath}`;
|
||||
@@ -98,8 +104,8 @@ export class Element {
|
||||
getCopyString(platform, link) {}
|
||||
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')];
|
||||
case Platform.iOS: return [new AppleStoreLink('element-x-secure-chat-call', 'id1631335820')];
|
||||
case Platform.Android: return [new PlayStoreLink('io.element.android.x'), new FDroidLink('io.element.android.x')];
|
||||
default: return [new WebsiteLink("https://element.io/download")];
|
||||
}
|
||||
}
|
||||
@@ -109,6 +115,8 @@ export class Element {
|
||||
}
|
||||
|
||||
getPreferredWebInstance(link) {
|
||||
// Hier geben wir dem System die Erlaubnis, gespeicherte Präferenzen zu finden.
|
||||
// Wenn keine da sind, greift oben automatisch trustedWebInstances[0].
|
||||
const idx = trustedWebInstances.indexOf(link.webInstances[this.id])
|
||||
return idx === -1 ? undefined : trustedWebInstances[idx];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user