Compare commits
No commits in common. "main" and "jryans/hide-focus-mouse" have entirely different histories.
main
...
jryans/hid
|
@ -1,14 +0,0 @@
|
||||||
# top-most EditorConfig file
|
|
||||||
root = true
|
|
||||||
|
|
||||||
# Unix-style newlines with a newline ending every file
|
|
||||||
[*]
|
|
||||||
end_of_line = lf
|
|
||||||
insert_final_newline = true
|
|
||||||
charset = utf-8
|
|
||||||
indent_style = space
|
|
||||||
indent_size = 4
|
|
||||||
|
|
||||||
# Matches multiple files with brace expansion notation
|
|
||||||
# Set default charset
|
|
||||||
# [*.{js,py}]
|
|
19
.eslintrc.js
|
@ -1,14 +1,7 @@
|
||||||
module.exports = {
|
module.exports = {
|
||||||
"env": {
|
"extends": [
|
||||||
"browser": true,
|
"matrix-org/ts",
|
||||||
"es6": true
|
"matrix-org/react",
|
||||||
},
|
],
|
||||||
"extends": "eslint:recommended",
|
}
|
||||||
"parserOptions": {
|
|
||||||
"ecmaVersion": 2020,
|
|
||||||
"sourceType": "module"
|
|
||||||
},
|
|
||||||
"rules": {
|
|
||||||
"no-console": "off"
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
34
.gitignore
vendored
|
@ -1,5 +1,31 @@
|
||||||
node_modules
|
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||||
build
|
|
||||||
*.tar.gz
|
# dependencies
|
||||||
/.idea
|
/node_modules
|
||||||
|
/.pnp
|
||||||
|
.pnp.js
|
||||||
|
|
||||||
|
# testing
|
||||||
|
/coverage
|
||||||
|
|
||||||
|
# development
|
||||||
|
bundle.js
|
||||||
|
bundle.js.map
|
||||||
|
|
||||||
|
# production
|
||||||
|
/build
|
||||||
|
|
||||||
|
# misc
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
.env.local
|
||||||
|
.env.development.local
|
||||||
|
.env.test.local
|
||||||
|
.env.production.local
|
||||||
|
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
|
||||||
|
.vercel
|
||||||
|
|
||||||
|
storybook-static
|
||||||
|
|
32
.storybook/main.js
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
/*
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
stories: ['../src/**/*.stories.tsx'],
|
||||||
|
addons: [
|
||||||
|
'@storybook/preset-create-react-app',
|
||||||
|
'@storybook/addon-actions',
|
||||||
|
'@storybook/addon-links',
|
||||||
|
'@storybook/addon-storysource',
|
||||||
|
'@storybook/addon-viewport/register',
|
||||||
|
'@storybook/addon-a11y/register',
|
||||||
|
'@storybook/addon-knobs/register',
|
||||||
|
'@storybook/addon-actions/register',
|
||||||
|
'storybook-addon-designs',
|
||||||
|
'@storybook/addon-backgrounds/register',
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
45
.storybook/preview.js
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
/*
|
||||||
|
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 React from 'react';
|
||||||
|
import { addDecorator } from '@storybook/react';
|
||||||
|
import { withA11y } from '@storybook/addon-a11y';
|
||||||
|
import { withKnobs } from '@storybook/addon-knobs';
|
||||||
|
import { withDesign } from 'storybook-addon-designs'
|
||||||
|
import { addParameters } from '@storybook/react';
|
||||||
|
|
||||||
|
import SingleColumn from '../src/layouts/SingleColumn';
|
||||||
|
|
||||||
|
// Default styles
|
||||||
|
import "../src/index.scss";
|
||||||
|
|
||||||
|
addDecorator(
|
||||||
|
storyFn => <SingleColumn>{storyFn()}</SingleColumn>
|
||||||
|
);
|
||||||
|
|
||||||
|
addDecorator(withA11y);
|
||||||
|
|
||||||
|
addDecorator(withKnobs);
|
||||||
|
|
||||||
|
addDecorator(withDesign);
|
||||||
|
|
||||||
|
addParameters({
|
||||||
|
backgrounds: [
|
||||||
|
{name: 'light', value: '#F4F4F4', default: true},
|
||||||
|
{name: 'white', value: '#FFFFFF'},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
|
@ -1,59 +0,0 @@
|
||||||
# Contributing to Matrix.to
|
|
||||||
|
|
||||||
Thank you for taking the time to contribute to Matrix!
|
|
||||||
|
|
||||||
This is the repository for Matrix.to, a simple url redirection service for the Matrix.org ecosystem which lets users share links to matrix entities without being tied to a specific app.
|
|
||||||
|
|
||||||
## Sign off
|
|
||||||
|
|
||||||
We ask that everybody who contributes to this project signs off their contributions, as explained below.
|
|
||||||
|
|
||||||
We follow a simple 'inbound=outbound' model for contributions: the act of submitting an 'inbound' contribution means that the contributor agrees to license their contribution under the same terms as the project's overall 'outbound' license - in our case, this is Apache Software License v2 (see [LICENSE](./LICENSE)).
|
|
||||||
|
|
||||||
In order to have a concrete record that your contribution is intentional and you agree to license it under the same terms as the project's license, we've adopted the same lightweight approach used by the [Linux Kernel](https://www.kernel.org/doc/html/latest/process/submitting-patches.html), [Docker](https://github.com/docker/docker/blob/master/CONTRIBUTING.md), and many other projects: the [Developer Certificate of Origin](https://developercertificate.org/) (DCO). This is a simple declaration that you wrote the contribution or otherwise have the right to contribute it to Matrix:
|
|
||||||
|
|
||||||
```
|
|
||||||
Developer Certificate of Origin
|
|
||||||
Version 1.1
|
|
||||||
|
|
||||||
Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
|
|
||||||
660 York Street, Suite 102,
|
|
||||||
San Francisco, CA 94110 USA
|
|
||||||
|
|
||||||
Everyone is permitted to copy and distribute verbatim copies of this
|
|
||||||
license document, but changing it is not allowed.
|
|
||||||
|
|
||||||
Developer's Certificate of Origin 1.1
|
|
||||||
|
|
||||||
By making a contribution to this project, I certify that:
|
|
||||||
|
|
||||||
(a) The contribution was created in whole or in part by me and I
|
|
||||||
have the right to submit it under the open source license
|
|
||||||
indicated in the file; or
|
|
||||||
|
|
||||||
(b) The contribution is based upon previous work that, to the best
|
|
||||||
of my knowledge, is covered under an appropriate open source
|
|
||||||
license and I have the right under that license to submit that
|
|
||||||
work with modifications, whether created in whole or in part
|
|
||||||
by me, under the same open source license (unless I am
|
|
||||||
permitted to submit under a different license), as indicated
|
|
||||||
in the file; or
|
|
||||||
|
|
||||||
(c) The contribution was provided directly to me by some other
|
|
||||||
person who certified (a), (b) or (c) and I have not modified
|
|
||||||
it.
|
|
||||||
|
|
||||||
(d) I understand and agree that this project and the contribution
|
|
||||||
are public and that a record of the contribution (including all
|
|
||||||
personal information I submit with it, including my sign-off) is
|
|
||||||
maintained indefinitely and may be redistributed consistent with
|
|
||||||
this project or the open source license(s) involved.
|
|
||||||
```
|
|
||||||
|
|
||||||
If you agree to this for your contribution, then all that's needed is to include the line in your commit or pull request comment:
|
|
||||||
|
|
||||||
```
|
|
||||||
Signed-off-by: Your Name <your@email.example.org>
|
|
||||||
```
|
|
||||||
|
|
||||||
Git allows you to add this signoff automatically when using the `-s` flag to `git commit`, which uses the name and email set in your `user.name` and `user.email` git configs.
|
|
19
Dockerfile
|
@ -1,19 +0,0 @@
|
||||||
FROM node:latest
|
|
||||||
|
|
||||||
# Set working directory
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
# Copy package files first to leverage Docker cache
|
|
||||||
COPY package.json yarn.lock ./
|
|
||||||
|
|
||||||
# Install dependencies
|
|
||||||
RUN yarn install
|
|
||||||
|
|
||||||
# Copy the rest of the application
|
|
||||||
COPY . .
|
|
||||||
|
|
||||||
# Expose port 5000
|
|
||||||
EXPOSE 5000
|
|
||||||
|
|
||||||
# Start the application
|
|
||||||
CMD ["yarn", "start"]
|
|
177
LICENSE
|
@ -1,177 +0,0 @@
|
||||||
|
|
||||||
Apache License
|
|
||||||
Version 2.0, January 2004
|
|
||||||
http://www.apache.org/licenses/
|
|
||||||
|
|
||||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
||||||
|
|
||||||
1. Definitions.
|
|
||||||
|
|
||||||
"License" shall mean the terms and conditions for use, reproduction,
|
|
||||||
and distribution as defined by Sections 1 through 9 of this document.
|
|
||||||
|
|
||||||
"Licensor" shall mean the copyright owner or entity authorized by
|
|
||||||
the copyright owner that is granting the License.
|
|
||||||
|
|
||||||
"Legal Entity" shall mean the union of the acting entity and all
|
|
||||||
other entities that control, are controlled by, or are under common
|
|
||||||
control with that entity. For the purposes of this definition,
|
|
||||||
"control" means (i) the power, direct or indirect, to cause the
|
|
||||||
direction or management of such entity, whether by contract or
|
|
||||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
||||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
||||||
|
|
||||||
"You" (or "Your") shall mean an individual or Legal Entity
|
|
||||||
exercising permissions granted by this License.
|
|
||||||
|
|
||||||
"Source" form shall mean the preferred form for making modifications,
|
|
||||||
including but not limited to software source code, documentation
|
|
||||||
source, and configuration files.
|
|
||||||
|
|
||||||
"Object" form shall mean any form resulting from mechanical
|
|
||||||
transformation or translation of a Source form, including but
|
|
||||||
not limited to compiled object code, generated documentation,
|
|
||||||
and conversions to other media types.
|
|
||||||
|
|
||||||
"Work" shall mean the work of authorship, whether in Source or
|
|
||||||
Object form, made available under the License, as indicated by a
|
|
||||||
copyright notice that is included in or attached to the work
|
|
||||||
(an example is provided in the Appendix below).
|
|
||||||
|
|
||||||
"Derivative Works" shall mean any work, whether in Source or Object
|
|
||||||
form, that is based on (or derived from) the Work and for which the
|
|
||||||
editorial revisions, annotations, elaborations, or other modifications
|
|
||||||
represent, as a whole, an original work of authorship. For the purposes
|
|
||||||
of this License, Derivative Works shall not include works that remain
|
|
||||||
separable from, or merely link (or bind by name) to the interfaces of,
|
|
||||||
the Work and Derivative Works thereof.
|
|
||||||
|
|
||||||
"Contribution" shall mean any work of authorship, including
|
|
||||||
the original version of the Work and any modifications or additions
|
|
||||||
to that Work or Derivative Works thereof, that is intentionally
|
|
||||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
||||||
or by an individual or Legal Entity authorized to submit on behalf of
|
|
||||||
the copyright owner. For the purposes of this definition, "submitted"
|
|
||||||
means any form of electronic, verbal, or written communication sent
|
|
||||||
to the Licensor or its representatives, including but not limited to
|
|
||||||
communication on electronic mailing lists, source code control systems,
|
|
||||||
and issue tracking systems that are managed by, or on behalf of, the
|
|
||||||
Licensor for the purpose of discussing and improving the Work, but
|
|
||||||
excluding communication that is conspicuously marked or otherwise
|
|
||||||
designated in writing by the copyright owner as "Not a Contribution."
|
|
||||||
|
|
||||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
||||||
on behalf of whom a Contribution has been received by Licensor and
|
|
||||||
subsequently incorporated within the Work.
|
|
||||||
|
|
||||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
copyright license to reproduce, prepare Derivative Works of,
|
|
||||||
publicly display, publicly perform, sublicense, and distribute the
|
|
||||||
Work and such Derivative Works in Source or Object form.
|
|
||||||
|
|
||||||
3. Grant of Patent License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
(except as stated in this section) patent license to make, have made,
|
|
||||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
||||||
where such license applies only to those patent claims licensable
|
|
||||||
by such Contributor that are necessarily infringed by their
|
|
||||||
Contribution(s) alone or by combination of their Contribution(s)
|
|
||||||
with the Work to which such Contribution(s) was submitted. If You
|
|
||||||
institute patent litigation against any entity (including a
|
|
||||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
||||||
or a Contribution incorporated within the Work constitutes direct
|
|
||||||
or contributory patent infringement, then any patent licenses
|
|
||||||
granted to You under this License for that Work shall terminate
|
|
||||||
as of the date such litigation is filed.
|
|
||||||
|
|
||||||
4. Redistribution. You may reproduce and distribute copies of the
|
|
||||||
Work or Derivative Works thereof in any medium, with or without
|
|
||||||
modifications, and in Source or Object form, provided that You
|
|
||||||
meet the following conditions:
|
|
||||||
|
|
||||||
(a) You must give any other recipients of the Work or
|
|
||||||
Derivative Works a copy of this License; and
|
|
||||||
|
|
||||||
(b) You must cause any modified files to carry prominent notices
|
|
||||||
stating that You changed the files; and
|
|
||||||
|
|
||||||
(c) You must retain, in the Source form of any Derivative Works
|
|
||||||
that You distribute, all copyright, patent, trademark, and
|
|
||||||
attribution notices from the Source form of the Work,
|
|
||||||
excluding those notices that do not pertain to any part of
|
|
||||||
the Derivative Works; and
|
|
||||||
|
|
||||||
(d) If the Work includes a "NOTICE" text file as part of its
|
|
||||||
distribution, then any Derivative Works that You distribute must
|
|
||||||
include a readable copy of the attribution notices contained
|
|
||||||
within such NOTICE file, excluding those notices that do not
|
|
||||||
pertain to any part of the Derivative Works, in at least one
|
|
||||||
of the following places: within a NOTICE text file distributed
|
|
||||||
as part of the Derivative Works; within the Source form or
|
|
||||||
documentation, if provided along with the Derivative Works; or,
|
|
||||||
within a display generated by the Derivative Works, if and
|
|
||||||
wherever such third-party notices normally appear. The contents
|
|
||||||
of the NOTICE file are for informational purposes only and
|
|
||||||
do not modify the License. You may add Your own attribution
|
|
||||||
notices within Derivative Works that You distribute, alongside
|
|
||||||
or as an addendum to the NOTICE text from the Work, provided
|
|
||||||
that such additional attribution notices cannot be construed
|
|
||||||
as modifying the License.
|
|
||||||
|
|
||||||
You may add Your own copyright statement to Your modifications and
|
|
||||||
may provide additional or different license terms and conditions
|
|
||||||
for use, reproduction, or distribution of Your modifications, or
|
|
||||||
for any such Derivative Works as a whole, provided Your use,
|
|
||||||
reproduction, and distribution of the Work otherwise complies with
|
|
||||||
the conditions stated in this License.
|
|
||||||
|
|
||||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
||||||
any Contribution intentionally submitted for inclusion in the Work
|
|
||||||
by You to the Licensor shall be under the terms and conditions of
|
|
||||||
this License, without any additional terms or conditions.
|
|
||||||
Notwithstanding the above, nothing herein shall supersede or modify
|
|
||||||
the terms of any separate license agreement you may have executed
|
|
||||||
with Licensor regarding such Contributions.
|
|
||||||
|
|
||||||
6. Trademarks. This License does not grant permission to use the trade
|
|
||||||
names, trademarks, service marks, or product names of the Licensor,
|
|
||||||
except as required for reasonable and customary use in describing the
|
|
||||||
origin of the Work and reproducing the content of the NOTICE file.
|
|
||||||
|
|
||||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
||||||
agreed to in writing, Licensor provides the Work (and each
|
|
||||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
||||||
implied, including, without limitation, any warranties or conditions
|
|
||||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
||||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
||||||
appropriateness of using or redistributing the Work and assume any
|
|
||||||
risks associated with Your exercise of permissions under this License.
|
|
||||||
|
|
||||||
8. Limitation of Liability. In no event and under no legal theory,
|
|
||||||
whether in tort (including negligence), contract, or otherwise,
|
|
||||||
unless required by applicable law (such as deliberate and grossly
|
|
||||||
negligent acts) or agreed to in writing, shall any Contributor be
|
|
||||||
liable to You for damages, including any direct, indirect, special,
|
|
||||||
incidental, or consequential damages of any character arising as a
|
|
||||||
result of this License or out of the use or inability to use the
|
|
||||||
Work (including but not limited to damages for loss of goodwill,
|
|
||||||
work stoppage, computer failure or malfunction, or any and all
|
|
||||||
other commercial damages or losses), even if such Contributor
|
|
||||||
has been advised of the possibility of such damages.
|
|
||||||
|
|
||||||
9. Accepting Warranty or Additional Liability. While redistributing
|
|
||||||
the Work or Derivative Works thereof, You may choose to offer,
|
|
||||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
||||||
or other liability obligations and/or rights consistent with this
|
|
||||||
License. However, in accepting such obligations, You may act only
|
|
||||||
on Your own behalf and on Your sole responsibility, not on behalf
|
|
||||||
of any other Contributor, and only if You agree to indemnify,
|
|
||||||
defend, and hold each Contributor harmless for any liability
|
|
||||||
incurred by, or claims asserted against, such Contributor by reason
|
|
||||||
of your accepting any such warranty or additional liability.
|
|
||||||
|
|
||||||
END OF TERMS AND CONDITIONS
|
|
67
README.md
|
@ -1,54 +1,10 @@
|
||||||
# dm.sij.law
|
# Matrix.to
|
||||||
This repo is cloned from the official Matrix.to repo on Github. I've added a Dockerfile and will make modifications in the future. The current version is live at [dm.sij.law](https://dm.sij.law)
|
|
||||||
|
|
||||||
## First clone the repo
|
|
||||||
```bash
|
|
||||||
git clone https://sij.ai/sij/env.esq.git
|
|
||||||
```
|
|
||||||
|
|
||||||
## Next build the image
|
|
||||||
```bash
|
|
||||||
docker build -t matrix-to .
|
|
||||||
```
|
|
||||||
|
|
||||||
## Then run it
|
|
||||||
```bash
|
|
||||||
docker run -d \
|
|
||||||
--name matrix-to \
|
|
||||||
-p 3636:5000 \
|
|
||||||
matrix-to
|
|
||||||
```
|
|
||||||
|
|
||||||
# EVERYTHING BELOW HERE IS ORIGINAL DOCUMENTATION
|
|
||||||
|
|
||||||
## Matrix.to
|
|
||||||
|
|
||||||
Matrix.to is a simple url redirection service for the Matrix.org ecosystem
|
Matrix.to is a simple url redirection service for the Matrix.org ecosystem
|
||||||
which lets users share links to matrix entities without being tied to a
|
which lets users share links to matrix entities without being tied to a
|
||||||
specific app.
|
specific app.
|
||||||
Stylistically it serves as a landing page for rooms and communities.
|
Stylistically it serves as a landing page for rooms and communities.
|
||||||
|
|
||||||
## How can I put a badge on my website linking to my matrix room?
|
|
||||||
|
|
||||||
You can use the badge image we've put up at https://matrix.to/img/matrix-badge.svg, and use it in a link like this:
|
|
||||||
|
|
||||||
[![Chat on Matrix](https://matrix.to/img/matrix-badge.svg)](https://matrix.to/#/#matrix.to:matrix.org)
|
|
||||||
|
|
||||||
You can use this Markdown:
|
|
||||||
```md
|
|
||||||
[![Chat on Matrix](https://matrix.to/img/matrix-badge.svg)](https://matrix.to/#/#matrix.to:matrix.org)
|
|
||||||
```
|
|
||||||
|
|
||||||
Or this HTML:
|
|
||||||
|
|
||||||
```html
|
|
||||||
<a href="https://matrix.to/#/#matrix.to:matrix.org" rel="noopener" target="_blank"><img src="https://matrix.to/img/matrix-badge.svg" alt="Chat on Matrix"></a>
|
|
||||||
```
|
|
||||||
|
|
||||||
to show the badge.
|
|
||||||
|
|
||||||
## How does matrix.to work?
|
|
||||||
|
|
||||||
Matrix.to preserves user privacy by not sharing any information about the links
|
Matrix.to preserves user privacy by not sharing any information about the links
|
||||||
being followed with the Matrix.to server - the redirection is calculated
|
being followed with the Matrix.to server - the redirection is calculated
|
||||||
entirely clientside using JavaScript, and the link details is hidden behind a
|
entirely clientside using JavaScript, and the link details is hidden behind a
|
||||||
|
@ -65,8 +21,6 @@ point at an alternative deployment of the service. The Matrix.to service could
|
||||||
also be hosted in an immutable/signed environment such as IPFS to further
|
also be hosted in an immutable/signed environment such as IPFS to further
|
||||||
increase its availability and avoid tampering.
|
increase its availability and avoid tampering.
|
||||||
|
|
||||||
## URL Scheme
|
|
||||||
|
|
||||||
The matrix.to URL scheme is
|
The matrix.to URL scheme is
|
||||||
|
|
||||||
| Entity type: | Example URL |
|
| Entity type: | Example URL |
|
||||||
|
@ -86,23 +40,10 @@ visitors.
|
||||||
(Technically the # and @ in the URL fragment should probably be escaped, but in
|
(Technically the # and @ in the URL fragment should probably be escaped, but in
|
||||||
practice for legibility we bend the rules and include it verbatim)
|
practice for legibility we bend the rules and include it verbatim)
|
||||||
|
|
||||||
### Optional parameters
|
|
||||||
|
|
||||||
https://matrix.to/#/#matrix:matrix.org?web-instance[element.io]=chat.mozilla.org
|
|
||||||
|
|
||||||
- `client`, e.g. `client=im.fluffychat`, `client=element.io`
|
|
||||||
- `web-instance[]`, e.g. `web-instance[element.io]=chat.mozilla.org`.
|
|
||||||
- For [matrix.to](https://matrix.to/), we have a list of [trusted web instances configured](src/open/clients/Element.js) (see `trustedWebInstances`).
|
|
||||||
- `via`, e.g. `via=mozilla.org`
|
|
||||||
|
|
||||||
You can discuss matrix.to in
|
You can discuss matrix.to in
|
||||||
[`#matrix.to:matrix.org`](https://matrix.to/#/#matrix.to:matrix.org)
|
[`#matrix.to:matrix.org`](https://matrix.to/#/#matrix.to:matrix.org)
|
||||||
|
|
||||||
## Build Instructions
|
A development build of matrix-two can be found at https://matrix-to.vercel.app
|
||||||
|
|
||||||
1. Install [yarn](https://classic.yarnpkg.com/en/docs/install)
|
A preview of all components can be found at
|
||||||
1. `git clone https://github.com/matrix-org/matrix.to`
|
https://matrix-to-storybook.vercel.app
|
||||||
1. `cd matrix.to`
|
|
||||||
1. `yarn`
|
|
||||||
1. `yarn start`
|
|
||||||
1. Go to http://localhost:5000 in your browser
|
|
||||||
|
|
|
@ -1,98 +0,0 @@
|
||||||
.ClientListView h2 {
|
|
||||||
text-align: center;
|
|
||||||
margin: 18px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ClientListView .filterOption {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
margin: 8px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ClientView {
|
|
||||||
border: 1px solid #E6E6E6;
|
|
||||||
border-radius: 8px;
|
|
||||||
margin: 16px 0;
|
|
||||||
padding: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ClientView.isPreferred {
|
|
||||||
border: 3px solid var(--link);
|
|
||||||
box-shadow: 0px 8px 4px rgba(0, 0, 0, 0.05);
|
|
||||||
}
|
|
||||||
|
|
||||||
.ClientView .hostedBanner {
|
|
||||||
text-align: center;
|
|
||||||
margin-bottom: 29px;
|
|
||||||
padding: 4px 0;
|
|
||||||
line-height: 20px;
|
|
||||||
border-radius: 8px;
|
|
||||||
font-weight: bold;
|
|
||||||
font-size: 16px;
|
|
||||||
background-color: var(--lightgrey);
|
|
||||||
}
|
|
||||||
|
|
||||||
.ClientView .header {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ClientView .description {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ClientView h3 {
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ClientView .clientIcon {
|
|
||||||
border-radius: 8px;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-size: cover;
|
|
||||||
width: 60px;
|
|
||||||
height: 60px;
|
|
||||||
overflow: hidden;
|
|
||||||
display: block;
|
|
||||||
margin-left: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.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');
|
|
||||||
}
|
|
||||||
|
|
||||||
.InstallClientView .instructions button.tick {
|
|
||||||
background-image: url('../images/tick-dark.svg');
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,13 +0,0 @@
|
||||||
.CreateLinkView h2 {
|
|
||||||
padding: 0 40px;
|
|
||||||
word-break: break-all;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.CreateLinkView form {
|
|
||||||
margin-top: 36px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.CreateLinkView form > *:not(:first-child) {
|
|
||||||
margin-top: 24px;
|
|
||||||
}
|
|
223
css/main.css
|
@ -1,223 +0,0 @@
|
||||||
/*
|
|
||||||
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 url('spinner.css');
|
|
||||||
@import url('client.css');
|
|
||||||
@import url('preview.css');
|
|
||||||
@import url('create.css');
|
|
||||||
@import url('open.css');
|
|
||||||
|
|
||||||
:root {
|
|
||||||
--app-background: #f4f4f4;
|
|
||||||
--background: #ffffff;
|
|
||||||
--foreground: #000000;
|
|
||||||
--font: #333333;
|
|
||||||
--grey: #666666;
|
|
||||||
--accent: #0098d4;
|
|
||||||
--error: #d6001c;
|
|
||||||
--link: #0098d4;
|
|
||||||
--borders: #f4f4f4;
|
|
||||||
--lightgrey: #E6E6E6;
|
|
||||||
--spinner-stroke-size: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
html {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
color: var(--font);
|
|
||||||
padding: 120px 0 0 0;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
noscript {
|
|
||||||
display: block;
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
p { line-height: 150%; }
|
|
||||||
a { text-decoration: none; }
|
|
||||||
|
|
||||||
h1 { font-size: 24px; }
|
|
||||||
h2 { font-size: 21px; }
|
|
||||||
h3 { font-size: 16px; }
|
|
||||||
|
|
||||||
body,
|
|
||||||
button,
|
|
||||||
input,
|
|
||||||
textarea {
|
|
||||||
font-family: Helvetica Neue, Helvetica, Arial, sans-serif;
|
|
||||||
font-style: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
.RootView {
|
|
||||||
margin: 0 auto;
|
|
||||||
max-width: 480px;
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
.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;
|
|
||||||
}
|
|
||||||
|
|
||||||
.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 {
|
|
||||||
border: none;
|
|
||||||
font-size: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type='text'].large {
|
|
||||||
width: 100%;
|
|
||||||
padding: 12px;
|
|
||||||
background: var(--background);
|
|
||||||
border: 1px solid var(--foreground);
|
|
||||||
border-radius: 16px;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fullwidth {
|
|
||||||
display: block;
|
|
||||||
width: 100%;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
.LoadServerPolicyView {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.LoadServerPolicyView .spinner {
|
|
||||||
width: 32px;
|
|
||||||
height: 32px;
|
|
||||||
margin-right: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.LoadServerPolicyView h2 {
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
52
css/open.css
|
@ -1,52 +0,0 @@
|
||||||
/*
|
|
||||||
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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
.OpenLinkView .caption {
|
|
||||||
color: var(--grey);
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ServerConsentView .actions label {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ServerConsentView .actions {
|
|
||||||
margin-top: 24px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ServerConsentView input[type=submit] {
|
|
||||||
flex: 1;
|
|
||||||
margin-left: 32px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ServerOptions div {
|
|
||||||
margin: 8px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ServerOptions label {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ServerOptions label > .line {
|
|
||||||
flex: 1;
|
|
||||||
border: none;
|
|
||||||
border-bottom: 1px solid var(--grey);
|
|
||||||
padding: 4px 0;
|
|
||||||
}
|
|
133
css/preview.css
|
@ -1,133 +0,0 @@
|
||||||
.PreviewView {
|
|
||||||
text-align: center;
|
|
||||||
margin-bottom: 32px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.PreviewView h1 {
|
|
||||||
font-size: 24px;
|
|
||||||
line-height: 32px;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
word-wrap: anywhere;
|
|
||||||
}
|
|
||||||
|
|
||||||
.PreviewView .avatarContainer {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.PreviewView .avatar {
|
|
||||||
border-radius: 100%;
|
|
||||||
width: 64px;
|
|
||||||
height: 64px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.PreviewView .mxSpace .avatar {
|
|
||||||
border-radius: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.PreviewView .defaultAvatar {
|
|
||||||
width: 64px;
|
|
||||||
height: 64px;
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
.PreviewView .identifier {
|
|
||||||
color: var(--grey);
|
|
||||||
font-size: 12px;
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
.PreviewView .memberCount p:not(.placeholder) {
|
|
||||||
padding: 4px 8px 4px 24px;
|
|
||||||
border-radius: 14px;
|
|
||||||
background-image: url(../images/member-icon.svg);
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-position: 2px center;
|
|
||||||
background-color: var(--lightgrey);
|
|
||||||
}
|
|
||||||
|
|
||||||
.PreviewView .memberCount p.placeholder {
|
|
||||||
height: 1.5em;
|
|
||||||
width: 100px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.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%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.placeholder {
|
|
||||||
border-radius: 1em;
|
|
||||||
--flash-bg: #ddd;
|
|
||||||
--flash-fg: #eee;
|
|
||||||
background: linear-gradient(120deg,
|
|
||||||
var(--flash-bg),
|
|
||||||
var(--flash-bg) 10%,
|
|
||||||
var(--flash-fg) calc(10% + 25px),
|
|
||||||
var(--flash-bg) calc(10% + 50px)
|
|
||||||
);
|
|
||||||
animation: flash 2s 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%; }
|
|
||||||
}
|
|
|
@ -1,27 +0,0 @@
|
||||||
@keyframes rotate {
|
|
||||||
0% { transform: rotate(0deg); }
|
|
||||||
100% { transform: rotate(360deg); }
|
|
||||||
}
|
|
||||||
|
|
||||||
.spinner {
|
|
||||||
width: 40px;
|
|
||||||
height: 40px;
|
|
||||||
border-radius: 100%;
|
|
||||||
border: var(--spinner-stroke-size) solid var(--app-background);
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
.spinner::before {
|
|
||||||
content: "";
|
|
||||||
display: block;
|
|
||||||
width: inherit;
|
|
||||||
height: inherit;
|
|
||||||
border-radius: 100%;
|
|
||||||
border-width: var(--spinner-stroke-size);
|
|
||||||
border-style: solid;
|
|
||||||
border-color: transparent;
|
|
||||||
border-top-color: var(--grey);
|
|
||||||
animation: rotate 0.8s linear infinite;
|
|
||||||
box-sizing: border-box;
|
|
||||||
margin: calc(-1 * var(--spinner-stroke-size));
|
|
||||||
}
|
|
|
@ -1,6 +0,0 @@
|
||||||
version: '3'
|
|
||||||
services:
|
|
||||||
matrix-to:
|
|
||||||
build: .
|
|
||||||
ports:
|
|
||||||
- "3636:5000"
|
|
|
@ -1,20 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="99" height="20">
|
|
||||||
<linearGradient id="b" x2="0" y2="100%">
|
|
||||||
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
|
|
||||||
<stop offset="1" stop-opacity=".1"/>
|
|
||||||
</linearGradient>
|
|
||||||
<mask id="a">
|
|
||||||
<rect width="99" height="20" rx="3" fill="#fff"/>
|
|
||||||
</mask>
|
|
||||||
<g mask="url(#a)">
|
|
||||||
<path fill="#555" d="M0 0h42v20H0z"/>
|
|
||||||
<path fill="#46BC99" d="M42 0h59v20H42z"/>
|
|
||||||
<path fill="url(#b)" d="M0 0h99v20H0z"/>
|
|
||||||
</g>
|
|
||||||
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
|
|
||||||
<text x="21" y="15" fill="#010101" fill-opacity=".3">matrix</text>
|
|
||||||
<text x="21" y="14">matrix</text>
|
|
||||||
<text x="68.5" y="15" fill="#010101" fill-opacity=".3">join chat</text>
|
|
||||||
<text x="68.5" y="14">join chat</text>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 781 B |
Before Width: | Height: | Size: 13 KiB |
|
@ -1,19 +0,0 @@
|
||||||
<!-- Generator: Adobe Illustrator 18.0.0, SVG Export Plug-In -->
|
|
||||||
<svg version="1.1"
|
|
||||||
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/"
|
|
||||||
x="0px" y="0px" width="18px" height="18px" viewBox="0 0 18 18" enable-background="new 0 0 18 18" xml:space="preserve">
|
|
||||||
<defs>
|
|
||||||
</defs>
|
|
||||||
<g>
|
|
||||||
<g>
|
|
||||||
<circle fill="#FFFFFF" cx="9" cy="9" r="8.5"/>
|
|
||||||
</g>
|
|
||||||
<g>
|
|
||||||
<path d="M9,0C4,0,0,4,0,9c0,5,4,9,9,9c5,0,9-4,9-9C18,4,14,0,9,0z M1.2,10.8l3.5-2.3c0-0.1,0-0.2,0-0.3c0-1.8,1.3-3.2,3.1-3.4
|
|
||||||
c0.1,0,0.2,0,0.4,0c1.2,0,2.3,0.6,2.9,1.6c0.3-0.1,0.6-0.1,0.9-0.1c0.4,0,0.8,0,1.2,0.1c0.7,0.2,1.4,0.5,2,0.9
|
|
||||||
C14.6,7.1,14,7,13.3,7c-1.2,0-2.2,0.4-2.9,1.4c-0.7,0.9-1.1,2-1.1,3.2c0,1.5-0.4,2.9-1.3,4.2c-0.3,0.4-0.5,0.7-0.8,1
|
|
||||||
C4.2,16.1,1.9,13.8,1.2,10.8z"/>
|
|
||||||
<circle cx="9.5" cy="6.4" r="0.5"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 871 B |
|
@ -1,9 +0,0 @@
|
||||||
<svg width="64" height="64" version="1.1" viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<rect x="0" y="0" width="64" height="64" fill="#0dbd8b" stop-color="#000000" stroke="#000" stroke-dasharray="0.188976, 0.37795299999999998" stroke-width=".18898"/>
|
|
||||||
<g clip-rule="evenodd" fill="#fff" fill-rule="evenodd">
|
|
||||||
<path d="m25.28 10.88c0-1.5906 1.2894-2.88 2.88-2.88 10.604 0 19.2 8.5961 19.2 19.2 0 1.5906-1.2894 2.88-2.88 2.88s-2.88-1.2894-2.88-2.88c0-7.4227-6.0173-13.44-13.44-13.44-1.5906 0-2.88-1.2894-2.88-2.88z"/>
|
|
||||||
<path d="m38.72 53.12c0 1.5906-1.2894 2.88-2.88 2.88-10.604 0-19.2-8.5961-19.2-19.2 0-1.5906 1.2894-2.88 2.88-2.88 1.5905 0 2.88 1.2894 2.88 2.88 0 7.4227 6.0173 13.44 13.44 13.44 1.5906 0 2.88 1.2894 2.88 2.88z"/>
|
|
||||||
<path d="m10.88 38.72c-1.5906 0-2.88-1.2894-2.88-2.88 0-10.604 8.5961-19.2 19.2-19.2 1.5906 0 2.88 1.2894 2.88 2.88 0 1.5905-1.2894 2.88-2.88 2.88-7.4227 0-13.44 6.0173-13.44 13.44 0 1.5906-1.2894 2.88-2.88 2.88z"/>
|
|
||||||
<path d="m53.12 25.28c1.5906 0 2.88 1.2894 2.88 2.88 0 10.604-8.5961 19.2-19.2 19.2-1.5906 0-2.88-1.2894-2.88-2.88 0-1.5905 1.2894-2.88 2.88-2.88 7.4227 0 13.44-6.0173 13.44-13.44 0-1.5906 1.2894-2.88 2.88-2.88z"/>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 1.2 KiB |
|
@ -1,43 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!-- Generator: Adobe Illustrator 22.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
|
||||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
|
||||||
viewBox="0 0 181.4 181.9" style="enable-background:new 0 0 181.4 181.9;" xml:space="preserve">
|
|
||||||
<style type="text/css">
|
|
||||||
.st0{fill:url(#SVGID_1_);}
|
|
||||||
.st1{fill:#F094BE;}
|
|
||||||
.st2{fill:#4D3F92;}
|
|
||||||
.st3{fill:#FFFFFF;}
|
|
||||||
</style>
|
|
||||||
<g id="Capa_1">
|
|
||||||
<rect x="0" y="0" style="color:#FFFFFF" width="181.4" height="181.9" class="st3"/>
|
|
||||||
</g>
|
|
||||||
<g id="Capa_2">
|
|
||||||
<g>
|
|
||||||
<path class="st2" d="M151.6,95.1c1.5-0.3,2.8-1,3.8-2c4-5.3,0.8-11.8-4.5-12.6c-0.8,0-1.5-0.8-1.5-1.5c0-0.3,0-0.5,0-0.5
|
|
||||||
c0.8-0.8,1.5-1.8,2.5-3.3c8.1-10.8,11.8-50.6,3.8-53.7c-9.8-3.3-29.7,6.3-38.3,17.4c-0.5-0.3-1-1-1-1.8c0.3-3-1.3-5.5-3.5-6.8
|
|
||||||
c-4.5-2.3-8.8,0-10.6,3.3c-0.5,0.8-1.3,1.3-2,1c-0.8,0-1.5-0.8-1.5-1.5c-0.5-2.5-2-4.5-4.3-5.5c-4.8-2-9.8,0.8-10.6,5.3
|
|
||||||
c-0.3,0.8-0.8,1.5-1.5,1.5c-0.8,0.3-1.5-0.3-2-1c-1.5-2.3-4-3.8-6.5-3.8c-4,0-7.6,3.3-7.8,7.3v0.3v0.3c0,0.8-0.5,1.5-1,1.8h-0.3
|
|
||||||
c-8.3-10.8-28.5-20.7-38.5-17.4c-8.1,2.8-4.3,42.6,4,53.4c1.5,2,2.8,3.5,3.8,4.5c-0.3,0.8-1,1.5-1.8,1.5c-1.3,0-2.5,0.5-3.5,1.3
|
|
||||||
c-5.3,5-2.3,12.1,3,13.4c0.8,0.3,1.5,1,1.5,1.8c0,0.8-0.5,1.8-1.3,2c-1,0.5-2,1-2.8,2c-4,5.8,0,12.3,5.5,12.3
|
|
||||||
c0.8,0,1.5,0.5,1.8,1.3c0.3,0.8,0.3,1.5-0.5,2c-1.5,1.5-2.3,3.5-2,5.5c0.3,2.8,2,5.3,4.8,6.5c1.5,0.8,3,0.8,4.5,0.5
|
|
||||||
c0.8-0.3,1.5,0,2,0.8c0.5,0.5,0.5,1.5,0.3,2c-0.8,1.5-1,3.3-0.5,5c0.8,2.8,2.8,4.8,5.5,5.5c2.5,0.5,4.3-0.3,5.5-0.8
|
|
||||||
c0.5-0.3-3.3,9.1-6,15.4c-0.8,2,1.3,4.3,3.5,3.3c8.3-3.8,22.2-10.3,22.2-9.8c0.5,5.3,6.5,9.1,12.3,5.3c1.3-0.8,2-2.3,2.3-3.5
|
|
||||||
c0.3-0.8,1-1.5,2-1.5c1,0,1.8,0.5,2,1.5c0.3,1.3,0.8,2.3,1.8,3c5.8,4.5,12.3,0.8,12.8-4.8c0-0.8,0.5-1.5,1.3-1.8
|
|
||||||
c0.8-0.3,1.5,0,2,0.5c1.5,1.5,3.3,2.5,5.3,2.5l0,0c2.5,0,5-1.3,6.5-3.8c1-1.5,1.3-3,1-5c0-0.8,0.3-1.5,0.8-2c0.5-0.5,1.5-0.5,2,0
|
|
||||||
c1.5,0.8,3.3,1.3,5,0.8c2.8-0.5,5-2.8,5.8-5.3c0.5-1.8,0.3-3.5-0.5-5.3c-0.3-0.8-0.3-1.5,0.3-2s1.3-0.8,2-0.8
|
|
||||||
c1.8,0.3,3.3,0.3,4.8-0.5c2.3-1,3.8-3,4.3-5.5c0.5-2.5-0.3-4.8-2-6.5c-0.5-0.5-0.8-1.3-0.5-2s1-1.3,1.8-1.3c1.8,0,3.8-0.5,5-2
|
|
||||||
c4.3-4.5,2.3-10.6-2.5-12.6c-0.8-0.3-1.3-1-1.3-2C150.1,95.8,150.8,95.1,151.6,95.1z"/>
|
|
||||||
<path class="st3" d="M131.4,42.2c0.5,1.5,0.5,3,0,4.5c-0.3,0.8,0,1.5,0.5,2s1.3,0.8,2,0.5c1-0.5,2-0.5,3-0.5c2.3,0,4.3,1,5.8,3
|
|
||||||
c1,1.3,1.8,3,1.5,4.8c0,1.5-0.5,2.8-1.3,4c-0.5,0.5-0.5,1.5,0,2c0.3,0.3,0.5,0.8,1,0.8c1-0.3,2-1,2.8-2c4.5-6.3,5.3-26.2,0.8-27.7
|
|
||||||
c-4.5-1.5-12.3,1.5-17.9,6C130.7,40.1,131.2,40.9,131.4,42.2z"/>
|
|
||||||
<path class="st3" d="M39,63.6c0.3-0.3,0.5-0.5,0.8-0.8c0.5-0.8,0.3-1.5,0-2C38.5,59,38.2,57,38.5,55c0.5-2.8,2.8-5,5.5-5.8
|
|
||||||
c1.5-0.5,3-0.3,4.5,0.3c0.8,0.3,1.5,0,2-0.5c0.5-0.5,0.8-1.3,0.5-2c-0.5-1.5-0.5-3,0-4.5c0.3-1,0.8-2,1.5-2.8
|
|
||||||
c-5.5-4.5-13.9-7.8-18.4-6.3S30.4,54.8,35,61.1C36,62.6,37.2,63.3,39,63.6z"/>
|
|
||||||
<g>
|
|
||||||
<circle class="st3" cx="60.9" cy="94.6" r="9.3"/>
|
|
||||||
<path class="st3" d="M100.7,94.6c0,5.3-4.3,9.3-9.3,9.3c-5.3,0-9.3-4.3-9.3-9.3S100.7,89.3,100.7,94.6z"/>
|
|
||||||
<circle class="st3" cx="121.6" cy="94.6" r="9.3"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 3.3 KiB |
|
@ -1,55 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<svg height="128px" viewBox="0 0 128 128" width="128px" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
|
||||||
<linearGradient id="a" gradientUnits="userSpaceOnUse" x1="8" x2="58" y1="69.999985" y2="69.999985">
|
|
||||||
<stop offset="0" stop-color="#4aaac9"/>
|
|
||||||
<stop offset="0.16" stop-color="#8bddf7"/>
|
|
||||||
<stop offset="0.32" stop-color="#4aaac9"/>
|
|
||||||
<stop offset="1" stop-color="#4aaac9"/>
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient id="b" gradientUnits="userSpaceOnUse" x1="31.462524" x2="39" y1="113.997253" y2="113.997253">
|
|
||||||
<stop offset="0" stop-color="#4aaac9"/>
|
|
||||||
<stop offset="0.469318" stop-color="#74d7f7"/>
|
|
||||||
<stop offset="1" stop-color="#4aaac9"/>
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient id="c" gradientUnits="userSpaceOnUse" x1="104" x2="120" y1="84" y2="84">
|
|
||||||
<stop offset="0" stop-color="#1a5fb4"/>
|
|
||||||
<stop offset="0.5" stop-color="#4296ff"/>
|
|
||||||
<stop offset="1" stop-color="#1a5fb4"/>
|
|
||||||
</linearGradient>
|
|
||||||
<clipPath id="d">
|
|
||||||
<path d="m 8 24 h 97 v 84 h -97 z m 0 0"/>
|
|
||||||
</clipPath>
|
|
||||||
<clipPath id="e">
|
|
||||||
<path d="m 24 24 h 80 c 8.835938 0 16 7.164062 16 16 v 52 c 0 8.835938 -7.164062 16 -16 16 h -80 c -8.835938 0 -16 -7.164062 -16 -16 v -52 c 0 -8.835938 7.164062 -16 16 -16 z m 0 0"/>
|
|
||||||
</clipPath>
|
|
||||||
<linearGradient id="f" gradientUnits="userSpaceOnUse" x1="55.608135" x2="71.783539" y1="100" y2="48.532928">
|
|
||||||
<stop offset="0" stop-color="#81dffe"/>
|
|
||||||
<stop offset="1" stop-color="#9bf8fe"/>
|
|
||||||
</linearGradient>
|
|
||||||
<filter id="g" height="100%" width="100%" x="0%" y="0%">
|
|
||||||
<feColorMatrix in="SourceGraphic" type="matrix" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"/>
|
|
||||||
</filter>
|
|
||||||
<mask id="h">
|
|
||||||
<g filter="url(#g)">
|
|
||||||
<rect fill-opacity="0.35" height="128" width="128"/>
|
|
||||||
</g>
|
|
||||||
</mask>
|
|
||||||
<clipPath id="i">
|
|
||||||
<rect height="152" width="192"/>
|
|
||||||
</clipPath>
|
|
||||||
<path d="m 24 28 h 72 c 8.835938 0 16 7.164062 16 16 v 52 c 0 8.835938 -7.164062 16 -16 16 h -72 c -8.835938 0 -16 -7.164062 -16 -16 v -52 c 0 -8.835938 7.164062 -16 16 -16 z m 0 0" fill="url(#a)"/>
|
|
||||||
<path d="m 24 28 h 80 c 8.835938 0 16 7.164062 16 16 v 48 c 0 8.835938 -7.164062 16 -16 16 h -80 c -8.835938 0 -16 -7.164062 -16 -16 v -48 c 0 -8.835938 7.164062 -16 16 -16 z m 0 0" fill="#53bde0"/>
|
|
||||||
<path d="m 24 100 v 12 h 4 c 2.210938 0 4 1.789062 4 4 v 7 c 0 1.992188 1.183594 3.792969 3.011719 4.585938 c 1.828125 0.789062 3.953125 0.417968 5.40625 -0.945313 l 13.523437 -12.707031 c 1.324219 -1.242188 3.070313 -1.933594 4.882813 -1.933594 h 9.175781 v -12 z m 0 0" fill="url(#b)" fill-rule="evenodd"/>
|
|
||||||
<path d="m 102 58.566406 h 2 c 8.835938 0 16 7.164063 16 16 v 21.433594 c 0 8.835938 -7.164062 16 -16 16 h -2 c -8.835938 0 -16 -7.164062 -16 -16 v -21.433594 c 0 -8.835937 7.164062 -16 16 -16 z m 0 0" fill="url(#c)"/>
|
|
||||||
<path d="m 86 87 h 18 v 25 h -18 z m 0 0" fill="#1a5fb4"/>
|
|
||||||
<path d="m 48 24 h 56 c 8.835938 0 16 7.164062 16 16 v 52 c 0 8.835938 -7.164062 16 -16 16 h -56 c -8.835938 0 -16 -7.164062 -16 -16 v -52 c 0 -8.835938 7.164062 -16 16 -16 z m 0 0" fill="#3584e4"/>
|
|
||||||
<g clip-path="url(#d)">
|
|
||||||
<g clip-path="url(#e)">
|
|
||||||
<path d="m 78.804688 16.023438 l 0.527343 2.460937 c -1.207031 -0.082031 -2.417969 4.964844 -3.621093 4.988281 c -19.335938 0.371094 -38.003907 14.230469 -39.148438 34.546875 c -0.835938 14.761719 9.570312 29.839844 25.15625 30.488281 c 10.371094 0.433594 20.96875 -6.957031 21.242188 -17.925781 c 0.179687 -7.078125 -4.953126 -14.3125 -12.488282 -14.355469 c -4.683594 -0.027343 -9.484375 3.425782 -9.398437 8.429688 c 0.074219 2.980469 2.300781 6.042969 5.511719 5.902344 c 1.8125 -0.082032 3.691406 -1.488282 3.539062 -3.453125 c -0.078125 -1.042969 -0.921875 -2.128907 -2.0625 -1.996094 c -0.5625 0.066406 -1.148438 0.539063 -1.046875 1.15625 c 0.070313 0.273437 0.285156 0.570313 0.597656 0.5 c 0.121094 -0.03125 0.25 -0.144531 0.214844 -0.28125 c 0 -0.042969 -0.070313 -0.09375 -0.113281 -0.074219 c 0 0.003906 -0.070313 0.023438 0 0.035156 v 0.007813 v -0.003906 v 0.035156 c 0 0.050781 -0.09375 0.050781 -0.136719 0.03125 c -0.121094 -0.066406 -0.113281 -0.242187 -0.070313 -0.347656 c 0.164063 -0.265625 0.542969 -0.230469 0.777344 -0.074219 c 0.519532 0.367188 0.429688 1.117188 0.070313 1.558594 c -0.710938 0.898437 -2.074219 0.726562 -2.867188 0.042968 c -1.5 -1.28125 -1.167969 -3.601562 0.070313 -4.941406 c 2.167968 -2.367187 5.929687 -1.792968 8.074218 0.28125 c 3.601563 3.476563 2.652344 9.308594 -0.675781 12.597656 c -5.359375 5.292969 -14.109375 3.800782 -18.992187 -1.324218 c -7.570313 -7.953125 -5.304688 -20.664063 2.335937 -27.6875 c 11.480469 -10.550782 29.507813 -7.242188 39.363281 3.785156 c 14.414063 16.121094 9.6875 41.066406 -5.855468 54.582031 c -11.121094 9.226563 -22.246094 15.429688 -32.949219 19.4375 c -13.058594 75.445313 -75.230469 6.835938 -81.039063 -4.195312 l 0.285157 -105.054688 z m 0 0" fill="url(#f)"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<path d="m 24 106 v 2 h 4 c 2.210938 0 4 1.789062 4 4 v 7 c 0 1.992188 1.183594 3.792969 3.011719 4.585938 c 1.828125 0.789062 3.953125 0.417968 5.40625 -0.945313 l 13.523437 -12.707031 c 1.324219 -1.242188 3.070313 -1.933594 4.882813 -1.933594 h 9.175781 v -2 z m 0 0" fill="#81dffe" fill-rule="evenodd"/>
|
|
||||||
<g clip-path="url(#i)" mask="url(#h)" transform="matrix(1 0 0 1 -8 -16)">
|
|
||||||
<path d="m 173 17 h 8 c 1.65625 0 3 1.34375 3 3 v 7 c 0 1.65625 -1.34375 3 -3 3 h -8 c -1.65625 0 -3 -1.34375 -3 -3 v -7 c 0 -1.65625 1.34375 -3 3 -3 z m 0 0" fill="#241f31"/>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 5.5 KiB |
|
@ -1,34 +0,0 @@
|
||||||
<svg width="56" height="56" viewBox="0 0 56 56" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<path d="M51 12C51 10.8954 50.1046 10 49 10H13C11.8954 10 11 10.8954 11 12V38C11 39.1046 11.8954 40 13 40H30.5858C30.851 40 31.1054 40.1054 31.2929 40.2929L39.2929 48.2929C39.9229 48.9229 41 48.4767 41 47.5858V41C41 40.4477 41.4477 40 42 40H49C50.1046 40 51 39.1046 51 38V12Z" fill="url(#paint0_linear)"/>
|
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M11 37V38C11 39.1046 11.8954 40 13 40H30.5858C30.851 40 31.1054 40.1054 31.2929 40.2929L39.2929 48.2929C39.9229 48.9229 41 48.4767 41 47.5858V46.5858C41 47.4767 39.9229 47.9229 39.2929 47.2929L31.2929 39.2929C31.1054 39.1054 30.851 39 30.5858 39H13C11.8954 39 11 38.1046 11 37ZM41 41C41 40.4477 41.4477 40 42 40H49C50.1046 40 51 39.1046 51 38V37C51 38.1046 50.1046 39 49 39H42C41.4477 39 41 39.4477 41 40V41Z" fill="#000405" fill-opacity="0.1"/>
|
|
||||||
<path d="M8 10C8 8.89543 8.89543 8 10 8H46C47.1046 8 48 8.89543 48 10V36C48 37.1046 47.1046 38 46 38H28.4142C28.149 38 27.8946 38.1054 27.7071 38.2929L19.7071 46.2929C19.0771 46.9229 18 46.4767 18 45.5858V39C18 38.4477 17.5523 38 17 38H10C8.89543 38 8 37.1046 8 36V10Z" fill="url(#paint1_linear)"/>
|
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M8 35V36C8 37.1046 8.89543 38 10 38H17C17.5523 38 18 38.4477 18 39V38C18 37.4477 17.5523 37 17 37H10C8.89543 37 8 36.1046 8 35ZM18 44.5858V45.5858C18 46.4767 19.0771 46.9229 19.7071 46.2929L27.7071 38.2929C27.8946 38.1054 28.149 38 28.4142 38H46C47.1046 38 48 37.1046 48 36V35C48 36.1046 47.1046 37 46 37H28.4142C28.149 37 27.8946 37.1054 27.7071 37.2929L19.7071 45.2929C19.0771 45.9229 18 45.4767 18 44.5858Z" fill="#031C5A" fill-opacity="0.1"/>
|
|
||||||
<rect x="13" y="14" width="30" height="4" rx="2" fill="url(#paint2_linear)"/>
|
|
||||||
<rect x="12.5" y="13.5" width="31" height="5" rx="2.5" stroke="#004E6E" stroke-opacity="0.1"/>
|
|
||||||
<rect x="13" y="21" width="25" height="4" rx="2" fill="url(#paint3_linear)"/>
|
|
||||||
<rect x="12.5" y="20.5" width="26" height="5" rx="2.5" stroke="#004E6E" stroke-opacity="0.1"/>
|
|
||||||
<rect x="13" y="28" width="20" height="4" rx="2" fill="url(#paint4_linear)"/>
|
|
||||||
<rect x="12.5" y="27.5" width="21" height="5" rx="2.5" stroke="#004E6E" stroke-opacity="0.1"/>
|
|
||||||
<defs>
|
|
||||||
<linearGradient id="paint0_linear" x1="51" y1="10" x2="11" y2="50" gradientUnits="userSpaceOnUse">
|
|
||||||
<stop stop-color="#6EB4D9"/>
|
|
||||||
<stop offset="1" stop-color="#004E6E"/>
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient id="paint1_linear" x1="8" y1="8" x2="48" y2="48" gradientUnits="userSpaceOnUse">
|
|
||||||
<stop stop-color="#44F0D3"/>
|
|
||||||
<stop offset="1" stop-color="#3DAEE9"/>
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient id="paint2_linear" x1="43" y1="18" x2="19.5854" y2="-11.2683" gradientUnits="userSpaceOnUse">
|
|
||||||
<stop stop-color="#D1D5D9"/>
|
|
||||||
<stop offset="1" stop-color="#FCFFFF"/>
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient id="paint3_linear" x1="38" y1="25" x2="14.02" y2="0.0208158" gradientUnits="userSpaceOnUse">
|
|
||||||
<stop stop-color="#D1D5D9"/>
|
|
||||||
<stop offset="1" stop-color="#FCFFFF"/>
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient id="paint4_linear" x1="33" y1="32" x2="9.39344" y2="12.3279" gradientUnits="userSpaceOnUse">
|
|
||||||
<stop stop-color="#D1D5D9"/>
|
|
||||||
<stop offset="1" stop-color="#FCFFFF"/>
|
|
||||||
</linearGradient>
|
|
||||||
</defs>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 3.1 KiB |
|
@ -1,464 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
|
||||||
|
|
||||||
<svg
|
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
width="48"
|
|
||||||
height="48"
|
|
||||||
viewBox="0 0 48 48.000001"
|
|
||||||
id="svg2"
|
|
||||||
version="1.1"
|
|
||||||
inkscape:version="0.91 r13725"
|
|
||||||
sodipodi:docname="quaternion2.svg">
|
|
||||||
<defs
|
|
||||||
id="defs4">
|
|
||||||
<linearGradient
|
|
||||||
id="b"
|
|
||||||
y1="23.774559"
|
|
||||||
x1="22.540125"
|
|
||||||
y2="44.054428"
|
|
||||||
x2="42.645557"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
gradientTransform="translate(30,0)">
|
|
||||||
<stop
|
|
||||||
stop-color="#292c2f"
|
|
||||||
id="stop12" />
|
|
||||||
<stop
|
|
||||||
offset="1"
|
|
||||||
stop-color="#292c2f"
|
|
||||||
stop-opacity="0"
|
|
||||||
id="stop14" />
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient
|
|
||||||
inkscape:collect="always"
|
|
||||||
id="linearGradient4416">
|
|
||||||
<stop
|
|
||||||
style="stop-color:#ffffff;stop-opacity:0"
|
|
||||||
offset="0"
|
|
||||||
id="stop4418" />
|
|
||||||
<stop
|
|
||||||
style="stop-color:#ffffff;stop-opacity:1"
|
|
||||||
offset="1"
|
|
||||||
id="stop4420" />
|
|
||||||
</linearGradient>
|
|
||||||
<style
|
|
||||||
id="current-color-scheme"
|
|
||||||
type="text/css">
|
|
||||||
.ColorScheme-Text {
|
|
||||||
color:#4d4d4d;
|
|
||||||
}
|
|
||||||
.ColorScheme-Highlight {
|
|
||||||
color:#3daee9;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<linearGradient
|
|
||||||
inkscape:collect="always"
|
|
||||||
xlink:href="#b"
|
|
||||||
id="linearGradient4384-2"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
gradientTransform="translate(18.78125,-16.0625)"
|
|
||||||
x1="-21.260931"
|
|
||||||
y1="13.89889"
|
|
||||||
x2="10.555012"
|
|
||||||
y2="48.902145" />
|
|
||||||
<linearGradient
|
|
||||||
inkscape:collect="always"
|
|
||||||
xlink:href="#linearGradient4416"
|
|
||||||
id="linearGradient4422-7"
|
|
||||||
x1="-30.500504"
|
|
||||||
y1="28.249998"
|
|
||||||
x2="-10.500504"
|
|
||||||
y2="7.8749971"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
gradientTransform="translate(4.0005053,-4.0000137)" />
|
|
||||||
<linearGradient
|
|
||||||
inkscape:collect="always"
|
|
||||||
xlink:href="#b"
|
|
||||||
id="linearGradient5173"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
gradientTransform="matrix(0.80454501,0,0,0.80454501,29.039903,1009.4479)"
|
|
||||||
x1="-8.4545803"
|
|
||||||
y1="4.9617052"
|
|
||||||
x2="10.555012"
|
|
||||||
y2="48.902145" />
|
|
||||||
<linearGradient
|
|
||||||
inkscape:collect="always"
|
|
||||||
xlink:href="#linearGradient4416"
|
|
||||||
id="linearGradient5175"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
gradientTransform="matrix(0.89628968,0,0,0.89628968,2.535611,1907.0938)"
|
|
||||||
x1="34.2505"
|
|
||||||
y1="-976.61401"
|
|
||||||
x2="8.5004978"
|
|
||||||
y2="-1002.614" />
|
|
||||||
<linearGradient
|
|
||||||
gradientTransform="translate(-384.57,-499.8)"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
x2="0"
|
|
||||||
y2="503.8"
|
|
||||||
y1="543.8"
|
|
||||||
id="a">
|
|
||||||
<stop
|
|
||||||
id="stop7"
|
|
||||||
stop-color="#197cf1" />
|
|
||||||
<stop
|
|
||||||
id="stop9"
|
|
||||||
stop-color="#20bcfa"
|
|
||||||
offset="1" />
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient
|
|
||||||
x1="432.5705"
|
|
||||||
id="a-6"
|
|
||||||
y1="547.79999"
|
|
||||||
y2="500.04578"
|
|
||||||
x2="432.5705"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
gradientTransform="translate(-432.5705,-499.79999)">
|
|
||||||
<stop
|
|
||||||
style="stop-color:#1e2b35;stop-opacity:1"
|
|
||||||
stop-color="#18222a"
|
|
||||||
id="stop4216" />
|
|
||||||
<stop
|
|
||||||
style="stop-color:#525c64;stop-opacity:1"
|
|
||||||
offset="1"
|
|
||||||
stop-color="#566069"
|
|
||||||
id="stop4218" />
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient
|
|
||||||
gradientTransform="translate(-344.5695,-499.79999)"
|
|
||||||
id="b-7"
|
|
||||||
y1="517.8"
|
|
||||||
x1="399.57"
|
|
||||||
y2="534.8"
|
|
||||||
x2="416.57"
|
|
||||||
gradientUnits="userSpaceOnUse">
|
|
||||||
<stop
|
|
||||||
id="stop4221" />
|
|
||||||
<stop
|
|
||||||
offset="1"
|
|
||||||
stop-opacity="0"
|
|
||||||
id="stop4223" />
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient
|
|
||||||
gradientTransform="translate(-344.5695,-499.79999)"
|
|
||||||
id="c"
|
|
||||||
y1="537.8"
|
|
||||||
y2="508.8"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
x2="0">
|
|
||||||
<stop
|
|
||||||
stop-color="#026ddc"
|
|
||||||
id="stop4226" />
|
|
||||||
<stop
|
|
||||||
offset="1"
|
|
||||||
stop-color="#28b0fd"
|
|
||||||
id="stop4228" />
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient
|
|
||||||
id="d"
|
|
||||||
y1="525.28"
|
|
||||||
x1="408.65"
|
|
||||||
y2="533.28"
|
|
||||||
x2="416.65"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
gradientTransform="translate(-.081-.48)">
|
|
||||||
<stop
|
|
||||||
stop-opacity=".065"
|
|
||||||
id="stop4231" />
|
|
||||||
<stop
|
|
||||||
offset="1"
|
|
||||||
stop-opacity="0"
|
|
||||||
id="stop4233" />
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient
|
|
||||||
xlink:href="#d"
|
|
||||||
id="e"
|
|
||||||
y1="517.96"
|
|
||||||
x1="417.79"
|
|
||||||
y2="525.91"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
x2="426.22"
|
|
||||||
gradientTransform="matrix(-1,0,0,1,817.2,-0.48)" />
|
|
||||||
<linearGradient
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
x2="39.279999"
|
|
||||||
y2="36.632999"
|
|
||||||
x1="22.285"
|
|
||||||
y1="18.709999"
|
|
||||||
id="b-5"
|
|
||||||
gradientTransform="translate(-13.182,-16.463)">
|
|
||||||
<stop
|
|
||||||
id="stop12-3"
|
|
||||||
stop-color="#292c2f" />
|
|
||||||
<stop
|
|
||||||
id="stop14-5"
|
|
||||||
stop-opacity="0"
|
|
||||||
stop-color="#292c2f"
|
|
||||||
offset="1" />
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient
|
|
||||||
x1="463.30493"
|
|
||||||
id="b-3"
|
|
||||||
y1="791.61914"
|
|
||||||
y2="721.56116"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
x2="520.69641"
|
|
||||||
gradientTransform="matrix(0.71874732,0,0,0.68157806,-378.12417,-491.55024)">
|
|
||||||
<stop
|
|
||||||
style="stop-color:#00945a;stop-opacity:1"
|
|
||||||
stop-color="#26c281"
|
|
||||||
id="stop4252" />
|
|
||||||
<stop
|
|
||||||
style="stop-color:#38fa95;stop-opacity:1"
|
|
||||||
offset="1"
|
|
||||||
stop-color="#3fc380"
|
|
||||||
id="stop4254" />
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient
|
|
||||||
id="a-67"
|
|
||||||
y1="517.79999"
|
|
||||||
y2="533.79999"
|
|
||||||
x1="404.98001"
|
|
||||||
x2="420.98001"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
gradientTransform="matrix(0.98828127,0,0,0.98828127,-383.20097,-495.43517)">
|
|
||||||
<stop
|
|
||||||
stop-color="#383e51"
|
|
||||||
id="stop4247" />
|
|
||||||
<stop
|
|
||||||
offset="1"
|
|
||||||
stop-color="#655c6f"
|
|
||||||
stop-opacity="0"
|
|
||||||
id="stop4249" />
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient
|
|
||||||
gradientTransform="matrix(0.98828127,0,0,0.98828127,-378.84265,-493.45861)"
|
|
||||||
id="c-5"
|
|
||||||
y1="531.79999"
|
|
||||||
y2="515.79999"
|
|
||||||
x2="0"
|
|
||||||
gradientUnits="userSpaceOnUse">
|
|
||||||
<stop
|
|
||||||
stop-color="#70e4b3"
|
|
||||||
id="stop4257" />
|
|
||||||
<stop
|
|
||||||
offset="1"
|
|
||||||
stop-color="#c8f0dc"
|
|
||||||
id="stop4259" />
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient
|
|
||||||
id="b-3-3"
|
|
||||||
y1="785.71002"
|
|
||||||
y2="727.71002"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
x2="0"
|
|
||||||
gradientTransform="matrix(0.71874732,0,0,0.68157806,-387.2511,-495.98773)">
|
|
||||||
<stop
|
|
||||||
stop-color="#26c281"
|
|
||||||
id="stop4252-5" />
|
|
||||||
<stop
|
|
||||||
offset="1"
|
|
||||||
stop-color="#3fc380"
|
|
||||||
id="stop4254-6" />
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient
|
|
||||||
gradientTransform="translate(-13.130467,-10.269952)"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
x2="42.645557"
|
|
||||||
y2="44.054428"
|
|
||||||
x1="22.540125"
|
|
||||||
y1="23.774559"
|
|
||||||
id="b-1">
|
|
||||||
<stop
|
|
||||||
id="stop12-2"
|
|
||||||
stop-color="#292c2f" />
|
|
||||||
<stop
|
|
||||||
id="stop14-7"
|
|
||||||
stop-opacity="0"
|
|
||||||
stop-color="#292c2f"
|
|
||||||
offset="1" />
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient
|
|
||||||
y2="39.179428"
|
|
||||||
x2="3.3955555"
|
|
||||||
y1="32.924278"
|
|
||||||
x1="-2.8595951"
|
|
||||||
gradientTransform="translate(7.947106,-34.03464)"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
id="linearGradient4318-9"
|
|
||||||
xlink:href="#b"
|
|
||||||
inkscape:collect="always" />
|
|
||||||
<linearGradient
|
|
||||||
y2="39.179428"
|
|
||||||
x2="3.3955555"
|
|
||||||
y1="39.799278"
|
|
||||||
x1="-13.672095"
|
|
||||||
gradientTransform="translate(14.488281,-20.003906)"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
id="linearGradient4345-7"
|
|
||||||
xlink:href="#b"
|
|
||||||
inkscape:collect="always" />
|
|
||||||
<linearGradient
|
|
||||||
y2="48.902145"
|
|
||||||
x2="10.555012"
|
|
||||||
y1="12.39889"
|
|
||||||
x1="-22.510931"
|
|
||||||
gradientTransform="translate(18.78125,-16.0625)"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
id="linearGradient4384-2-2"
|
|
||||||
xlink:href="#b"
|
|
||||||
inkscape:collect="always" />
|
|
||||||
<linearGradient
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
x2="42.645557"
|
|
||||||
y2="44.054428"
|
|
||||||
x1="22.540125"
|
|
||||||
y1="23.774559"
|
|
||||||
id="b-36"
|
|
||||||
gradientTransform="translate(-71.999501,32.001813)">
|
|
||||||
<stop
|
|
||||||
id="stop12-7"
|
|
||||||
stop-color="#292c2f" />
|
|
||||||
<stop
|
|
||||||
id="stop14-53"
|
|
||||||
stop-opacity="0"
|
|
||||||
stop-color="#292c2f"
|
|
||||||
offset="1" />
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient
|
|
||||||
x1="389.3205"
|
|
||||||
id="a-6-5"
|
|
||||||
y1="547.67499"
|
|
||||||
y2="499.92078"
|
|
||||||
x2="426.6955"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
gradientTransform="translate(-360.571,-467.79817)">
|
|
||||||
<stop
|
|
||||||
style="stop-color:#1e2b35;stop-opacity:1"
|
|
||||||
stop-color="#18222a"
|
|
||||||
id="stop4216-6" />
|
|
||||||
<stop
|
|
||||||
style="stop-color:#6d7983;stop-opacity:1"
|
|
||||||
offset="1"
|
|
||||||
stop-color="#566069"
|
|
||||||
id="stop4218-2" />
|
|
||||||
</linearGradient>
|
|
||||||
</defs>
|
|
||||||
<sodipodi:namedview
|
|
||||||
id="base"
|
|
||||||
pagecolor="#ffffff"
|
|
||||||
bordercolor="#666666"
|
|
||||||
borderopacity="1.0"
|
|
||||||
inkscape:pageopacity="0.0"
|
|
||||||
inkscape:pageshadow="2"
|
|
||||||
inkscape:zoom="1"
|
|
||||||
inkscape:cx="32.789359"
|
|
||||||
inkscape:cy="42.117635"
|
|
||||||
inkscape:document-units="px"
|
|
||||||
inkscape:current-layer="layer1"
|
|
||||||
showgrid="false"
|
|
||||||
units="px"
|
|
||||||
showguides="false"
|
|
||||||
inkscape:guide-bbox="true"
|
|
||||||
inkscape:snap-global="false"
|
|
||||||
showborder="false"
|
|
||||||
borderlayer="true">
|
|
||||||
<inkscape:grid
|
|
||||||
type="xygrid"
|
|
||||||
id="grid5026" />
|
|
||||||
<sodipodi:guide
|
|
||||||
position="13,11.25"
|
|
||||||
orientation="0.70710678,0.70710678"
|
|
||||||
id="guide5089" />
|
|
||||||
<sodipodi:guide
|
|
||||||
position="7.875,26.375001"
|
|
||||||
orientation="0.70710678,0.70710678"
|
|
||||||
id="guide5091" />
|
|
||||||
<sodipodi:guide
|
|
||||||
position="29.875,39.500001"
|
|
||||||
orientation="0.70710678,0.70710678"
|
|
||||||
id="guide5093" />
|
|
||||||
<sodipodi:guide
|
|
||||||
position="35,36.250001"
|
|
||||||
orientation="0.70710678,0.70710678"
|
|
||||||
id="guide5097" />
|
|
||||||
<sodipodi:guide
|
|
||||||
position="9.875,20.5"
|
|
||||||
orientation="0.70710678,0.70710678"
|
|
||||||
id="guide5126" />
|
|
||||||
</sodipodi:namedview>
|
|
||||||
<metadata
|
|
||||||
id="metadata7">
|
|
||||||
<rdf:RDF>
|
|
||||||
<cc:Work
|
|
||||||
rdf:about="">
|
|
||||||
<dc:format>image/svg+xml</dc:format>
|
|
||||||
<dc:type
|
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
|
||||||
<dc:title />
|
|
||||||
</cc:Work>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
<g
|
|
||||||
inkscape:groupmode="layer"
|
|
||||||
id="layer2"
|
|
||||||
inkscape:label="Livello 2">
|
|
||||||
<path
|
|
||||||
id="path5159"
|
|
||||||
d="m 24.04876,6.1174361 c -9.930889,0 -17.925793,7.9949039 -17.925793,17.9257939 0,9.930889 7.994904,17.925793 17.925793,17.925793 0.02411,0 0.04771,-0.0017 0.07178,-0.0018 l 16.955978,4.3e-5 c 0.496545,0 0.89629,-0.399737 0.89629,-0.89629 l 0,-16.961232 c 8.1e-5,-0.02232 0.0018,-0.04418 0.0018,-0.06652 0,-9.930889 -7.994904,-17.9257934 -17.925795,-17.9257934 z"
|
|
||||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#1d99f3;fill-opacity:1;fill-rule:nonzero;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
inkscape:export-xdpi="96"
|
|
||||||
inkscape:export-ydpi="96" />
|
|
||||||
</g>
|
|
||||||
<g
|
|
||||||
inkscape:label="Livello 1"
|
|
||||||
inkscape:groupmode="layer"
|
|
||||||
id="layer1"
|
|
||||||
transform="translate(0,-1004.3622)">
|
|
||||||
<path
|
|
||||||
style="opacity:0.3;fill:#ffffff;fill-opacity:1;stroke-width:2.79999995;stroke-opacity:0.55"
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
d="m 6.1310739,1028.8503 c -0.00358,-0.1498 -0.010755,-0.2976 -0.010755,-0.4483 0,-9.9308 7.994904,-17.9257 17.925794,-17.9257 9.93089,0 17.925795,7.9949 17.925795,17.9257 0,0.1507 -0.0072,0.2985 -0.01076,0.4483 -0.23662,-9.7221 -8.138312,-17.4777 -17.91504,-17.4777 -9.780313,0 -17.6784177,7.7556 -17.9150382,17.4777"
|
|
||||||
id="path5163"
|
|
||||||
inkscape:export-xdpi="96"
|
|
||||||
inkscape:export-ydpi="96" />
|
|
||||||
<path
|
|
||||||
sodipodi:nodetypes="ccccccccccccccccc"
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
id="path5165"
|
|
||||||
d="m 11.232155,1026.802 14.222481,14.2935 -3.520064,-0.3911 -9.191278,-9.1912 2.684492,7.5202 6.204245,6.2684 2.49952,0.1156 17.241676,-0.023 0.640018,-1.0668 -0.05709,-14.8384 -0.0649,-1.7857 -8.756475,-8.7243 -0.64001,0.8532 -3.555621,-3.6266 -10.240187,0.64 -4.76453,4.1244 z"
|
|
||||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.18800001;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:url(#linearGradient5173);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
|
||||||
inkscape:export-xdpi="96"
|
|
||||||
inkscape:export-ydpi="96" />
|
|
||||||
<path
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
id="path5167"
|
|
||||||
d="m 6.1334706,1027.9574 c -0.00367,0.1495 -0.010504,0.2976 -0.010504,0.448 0,9.9309 7.9949037,17.9258 17.9257927,17.9258 0.02411,0 0.04771,0 0.07178,0 l 16.955981,0 c 0.496544,0 0.896289,-0.3996 0.896289,-0.8962 l 0,-0.8945 c 0,0.4965 -0.399745,0.8963 -0.896289,0.8963 l -16.955983,0 -0.07178,0 c -9.78071,0 -17.6785062,-7.7561 -17.9152898,-17.4777 z m 35.8393324,0.3763 0,0.1383 c 8.2e-5,-0.022 0.0018,-0.044 0.0018,-0.066 0,-0.024 -0.0018,-0.048 -0.0018,-0.072 z"
|
|
||||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.3;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#161e25;fill-opacity:1;fill-rule:nonzero;stroke-width:2.79999995;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0.55;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
|
||||||
inkscape:export-xdpi="96"
|
|
||||||
inkscape:export-ydpi="96" />
|
|
||||||
<path
|
|
||||||
sodipodi:nodetypes="cccccccccccccccccccccccccccccccccccccccc"
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
id="path5169"
|
|
||||||
d="m 24.850998,1015.3211 -2.3e-4,0 c -4.221957,-0.2137 -8.462616,1.6173 -11.185311,5.279 -1.410567,1.8989 -2.22395,4.0471 -2.493121,6.2301 l 1.696347,-0.2507 3.220344,-0.4718 2.68403,-2.0567 -5.463182,0.8056 c 0.374562,-1.1444 0.916754,-2.2533 1.67664,-3.2759 2.961145,-3.9843 7.972375,-5.4929 12.459008,-4.168 l 1.563639,-1.1933 c -1.347181,-0.5332 -2.750838,-0.8302 -4.158164,-0.9015 z m 6.710874,2.5126 -4.6e-4,0.01 -0.521308,1.6335 -0.859253,2.6992 0.499033,3.8383 1.715908,-5.384 c 0.863373,0.8389 1.619917,1.8132 2.204556,2.9452 2.442167,4.7279 1.298074,10.3196 -2.445266,13.7365 l 0.0072,0.053 c -0.02757,0.026 -0.05527,0.055 -0.0831,0.081 l 4.152714,4.8087 1.274973,-1.1026 -3.204232,-3.7103 c 3.427251,-3.9365 4.309935,-9.6879 1.761636,-14.6216 -1.086112,-2.1028 -2.658768,-3.7804 -4.502389,-4.9823 z m -20.627884,12.8598 c 0.795585,5.8959 5.518184,10.6684 11.708243,11.2806 2.355072,0.2329 4.622679,-0.1736 6.634024,-1.0664 l -1.086358,-1.3241 -0.0062,-5e-4 -2.138088,-2.6048 -3.22492,-1.339 3.657256,4.4524 c -1.173938,0.2676 -2.40585,0.375 -3.673603,0.2496 -5.070346,-0.501 -9.016389,-4.197 -10.072229,-8.8986 z"
|
|
||||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.63318729;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
|
||||||
inkscape:export-xdpi="96"
|
|
||||||
inkscape:export-ydpi="96" />
|
|
||||||
<path
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.45;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:url(#linearGradient5175);fill-opacity:1;fill-rule:nonzero;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
|
||||||
d="m 24.04876,1010.4797 c -9.930889,0 -17.925793,7.9949 -17.925793,17.9257 0,9.9309 7.994904,17.9258 17.925793,17.9258 0.02411,0 0.04771,0 0.07178,0 l 16.955978,0 c 0.496545,0 0.89629,-0.3996 0.89629,-0.8962 l 0,-16.9612 c 8.1e-5,-0.022 0.0018,-0.044 0.0018,-0.066 0,-9.931 -7.994902,-17.9259 -17.925793,-17.9259 z"
|
|
||||||
id="path5171"
|
|
||||||
inkscape:export-xdpi="96"
|
|
||||||
inkscape:export-ydpi="96" />
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 43 KiB |
Before Width: | Height: | Size: 118 KiB |
Before Width: | Height: | Size: 6.5 KiB |
|
@ -1,65 +0,0 @@
|
||||||
<svg width="1024" height="1024" viewBox="0 0 1024 1024" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<path d="M452.1 306.5H452.16C474.2 229.45 570.24 191.84 668.11 191.84C735.74 191.84 796.45 213.25 838.07 247.24C813.232 248.411 788.708 253.285 765.31 261.7C798.94 274.2 827.83 293.43 849.24 317.22C833.087 314.439 816.707 313.201 800.32 313.52C840.596 371.863 862.114 441.105 862 512C862 705.3 705.3 862 512 862C321.68 862 162 702.65 162 512C162 481.89 166 450.8 173.77 421.64C175.81 415.52 178.65 409.65 182.42 407.5C187.14 404.81 191.44 412.83 192.13 415.44C197.248 434.61 204.134 453.263 212.7 471.16C211.95 431.2 229.02 394.8 252.5 363.32C268.16 342.33 282.68 322.88 289.38 266.75C289.83 262.98 293.4 260.27 297.01 261.45C347.97 278.13 375.21 362.99 370.98 433.95C399.13 437.98 399 408.57 399 408.57C390 380.91 396 329.5 452 306.5H452.1Z" fill="url(#paint0_linear_1_4)"/>
|
|
||||||
<path opacity="0.9" d="M850.55 422.87C859.06 615.37 700.81 781.34 507.84 781.34C327.19 781.34 179.16 641.71 165.77 464.49C163.381 481.085 162.125 497.824 162.01 514.59C163.38 704.21 322.67 862 512.01 862C705.31 862 862.01 705.3 862.01 512C862.01 481.2 858.02 451.33 850.55 422.87Z" fill="url(#paint1_radial_1_4)"/>
|
|
||||||
<path style="mix-blend-mode:screen" d="M503.47 332.01C499.7 325.34 482.29 315.47 474.68 313.75C503.48 221.5 650.2 193.18 740 209.5C777.37 216.3 823.91 236.66 838.07 247.24C796.45 213.25 735.73 191.84 668.11 191.84C570.24 191.84 474.2 229.45 452.16 306.5H452C396 329.5 390 380.93 399 408.58C407.64 375.59 448.75 335.05 503.47 332.01Z" fill="url(#paint2_radial_1_4)"/>
|
|
||||||
<path d="M605.18 268.3C526.56 283.77 500.87 288.83 474.56 313.84C504.11 235.61 579.54 219.75 669.42 255.41C648.028 259.82 626.615 264.12 605.18 268.31V268.3Z" fill="url(#paint3_linear_1_4)"/>
|
|
||||||
<path d="M181 410.93C159.52 498.87 176.12 602.23 273.7 688.97C244.65 657.2 209.18 539.9 287.45 456.1C292.72 450.45 301.79 454.6 302.08 462.32C308.53 636.48 449.06 742.85 611.08 722.96C560.88 720.14 394.86 661.99 518.36 638.99C582.91 626.96 684.12 608.11 684.12 517.29C684.12 370.07 570.29 327.03 501.26 333.43C454.02 337.81 411.97 367.79 399.03 408.56C404 424.63 384.19 435.88 370.98 433.99C375.22 363.04 347.98 278.13 297.01 261.45C293.41 260.27 289.83 262.98 289.38 266.75C282.68 322.88 268.16 342.33 252.51 363.32C229.02 394.81 211.95 431.2 212.7 471.16C204.134 453.263 197.248 434.61 192.13 415.44C191.56 413.28 188.43 407.25 184.65 406.97C182.6 406.82 181.51 408.82 181 410.93Z" fill="url(#paint4_radial_1_4)"/>
|
|
||||||
<path style="mix-blend-mode:screen" d="M474.99 647.9C570.03 725.07 761.16 667.21 761.16 479.58C684 596.54 585.72 677.22 475 647.9H474.99Z" fill="url(#paint5_linear_1_4)"/>
|
|
||||||
<path style="mix-blend-mode:screen" d="M287.45 456.1C288.314 455.133 289.403 454.394 290.62 453.947C291.838 453.501 293.146 453.361 294.43 453.54C224.37 538.99 280.88 689.06 319.66 725.93C321.83 732.07 282.87 700.14 277.5 692.67C248 667.64 205.72 543.61 287.45 456.1Z" fill="url(#paint6_linear_1_4)"/>
|
|
||||||
<path d="M512 654.97C607.06 654.97 684.13 592.09 684.13 514.52C684.13 436.95 607.06 374.07 512 374.07C430.9 374.07 339.83 426.83 339.87 516.57C339.91 655.24 486.41 735.02 611.29 722.94C601.91 721.85 543.39 718.74 503.84 674C500.27 669.97 494.08 662.93 496.89 658.36C499.69 653.79 507.41 654.96 511.99 654.96L512 654.97Z" fill="url(#paint7_linear_1_4)"/>
|
|
||||||
<path opacity="0.6" d="M665.38 450.7L529.34 580.88C517.27 589.46 504.42 590.08 491.74 582.3L358.31 451.18C362.117 445.062 366.396 439.251 371.11 433.8L385.11 446.9C420.15 479.74 448.48 506.27 488.41 540.35C506.43 555.73 512.03 555.43 529.71 540.35C575.39 501.35 608.8 471.85 652.23 433.06C657.07 438.586 661.464 444.488 665.37 450.71L665.38 450.7Z" fill="white"/>
|
|
||||||
<mask id="mask0_1_4" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="339" y="448" width="346" height="277">
|
|
||||||
<path d="M684.13 514.52C684.13 592.09 607.06 654.97 512 654.97C507.41 654.97 499.7 653.79 496.89 658.37C494.08 662.93 500.27 669.97 503.84 674C541 716.04 594.91 721.33 609.06 722.72L611.29 722.94C486.41 735.02 339.91 655.24 339.87 516.57C339.74 493.482 346.165 470.831 358.4 451.25L492.48 573.03C502.02 581.69 518.09 581.69 527.63 573.03L664.25 448.94C676.95 468.52 684.13 490.84 684.13 514.52Z" fill="white"/>
|
|
||||||
</mask>
|
|
||||||
<g mask="url(#mask0_1_4)">
|
|
||||||
<path opacity="0.7" d="M300 352.53H735.07V747H300V352.53Z" fill="url(#paint8_linear_1_4)"/>
|
|
||||||
<g filter="url(#filter0_f_1_4)">
|
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M472.04 573.85C446.69 547.47 370.77 459.82 370.77 459.82L376.64 460.08L495.5 548.85C504.4 555.32 517.1 555.25 525.92 548.7L642.47 460.2L648.65 459.71C648.65 459.71 575.21 545.28 547.08 573.47C518.95 601.66 497.39 600.23 472.04 573.85Z" fill="#458FCD"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<path d="M536.15 303.81C554.57 298.01 552.95 279.78 552.95 279.78C552.95 279.78 543.74 268.93 525.49 274.94C508.41 280.57 505.76 292.74 505.76 292.74C505.76 292.74 515.09 310.44 536.15 303.81Z" fill="white"/>
|
|
||||||
<defs>
|
|
||||||
<filter id="filter0_f_1_4" x="338.77" y="427.71" width="341.88" height="198.416" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
|
||||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
|
||||||
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
|
||||||
<feGaussianBlur stdDeviation="16" result="effect1_foregroundBlur_1_4"/>
|
|
||||||
</filter>
|
|
||||||
<linearGradient id="paint0_linear_1_4" x1="283.48" y1="307.2" x2="776.88" y2="767.42" gradientUnits="userSpaceOnUse">
|
|
||||||
<stop stop-color="#1B91F3"/>
|
|
||||||
<stop offset="1" stop-color="#0B68CB"/>
|
|
||||||
</linearGradient>
|
|
||||||
<radialGradient id="paint1_radial_1_4" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(282.57 465.55) rotate(66.5177) scale(295.96 283.519)">
|
|
||||||
<stop offset="0.53" stop-color="#0B4186" stop-opacity="0"/>
|
|
||||||
<stop offset="1" stop-color="#0B4186" stop-opacity="0.45"/>
|
|
||||||
</radialGradient>
|
|
||||||
<radialGradient id="paint2_radial_1_4" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(521 344) rotate(-127.997) scale(63.2702 104.698)">
|
|
||||||
<stop stop-color="#EF3ACC" stop-opacity="0"/>
|
|
||||||
<stop offset="1" stop-color="#EF3ACC" stop-opacity="0.64"/>
|
|
||||||
</radialGradient>
|
|
||||||
<linearGradient id="paint3_linear_1_4" x1="420.77" y1="425.01" x2="598.31" y2="227.37" gradientUnits="userSpaceOnUse">
|
|
||||||
<stop stop-color="#0F5DB0"/>
|
|
||||||
<stop offset="1" stop-color="#0F5DB0" stop-opacity="0"/>
|
|
||||||
</linearGradient>
|
|
||||||
<radialGradient id="paint4_radial_1_4" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(380 702.7) rotate(-64.2624) scale(461.716 570.355)">
|
|
||||||
<stop offset="0.02" stop-color="#094188"/>
|
|
||||||
<stop offset="0.97" stop-color="#0B4186" stop-opacity="0"/>
|
|
||||||
</radialGradient>
|
|
||||||
<linearGradient id="paint5_linear_1_4" x1="731.92" y1="568.11" x2="649.37" y2="770.8" gradientUnits="userSpaceOnUse">
|
|
||||||
<stop stop-color="#E247C4" stop-opacity="0"/>
|
|
||||||
<stop offset="1" stop-color="#E247C4" stop-opacity="0.64"/>
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient id="paint6_linear_1_4" x1="220.01" y1="386.34" x2="292.74" y2="679.06" gradientUnits="userSpaceOnUse">
|
|
||||||
<stop offset="0.1" stop-color="#EF3ACC"/>
|
|
||||||
<stop offset="1" stop-color="#EF3ACC" stop-opacity="0"/>
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient id="paint7_linear_1_4" x1="512" y1="425.5" x2="512" y2="721.5" gradientUnits="userSpaceOnUse">
|
|
||||||
<stop stop-color="white"/>
|
|
||||||
<stop offset="0.91" stop-color="#BEE1FE"/>
|
|
||||||
<stop offset="1" stop-color="#96CEFD"/>
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient id="paint8_linear_1_4" x1="517.54" y1="593" x2="517.54" y2="717" gradientUnits="userSpaceOnUse">
|
|
||||||
<stop stop-color="#BCE0FD"/>
|
|
||||||
<stop offset="1" stop-color="#88CCFC"/>
|
|
||||||
</linearGradient>
|
|
||||||
</defs>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 7.3 KiB |
Before Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 8.1 KiB |
Before Width: | Height: | Size: 18 KiB |
|
@ -1,7 +0,0 @@
|
||||||
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<mask id="path-1-inside-1" fill="white">
|
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.1332 16.3632C11.1709 16.7731 10.112 17 9 17C4.58172 17 1 13.4183 1 9C1 4.58172 4.58172 1 9 1C13.4183 1 17 4.58172 17 9C17 11.6173 15.7432 13.941 13.8001 15.4005V15.4005C13.4881 15.6349 13.1583 15.847 12.8133 16.0344C12.5926 16.1543 12.3657 16.2641 12.1332 16.3632ZM9.00004 9.39998C10.3255 9.39998 11.4 8.23592 11.4 6.79998C11.4 5.36404 10.3255 4.19998 9.00004 4.19998C7.67456 4.19998 6.60004 5.36404 6.60004 6.79998C6.60004 8.23592 7.67456 9.39998 9.00004 9.39998ZM8.99989 15.4001C10.7295 15.4001 12.2989 14.7139 13.4507 13.599C12.7384 11.8404 11.0141 10.6 9.00009 10.6C6.98597 10.6 5.26159 11.8406 4.54932 13.5992C5.7011 14.714 7.27038 15.4001 8.99989 15.4001Z"/>
|
|
||||||
</mask>
|
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.1332 16.3632C11.1709 16.7731 10.112 17 9 17C4.58172 17 1 13.4183 1 9C1 4.58172 4.58172 1 9 1C13.4183 1 17 4.58172 17 9C17 11.6173 15.7432 13.941 13.8001 15.4005V15.4005C13.4881 15.6349 13.1583 15.847 12.8133 16.0344C12.5926 16.1543 12.3657 16.2641 12.1332 16.3632ZM9.00004 9.39998C10.3255 9.39998 11.4 8.23592 11.4 6.79998C11.4 5.36404 10.3255 4.19998 9.00004 4.19998C7.67456 4.19998 6.60004 5.36404 6.60004 6.79998C6.60004 8.23592 7.67456 9.39998 9.00004 9.39998ZM8.99989 15.4001C10.7295 15.4001 12.2989 14.7139 13.4507 13.599C12.7384 11.8404 11.0141 10.6 9.00009 10.6C6.98597 10.6 5.26159 11.8406 4.54932 13.5992C5.7011 14.714 7.27038 15.4001 8.99989 15.4001Z" fill="#666666"/>
|
|
||||||
<path d="M12.1332 16.3632L11.3492 14.5232L12.1332 16.3632ZM13.8001 15.4005H11.8001V19.4042L15.0013 16.9996L13.8001 15.4005ZM13.8001 15.4005H15.8001V11.3967L12.5989 13.8014L13.8001 15.4005ZM12.8133 16.0344L13.768 17.7919L13.768 17.7919L12.8133 16.0344ZM13.4507 13.599L14.8418 15.036L15.8107 14.098L15.3044 12.8481L13.4507 13.599ZM4.54932 13.5992L2.69558 12.8485L2.18935 14.0984L3.15837 15.0363L4.54932 13.5992ZM9 19C10.3863 19 11.7115 18.7168 12.9171 18.2031L11.3492 14.5232C10.6303 14.8295 9.83767 15 9 15V19ZM-1 9C-1 14.5228 3.47715 19 9 19V15C5.68629 15 3 12.3137 3 9H-1ZM9 -1C3.47715 -1 -1 3.47715 -1 9H3C3 5.68629 5.68629 3 9 3V-1ZM19 9C19 3.47715 14.5228 -1 9 -1V3C12.3137 3 15 5.68629 15 9H19ZM15.0013 16.9996C17.4256 15.1786 19 12.2729 19 9H15C15 10.9617 14.0607 12.7034 12.5989 13.8014L15.0013 16.9996ZM15.8001 15.4005V15.4005H11.8001V15.4005H15.8001ZM12.5989 13.8014C12.3645 13.9774 12.1171 14.1365 11.8586 14.277L13.768 17.7919C14.1995 17.5574 14.6116 17.2923 15.0013 16.9996L12.5989 13.8014ZM12.9171 18.2031C13.2082 18.0791 13.4921 17.9418 13.768 17.7919L11.8586 14.277C11.6932 14.3668 11.5233 14.449 11.3492 14.5232L12.9171 18.2031ZM9.40004 6.79998C9.40004 7.01655 9.31985 7.18185 9.22749 7.2819C9.13763 7.37925 9.05614 7.39998 9.00004 7.39998V11.4C11.5778 11.4 13.4 9.18675 13.4 6.79998H9.40004ZM9.00004 6.19998C9.05614 6.19998 9.13763 6.22071 9.22749 6.31806C9.31985 6.41811 9.40004 6.58341 9.40004 6.79998H13.4C13.4 4.41321 11.5778 2.19998 9.00004 2.19998V6.19998ZM8.60004 6.79998C8.60004 6.58341 8.68024 6.41811 8.77259 6.31806C8.86246 6.22071 8.94395 6.19998 9.00004 6.19998V2.19998C6.42228 2.19998 4.60004 4.41321 4.60004 6.79998H8.60004ZM9.00004 7.39998C8.94395 7.39998 8.86246 7.37925 8.77259 7.28189C8.68024 7.18184 8.60004 7.01655 8.60004 6.79998H4.60004C4.60004 9.18675 6.42228 11.4 9.00004 11.4V7.39998ZM12.0597 12.162C11.2659 12.9304 10.1898 13.4001 8.99989 13.4001V17.4001C11.2693 17.4001 13.332 16.4975 14.8418 15.036L12.0597 12.162ZM9.00009 12.6C10.1718 12.6 11.18 13.3204 11.597 14.3498L15.3044 12.8481C14.2968 10.3605 11.8564 8.60004 9.00009 8.60004V12.6ZM6.40306 14.35C6.82004 13.3204 7.82833 12.6 9.00009 12.6V8.60004C6.1436 8.60004 3.70313 10.3607 2.69558 12.8485L6.40306 14.35ZM8.99989 13.4001C7.81008 13.4001 6.73407 12.9304 5.94027 12.1621L3.15837 15.0363C4.66812 16.4976 6.73068 17.4001 8.99989 17.4001V13.4001Z" fill="#666666" mask="url(#path-1-inside-1)"/>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 3.9 KiB |
|
@ -1 +0,0 @@
|
||||||
<svg width="22" height="22" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M13.358 14.752H4.97a1.048 1.048 0 01-1.047-1.048V6.286c0-.579.469-1.048 1.047-1.048H17.08c.579 0 1.048.47 1.048 1.048v1.33M11.424 15.152v1.21M9.304 16.362h3.653" stroke="#666" stroke-width=".8" stroke-linecap="round" stroke-linejoin="round"/><rect x="14.543" y="8.955" width="3.914" height="7.371" rx=".648" stroke="#666" stroke-width=".8"/><path stroke="#666" stroke-width=".8" d="M16.028 15.174h1.048"/></svg>
|
|
Before Width: | Height: | Size: 495 B |
|
@ -1,3 +0,0 @@
|
||||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<path d="M5.5 12.5L8.84497 15.845C9.71398 16.714 11.1538 16.601 11.8767 15.6071L18.5 6.5" stroke="#333" stroke-width="2" stroke-linecap="round"/>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 249 B |
26
index.html
|
@ -1,26 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>You're invited to talk on Matrix</title>
|
|
||||||
<meta name="description" content="You're invited to talk on Matrix">
|
|
||||||
<meta name="viewport" content="width=device-width, user-scalable=no">
|
|
||||||
<link rel="stylesheet" type="text/css" href="css/main.css">
|
|
||||||
<meta property="twitter:card" content="summary"/>
|
|
||||||
<meta property="twitter:image" content="https://matrix.org/blog/img/matrix-logo.png"/>
|
|
||||||
<meta property="twitter:site" content="@matrixdotorg"/>
|
|
||||||
<meta property="twitter:title" content="Matrix - Decentralised and secure communication"/>
|
|
||||||
<meta property="twitter:description" content="You're invited to talk on Matrix. If you don't already have a client this link will help you pick one, and join the conversation. If you already have one, this link will help you join the conversation"/>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<script id="main" type="module">
|
|
||||||
import {main} from "./src/main.js";
|
|
||||||
main(document.body);
|
|
||||||
</script>
|
|
||||||
<noscript>
|
|
||||||
<h1>Please enable javascript</h1>
|
|
||||||
<p>Matrix.to is a preview service from chat rooms, people and communities on <a href="https://matrix.org">Matrix</a>.</p>
|
|
||||||
<p>It preserves your privacy by only processing what you view on the client side. For this to work, it needs javascript.</p>
|
|
||||||
</noscript>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
4
jest.config.js
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
module.exports = {
|
||||||
|
preset: 'ts-jest',
|
||||||
|
testEnvironment: 'node',
|
||||||
|
};
|
115
package.json
|
@ -1,37 +1,82 @@
|
||||||
{
|
{
|
||||||
"name": "matrix.to",
|
"name": "matrix.to",
|
||||||
"version": "1.2.17",
|
"version": "0.1.0",
|
||||||
"type": "module",
|
"private": true,
|
||||||
"license": "Apache-2.0",
|
"dependencies": {
|
||||||
"engines": {
|
"@quentin-sommer/react-useragent": "^3.1.0",
|
||||||
"node": ">= 14.0.0"
|
"classnames": "^2.2.6",
|
||||||
},
|
"cross-fetch": "^3.0.6",
|
||||||
"scripts": {
|
"formik": "^2.1.4",
|
||||||
"start": "node scripts/serve-local.js",
|
"promise.any": "^2.0.1",
|
||||||
"build": "node scripts/build.js"
|
"react": "^16.13.1",
|
||||||
},
|
"react-dom": "^16.13.1",
|
||||||
"devDependencies": {
|
"react-scripts": "3.4.1",
|
||||||
"@babel/core": "^7.11.1",
|
"what-input": "^5.2.10",
|
||||||
"@babel/preset-env": "^7.11.0",
|
"zod": "^1.10.3"
|
||||||
"@rollup/plugin-babel": "^5.1.0",
|
},
|
||||||
"@rollup/plugin-commonjs": "^15.0.0",
|
"scripts": {
|
||||||
"@rollup/plugin-multi-entry": "^4.0.0",
|
"start": "react-scripts start",
|
||||||
"@rollup/plugin-node-resolve": "^9.0.0",
|
"build": "react-scripts build",
|
||||||
"@rollup/plugin-replace": "^2.3.4",
|
"test": "react-scripts test",
|
||||||
"autoprefixer": "^10.0.1",
|
"eject": "react-scripts eject",
|
||||||
"cheerio": "^1.0.0-rc.3",
|
"lint:fix": "eslint src/**/*.ts src/**/*.tsx --fix",
|
||||||
"core-js": "^3.6.5",
|
"storybook": "start-storybook -p 9009 -s public",
|
||||||
"finalhandler": "^1.1.2",
|
"build-storybook": "build-storybook -s public"
|
||||||
"mdn-polyfills": "^5.20.0",
|
},
|
||||||
"postcss": "^8.4.31",
|
"lint-staged": {
|
||||||
"postcss-css-variables": "^0.17.0",
|
"src/**/*.{js,jsx,ts,tsx}": [
|
||||||
"postcss-flexbugs-fixes": "^4.2.1",
|
"eslint --fix",
|
||||||
"postcss-import": "^12.0.1",
|
"prettier --write --tab-width 4 --single-quote"
|
||||||
"postcss-url": "^8.0.0",
|
],
|
||||||
"regenerator-runtime": "^0.13.7",
|
"src/**/*.{json,css,scss,md}": [
|
||||||
"rollup": "^2.26.4",
|
"prettier --write --tab-width 4 --single-quote"
|
||||||
"rollup-plugin-terser": "^7.0.2",
|
]
|
||||||
"serve-static": "^1.14.1",
|
},
|
||||||
"xxhashjs": "^0.2.2"
|
"husky": {
|
||||||
}
|
"hooks": {
|
||||||
|
"pre-commit": "lint-staged"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"browserslist": {
|
||||||
|
"production": [
|
||||||
|
">0.2%",
|
||||||
|
"not dead",
|
||||||
|
"not op_mini all"
|
||||||
|
],
|
||||||
|
"development": [
|
||||||
|
"last 1 chrome version",
|
||||||
|
"last 1 firefox version",
|
||||||
|
"last 1 safari version"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@storybook/addon-a11y": "^5.3.19",
|
||||||
|
"@storybook/addon-actions": "^5.3.19",
|
||||||
|
"@storybook/addon-backgrounds": "^5.3.19",
|
||||||
|
"@storybook/addon-knobs": "^5.3.19",
|
||||||
|
"@storybook/addon-links": "^5.3.19",
|
||||||
|
"@storybook/addon-storysource": "^5.3.19",
|
||||||
|
"@storybook/addon-viewport": "^5.3.19",
|
||||||
|
"@storybook/addons": "^5.3.19",
|
||||||
|
"@storybook/preset-create-react-app": "^3.1.4",
|
||||||
|
"@storybook/react": "^5.3.19",
|
||||||
|
"@testing-library/jest-dom": "^4.2.4",
|
||||||
|
"@testing-library/react": "^9.3.2",
|
||||||
|
"@testing-library/user-event": "^7.1.2",
|
||||||
|
"@types/classnames": "^2.2.10",
|
||||||
|
"@types/jest": "^24.0.0",
|
||||||
|
"@types/lodash": "^4.14.159",
|
||||||
|
"@types/node": "^12.0.0",
|
||||||
|
"@types/react": "^16.9.0",
|
||||||
|
"@types/react-dom": "^16.9.0",
|
||||||
|
"@types/yup": "^0.29.3",
|
||||||
|
"eslint-config-matrix-org": "^0.1.0",
|
||||||
|
"husky": "^4.2.5",
|
||||||
|
"lint-staged": "^10.2.7",
|
||||||
|
"node-sass": "^4.14.1",
|
||||||
|
"prettier": "^2.0.5",
|
||||||
|
"storybook-addon-designs": "^5.4.0",
|
||||||
|
"ts-jest": "^26.1.4",
|
||||||
|
"typescript": "~3.7.2"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
16
public/index.html
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<meta
|
||||||
|
name="description"
|
||||||
|
content="A client-side matrix link redirection service"
|
||||||
|
/>
|
||||||
|
<title>Matrix.to</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||||
|
<div id="root"></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
307
scripts/build.js
|
@ -1,307 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright 2020 Bruno Windels <bruno@windels.cloud>
|
|
||||||
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 cheerio from "cheerio";
|
|
||||||
import fs from "fs/promises";
|
|
||||||
import path from "path";
|
|
||||||
import xxhash from 'xxhashjs';
|
|
||||||
import { rollup } from 'rollup';
|
|
||||||
import postcss from "postcss";
|
|
||||||
import postcssImport from "postcss-import";
|
|
||||||
// needed for legacy bundle
|
|
||||||
import babel from '@rollup/plugin-babel';
|
|
||||||
// needed to find the polyfill modules in the main-legacy.js bundle
|
|
||||||
import { nodeResolve } from '@rollup/plugin-node-resolve';
|
|
||||||
// needed because some of the polyfills are written as commonjs modules
|
|
||||||
import commonjs from '@rollup/plugin-commonjs';
|
|
||||||
// multi-entry plugin so we can add polyfill file to main
|
|
||||||
import multi from '@rollup/plugin-multi-entry';
|
|
||||||
import { terser } from "rollup-plugin-terser";
|
|
||||||
import replace from "@rollup/plugin-replace";
|
|
||||||
// replace urls of asset names with content hashed version
|
|
||||||
import postcssUrl from "postcss-url";
|
|
||||||
import cssvariables from "postcss-css-variables";
|
|
||||||
import autoprefixer from "autoprefixer";
|
|
||||||
import flexbugsFixes from "postcss-flexbugs-fixes";
|
|
||||||
|
|
||||||
import {createClients} from "../src/open/clients/index.js";
|
|
||||||
|
|
||||||
import { fileURLToPath } from 'url';
|
|
||||||
import { dirname } from 'path';
|
|
||||||
const projectDir = path.join(dirname(fileURLToPath(import.meta.url)), "../");
|
|
||||||
|
|
||||||
async function build() {
|
|
||||||
// get version number
|
|
||||||
const version = JSON.parse(await fs.readFile(path.join(projectDir, "package.json"), "utf8")).version;
|
|
||||||
// clear target dir
|
|
||||||
const targetDir = path.join(projectDir, "build/");
|
|
||||||
await removeDirIfExists(targetDir);
|
|
||||||
await fs.mkdir(targetDir);
|
|
||||||
await fs.mkdir(path.join(targetDir, "images"));
|
|
||||||
await fs.mkdir(path.join(targetDir, "img")); // contains the badge image for historical reasons, unhashed
|
|
||||||
await fs.mkdir(path.join(targetDir, ".well-known"));
|
|
||||||
const assets = new AssetMap(targetDir);
|
|
||||||
const imageAssets = await copyFolder(path.join(projectDir, "images"), path.join(targetDir, "images"));
|
|
||||||
assets.addSubMap(imageAssets);
|
|
||||||
await assets.write(`bundle.js`, await buildJs("src/main.js", assets, ["src/polyfill.js"]));
|
|
||||||
await assets.write(`bundle.css`, await buildCss("css/main.css", targetDir, assets));
|
|
||||||
await assets.writeUnhashed(".well-known/apple-app-site-association", buildAppleAssociatedAppsFile(createClients()));
|
|
||||||
await assets.writeUnhashed("index.html", await buildHtml(assets));
|
|
||||||
await assets.writeUnhashed("img/matrix-badge.svg", await fs.readFile(path.join(projectDir, "images-nohash/matrix-badge.svg")));
|
|
||||||
const globalHash = assets.hashForAll();
|
|
||||||
console.log(`built matrix.to ${version} (${globalHash}) successfully with ${assets.size} files`);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function buildHtml(assets) {
|
|
||||||
const devHtml = await fs.readFile(path.join(projectDir, "index.html"), "utf8");
|
|
||||||
const doc = cheerio.load(devHtml);
|
|
||||||
doc("link[rel=stylesheet]").attr("href", assets.resolve(`bundle.css`));
|
|
||||||
const mainScripts = [
|
|
||||||
// this is needed to avoid hitting https://github.com/facebook/regenerator/issues/378
|
|
||||||
// which prevents the whole bundle to load, as our CSP headers don't allow unsafe-eval
|
|
||||||
// and I preferred this over disabling strict mode for the whole bundle
|
|
||||||
`<script type="text/javascript">window.regeneratorRuntime = undefined;</script>`,
|
|
||||||
`<script type="text/javascript" src="${assets.resolve(`bundle.js`)}"></script>`,
|
|
||||||
`<script type="text/javascript">bundle.main(document.body);</script>`
|
|
||||||
];
|
|
||||||
doc("script#main").replaceWith(mainScripts.join(""));
|
|
||||||
return doc.html();
|
|
||||||
}
|
|
||||||
|
|
||||||
function createReplaceUrlPlugin(assets) {
|
|
||||||
const replacements = {};
|
|
||||||
for (const [key, value] of assets) {
|
|
||||||
replacements[key] = value;
|
|
||||||
}
|
|
||||||
return replace(replacements);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function buildJs(mainFile, assets, extraFiles = []) {
|
|
||||||
// compile down to whatever IE 11 needs
|
|
||||||
const babelPlugin = babel.babel({
|
|
||||||
babelHelpers: 'bundled',
|
|
||||||
exclude: 'node_modules/**',
|
|
||||||
presets: [
|
|
||||||
[
|
|
||||||
"@babel/preset-env",
|
|
||||||
{
|
|
||||||
useBuiltIns: "entry",
|
|
||||||
corejs: "3",
|
|
||||||
targets: "IE 11",
|
|
||||||
}
|
|
||||||
]
|
|
||||||
]
|
|
||||||
});
|
|
||||||
// create js bundle
|
|
||||||
const rollupConfig = {
|
|
||||||
// important the extraFiles come first,
|
|
||||||
// so polyfills are available in the global scope
|
|
||||||
// if needed for the mainfile
|
|
||||||
input: extraFiles.concat(mainFile),
|
|
||||||
plugins: [multi(), commonjs(), nodeResolve(), createReplaceUrlPlugin(assets), babelPlugin, terser()]
|
|
||||||
};
|
|
||||||
const bundle = await rollup(rollupConfig);
|
|
||||||
const {output} = await bundle.generate({
|
|
||||||
format: 'iife',
|
|
||||||
name: `bundle`
|
|
||||||
});
|
|
||||||
const code = output[0].code;
|
|
||||||
return code;
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildAppleAssociatedAppsFile(clients) {
|
|
||||||
const appIds = clients.map(c => c.appleAssociatedAppId).flat().filter(id => !!id);
|
|
||||||
return JSON.stringify({
|
|
||||||
"applinks": {
|
|
||||||
"details": [
|
|
||||||
{
|
|
||||||
appIDs: appIds,
|
|
||||||
components: [
|
|
||||||
{
|
|
||||||
"#": "/*",
|
|
||||||
"comment": "Only open urls with a fragment, so you can still create links"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async function buildCss(entryPath, targetDir, assets) {
|
|
||||||
entryPath = path.join(projectDir, entryPath);
|
|
||||||
const assetUrlMapper = ({absolutePath}) => {
|
|
||||||
const relPath = absolutePath.slice(projectDir.length);
|
|
||||||
return assets.resolve(path.join(targetDir, relPath));
|
|
||||||
};
|
|
||||||
|
|
||||||
const preCss = await fs.readFile(entryPath, "utf8");
|
|
||||||
const options = [
|
|
||||||
postcssImport,
|
|
||||||
cssvariables(),
|
|
||||||
autoprefixer({overrideBrowserslist: ["IE 11"], grid: "no-autoplace"}),
|
|
||||||
flexbugsFixes(),
|
|
||||||
postcssUrl({url: assetUrlMapper}),
|
|
||||||
];
|
|
||||||
const cssBundler = postcss(options);
|
|
||||||
const result = await cssBundler.process(preCss, {from: entryPath});
|
|
||||||
return result.css;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function removeDirIfExists(targetDir) {
|
|
||||||
try {
|
|
||||||
await fs.rmdir(targetDir, {recursive: true});
|
|
||||||
} catch (err) {
|
|
||||||
if (err.code !== "ENOENT") {
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function copyFolder(srcRoot, dstRoot, filter = null, assets = null) {
|
|
||||||
assets = assets || new AssetMap(dstRoot);
|
|
||||||
const dirEnts = await fs.readdir(srcRoot, {withFileTypes: true});
|
|
||||||
for (const dirEnt of dirEnts) {
|
|
||||||
const dstPath = path.join(dstRoot, dirEnt.name);
|
|
||||||
const srcPath = path.join(srcRoot, dirEnt.name);
|
|
||||||
if (dirEnt.isDirectory()) {
|
|
||||||
await fs.mkdir(dstPath);
|
|
||||||
await copyFolder(srcPath, dstPath, filter, assets);
|
|
||||||
} else if ((dirEnt.isFile() || dirEnt.isSymbolicLink()) && (!filter || filter(srcPath))) {
|
|
||||||
const content = await fs.readFile(srcPath);
|
|
||||||
await assets.write(dstPath, content);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return assets;
|
|
||||||
}
|
|
||||||
|
|
||||||
function contentHash(str) {
|
|
||||||
var hasher = new xxhash.h32(0);
|
|
||||||
hasher.update(str);
|
|
||||||
return hasher.digest();
|
|
||||||
}
|
|
||||||
|
|
||||||
class AssetMap {
|
|
||||||
constructor(targetDir) {
|
|
||||||
// remove last / if any, so substr in create works well
|
|
||||||
this._targetDir = path.resolve(targetDir);
|
|
||||||
this._assets = new Map();
|
|
||||||
// hashes for unhashed resources so changes in these resources also contribute to the hashForAll
|
|
||||||
this._unhashedHashes = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
_toRelPath(resourcePath) {
|
|
||||||
let relPath = resourcePath;
|
|
||||||
if (path.isAbsolute(resourcePath)) {
|
|
||||||
if (!resourcePath.startsWith(this._targetDir)) {
|
|
||||||
throw new Error(`absolute path ${resourcePath} that is not within target dir ${this._targetDir}`);
|
|
||||||
}
|
|
||||||
relPath = resourcePath.slice(this._targetDir.length + 1); // + 1 for the /
|
|
||||||
}
|
|
||||||
return relPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
_create(resourcePath, content) {
|
|
||||||
const relPath = this._toRelPath(resourcePath);
|
|
||||||
const hash = contentHash(Buffer.from(content));
|
|
||||||
const dir = path.dirname(relPath);
|
|
||||||
const extname = path.extname(relPath);
|
|
||||||
const basename = path.basename(relPath, extname);
|
|
||||||
const dstRelPath = path.join(dir, `${basename}-${hash}${extname}`);
|
|
||||||
this._assets.set(relPath, dstRelPath);
|
|
||||||
return dstRelPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
async write(resourcePath, content) {
|
|
||||||
const relPath = this._create(resourcePath, content);
|
|
||||||
const fullPath = path.join(this.directory, relPath);
|
|
||||||
if (typeof content === "string") {
|
|
||||||
await fs.writeFile(fullPath, content, "utf8");
|
|
||||||
} else {
|
|
||||||
await fs.writeFile(fullPath, content);
|
|
||||||
}
|
|
||||||
return relPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
async writeUnhashed(resourcePath, content) {
|
|
||||||
const relPath = this._toRelPath(resourcePath);
|
|
||||||
this._assets.set(relPath, relPath);
|
|
||||||
const fullPath = path.join(this.directory, relPath);
|
|
||||||
if (typeof content === "string") {
|
|
||||||
await fs.writeFile(fullPath, content, "utf8");
|
|
||||||
} else {
|
|
||||||
await fs.writeFile(fullPath, content);
|
|
||||||
}
|
|
||||||
return relPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
get directory() {
|
|
||||||
return this._targetDir;
|
|
||||||
}
|
|
||||||
|
|
||||||
resolve(resourcePath) {
|
|
||||||
const relPath = this._toRelPath(resourcePath);
|
|
||||||
const result = this._assets.get(relPath);
|
|
||||||
if (!result) {
|
|
||||||
throw new Error(`unknown path: ${relPath}, only know ${Array.from(this._assets.keys()).join(", ")}`);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
addSubMap(assetMap) {
|
|
||||||
if (!assetMap.directory.startsWith(this.directory)) {
|
|
||||||
throw new Error(`map directory doesn't start with this directory: ${assetMap.directory} ${this.directory}`);
|
|
||||||
}
|
|
||||||
const relSubRoot = assetMap.directory.slice(this.directory.length + 1);
|
|
||||||
for (const [key, value] of assetMap._assets.entries()) {
|
|
||||||
this._assets.set(path.join(relSubRoot, key), path.join(relSubRoot, value));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Symbol.iterator]() {
|
|
||||||
return this._assets.entries();
|
|
||||||
}
|
|
||||||
|
|
||||||
isUnhashed(relPath) {
|
|
||||||
const resolvedPath = this._assets.get(relPath);
|
|
||||||
if (!resolvedPath) {
|
|
||||||
throw new Error("Unknown asset: " + relPath);
|
|
||||||
}
|
|
||||||
return relPath === resolvedPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
get size() {
|
|
||||||
return this._assets.size;
|
|
||||||
}
|
|
||||||
|
|
||||||
has(relPath) {
|
|
||||||
return this._assets.has(relPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
hashForAll() {
|
|
||||||
const globalHashAssets = Array.from(this).map(([, resolved]) => resolved);
|
|
||||||
globalHashAssets.push(...this._unhashedHashes);
|
|
||||||
globalHashAssets.sort();
|
|
||||||
return contentHash(globalHashAssets.join(","));
|
|
||||||
}
|
|
||||||
|
|
||||||
addToHashForAll(resourcePath, content) {
|
|
||||||
this._unhashedHashes.push(`${resourcePath}-${contentHash(Buffer.from(content))}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
build().catch(err => console.error(err));
|
|
|
@ -1,8 +0,0 @@
|
||||||
set -e
|
|
||||||
VERSION=$(jq -r ".version" package.json)
|
|
||||||
PACKAGE=matrixto-$VERSION.tar.gz
|
|
||||||
yarn build
|
|
||||||
pushd build
|
|
||||||
tar -czvf ../$PACKAGE ./
|
|
||||||
popd
|
|
||||||
echo $PACKAGE
|
|
|
@ -1,48 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright 2020 Bruno Windels <bruno@windels.cloud>
|
|
||||||
|
|
||||||
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 finalhandler from "finalhandler"
|
|
||||||
import http from "http"
|
|
||||||
import serveStatic from "serve-static"
|
|
||||||
import path from "path"
|
|
||||||
import { fileURLToPath } from "url";
|
|
||||||
const projectDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../");
|
|
||||||
|
|
||||||
// Serve up parent directory with cache disabled
|
|
||||||
const serve = serveStatic(
|
|
||||||
projectDir,
|
|
||||||
{
|
|
||||||
etag: false,
|
|
||||||
setHeaders: res => {
|
|
||||||
res.setHeader("Pragma", "no-cache");
|
|
||||||
res.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
|
|
||||||
res.setHeader("Expires", "Wed, 21 Oct 2015 07:28:00 GMT");
|
|
||||||
// same CSP as matrix.to server is using, so local testing happens under similar environment
|
|
||||||
res.setHeader("Content-Security-Policy", "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src * data:; connect-src *; font-src 'self'; manifest-src 'self'; form-action 'self'; navigate-to *;");
|
|
||||||
},
|
|
||||||
index: ['index.html', 'index.htm']
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
// Create server
|
|
||||||
const server = http.createServer(function onRequest (req, res) {
|
|
||||||
console.log(req.method, req.url);
|
|
||||||
serve(req, res, finalhandler(req, res))
|
|
||||||
});
|
|
||||||
|
|
||||||
// Listen
|
|
||||||
server.listen(5000);
|
|
||||||
console.log("Listening on port 5000");
|
|
42
src/App.scss
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
/*
|
||||||
|
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 './color-scheme';
|
||||||
|
|
||||||
|
#root {
|
||||||
|
background-color: $app-background;
|
||||||
|
background-image: url('./imgs/background.svg');
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin spacer {
|
||||||
|
width: 100%;
|
||||||
|
flex-grow: 0;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.topSpacer {
|
||||||
|
@include spacer;
|
||||||
|
|
||||||
|
height: 10vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bottomSpacer {
|
||||||
|
@include spacer;
|
||||||
|
|
||||||
|
height: 10vh;
|
||||||
|
}
|
81
src/App.tsx
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 React, { useState, useEffect } from 'react';
|
||||||
|
|
||||||
|
import SingleColumn from './layouts/SingleColumn';
|
||||||
|
import CreateLinkTile from './components/CreateLinkTile';
|
||||||
|
import MatrixTile from './components/MatrixTile';
|
||||||
|
import Tile from './components/Tile';
|
||||||
|
import LinkRouter from './pages/LinkRouter';
|
||||||
|
import Footer from './components/Footer';
|
||||||
|
|
||||||
|
import './App.scss';
|
||||||
|
|
||||||
|
import GlobalContext from './contexts/GlobalContext';
|
||||||
|
|
||||||
|
/* eslint-disable no-restricted-globals */
|
||||||
|
|
||||||
|
const App: React.FC = () => {
|
||||||
|
let page = (
|
||||||
|
<>
|
||||||
|
<CreateLinkTile />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
const [hash, setHash] = useState(location.hash);
|
||||||
|
|
||||||
|
console.log(`Link for ${hash}`);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Some hacky uri decoding
|
||||||
|
if (location.href.split('/').length > 4) {
|
||||||
|
location.href = decodeURIComponent(location.href);
|
||||||
|
}
|
||||||
|
|
||||||
|
window.onhashchange = () => setHash(location.hash);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
if (hash) {
|
||||||
|
if (hash.startsWith('#/')) {
|
||||||
|
page = <LinkRouter link={hash.slice(2)} />;
|
||||||
|
} else {
|
||||||
|
page = (
|
||||||
|
<Tile>
|
||||||
|
Links should be in the format {location.host}/#/{'<'}
|
||||||
|
matrix-resource-identifier{'>'}
|
||||||
|
</Tile>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<GlobalContext>
|
||||||
|
<SingleColumn>
|
||||||
|
<div className="topSpacer" />
|
||||||
|
{page}
|
||||||
|
<div>
|
||||||
|
<MatrixTile isLink={!!location.hash} />
|
||||||
|
<br />
|
||||||
|
<Footer />
|
||||||
|
</div>
|
||||||
|
<div className="bottomSpacer" />
|
||||||
|
</SingleColumn>
|
||||||
|
</GlobalContext>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default App;
|
|
@ -1,56 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright 2021 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 {LinkKind, IdentifierKind} from "./Link.js";
|
|
||||||
|
|
||||||
export class InvalidUrlView extends TemplateView {
|
|
||||||
render(t, vm) {
|
|
||||||
return t.div({ className: "DisclaimerView card" }, [
|
|
||||||
t.h1("Invalid URL"),
|
|
||||||
t.p([
|
|
||||||
'The link you have entered is not valid. If you like, you can ',
|
|
||||||
t.a({ href: "#/" }, 'return to the home page.')
|
|
||||||
]),
|
|
||||||
vm.validFixes.length ? this._renderValidFixes(t, vm.validFixes) : [],
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
_describeRoom(identifierKind) {
|
|
||||||
return identifierKind === IdentifierKind.RoomAlias ? "room alias" : "room";
|
|
||||||
}
|
|
||||||
|
|
||||||
_describeLinkKind(linkKind, identifierKind) {
|
|
||||||
switch (linkKind) {
|
|
||||||
case LinkKind.Room: return `The ${this._describeRoom(identifierKind)} `;
|
|
||||||
case LinkKind.User: return "The user ";
|
|
||||||
case LinkKind.Group: return "The group ";
|
|
||||||
case LinkKind.Event: return `An event in ${this._describeRoom(identifierKind)} `;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_renderValidFixes(t, validFixes) {
|
|
||||||
return t.p([
|
|
||||||
'Did you mean any of the following?',
|
|
||||||
t.ul(validFixes.map(fix =>
|
|
||||||
t.li([
|
|
||||||
this._describeLinkKind(fix.link.kind, fix.link.identifierKind),
|
|
||||||
t.a({ href: fix.url }, fix.link.identifier)
|
|
||||||
])
|
|
||||||
))
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
212
src/Link.js
|
@ -1,212 +0,0 @@
|
||||||
/*
|
|
||||||
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";
|
|
||||||
import {orderedUnique} from "./utils/unique.js";
|
|
||||||
|
|
||||||
const ROOMALIAS_PATTERN = /^#([^:]*):(.+)$/;
|
|
||||||
const ROOMID_PATTERN = /^!([^:]*):(.+)$/;
|
|
||||||
const USERID_PATTERN = /^@([^:]+):(.+)$/;
|
|
||||||
const EVENTID_PATTERN = /^$([^:]+):(.+)$/;
|
|
||||||
const GROUPID_PATTERN = /^\+([^:]+):(.+)$/;
|
|
||||||
|
|
||||||
export const IdentifierKind = createEnum(
|
|
||||||
"RoomId",
|
|
||||||
"RoomAlias",
|
|
||||||
"UserId",
|
|
||||||
"GroupId",
|
|
||||||
);
|
|
||||||
|
|
||||||
function asPrefix(identifierKind) {
|
|
||||||
switch (identifierKind) {
|
|
||||||
case IdentifierKind.RoomId: return "!";
|
|
||||||
case IdentifierKind.RoomAlias: return "#";
|
|
||||||
case IdentifierKind.GroupId: return "+";
|
|
||||||
case IdentifierKind.UserId: return "@";
|
|
||||||
default: throw new Error("invalid id kind " + identifierKind);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getWebInstanceMap(queryParams) {
|
|
||||||
const prefix = "web-instance[";
|
|
||||||
const postfix = "]";
|
|
||||||
const webInstanceParams = queryParams.filter(([key]) => key.startsWith(prefix) && key.endsWith(postfix));
|
|
||||||
const webInstances = webInstanceParams.map(([key, value]) => {
|
|
||||||
const noPrefix = key.slice(prefix.length);
|
|
||||||
const clientId = noPrefix.slice(0, -postfix.length);
|
|
||||||
return [clientId, value];
|
|
||||||
});
|
|
||||||
return webInstances.reduce((map, [clientId, host]) => {
|
|
||||||
map[clientId] = host;
|
|
||||||
return map;
|
|
||||||
}, {});
|
|
||||||
}
|
|
||||||
|
|
||||||
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(
|
|
||||||
"Room",
|
|
||||||
"User",
|
|
||||||
"Group",
|
|
||||||
"Event"
|
|
||||||
)
|
|
||||||
|
|
||||||
export function tryFixUrl(fragment) {
|
|
||||||
const attempts = [];
|
|
||||||
const afterHash = fragment.substring(fragment.startsWith("#/") ? 2 : 1);
|
|
||||||
attempts.push('#/@' + afterHash);
|
|
||||||
attempts.push('#/#' + afterHash);
|
|
||||||
attempts.push('#/!' + afterHash);
|
|
||||||
|
|
||||||
const validAttempts = [];
|
|
||||||
for (const attempt of [...new Set(attempts)]) {
|
|
||||||
const link = Link.parseFragment(attempt);
|
|
||||||
if (link) {
|
|
||||||
validAttempts.push({ url: attempt, link });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return validAttempts;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Link {
|
|
||||||
static validateIdentifier(identifier) {
|
|
||||||
return !!(
|
|
||||||
USERID_PATTERN.exec(identifier) ||
|
|
||||||
ROOMALIAS_PATTERN.exec(identifier) ||
|
|
||||||
ROOMID_PATTERN.exec(identifier) ||
|
|
||||||
GROUPID_PATTERN.exec(identifier)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
static parseIdentifier(identifier) {
|
|
||||||
return Link._parse(identifier);
|
|
||||||
}
|
|
||||||
|
|
||||||
static parseFragment(fragment) {
|
|
||||||
if (!fragment) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
let [linkStr, queryParamsStr] = fragment.split("?");
|
|
||||||
if (!linkStr.startsWith("#/")) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
linkStr = linkStr.slice(2);
|
|
||||||
const [identifier, eventId] = linkStr.split("/");
|
|
||||||
|
|
||||||
let viaServers = [];
|
|
||||||
let clientId = null;
|
|
||||||
let webInstances = {};
|
|
||||||
if (queryParamsStr) {
|
|
||||||
const queryParams = queryParamsStr.split("&").map(pair => {
|
|
||||||
const [key, value] = pair.split("=");
|
|
||||||
return [decodeURIComponent(key), decodeURIComponent(value)];
|
|
||||||
});
|
|
||||||
viaServers = queryParams
|
|
||||||
.filter(([key, value]) => key === "via")
|
|
||||||
.map(([,value]) => value);
|
|
||||||
const clientParam = queryParams.find(([key]) => key === "client");
|
|
||||||
if (clientParam) {
|
|
||||||
clientId = clientParam[1];
|
|
||||||
}
|
|
||||||
webInstances = getWebInstanceMap(queryParams);
|
|
||||||
}
|
|
||||||
return Link._parse(identifier, eventId, clientId, viaServers, webInstances);
|
|
||||||
}
|
|
||||||
|
|
||||||
static _parse(identifier, eventId = undefined, clientId = null, viaServers = [], webInstances = {}) {
|
|
||||||
if (!identifier) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
let matches;
|
|
||||||
matches = USERID_PATTERN.exec(identifier);
|
|
||||||
if (matches) {
|
|
||||||
const server = matches[2];
|
|
||||||
const localPart = matches[1];
|
|
||||||
return new Link(clientId, viaServers, IdentifierKind.UserId, localPart, server, webInstances);
|
|
||||||
}
|
|
||||||
matches = ROOMALIAS_PATTERN.exec(identifier);
|
|
||||||
if (matches) {
|
|
||||||
const server = matches[2];
|
|
||||||
const localPart = matches[1];
|
|
||||||
return new Link(clientId, viaServers, IdentifierKind.RoomAlias, localPart, server, webInstances, eventId);
|
|
||||||
}
|
|
||||||
matches = ROOMID_PATTERN.exec(identifier);
|
|
||||||
if (matches) {
|
|
||||||
const server = matches[2];
|
|
||||||
const localPart = matches[1];
|
|
||||||
return new Link(clientId, viaServers, IdentifierKind.RoomId, localPart, server, webInstances, eventId);
|
|
||||||
}
|
|
||||||
matches = GROUPID_PATTERN.exec(identifier);
|
|
||||||
if (matches) {
|
|
||||||
const server = matches[2];
|
|
||||||
const localPart = matches[1];
|
|
||||||
return new Link(clientId, viaServers, IdentifierKind.GroupId, localPart, server, webInstances);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(clientId, viaServers, identifierKind, localPart, server, webInstances, eventId) {
|
|
||||||
const servers = [server];
|
|
||||||
servers.push(...viaServers);
|
|
||||||
this.webInstances = webInstances;
|
|
||||||
this.servers = orderedUnique(servers);
|
|
||||||
this.identifierKind = identifierKind;
|
|
||||||
this.identifier = `${asPrefix(identifierKind)}${localPart}:${server}`;
|
|
||||||
this.eventId = eventId;
|
|
||||||
this.clientId = clientId;
|
|
||||||
}
|
|
||||||
|
|
||||||
get kind() {
|
|
||||||
if (this.eventId) {
|
|
||||||
return LinkKind.Event;
|
|
||||||
}
|
|
||||||
switch (this.identifierKind) {
|
|
||||||
case IdentifierKind.RoomId:
|
|
||||||
case IdentifierKind.RoomAlias:
|
|
||||||
return LinkKind.Room;
|
|
||||||
case IdentifierKind.UserId:
|
|
||||||
return LinkKind.User;
|
|
||||||
case IdentifierKind.GroupId:
|
|
||||||
return LinkKind.Group;
|
|
||||||
default:
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
equals(link) {
|
|
||||||
return link &&
|
|
||||||
link.identifier === this.identifier &&
|
|
||||||
this.servers.length === link.servers.length &&
|
|
||||||
this.servers.every((s, i) => link.servers[i] === s) &&
|
|
||||||
Object.keys(this.webInstances).length === Object.keys(link.webInstances).length &&
|
|
||||||
Object.keys(this.webInstances).every(k => this.webInstances[k] === link.webInstances[k]);
|
|
||||||
}
|
|
||||||
|
|
||||||
toFragment() {
|
|
||||||
if (this.eventId) {
|
|
||||||
return `/${this.identifier}/${this.eventId}`;
|
|
||||||
} else {
|
|
||||||
return `/${this.identifier}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,64 +0,0 @@
|
||||||
/*
|
|
||||||
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, platform) {
|
|
||||||
// return [Platform.DesktopWeb, Platform.Linux];
|
|
||||||
let nativePlatform;
|
|
||||||
let webPlatform;
|
|
||||||
if (/android/i.test(userAgent)) {
|
|
||||||
nativePlatform = Platform.Android;
|
|
||||||
webPlatform = Platform.MobileWeb;
|
|
||||||
} else if ( // https://stackoverflow.com/questions/9038625/detect-if-device-is-ios/9039885
|
|
||||||
(
|
|
||||||
/iPad|iPhone|iPod/.test(navigator.platform) ||
|
|
||||||
(navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1)
|
|
||||||
) && !window.MSStream
|
|
||||||
) {
|
|
||||||
nativePlatform = Platform.iOS;
|
|
||||||
webPlatform = Platform.MobileWeb;
|
|
||||||
} else if (platform.toLowerCase().indexOf("linux") !== -1) {
|
|
||||||
nativePlatform = Platform.Linux;
|
|
||||||
webPlatform = Platform.DesktopWeb;
|
|
||||||
} else if (platform.toLowerCase().indexOf("mac") !== -1) {
|
|
||||||
nativePlatform = Platform.macOS;
|
|
||||||
webPlatform = Platform.DesktopWeb;
|
|
||||||
} else {
|
|
||||||
nativePlatform = Platform.Windows;
|
|
||||||
webPlatform = Platform.DesktopWeb;
|
|
||||||
}
|
|
||||||
return [nativePlatform, webPlatform];
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isWebPlatform(p) {
|
|
||||||
return p === Platform.DesktopWeb || p === Platform.MobileWeb;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export function isDesktopPlatform(p) {
|
|
||||||
return p === Platform.Linux || p === Platform.Windows || p === Platform.macOS;
|
|
||||||
}
|
|
|
@ -1,68 +0,0 @@
|
||||||
/*
|
|
||||||
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 {Platform} from "./Platform.js";
|
|
||||||
import {EventEmitter} from "./utils/ViewModel.js";
|
|
||||||
|
|
||||||
export class Preferences extends EventEmitter {
|
|
||||||
constructor(localStorage) {
|
|
||||||
super();
|
|
||||||
this._localStorage = localStorage;
|
|
||||||
this.clientId = null;
|
|
||||||
// used to differentiate web from native if a client supports both
|
|
||||||
this.platform = null;
|
|
||||||
this.homeservers = null;
|
|
||||||
|
|
||||||
const prefsStr = localStorage.getItem("preferred_client");
|
|
||||||
if (prefsStr) {
|
|
||||||
const {id, platform} = JSON.parse(prefsStr);
|
|
||||||
this.clientId = id;
|
|
||||||
this.platform = Platform[platform];
|
|
||||||
}
|
|
||||||
const serversStr = localStorage.getItem("consented_servers");
|
|
||||||
if (serversStr) {
|
|
||||||
this.homeservers = JSON.parse(serversStr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setClient(id, platform) {
|
|
||||||
this.clientId = id;
|
|
||||||
platform = Platform[platform];
|
|
||||||
this.platform = platform;
|
|
||||||
this._localStorage.setItem("preferred_client", JSON.stringify({id, platform}));
|
|
||||||
this.emit("canClear")
|
|
||||||
}
|
|
||||||
|
|
||||||
setHomeservers(homeservers, persist) {
|
|
||||||
this.homeservers = homeservers;
|
|
||||||
if (persist) {
|
|
||||||
this._localStorage.setItem("consented_servers", JSON.stringify(homeservers));
|
|
||||||
this.emit("canClear");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
clear() {
|
|
||||||
this._localStorage.removeItem("preferred_client");
|
|
||||||
this._localStorage.removeItem("consented_servers");
|
|
||||||
this.clientId = null;
|
|
||||||
this.platform = null;
|
|
||||||
this.homeservers = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
get canClear() {
|
|
||||||
return !!this.clientId || !!this.platform || !!this.homeservers;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,49 +0,0 @@
|
||||||
/*
|
|
||||||
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 {OpenLinkView} from "./open/OpenLinkView.js";
|
|
||||||
import {CreateLinkView} from "./create/CreateLinkView.js";
|
|
||||||
import {LoadServerPolicyView} from "./policy/LoadServerPolicyView.js";
|
|
||||||
import {DisclaimerView} from "./disclaimer/DisclaimerView.js";
|
|
||||||
import {InvalidUrlView} from "./InvalidUrlView.js";
|
|
||||||
|
|
||||||
export class RootView extends TemplateView {
|
|
||||||
render(t, vm) {
|
|
||||||
return t.div({className: "RootView"}, [
|
|
||||||
t.mapView(vm => vm.invalidUrlViewModel, invalidVM => invalidVM ? new InvalidUrlView(invalidVM) : null),
|
|
||||||
t.mapView(vm => vm.showDisclaimer, disclaimer => disclaimer ? new DisclaimerView() : null),
|
|
||||||
t.mapView(vm => vm.openLinkViewModel, vm => vm ? new OpenLinkView(vm) : null),
|
|
||||||
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")),
|
|
||||||
])
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function externalLink(t, href, label) {
|
|
||||||
return t.a({href, target: "_blank", rel: "noopener noreferrer"}, label);
|
|
||||||
}
|
|
|
@ -1,98 +0,0 @@
|
||||||
/*
|
|
||||||
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 {Link} from "./Link.js";
|
|
||||||
import {ViewModel} from "./utils/ViewModel.js";
|
|
||||||
import {OpenLinkViewModel} from "./open/OpenLinkViewModel.js";
|
|
||||||
import {createClients} from "./open/clients/index.js";
|
|
||||||
import {CreateLinkViewModel} from "./create/CreateLinkViewModel.js";
|
|
||||||
import {LoadServerPolicyViewModel} from "./policy/LoadServerPolicyViewModel.js";
|
|
||||||
import {InvalidUrlViewModel} from "./InvalidUrlViewModel.js";
|
|
||||||
import {Platform} from "./Platform.js";
|
|
||||||
|
|
||||||
export class RootViewModel extends ViewModel {
|
|
||||||
constructor(options) {
|
|
||||||
super(options);
|
|
||||||
this.link = null;
|
|
||||||
this.openLinkViewModel = null;
|
|
||||||
this.createLinkViewModel = null;
|
|
||||||
this.loadServerPolicyViewModel = null;
|
|
||||||
this.invalidUrlViewModel = null;
|
|
||||||
this.showDisclaimer = false;
|
|
||||||
this.preferences.on("canClear", () => {
|
|
||||||
this.emitChange();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
_updateChildVMs(newLink, oldLink) {
|
|
||||||
this.link = newLink;
|
|
||||||
if (!newLink) {
|
|
||||||
this.openLinkViewModel = null;
|
|
||||||
} else if (!oldLink || !oldLink.equals(newLink)) {
|
|
||||||
this.openLinkViewModel = new OpenLinkViewModel(this.childOptions({
|
|
||||||
link: newLink,
|
|
||||||
clients: createClients(),
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_hideLinks() {
|
|
||||||
this.link = null;
|
|
||||||
this.openLinkViewModel = null;
|
|
||||||
this.createLinkViewModel = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
updateHash(hash) {
|
|
||||||
// All view models except openLink are re-created anyway. Might as well
|
|
||||||
// clear them to avoid having to manually reset (n-1)/n view models in every case.
|
|
||||||
// That just doesn't scale well when we add new views.
|
|
||||||
const oldLink = this.link;
|
|
||||||
this.invalidUrlViewModel = null;
|
|
||||||
this.showDisclaimer = false;
|
|
||||||
this.loadServerPolicyViewModel = null;
|
|
||||||
this.createLinkViewModel = null;
|
|
||||||
let newLink;
|
|
||||||
if (hash.startsWith("#/policy/")) {
|
|
||||||
const server = hash.slice(9);
|
|
||||||
this._updateChildVMs(null, oldLink);
|
|
||||||
this.loadServerPolicyViewModel = new LoadServerPolicyViewModel(this.childOptions({server}));
|
|
||||||
this.loadServerPolicyViewModel.load();
|
|
||||||
} else if (hash.startsWith("#/disclaimer/")) {
|
|
||||||
this._updateChildVMs(null, oldLink);
|
|
||||||
this.showDisclaimer = true;
|
|
||||||
} else if (hash === "" || hash === "#" || hash === "#/") {
|
|
||||||
this._updateChildVMs(null, oldLink);
|
|
||||||
this.createLinkViewModel = new CreateLinkViewModel(this.childOptions());
|
|
||||||
} else if (newLink = Link.parseFragment(hash)) {
|
|
||||||
this._updateChildVMs(newLink, oldLink);
|
|
||||||
} else {
|
|
||||||
this._updateChildVMs(null, oldLink);
|
|
||||||
this.invalidUrlViewModel = new InvalidUrlViewModel(this.childOptions({
|
|
||||||
fragment: hash
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
this.emitChange();
|
|
||||||
}
|
|
||||||
|
|
||||||
clearPreferences() {
|
|
||||||
this.preferences.clear();
|
|
||||||
this._updateChildVMs();
|
|
||||||
}
|
|
||||||
|
|
||||||
get hasPreferences() {
|
|
||||||
return this.preferences.canClear;
|
|
||||||
}
|
|
||||||
}
|
|
25
src/_color-scheme.scss
Normal 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
$app-background: #f4f4f4;
|
||||||
|
$background: #ffffff;
|
||||||
|
$foreground: #000000;
|
||||||
|
$font: #333333;
|
||||||
|
$grey: #666666;
|
||||||
|
$accent: #0098d4;
|
||||||
|
$error: #d6001c;
|
||||||
|
$link: #0098d4;
|
||||||
|
$borders: #f4f4f4;
|
|
@ -14,5 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import "core-js/stable";
|
@mixin error {
|
||||||
import "regenerator-runtime/runtime";
|
color: $error;
|
||||||
|
border-color: $error;
|
||||||
|
}
|
11
src/_mixins.scss
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
@mixin unreal-focus {
|
||||||
|
outline-width: 2px;
|
||||||
|
outline-style: solid;
|
||||||
|
outline-color: Highlight;
|
||||||
|
|
||||||
|
/* WebKit gets its native focus styles. */
|
||||||
|
@media (-webkit-min-device-pixel-ratio: 0) {
|
||||||
|
outline-color: -webkit-focus-ring-color;
|
||||||
|
outline-style: auto;
|
||||||
|
}
|
||||||
|
}
|
97
src/clients/Element.ts
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
/*
|
||||||
|
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 {
|
||||||
|
LinkedClient,
|
||||||
|
Maturity,
|
||||||
|
ClientKind,
|
||||||
|
ClientId,
|
||||||
|
Platform,
|
||||||
|
} from './types';
|
||||||
|
import { LinkKind } from '../parser/types';
|
||||||
|
import logo from '../imgs/element.svg';
|
||||||
|
|
||||||
|
export const Element: LinkedClient = {
|
||||||
|
kind: ClientKind.LINKED_CLIENT,
|
||||||
|
name: 'Element',
|
||||||
|
author: 'Element',
|
||||||
|
logo: logo,
|
||||||
|
homepage: 'https://element.io',
|
||||||
|
maturity: Maturity.STABLE,
|
||||||
|
description: 'Fully-featured Matrix client for the Web',
|
||||||
|
platforms: [Platform.Desktop, Platform.Android, Platform.iOS],
|
||||||
|
experimental: false,
|
||||||
|
clientId: ClientId.Element,
|
||||||
|
toUrl: (link) => {
|
||||||
|
const params = link.arguments.originalParams.toString();
|
||||||
|
const prefixedParams = params ? `?${params}` : '';
|
||||||
|
switch (link.kind) {
|
||||||
|
case LinkKind.Alias:
|
||||||
|
case LinkKind.RoomId:
|
||||||
|
return new URL(
|
||||||
|
`https://app.element.io/#/room/${link.identifier}${prefixedParams}`
|
||||||
|
);
|
||||||
|
case LinkKind.UserId:
|
||||||
|
return new URL(
|
||||||
|
`https://app.element.io/#/user/${link.identifier}${prefixedParams}`
|
||||||
|
);
|
||||||
|
case LinkKind.Permalink:
|
||||||
|
return new URL(
|
||||||
|
`https://app.element.io/#/room/${link.identifier}${prefixedParams}`
|
||||||
|
);
|
||||||
|
case LinkKind.GroupId:
|
||||||
|
return new URL(
|
||||||
|
`https://app.element.io/#/group/${link.identifier}${prefixedParams}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
linkSupport: () => true,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ElementDevelop: LinkedClient = {
|
||||||
|
kind: ClientKind.LINKED_CLIENT,
|
||||||
|
name: 'Element Develop',
|
||||||
|
author: 'Element',
|
||||||
|
logo: logo,
|
||||||
|
homepage: 'https://element.io',
|
||||||
|
maturity: Maturity.STABLE,
|
||||||
|
description: 'Fully-featured Matrix client for the Web',
|
||||||
|
platforms: [Platform.Desktop],
|
||||||
|
experimental: true,
|
||||||
|
clientId: ClientId.ElementDevelop,
|
||||||
|
toUrl: (link) => {
|
||||||
|
switch (link.kind) {
|
||||||
|
case LinkKind.Alias:
|
||||||
|
case LinkKind.RoomId:
|
||||||
|
return new URL(
|
||||||
|
`https://develop.element.io/#/room/${link.identifier}`
|
||||||
|
);
|
||||||
|
case LinkKind.UserId:
|
||||||
|
return new URL(
|
||||||
|
`https://develop.element.io/#/user/${link.identifier}`
|
||||||
|
);
|
||||||
|
case LinkKind.Permalink:
|
||||||
|
return new URL(
|
||||||
|
`https://develop.element.io/#/room/${link.identifier}`
|
||||||
|
);
|
||||||
|
case LinkKind.GroupId:
|
||||||
|
return new URL(
|
||||||
|
`https://develop.element.io/#/group/${link.identifier}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
linkSupport: () => true,
|
||||||
|
};
|
74
src/clients/Fractal.tsx
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
/*
|
||||||
|
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 React from 'react';
|
||||||
|
|
||||||
|
import { TextClient, Maturity, ClientKind, ClientId, Platform } from './types';
|
||||||
|
|
||||||
|
import { LinkKind } from '../parser/types';
|
||||||
|
|
||||||
|
import logo from '../imgs/fractal.png';
|
||||||
|
|
||||||
|
const Fractal: TextClient = {
|
||||||
|
kind: ClientKind.TEXT_CLIENT,
|
||||||
|
name: 'Fractal',
|
||||||
|
logo: logo,
|
||||||
|
author: 'Daniel Garcia Moreno',
|
||||||
|
homepage: 'https://github.com/poljar/weechat-matrix',
|
||||||
|
maturity: Maturity.BETA,
|
||||||
|
experimental: false,
|
||||||
|
platforms: [Platform.Desktop],
|
||||||
|
clientId: ClientId.Fractal,
|
||||||
|
toInviteString: (link) => {
|
||||||
|
switch (link.kind) {
|
||||||
|
case LinkKind.Alias:
|
||||||
|
case LinkKind.RoomId:
|
||||||
|
case LinkKind.UserId:
|
||||||
|
return (
|
||||||
|
<span>
|
||||||
|
Click the '+' button in the top right and paste the
|
||||||
|
identifier
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
default:
|
||||||
|
return <span>Fractal doesn't support this kind of link</span>;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
copyString: (link) => {
|
||||||
|
switch (link.kind) {
|
||||||
|
case LinkKind.Alias:
|
||||||
|
case LinkKind.RoomId:
|
||||||
|
case LinkKind.UserId:
|
||||||
|
return `${link.identifier}`;
|
||||||
|
default:
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
linkSupport: (link) => {
|
||||||
|
switch (link.kind) {
|
||||||
|
case LinkKind.Alias:
|
||||||
|
case LinkKind.RoomId:
|
||||||
|
case LinkKind.UserId:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
description: 'Fractal is a Matrix Client written in Rust',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Fractal;
|
91
src/clients/Nheko.tsx
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
/*
|
||||||
|
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 React from 'react';
|
||||||
|
|
||||||
|
import { TextClient, Maturity, ClientKind, ClientId, Platform } from './types';
|
||||||
|
|
||||||
|
import { LinkKind } from '../parser/types';
|
||||||
|
|
||||||
|
import logo from '../imgs/nheko.svg';
|
||||||
|
|
||||||
|
const Nheko: TextClient = {
|
||||||
|
kind: ClientKind.TEXT_CLIENT,
|
||||||
|
name: 'Nheko',
|
||||||
|
logo: logo,
|
||||||
|
author: 'mujx, red_sky, deepbluev7, Konstantinos Sideris',
|
||||||
|
homepage: 'https://github.com/Nheko-Reborn/nheko',
|
||||||
|
maturity: Maturity.BETA,
|
||||||
|
experimental: false,
|
||||||
|
platforms: [Platform.Desktop],
|
||||||
|
clientId: ClientId.Nheko,
|
||||||
|
toInviteString: (link) => {
|
||||||
|
switch (link.kind) {
|
||||||
|
case LinkKind.Alias:
|
||||||
|
case LinkKind.RoomId:
|
||||||
|
return (
|
||||||
|
<span>
|
||||||
|
Type{' '}
|
||||||
|
<code>
|
||||||
|
/join{' '}
|
||||||
|
<b className="matrixIdentifier">
|
||||||
|
{link.identifier}
|
||||||
|
</b>
|
||||||
|
</code>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
case LinkKind.UserId:
|
||||||
|
return (
|
||||||
|
<span>
|
||||||
|
Type{' '}
|
||||||
|
<code>
|
||||||
|
/invite{' '}
|
||||||
|
<b className="matrixIdentifier">
|
||||||
|
{link.identifier}
|
||||||
|
</b>
|
||||||
|
</code>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
default:
|
||||||
|
return <span>Nheko doesn't support this kind of link</span>;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
copyString: (link) => {
|
||||||
|
switch (link.kind) {
|
||||||
|
case LinkKind.Alias:
|
||||||
|
case LinkKind.RoomId:
|
||||||
|
return `/join ${link.identifier}`;
|
||||||
|
case LinkKind.UserId:
|
||||||
|
return `/invite ${link.identifier}`;
|
||||||
|
default:
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
linkSupport: (link) => {
|
||||||
|
switch (link.kind) {
|
||||||
|
case LinkKind.Alias:
|
||||||
|
case LinkKind.RoomId:
|
||||||
|
case LinkKind.UserId:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
description:
|
||||||
|
'A native desktop app for Matrix that feels more like a mainstream chat app.',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Nheko;
|
91
src/clients/Weechat.tsx
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
/*
|
||||||
|
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 React from 'react';
|
||||||
|
|
||||||
|
import { TextClient, Maturity, ClientKind, ClientId, Platform } from './types';
|
||||||
|
|
||||||
|
import { LinkKind } from '../parser/types';
|
||||||
|
|
||||||
|
import logo from '../imgs/weechat.svg';
|
||||||
|
|
||||||
|
const Weechat: TextClient = {
|
||||||
|
kind: ClientKind.TEXT_CLIENT,
|
||||||
|
name: 'Weechat',
|
||||||
|
logo: logo,
|
||||||
|
author: 'Poljar',
|
||||||
|
homepage: 'https://github.com/poljar/weechat-matrix',
|
||||||
|
maturity: Maturity.LATE_BETA,
|
||||||
|
experimental: false,
|
||||||
|
platforms: [Platform.Desktop],
|
||||||
|
clientId: ClientId.WeeChat,
|
||||||
|
toInviteString: (link) => {
|
||||||
|
switch (link.kind) {
|
||||||
|
case LinkKind.Alias:
|
||||||
|
case LinkKind.RoomId:
|
||||||
|
return (
|
||||||
|
<span>
|
||||||
|
Type{' '}
|
||||||
|
<code>
|
||||||
|
/join{' '}
|
||||||
|
<b className="matrixIdentifier">
|
||||||
|
{link.identifier}
|
||||||
|
</b>
|
||||||
|
</code>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
case LinkKind.UserId:
|
||||||
|
return (
|
||||||
|
<span>
|
||||||
|
Type{' '}
|
||||||
|
<code>
|
||||||
|
/invite{' '}
|
||||||
|
<b className="matrixIdentifier">
|
||||||
|
{link.identifier}
|
||||||
|
</b>
|
||||||
|
</code>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
default:
|
||||||
|
return <span>Weechat doesn't support this kind of link</span>;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
copyString: (link) => {
|
||||||
|
switch (link.kind) {
|
||||||
|
case LinkKind.Alias:
|
||||||
|
case LinkKind.RoomId:
|
||||||
|
return `/join ${link.identifier}`;
|
||||||
|
case LinkKind.UserId:
|
||||||
|
return `/invite ${link.identifier}`;
|
||||||
|
default:
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
linkSupport: (link) => {
|
||||||
|
switch (link.kind) {
|
||||||
|
case LinkKind.Alias:
|
||||||
|
case LinkKind.RoomId:
|
||||||
|
case LinkKind.UserId:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
description: 'Command-line Matrix interface using Weechat',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Weechat;
|
45
src/clients/index.ts
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
/*
|
||||||
|
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 { Client } from './types';
|
||||||
|
|
||||||
|
import { Element, ElementDevelop } from './Element';
|
||||||
|
import Weechat from './Weechat';
|
||||||
|
import Nheko from './Nheko';
|
||||||
|
import Fractal from './Fractal';
|
||||||
|
|
||||||
|
/*
|
||||||
|
* All the supported clients of matrix.to
|
||||||
|
*/
|
||||||
|
const clients: Client[] = [Element, Weechat, Nheko, Fractal, ElementDevelop];
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A map from sharer string to client.
|
||||||
|
* Configured by hand so we can change the mappings
|
||||||
|
* easily later.
|
||||||
|
*/
|
||||||
|
export const clientMap: { [key: string]: Client } = {
|
||||||
|
[Element.clientId]: Element,
|
||||||
|
[Weechat.clientId]: Weechat,
|
||||||
|
[ElementDevelop.clientId]: ElementDevelop,
|
||||||
|
[Nheko.clientId]: Nheko,
|
||||||
|
[Fractal.clientId]: Fractal,
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* All the supported clients of matrix.to
|
||||||
|
*/
|
||||||
|
export default clients;
|
94
src/clients/types.ts
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 { SafeLink } from '../parser/types';
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A collection of descriptive tags that can be added to
|
||||||
|
* a clients description.
|
||||||
|
*/
|
||||||
|
export enum Platform {
|
||||||
|
iOS = 'iOS',
|
||||||
|
Android = 'ANDROID',
|
||||||
|
Desktop = 'DESKTOP',
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A collection of states used for describing a clients maturity.
|
||||||
|
*/
|
||||||
|
export enum Maturity {
|
||||||
|
ALPHA = 'ALPHA',
|
||||||
|
LATE_ALPHA = 'LATE ALPHA',
|
||||||
|
BETA = 'BETA',
|
||||||
|
LATE_BETA = 'LATE_BETA',
|
||||||
|
STABLE = 'STABLE',
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Used for constructing the discriminated union of all client types.
|
||||||
|
*/
|
||||||
|
export enum ClientKind {
|
||||||
|
LINKED_CLIENT = 'LINKED_CLIENT',
|
||||||
|
TEXT_CLIENT = 'TEXT_CLIENT',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum ClientId {
|
||||||
|
Element = 'element.io',
|
||||||
|
ElementDevelop = 'develop.element.io',
|
||||||
|
WeeChat = 'weechat',
|
||||||
|
Nheko = 'nheko',
|
||||||
|
Fractal = 'fractal',
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The descriptive details of a client
|
||||||
|
*/
|
||||||
|
export interface ClientDescription {
|
||||||
|
name: string;
|
||||||
|
author: string;
|
||||||
|
homepage: string;
|
||||||
|
logo: string;
|
||||||
|
description: string;
|
||||||
|
platforms: Platform[];
|
||||||
|
maturity: Maturity;
|
||||||
|
clientId: ClientId;
|
||||||
|
experimental: boolean;
|
||||||
|
linkSupport: (link: SafeLink) => boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A client which can be opened using a link with the matrix resource.
|
||||||
|
*/
|
||||||
|
export interface LinkedClient extends ClientDescription {
|
||||||
|
kind: ClientKind.LINKED_CLIENT;
|
||||||
|
toUrl(parsedLink: SafeLink): URL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A client which provides isntructions for how to access the descired
|
||||||
|
* resource.
|
||||||
|
*/
|
||||||
|
export interface TextClient extends ClientDescription {
|
||||||
|
kind: ClientKind.TEXT_CLIENT;
|
||||||
|
toInviteString(parsedLink: SafeLink): JSX.Element;
|
||||||
|
copyString(parsedLink: SafeLink): string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A description for a client as well as a method for converting matrix.to
|
||||||
|
* links to the client's specific representation.
|
||||||
|
*/
|
||||||
|
export type Client = LinkedClient | TextClient;
|
29
src/components/Avatar.scss
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
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 '../color-scheme';
|
||||||
|
|
||||||
|
.avatar {
|
||||||
|
border-radius: 100%;
|
||||||
|
border: 1px solid $borders;
|
||||||
|
height: 60px;
|
||||||
|
width: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatarNoCrop {
|
||||||
|
border-radius: 0;
|
||||||
|
border: 0;
|
||||||
|
}
|
44
src/components/Avatar.stories.tsx
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
/*
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// disable camelcase check because our object keys come
|
||||||
|
// from the matrix spec
|
||||||
|
/* eslint-disable @typescript-eslint/camelcase */
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import { UserAvatar } from './Avatar';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'Avatar',
|
||||||
|
parameters: {
|
||||||
|
design: {
|
||||||
|
type: 'figma',
|
||||||
|
url:
|
||||||
|
'https://www.figma.com/file/WSXjCGc1k6FVI093qhlzOP/04-Recieving-share-link?node-id=143%3A5853',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Default: React.FC<{}> = () => (
|
||||||
|
<UserAvatar
|
||||||
|
user={{
|
||||||
|
avatar_url: 'mxc://matrix.org/EqMZYbAYhREvHXvYFyfxOlkf',
|
||||||
|
displayname: 'Jorik Schellekens',
|
||||||
|
}}
|
||||||
|
userId="@jorik:matrix.org"
|
||||||
|
/>
|
||||||
|
);
|
121
src/components/Avatar.tsx
Normal file
|
@ -0,0 +1,121 @@
|
||||||
|
/*
|
||||||
|
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 React, { useEffect, useState } from 'react';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
|
import { Group, Room, User } from '../matrix-cypher';
|
||||||
|
import useHSs from '../utils/getHS';
|
||||||
|
import { getThumbnailURI } from '../utils/cypher-wrapper';
|
||||||
|
import logo from '../imgs/chat-icon.svg';
|
||||||
|
|
||||||
|
import './Avatar.scss';
|
||||||
|
|
||||||
|
const AVATAR_SIZE = 96;
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
className?: string;
|
||||||
|
avatarUrl: string;
|
||||||
|
label: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Avatar: React.FC<IProps> = ({ className, avatarUrl, label }: IProps) => {
|
||||||
|
const [src, setSrc] = useState(avatarUrl);
|
||||||
|
useEffect(() => {
|
||||||
|
setSrc(avatarUrl ? avatarUrl : logo);
|
||||||
|
}, [avatarUrl]);
|
||||||
|
|
||||||
|
const _className = classNames('avatar', className, {
|
||||||
|
avatarNoCrop: src === logo,
|
||||||
|
});
|
||||||
|
return (
|
||||||
|
<img
|
||||||
|
src={src}
|
||||||
|
onError={(): void => setSrc(logo)}
|
||||||
|
alt={label}
|
||||||
|
className={_className}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
interface IPropsUserAvatar {
|
||||||
|
user: User;
|
||||||
|
userId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const UserAvatar: React.FC<IPropsUserAvatar> = ({
|
||||||
|
user,
|
||||||
|
userId,
|
||||||
|
}: IPropsUserAvatar) => {
|
||||||
|
const [hs] = useHSs({ identifier: userId });
|
||||||
|
return (
|
||||||
|
<Avatar
|
||||||
|
avatarUrl={getThumbnailURI(
|
||||||
|
hs,
|
||||||
|
AVATAR_SIZE,
|
||||||
|
AVATAR_SIZE,
|
||||||
|
user.avatar_url
|
||||||
|
)}
|
||||||
|
label={user.displayname ? user.displayname : userId}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
interface IPropsRoomAvatar {
|
||||||
|
room: Room;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const RoomAvatar: React.FC<IPropsRoomAvatar> = ({
|
||||||
|
room,
|
||||||
|
}: IPropsRoomAvatar) => {
|
||||||
|
const [hs] = useHSs({ identifier: room.room_id });
|
||||||
|
return (
|
||||||
|
<Avatar
|
||||||
|
avatarUrl={getThumbnailURI(
|
||||||
|
hs,
|
||||||
|
AVATAR_SIZE,
|
||||||
|
AVATAR_SIZE,
|
||||||
|
room.avatar_url
|
||||||
|
)}
|
||||||
|
label={room.name || room.room_id}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
interface IPropsGroupAvatar {
|
||||||
|
group: Group;
|
||||||
|
groupId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const GroupAvatar: React.FC<IPropsGroupAvatar> = ({
|
||||||
|
group,
|
||||||
|
groupId,
|
||||||
|
}: IPropsGroupAvatar) => {
|
||||||
|
const [hs] = useHSs({ identifier: groupId });
|
||||||
|
return (
|
||||||
|
<Avatar
|
||||||
|
avatarUrl={getThumbnailURI(
|
||||||
|
hs,
|
||||||
|
AVATAR_SIZE,
|
||||||
|
AVATAR_SIZE,
|
||||||
|
group.avatar_url
|
||||||
|
)}
|
||||||
|
label={group.name}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Avatar;
|
61
src/components/Button.scss
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
/*
|
||||||
|
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 '../color-scheme';
|
||||||
|
|
||||||
|
.button {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
height: 48px;
|
||||||
|
|
||||||
|
border-radius: 2rem;
|
||||||
|
border: 0;
|
||||||
|
|
||||||
|
background-color: $foreground;
|
||||||
|
color: $background;
|
||||||
|
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.buttonIcon {
|
||||||
|
position: absolute;
|
||||||
|
height: 24px;
|
||||||
|
width: 24px;
|
||||||
|
|
||||||
|
left: 18px;
|
||||||
|
top: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.buttonSecondary {
|
||||||
|
background-color: $background;
|
||||||
|
color: $foreground;
|
||||||
|
border: 1px solid $foreground;
|
||||||
|
}
|
||||||
|
|
||||||
|
.errorButton:hover {
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.buttonHighlight {
|
||||||
|
background-color: $accent;
|
||||||
|
}
|
33
src/components/Button.stories.tsx
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
/*
|
||||||
|
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 React from 'react';
|
||||||
|
import { action } from '@storybook/addon-actions';
|
||||||
|
import { text } from '@storybook/addon-knobs';
|
||||||
|
|
||||||
|
import Button from './Button';
|
||||||
|
|
||||||
|
export default { title: 'Button' };
|
||||||
|
|
||||||
|
export const WithText: React.FC = () => (
|
||||||
|
<Button onClick={action('clicked')}>
|
||||||
|
{text('label', 'Hello Story Book')}
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
|
||||||
|
export const Secondary: React.FC = () => (
|
||||||
|
<Button secondary>Secondary button</Button>
|
||||||
|
);
|
89
src/components/Button.tsx
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
/*
|
||||||
|
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 React from 'react';
|
||||||
|
import classnames from 'classnames';
|
||||||
|
|
||||||
|
import './Button.scss';
|
||||||
|
|
||||||
|
interface IProps extends React.ButtonHTMLAttributes<Element> {
|
||||||
|
// Briefly display these instead of the children onClick
|
||||||
|
flashChildren?: React.ReactNode;
|
||||||
|
secondary?: boolean;
|
||||||
|
icon?: string;
|
||||||
|
flashIcon?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Like a normal button except it will flash content when clicked.
|
||||||
|
*/
|
||||||
|
const Button: React.FC<
|
||||||
|
IProps & React.RefAttributes<HTMLButtonElement>
|
||||||
|
> = React.forwardRef(
|
||||||
|
(
|
||||||
|
{
|
||||||
|
onClick,
|
||||||
|
children,
|
||||||
|
flashChildren,
|
||||||
|
className,
|
||||||
|
secondary,
|
||||||
|
icon,
|
||||||
|
flashIcon,
|
||||||
|
...props
|
||||||
|
}: IProps,
|
||||||
|
ref: React.Ref<HTMLButtonElement>
|
||||||
|
) => {
|
||||||
|
const [wasClicked, setWasClicked] = React.useState(false);
|
||||||
|
|
||||||
|
const wrappedOnClick: React.MouseEventHandler = (e) => {
|
||||||
|
if (onClick) {
|
||||||
|
onClick(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
setWasClicked(true);
|
||||||
|
window.setTimeout(() => {
|
||||||
|
setWasClicked(false);
|
||||||
|
}, 1000);
|
||||||
|
};
|
||||||
|
|
||||||
|
const content = wasClicked && flashChildren ? flashChildren : children;
|
||||||
|
|
||||||
|
const classNames = classnames('button', className, {
|
||||||
|
buttonHighlight: wasClicked,
|
||||||
|
buttonSecondary: secondary,
|
||||||
|
});
|
||||||
|
|
||||||
|
const iconSrc = wasClicked && flashIcon ? flashIcon : icon;
|
||||||
|
|
||||||
|
const buttonIcon = icon ? (
|
||||||
|
<img className="buttonIcon" src={iconSrc} alt="" />
|
||||||
|
) : null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
className={classNames}
|
||||||
|
onClick={wrappedOnClick}
|
||||||
|
ref={ref}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{buttonIcon}
|
||||||
|
{content}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
export default Button;
|
22
src/components/ClientList.scss
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
/*
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.clientList {
|
||||||
|
display: grid;
|
||||||
|
row-gap: 20px;
|
||||||
|
list-style-type: none;
|
||||||
|
padding-inline-start: 0;
|
||||||
|
}
|
98
src/components/ClientList.tsx
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
/*
|
||||||
|
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 React, { useContext } from 'react';
|
||||||
|
import { UAContext } from '@quentin-sommer/react-useragent';
|
||||||
|
|
||||||
|
import { SafeLink } from '../parser/types';
|
||||||
|
import { ActionType, ClientContext } from '../contexts/ClientContext';
|
||||||
|
import Clients from '../clients';
|
||||||
|
import { Client, Platform } from '../clients/types';
|
||||||
|
import ClientTile from './ClientTile';
|
||||||
|
|
||||||
|
import './ClientList.scss';
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
link: SafeLink;
|
||||||
|
rememberSelection: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ClientList: React.FC<IProps> = ({ link, rememberSelection }: IProps) => {
|
||||||
|
const [
|
||||||
|
{ showOnlyDeviceClients, showExperimentalClients },
|
||||||
|
clientDispatcher,
|
||||||
|
] = useContext(ClientContext);
|
||||||
|
const { uaResults } = useContext(UAContext);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Function to decide whether a client is shown
|
||||||
|
*/
|
||||||
|
const showClient = (client: Client): boolean => {
|
||||||
|
let showClient = false;
|
||||||
|
|
||||||
|
if (!showOnlyDeviceClients || uaResults === {}) {
|
||||||
|
showClient = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const platform of client.platforms) {
|
||||||
|
switch (platform) {
|
||||||
|
case Platform.Desktop:
|
||||||
|
showClient = showClient || !(uaResults as any).mobile;
|
||||||
|
break;
|
||||||
|
case Platform.iOS:
|
||||||
|
showClient = showClient || (uaResults as any).ios;
|
||||||
|
break;
|
||||||
|
case Platform.Android:
|
||||||
|
showClient = showClient || (uaResults as any).android;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!showExperimentalClients && client.experimental) {
|
||||||
|
showClient = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!client.linkSupport(link)) {
|
||||||
|
showClient = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return showClient;
|
||||||
|
};
|
||||||
|
|
||||||
|
const clientLi = (client: Client): JSX.Element => (
|
||||||
|
<li
|
||||||
|
key={client.clientId}
|
||||||
|
onClick={(): void =>
|
||||||
|
rememberSelection
|
||||||
|
? clientDispatcher({
|
||||||
|
action: ActionType.SetClient,
|
||||||
|
clientId: client.clientId,
|
||||||
|
})
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<ClientTile client={client} link={link} />
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ul className="clientList">
|
||||||
|
{Clients.filter(showClient).map(clientLi)}
|
||||||
|
</ul>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ClientList;
|
|
@ -14,12 +14,14 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export function orderedUnique(array) {
|
.advanced {
|
||||||
const copy = [];
|
display: grid;
|
||||||
for (let i = 0; i < array.length; ++i) {
|
row-gap: 20px;
|
||||||
if (i === 0 || array.lastIndexOf(array[i], i - 1) === -1) {
|
|
||||||
copy.push(array[i]);
|
.advancedOptions {
|
||||||
}
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
align-items: flex-start;
|
||||||
}
|
}
|
||||||
return copy;
|
}
|
||||||
}
|
|
88
src/components/ClientSelection.tsx
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
/*
|
||||||
|
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 React, { useContext, useState } from 'react';
|
||||||
|
|
||||||
|
import './ClientSelection.scss';
|
||||||
|
import { ActionType, ClientContext } from '../contexts/ClientContext';
|
||||||
|
import ClientList from './ClientList';
|
||||||
|
import { SafeLink } from '../parser/types';
|
||||||
|
import Button from './Button';
|
||||||
|
import StyledCheckbox from './StyledCheckbox';
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
link: SafeLink;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ClientSelection: React.FC<IProps> = ({ link }: IProps) => {
|
||||||
|
const [clientState, clientStateDispatch] = useContext(ClientContext);
|
||||||
|
const [rememberSelection, setRememberSelection] = useState(false);
|
||||||
|
const options = (
|
||||||
|
<div className="advancedOptions">
|
||||||
|
<StyledCheckbox
|
||||||
|
onChange={(): void => {
|
||||||
|
setRememberSelection(!rememberSelection);
|
||||||
|
}}
|
||||||
|
checked={rememberSelection}
|
||||||
|
>
|
||||||
|
Remember for future invites in this browser
|
||||||
|
</StyledCheckbox>
|
||||||
|
<StyledCheckbox
|
||||||
|
onChange={(): void => {
|
||||||
|
clientStateDispatch({
|
||||||
|
action: ActionType.ToggleShowOnlyDeviceClients,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
checked={clientState.showOnlyDeviceClients}
|
||||||
|
>
|
||||||
|
Show only clients suggested for this device
|
||||||
|
</StyledCheckbox>
|
||||||
|
<StyledCheckbox
|
||||||
|
onChange={(): void => {
|
||||||
|
clientStateDispatch({
|
||||||
|
action: ActionType.ToggleShowExperimentalClients,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
checked={clientState.showExperimentalClients}
|
||||||
|
>
|
||||||
|
Show experimental clients
|
||||||
|
</StyledCheckbox>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
const clearSelection =
|
||||||
|
clientState.clientId !== null ? (
|
||||||
|
<Button
|
||||||
|
onClick={(): void =>
|
||||||
|
clientStateDispatch({
|
||||||
|
action: ActionType.ClearClient,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
>
|
||||||
|
Clear my default client
|
||||||
|
</Button>
|
||||||
|
) : null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="advanced">
|
||||||
|
{options}
|
||||||
|
<ClientList link={link} rememberSelection={rememberSelection} />
|
||||||
|
{clearSelection}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ClientSelection;
|
75
src/components/ClientTile.scss
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
/*
|
||||||
|
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 '../color-scheme';
|
||||||
|
|
||||||
|
.clientTile {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: flex-start;
|
||||||
|
|
||||||
|
min-height: 150px;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
color: $foreground;
|
||||||
|
|
||||||
|
> img {
|
||||||
|
flex-shrink: 0;
|
||||||
|
height: 116px;
|
||||||
|
width: 116px;
|
||||||
|
margin-right: 14px;
|
||||||
|
border-radius: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
> div {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
h1 {
|
||||||
|
text-align: left;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin-right: 8px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button {
|
||||||
|
height: 40px;
|
||||||
|
width: 130px;
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
border-radius: 8px;
|
||||||
|
|
||||||
|
padding: 15px;
|
||||||
|
|
||||||
|
// For the chevron
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: $app-background;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.clientTileLink {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
}
|
78
src/components/ClientTile.tsx
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
/*
|
||||||
|
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 React from 'react';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
|
import { Client, ClientKind } from '../clients/types';
|
||||||
|
import { SafeLink } from '../parser/types';
|
||||||
|
import Tile from './Tile';
|
||||||
|
import Button from './Button';
|
||||||
|
|
||||||
|
import './ClientTile.scss';
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
client: Client;
|
||||||
|
link: SafeLink;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ClientTile: React.FC<IProps> = ({ client, link }: IProps) => {
|
||||||
|
const inviteLine =
|
||||||
|
client.kind === ClientKind.TEXT_CLIENT ? (
|
||||||
|
<p>{client.toInviteString(link)}</p>
|
||||||
|
) : null;
|
||||||
|
|
||||||
|
const className = classNames('clientTile', {
|
||||||
|
clientTileLink: client.kind === ClientKind.LINKED_CLIENT,
|
||||||
|
});
|
||||||
|
|
||||||
|
let inviteButton: JSX.Element = <></>;
|
||||||
|
if (client.kind === ClientKind.LINKED_CLIENT) {
|
||||||
|
inviteButton = <Button>Accept invite</Button>;
|
||||||
|
} else {
|
||||||
|
const copyString = client.copyString(link);
|
||||||
|
if (copyString !== '') {
|
||||||
|
inviteButton = (
|
||||||
|
<Button
|
||||||
|
onClick={() => navigator.clipboard?.writeText(copyString)}
|
||||||
|
flashChildren="Invite copied"
|
||||||
|
>
|
||||||
|
Copy invite
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let clientTile = (
|
||||||
|
<Tile className={className}>
|
||||||
|
<img src={client.logo} alt={client.name + ' logo'} />
|
||||||
|
<div>
|
||||||
|
<h1>{client.name}</h1>
|
||||||
|
<p>{client.description}</p>
|
||||||
|
{inviteLine}
|
||||||
|
{inviteButton}
|
||||||
|
</div>
|
||||||
|
</Tile>
|
||||||
|
);
|
||||||
|
|
||||||
|
if (client.kind === ClientKind.LINKED_CLIENT) {
|
||||||
|
clientTile = <a href={client.toUrl(link).toString()}>{clientTile}</a>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return clientTile;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ClientTile;
|
89
src/components/CreateLinkTile.scss
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
/*
|
||||||
|
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 '../color-scheme';
|
||||||
|
|
||||||
|
.createLinkTile {
|
||||||
|
background: none;
|
||||||
|
|
||||||
|
row-gap: 24px;
|
||||||
|
|
||||||
|
* {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
> form {
|
||||||
|
display: grid;
|
||||||
|
row-gap: 24px;
|
||||||
|
align-self: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
> a {
|
||||||
|
color: $foreground;
|
||||||
|
}
|
||||||
|
|
||||||
|
.createLinkReset {
|
||||||
|
height: 40px;
|
||||||
|
width: 40px;
|
||||||
|
|
||||||
|
border-radius: 100%;
|
||||||
|
border: 1px solid lighten($grey, 50%);
|
||||||
|
|
||||||
|
background: $background;
|
||||||
|
|
||||||
|
padding: 6px;
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
> div {
|
||||||
|
// This is a terrible case of faking it till
|
||||||
|
// we make it. It will break. I'm so sorry
|
||||||
|
position: absolute;
|
||||||
|
display: none;
|
||||||
|
|
||||||
|
width: max-content;
|
||||||
|
top: -35px;
|
||||||
|
left: -17px;
|
||||||
|
|
||||||
|
border-radius: 30px;
|
||||||
|
padding: 5px 15px;
|
||||||
|
|
||||||
|
background: $background;
|
||||||
|
|
||||||
|
word-wrap: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
border: 0;
|
||||||
|
|
||||||
|
filter: invert(12%);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border: 0;
|
||||||
|
|
||||||
|
background: $foreground;
|
||||||
|
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
> div {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
32
src/components/CreateLinkTile.stories.tsx
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
/*
|
||||||
|
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 React from 'react';
|
||||||
|
|
||||||
|
import CreateLinkTile from './CreateLinkTile';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'CreateLinkTile',
|
||||||
|
parameters: {
|
||||||
|
design: {
|
||||||
|
type: 'figma',
|
||||||
|
url:
|
||||||
|
'https://figma.com/file/WSXjCGc1k6FVI093qhlzOP/04-Recieving-share-link?node-id=59%3A1',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Default: React.FC = () => <CreateLinkTile />;
|
156
src/components/CreateLinkTile.tsx
Normal file
|
@ -0,0 +1,156 @@
|
||||||
|
/*
|
||||||
|
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 React, { useEffect, useRef } from 'react';
|
||||||
|
import { Formik, Form } from 'formik';
|
||||||
|
|
||||||
|
import Tile from './Tile';
|
||||||
|
import Button from './Button';
|
||||||
|
import Input from './Input';
|
||||||
|
import { parseHash } from '../parser/parser';
|
||||||
|
import { LinkKind } from '../parser/types';
|
||||||
|
import linkIcon from '../imgs/link.svg';
|
||||||
|
import copyIcon from '../imgs/copy.svg';
|
||||||
|
import tickIcon from '../imgs/tick.svg';
|
||||||
|
import refreshIcon from '../imgs/refresh.svg';
|
||||||
|
import './CreateLinkTile.scss';
|
||||||
|
|
||||||
|
interface ILinkNotCreatedTileProps {
|
||||||
|
setLink: React.Dispatch<React.SetStateAction<string>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FormValues {
|
||||||
|
identifier: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hacky use of types here
|
||||||
|
function validate(values: FormValues): Partial<FormValues> {
|
||||||
|
const errors: Partial<FormValues> = {};
|
||||||
|
|
||||||
|
if (values.identifier === '') {
|
||||||
|
errors.identifier = '';
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
const parse = parseHash(values.identifier);
|
||||||
|
|
||||||
|
if (parse.kind === LinkKind.ParseFailed) {
|
||||||
|
errors.identifier =
|
||||||
|
"That identifier doesn't look right. Double check the details.";
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
const LinkNotCreatedTile: React.FC<ILinkNotCreatedTileProps> = (
|
||||||
|
props: ILinkNotCreatedTileProps
|
||||||
|
) => {
|
||||||
|
return (
|
||||||
|
<Tile className="createLinkTile">
|
||||||
|
<h1>
|
||||||
|
Create shareable links to Matrix rooms, users or messages
|
||||||
|
without being tied to any app
|
||||||
|
</h1>
|
||||||
|
<Formik
|
||||||
|
initialValues={{
|
||||||
|
identifier: '',
|
||||||
|
}}
|
||||||
|
validate={validate}
|
||||||
|
onSubmit={(values): void => {
|
||||||
|
props.setLink(
|
||||||
|
document.location.protocol +
|
||||||
|
'//' +
|
||||||
|
document.location.host +
|
||||||
|
'/#/' +
|
||||||
|
values.identifier
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{(formik): JSX.Element => (
|
||||||
|
<Form>
|
||||||
|
<Input
|
||||||
|
name={'identifier'}
|
||||||
|
type={'text'}
|
||||||
|
placeholder="#room:example.com, @user:example.com"
|
||||||
|
autoFocus
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
icon={linkIcon}
|
||||||
|
disabled={!!formik.errors.identifier}
|
||||||
|
className={
|
||||||
|
formik.errors.identifier ? 'errorButton' : ''
|
||||||
|
}
|
||||||
|
>
|
||||||
|
Create Link
|
||||||
|
</Button>
|
||||||
|
</Form>
|
||||||
|
)}
|
||||||
|
</Formik>
|
||||||
|
</Tile>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
interface ILinkCreatedTileProps {
|
||||||
|
link: string;
|
||||||
|
setLink: React.Dispatch<React.SetStateAction<string>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const LinkCreatedTile: React.FC<ILinkCreatedTileProps> = (props) => {
|
||||||
|
const buttonRef = useRef<HTMLButtonElement>(null);
|
||||||
|
|
||||||
|
// Focus button on render
|
||||||
|
useEffect((): void => {
|
||||||
|
if (buttonRef && buttonRef.current) {
|
||||||
|
buttonRef.current.focus();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Tile className="createLinkTile">
|
||||||
|
<button
|
||||||
|
className="createLinkReset"
|
||||||
|
onClick={(): void => props.setLink('')}
|
||||||
|
>
|
||||||
|
<div>New link</div>
|
||||||
|
<img src={refreshIcon} alt="Go back to matrix.to home page" />
|
||||||
|
</button>
|
||||||
|
<h1 className="linkHeader matrixIdentifier">{props.link}</h1>
|
||||||
|
<Button
|
||||||
|
flashChildren={'Copied'}
|
||||||
|
icon={copyIcon}
|
||||||
|
flashIcon={tickIcon}
|
||||||
|
onClick={(): void => {
|
||||||
|
navigator.clipboard?.writeText(props.link);
|
||||||
|
}}
|
||||||
|
ref={buttonRef}
|
||||||
|
>
|
||||||
|
Copy Link
|
||||||
|
</Button>
|
||||||
|
</Tile>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const CreateLinkTile: React.FC = () => {
|
||||||
|
const [link, setLink] = React.useState('');
|
||||||
|
if (!link) {
|
||||||
|
return <LinkNotCreatedTile setLink={setLink} />;
|
||||||
|
} else {
|
||||||
|
return <LinkCreatedTile link={link} setLink={setLink} />;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CreateLinkTile;
|
22
src/components/DefaultPreview.scss
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
/*
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.defaultPreview {
|
||||||
|
.avatar {
|
||||||
|
border-radius: 0;
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
}
|
42
src/components/DefaultPreview.tsx
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
/*
|
||||||
|
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 React from 'react';
|
||||||
|
|
||||||
|
import { SafeLink } from '../parser/types';
|
||||||
|
import Avatar from './Avatar';
|
||||||
|
|
||||||
|
import './DefaultPreview.scss';
|
||||||
|
|
||||||
|
import genericRoomPreview from '../imgs/chat-icon.svg';
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
link: SafeLink;
|
||||||
|
}
|
||||||
|
|
||||||
|
const DefaultPreview: React.FC<IProps> = ({ link }: IProps) => {
|
||||||
|
return (
|
||||||
|
<div className="defaultPreview">
|
||||||
|
<Avatar
|
||||||
|
avatarUrl={genericRoomPreview}
|
||||||
|
label={`Generic icon representing ${link.identifier}`}
|
||||||
|
/>
|
||||||
|
<h1 className="matrixIdentifier">{link.identifier}</h1>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DefaultPreview;
|
35
src/components/EventPreview.tsx
Normal 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 React from 'react';
|
||||||
|
import { Room, Event } from '../matrix-cypher';
|
||||||
|
|
||||||
|
import RoomPreview from './RoomPreview';
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
room: Room;
|
||||||
|
event: Event;
|
||||||
|
}
|
||||||
|
|
||||||
|
const EventPreview: React.FC<IProps> = ({ room, event }: IProps) => (
|
||||||
|
<>
|
||||||
|
<RoomPreview room={room} />
|
||||||
|
<p>"{event.content}"</p>
|
||||||
|
<p>{event.sender}</p>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default EventPreview;
|
31
src/components/FakeProgress.scss
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
/*
|
||||||
|
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 '../color-scheme';
|
||||||
|
|
||||||
|
.fakeProgress {
|
||||||
|
width: 100%;
|
||||||
|
height: 4px;
|
||||||
|
background-color: lighten($grey, 50%);
|
||||||
|
border-radius: 4px;
|
||||||
|
|
||||||
|
> div {
|
||||||
|
width: 60%;
|
||||||
|
height: 100%;
|
||||||
|
background-color: $foreground;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
}
|
27
src/components/FakeProgress.tsx
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 React from 'react';
|
||||||
|
|
||||||
|
import './FakeProgress.scss';
|
||||||
|
|
||||||
|
const FakeProgress = () => (
|
||||||
|
<div className="fakeProgress">
|
||||||
|
<div />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default FakeProgress;
|
|
@ -14,12 +14,20 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {ViewModel} from "./utils/ViewModel.js";
|
@import '../color-scheme';
|
||||||
import {tryFixUrl} from "./Link.js";
|
|
||||||
|
|
||||||
export class InvalidUrlViewModel extends ViewModel {
|
.footer {
|
||||||
constructor(options) {
|
display: grid;
|
||||||
super(options);
|
grid-auto-flow: column;
|
||||||
this.validFixes = tryFixUrl(options.fragment);
|
justify-content: center;
|
||||||
|
column-gap: 5px;
|
||||||
|
|
||||||
|
* {
|
||||||
|
color: $font;
|
||||||
|
}
|
||||||
|
|
||||||
|
.textButton {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
65
src/components/Footer.tsx
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
/*
|
||||||
|
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 React, { useContext } from 'react';
|
||||||
|
|
||||||
|
import HSContext, {
|
||||||
|
HSOptions,
|
||||||
|
ActionType as HSACtionType,
|
||||||
|
} from '../contexts/HSContext';
|
||||||
|
import ClientContext, {
|
||||||
|
ActionType as ClientActionType,
|
||||||
|
} from '../contexts/ClientContext';
|
||||||
|
import TextButton from './TextButton';
|
||||||
|
|
||||||
|
import './Footer.scss';
|
||||||
|
|
||||||
|
const Footer: React.FC = () => {
|
||||||
|
const [hsState, hsDispatch] = useContext(HSContext);
|
||||||
|
const [clientState, clientDispatch] = useContext(ClientContext);
|
||||||
|
|
||||||
|
const clear =
|
||||||
|
hsState.option !== HSOptions.Unset || clientState.clientId !== null ? (
|
||||||
|
<>
|
||||||
|
{' · '}
|
||||||
|
<TextButton
|
||||||
|
onClick={(): void => {
|
||||||
|
hsDispatch({
|
||||||
|
action: HSACtionType.Clear,
|
||||||
|
});
|
||||||
|
clientDispatch({
|
||||||
|
action: ClientActionType.ClearClient,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Clear preferences
|
||||||
|
</TextButton>
|
||||||
|
</>
|
||||||
|
) : null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="footer">
|
||||||
|
<a href="https://github.com/matrix-org/matrix.to">GitHub</a>
|
||||||
|
{' · '}
|
||||||
|
<a href="https://github.com/matrix-org/matrix.to/tree/main/src/clients">
|
||||||
|
Add your client
|
||||||
|
</a>
|
||||||
|
{clear}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Footer;
|
26
src/components/GroupPreview.scss
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.groupPreview {
|
||||||
|
> .avatar {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
> h1 {
|
||||||
|
font-size: 24px;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
}
|
45
src/components/GroupPreview.tsx
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
/*
|
||||||
|
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 React from 'react';
|
||||||
|
import { Group } from '../matrix-cypher';
|
||||||
|
|
||||||
|
import { GroupAvatar } from './Avatar';
|
||||||
|
|
||||||
|
import './GroupPreview.scss';
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
group: Group;
|
||||||
|
groupId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const GroupPreview: React.FC<IProps> = ({ group, groupId }: IProps) => {
|
||||||
|
const description = group.long_description
|
||||||
|
? group.long_description
|
||||||
|
: group.short_description
|
||||||
|
? group.short_description
|
||||||
|
: null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="groupPreview">
|
||||||
|
<GroupAvatar group={group} groupId={groupId}/>
|
||||||
|
<h1>{group.name}</h1>
|
||||||
|
{description ? <p>{description}</p> : null}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default GroupPreview;
|
57
src/components/HomeserverOptions.scss
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
/*
|
||||||
|
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 '../color-scheme';
|
||||||
|
|
||||||
|
.homeserverOptions {
|
||||||
|
display: grid;
|
||||||
|
row-gap: 20px;
|
||||||
|
|
||||||
|
background: $app-background;
|
||||||
|
text-align: left;
|
||||||
|
|
||||||
|
> * {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.homeserverOptionsDescription {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
> p {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
> img {
|
||||||
|
flex-shrink: 0;
|
||||||
|
flex-grow: 0;
|
||||||
|
background-color: $background;
|
||||||
|
height: 62px;
|
||||||
|
width: 62px;
|
||||||
|
padding: 11px;
|
||||||
|
border-radius: 100%;
|
||||||
|
margin-left: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
form {
|
||||||
|
display: grid;
|
||||||
|
row-gap: 25px;
|
||||||
|
}
|
||||||
|
}
|
42
src/components/HomeserverOptions.stories.tsx
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
/*
|
||||||
|
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 React from 'react';
|
||||||
|
|
||||||
|
import HomeserverOptions from './HomeserverOptions';
|
||||||
|
import { LinkKind } from '../parser/types';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'HomeserverOptions',
|
||||||
|
parameters: {
|
||||||
|
design: {
|
||||||
|
type: 'figma',
|
||||||
|
url:
|
||||||
|
'https://figma.com/file/WSXjCGc1k6FVI093qhlzOP/04-Recieving-share-link?node-id=143%3A5853',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Default: React.FC = () => (
|
||||||
|
<HomeserverOptions
|
||||||
|
link={{
|
||||||
|
identifier: '#banter:matrix.org',
|
||||||
|
arguments: { vias: [], originalParams: new URLSearchParams() },
|
||||||
|
kind: LinkKind.Alias,
|
||||||
|
originalLink: 'This is all made up',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
130
src/components/HomeserverOptions.tsx
Normal file
|
@ -0,0 +1,130 @@
|
||||||
|
/*
|
||||||
|
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 React, { useContext, useState } from 'react';
|
||||||
|
import { Formik, Form } from 'formik';
|
||||||
|
import { string } from 'zod';
|
||||||
|
|
||||||
|
import Tile from './Tile';
|
||||||
|
import HSContext, { TempHSContext, ActionType } from '../contexts/HSContext';
|
||||||
|
import icon from '../imgs/telecom-mast.svg';
|
||||||
|
import Button from './Button';
|
||||||
|
import Input from './Input';
|
||||||
|
import StyledCheckbox from './StyledCheckbox';
|
||||||
|
import { SafeLink } from '../parser/types';
|
||||||
|
|
||||||
|
import './HomeserverOptions.scss';
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
link: SafeLink;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FormValues {
|
||||||
|
HSUrl: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
function validateURL(values: FormValues): Partial<FormValues> {
|
||||||
|
const errors: Partial<FormValues> = {};
|
||||||
|
try {
|
||||||
|
string().url().parse(values.HSUrl);
|
||||||
|
} catch {
|
||||||
|
errors.HSUrl =
|
||||||
|
'This must be a valid homeserver URL, starting with https://';
|
||||||
|
}
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
const HomeserverOptions: React.FC<IProps> = ({ link }: IProps) => {
|
||||||
|
const HSStateDispatcher = useContext(HSContext)[1];
|
||||||
|
const TempHSStateDispatcher = useContext(TempHSContext)[1];
|
||||||
|
|
||||||
|
const [rememberSelection, setRemeberSelection] = useState(false);
|
||||||
|
|
||||||
|
// Select which disaptcher to use based on whether we're writing
|
||||||
|
// the choice to localstorage
|
||||||
|
const dispatcher = rememberSelection
|
||||||
|
? HSStateDispatcher
|
||||||
|
: TempHSStateDispatcher;
|
||||||
|
|
||||||
|
const hsInput = (
|
||||||
|
<Formik
|
||||||
|
initialValues={{
|
||||||
|
HSUrl: '',
|
||||||
|
}}
|
||||||
|
validate={validateURL}
|
||||||
|
onSubmit={({ HSUrl }): void =>
|
||||||
|
dispatcher({ action: ActionType.SetHS, HSURL: HSUrl })
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{({ values, errors }): JSX.Element => (
|
||||||
|
<Form>
|
||||||
|
<Input
|
||||||
|
muted={!values.HSUrl}
|
||||||
|
type="text"
|
||||||
|
name="HSUrl"
|
||||||
|
placeholder="Preferred homeserver URL"
|
||||||
|
/>
|
||||||
|
{values.HSUrl && !errors.HSUrl ? (
|
||||||
|
<Button secondary type="submit">
|
||||||
|
Use {values.HSUrl}
|
||||||
|
</Button>
|
||||||
|
) : null}
|
||||||
|
</Form>
|
||||||
|
)}
|
||||||
|
</Formik>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Tile className="homeserverOptions">
|
||||||
|
<div className="homeserverOptionsDescription">
|
||||||
|
<div>
|
||||||
|
<h3>
|
||||||
|
About
|
||||||
|
<span className="matrixIdentifier">
|
||||||
|
{link.identifier}
|
||||||
|
</span>
|
||||||
|
</h3>
|
||||||
|
<p>
|
||||||
|
A homeserver will show you metadata about the link, like
|
||||||
|
a description. Homeservers will be able to relate your
|
||||||
|
IP to things you've opened invites for in matrix.to.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<img
|
||||||
|
src={icon}
|
||||||
|
alt="Icon making it clear that connections may be made with external services"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<StyledCheckbox
|
||||||
|
checked={rememberSelection}
|
||||||
|
onChange={(e): void => setRemeberSelection(e.target.checked)}
|
||||||
|
>
|
||||||
|
Remember my choice
|
||||||
|
</StyledCheckbox>
|
||||||
|
<Button
|
||||||
|
secondary
|
||||||
|
onClick={(): void => {
|
||||||
|
dispatcher({ action: ActionType.SetAny });
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Use any homeserver
|
||||||
|
</Button>
|
||||||
|
{hsInput}
|
||||||
|
</Tile>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default HomeserverOptions;
|
50
src/components/Input.scss
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
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 '../color-scheme';
|
||||||
|
@import '../error';
|
||||||
|
|
||||||
|
.input {
|
||||||
|
width: 100%;
|
||||||
|
padding: 12px;
|
||||||
|
|
||||||
|
background: $background;
|
||||||
|
|
||||||
|
border: 1px solid $foreground;
|
||||||
|
font: lighten($grey, 60%);
|
||||||
|
border-radius: 24px;
|
||||||
|
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 24px;
|
||||||
|
|
||||||
|
&.error {
|
||||||
|
@include error;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
border: 1px solid $font;
|
||||||
|
font: $font;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.inputError {
|
||||||
|
@include error;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inputMuted {
|
||||||
|
border-color: lighten($grey, 60%);
|
||||||
|
}
|
45
src/components/Input.stories.tsx
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
/*
|
||||||
|
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 React from 'react';
|
||||||
|
import { withDesign } from 'storybook-addon-designs';
|
||||||
|
import { Formik, Form } from 'formik';
|
||||||
|
|
||||||
|
import Input from './Input';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'Input',
|
||||||
|
parameters: {
|
||||||
|
design: {
|
||||||
|
type: 'figma',
|
||||||
|
url:
|
||||||
|
'https://figma.com/file/WSXjCGc1k6FVI093qhlzOP/04-Recieving-share-link?node-id=59%3A1',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
decorators: [withDesign],
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Default: React.FC = () => (
|
||||||
|
<Formik initialValues={{}} onSubmit={(): void => {}}>
|
||||||
|
<Form>
|
||||||
|
<Input
|
||||||
|
name="Example input"
|
||||||
|
type="text"
|
||||||
|
placeholder="Write something"
|
||||||
|
/>
|
||||||
|
</Form>
|
||||||
|
</Formik>
|
||||||
|
);
|
50
src/components/Input.tsx
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
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 React from 'react';
|
||||||
|
import classnames from 'classnames';
|
||||||
|
import { useField } from 'formik';
|
||||||
|
|
||||||
|
import './Input.scss';
|
||||||
|
|
||||||
|
interface IProps extends React.InputHTMLAttributes<HTMLElement> {
|
||||||
|
name: string;
|
||||||
|
type: string;
|
||||||
|
muted?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Input: React.FC<IProps> = ({ className, muted, ...props }) => {
|
||||||
|
const [field, meta] = useField(props);
|
||||||
|
|
||||||
|
const errorBool = meta.touched && meta.value !== '' && meta.error;
|
||||||
|
const error = errorBool ? (
|
||||||
|
<div className="inputError">{meta.error}</div>
|
||||||
|
) : null;
|
||||||
|
|
||||||
|
const classNames = classnames('input', className, {
|
||||||
|
error: errorBool,
|
||||||
|
inputMuted: !!muted,
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<input type="text" className={classNames} {...field} {...props} />
|
||||||
|
{error}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Input;
|
39
src/components/InviteTile.scss
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
/*
|
||||||
|
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 '../color-scheme';
|
||||||
|
|
||||||
|
.inviteTile {
|
||||||
|
display: grid;
|
||||||
|
row-gap: 24px;
|
||||||
|
|
||||||
|
.inviteTileClientSelection {
|
||||||
|
margin: 0 auto;
|
||||||
|
display: grid;
|
||||||
|
|
||||||
|
justify-content: space-between;
|
||||||
|
row-gap: 20px;
|
||||||
|
|
||||||
|
h2 + p {
|
||||||
|
color: $foreground;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hr {
|
||||||
|
width: 100%;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
123
src/components/InviteTile.stories.tsx
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
/*
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// disable camelcase check because our object keys come
|
||||||
|
// from the matrix spec
|
||||||
|
/* eslint-disable @typescript-eslint/camelcase */
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import InviteTile from './InviteTile';
|
||||||
|
import UserPreview, { InviterPreview } from './UserPreview';
|
||||||
|
import RoomPreview, { RoomPreviewWithTopic } from './RoomPreview';
|
||||||
|
import Clients from '../clients';
|
||||||
|
import { LinkKind, SafeLink } from '../parser/types';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'InviteTile',
|
||||||
|
parameters: {
|
||||||
|
design: {
|
||||||
|
type: 'figma',
|
||||||
|
url:
|
||||||
|
'https://figma.com/file/WSXjCGc1k6FVI093qhlzOP/04-Recieving-share-link?node-id=59%3A334',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const userLink: SafeLink = {
|
||||||
|
kind: LinkKind.UserId,
|
||||||
|
identifier: '@jorik:matrix.org',
|
||||||
|
arguments: {
|
||||||
|
vias: [],
|
||||||
|
originalParams: new URLSearchParams(),
|
||||||
|
},
|
||||||
|
originalLink: 'asdfsadf',
|
||||||
|
};
|
||||||
|
|
||||||
|
const roomLink: SafeLink = {
|
||||||
|
kind: LinkKind.Alias,
|
||||||
|
identifier: '#element-dev:matrix.org',
|
||||||
|
arguments: {
|
||||||
|
vias: [],
|
||||||
|
originalParams: new URLSearchParams(),
|
||||||
|
},
|
||||||
|
originalLink: 'asdfsadf',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const withLink: React.FC<{}> = () => (
|
||||||
|
<InviteTile client={Clients[0]} link={userLink}>
|
||||||
|
This is an invite with a link
|
||||||
|
</InviteTile>
|
||||||
|
);
|
||||||
|
|
||||||
|
export const withInstruction: React.FC<{}> = () => (
|
||||||
|
<InviteTile client={Clients[0]} link={userLink}>
|
||||||
|
This is an invite with an instruction
|
||||||
|
</InviteTile>
|
||||||
|
);
|
||||||
|
|
||||||
|
export const withUserPreview: React.FC<{}> = () => (
|
||||||
|
<InviteTile client={Clients[0]} link={userLink}>
|
||||||
|
<UserPreview
|
||||||
|
user={{
|
||||||
|
avatar_url: 'mxc://matrix.org/EqMZYbAYhREvHXvYFyfxOlkf',
|
||||||
|
displayname: 'Nicholas Briteli',
|
||||||
|
}}
|
||||||
|
userId="@nicholasbritelli:matrix.org"
|
||||||
|
/>
|
||||||
|
</InviteTile>
|
||||||
|
);
|
||||||
|
|
||||||
|
export const withRoomPreviewAndRoomTopic: React.FC<{}> = () => (
|
||||||
|
<InviteTile client={Clients[0]} link={roomLink}>
|
||||||
|
<RoomPreviewWithTopic
|
||||||
|
room={{
|
||||||
|
aliases: ['#murrays:cheese.bar'],
|
||||||
|
avatar_url: 'mxc://bleeker.street/CHEDDARandBRIE',
|
||||||
|
guest_can_join: false,
|
||||||
|
name: 'CHEESE',
|
||||||
|
num_joined_members: 37,
|
||||||
|
room_id: '!ol19s:bleecker.street',
|
||||||
|
topic: 'Tasty tasty cheese',
|
||||||
|
world_readable: true,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</InviteTile>
|
||||||
|
);
|
||||||
|
|
||||||
|
export const withRoomPreviewAndInviter: React.FC<{}> = () => (
|
||||||
|
<InviteTile client={Clients[0]} link={roomLink}>
|
||||||
|
<InviterPreview
|
||||||
|
user={{
|
||||||
|
avatar_url: 'mxc://matrix.org/EqMZYbAYhREvHXvYFyfxOlkf',
|
||||||
|
displayname: 'Nicholas Briteli',
|
||||||
|
}}
|
||||||
|
userId="@nicholasbritelli:matrix.org"
|
||||||
|
/>
|
||||||
|
<RoomPreview
|
||||||
|
room={{
|
||||||
|
aliases: ['#murrays:cheese.bar'],
|
||||||
|
avatar_url: 'mxc://bleeker.street/CHEDDARandBRIE',
|
||||||
|
guest_can_join: false,
|
||||||
|
name: 'CHEESE',
|
||||||
|
num_joined_members: 37,
|
||||||
|
room_id: '!ol19s:bleecker.street',
|
||||||
|
topic: 'Tasty tasty cheese',
|
||||||
|
world_readable: true,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</InviteTile>
|
||||||
|
);
|
120
src/components/InviteTile.tsx
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
/*
|
||||||
|
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 React, { useState } from 'react';
|
||||||
|
|
||||||
|
import './InviteTile.scss';
|
||||||
|
|
||||||
|
import Tile from './Tile';
|
||||||
|
import LinkButton from './LinkButton';
|
||||||
|
import Button from './Button';
|
||||||
|
import ClientSelection from './ClientSelection';
|
||||||
|
import { Client, ClientKind } from '../clients/types';
|
||||||
|
import { SafeLink } from '../parser/types';
|
||||||
|
import TextButton from './TextButton';
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
children?: React.ReactNode;
|
||||||
|
client: Client | null;
|
||||||
|
link: SafeLink;
|
||||||
|
}
|
||||||
|
|
||||||
|
const InviteTile: React.FC<IProps> = ({ children, client, link }: IProps) => {
|
||||||
|
const [showAdvanced, setShowAdvanced] = useState(false);
|
||||||
|
let invite: React.ReactNode;
|
||||||
|
let advanced: React.ReactNode;
|
||||||
|
|
||||||
|
if (client === null) {
|
||||||
|
invite = showAdvanced ? null : (
|
||||||
|
<Button onClick={(): void => setShowAdvanced(!showAdvanced)}>
|
||||||
|
Accept invite
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
let inviteUseString: string;
|
||||||
|
|
||||||
|
switch (client.kind) {
|
||||||
|
case ClientKind.LINKED_CLIENT:
|
||||||
|
invite = (
|
||||||
|
<LinkButton href={client.toUrl(link).toString()}>
|
||||||
|
Accept invite
|
||||||
|
</LinkButton>
|
||||||
|
);
|
||||||
|
inviteUseString = `Accepting will open this link in ${client.name}.`;
|
||||||
|
break;
|
||||||
|
case ClientKind.TEXT_CLIENT:
|
||||||
|
// TODO: copy to clipboard
|
||||||
|
invite = <p>{client.toInviteString(link)}</p>;
|
||||||
|
navigator.clipboard?.writeText(client.copyString(link));
|
||||||
|
inviteUseString = `These are instructions for ${client.name}.`;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const advancedToggle = (
|
||||||
|
<p>
|
||||||
|
{inviteUseString}
|
||||||
|
<TextButton
|
||||||
|
onClick={(): void => setShowAdvanced(!showAdvanced)}
|
||||||
|
>
|
||||||
|
Change client
|
||||||
|
</TextButton>
|
||||||
|
</p>
|
||||||
|
);
|
||||||
|
|
||||||
|
invite = (
|
||||||
|
<>
|
||||||
|
{invite}
|
||||||
|
{advancedToggle}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (showAdvanced) {
|
||||||
|
if (client === null) {
|
||||||
|
advanced = (
|
||||||
|
<>
|
||||||
|
<hr />
|
||||||
|
<h2>Almost done!</h2>
|
||||||
|
<p>Great, pick a client below to confirm and continue</p>
|
||||||
|
<ClientSelection link={link} />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
advanced = (
|
||||||
|
<>
|
||||||
|
<hr />
|
||||||
|
<h4>Change app</h4>
|
||||||
|
<ClientSelection link={link} />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
advanced = advanced ? (
|
||||||
|
<div className="inviteTileClientSelection">{advanced}</div>
|
||||||
|
) : null;
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Tile className="inviteTile">
|
||||||
|
{children}
|
||||||
|
{invite}
|
||||||
|
{advanced}
|
||||||
|
</Tile>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default InviteTile;
|
23
src/components/InvitingClientTile.stories.tsx
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
/*
|
||||||
|
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 React from 'react';
|
||||||
|
|
||||||
|
import ClientTile from './InvitingClientTile';
|
||||||
|
|
||||||
|
export default { title: 'ClientTile' };
|
||||||
|
|
||||||
|
export const Element = <ClientTile clientName={'element.io'} />;
|
58
src/components/InvitingClientTile.tsx
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
/*
|
||||||
|
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 React from 'react';
|
||||||
|
import Tile from './Tile';
|
||||||
|
|
||||||
|
import { clientMap } from '../clients';
|
||||||
|
import './MatrixTile.scss';
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
clientName: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const InvitingClientTile: React.FC<IProps> = ({ clientName }: IProps) => {
|
||||||
|
const client = clientMap[clientName];
|
||||||
|
|
||||||
|
if (!client) {
|
||||||
|
return (
|
||||||
|
<Tile className="matrixTile">
|
||||||
|
{/* TODO: add gh link */}
|
||||||
|
<p>
|
||||||
|
The client that created this link "{clientName}" is not a
|
||||||
|
recognised client. If this is a mistake and you'd like a
|
||||||
|
nice advertisement for it here please{' '}
|
||||||
|
<a href="https://github.com/matrix-org/matrix.to">
|
||||||
|
open a pr
|
||||||
|
</a>
|
||||||
|
.
|
||||||
|
</p>
|
||||||
|
</Tile>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Tile className="matrixTile">
|
||||||
|
<img src={client.logo} alt={client.name} />
|
||||||
|
<h2>
|
||||||
|
Invite created with <a href={client.homepage}>{client.name}</a>
|
||||||
|
</h2>
|
||||||
|
<div>{client.description}</div>
|
||||||
|
</Tile>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default InvitingClientTile;
|
34
src/components/LinkButton.tsx
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 React from 'react';
|
||||||
|
import classnames from 'classnames';
|
||||||
|
|
||||||
|
import './Button.scss';
|
||||||
|
|
||||||
|
interface IProps extends React.LinkHTMLAttributes<HTMLElement> {}
|
||||||
|
|
||||||
|
const LinkButton: React.FC<IProps> = ({
|
||||||
|
className,
|
||||||
|
children,
|
||||||
|
...props
|
||||||
|
}: IProps) => (
|
||||||
|
<a className={classnames('button', className)} {...props}>
|
||||||
|
{children}
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default LinkButton;
|
193
src/components/LinkPreview.tsx
Normal file
|
@ -0,0 +1,193 @@
|
||||||
|
/*
|
||||||
|
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 React, { useState, useEffect, useContext } from 'react';
|
||||||
|
import { getEvent, client } from '../matrix-cypher';
|
||||||
|
|
||||||
|
import { RoomPreviewWithTopic } from './RoomPreview';
|
||||||
|
import InviteTile from './InviteTile';
|
||||||
|
import { SafeLink, LinkKind } from '../parser/types';
|
||||||
|
import UserPreview, { WrappedInviterPreview } from './UserPreview';
|
||||||
|
import EventPreview from './EventPreview';
|
||||||
|
import GroupPreview from './GroupPreview';
|
||||||
|
import HomeserverOptions from './HomeserverOptions';
|
||||||
|
import DefaultPreview from './DefaultPreview';
|
||||||
|
import Toggle from './Toggle';
|
||||||
|
import { clientMap } from '../clients';
|
||||||
|
import {
|
||||||
|
getRoomFromId,
|
||||||
|
getRoomFromAlias,
|
||||||
|
getRoomFromPermalink,
|
||||||
|
getUser,
|
||||||
|
getGroup,
|
||||||
|
} from '../utils/cypher-wrapper';
|
||||||
|
import { ClientContext } from '../contexts/ClientContext';
|
||||||
|
import useHSs from '../utils/getHS';
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
link: SafeLink;
|
||||||
|
}
|
||||||
|
|
||||||
|
const invite = async ({
|
||||||
|
clientAddress,
|
||||||
|
link,
|
||||||
|
}: {
|
||||||
|
clientAddress: string;
|
||||||
|
link: SafeLink;
|
||||||
|
}): Promise<JSX.Element> => {
|
||||||
|
// TODO: replace with client fetch
|
||||||
|
switch (link.kind) {
|
||||||
|
case LinkKind.Alias:
|
||||||
|
return (
|
||||||
|
<RoomPreviewWithTopic
|
||||||
|
room={
|
||||||
|
await getRoomFromAlias(clientAddress, link.identifier)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
case LinkKind.RoomId:
|
||||||
|
return (
|
||||||
|
<RoomPreviewWithTopic
|
||||||
|
room={await getRoomFromId(clientAddress, link.identifier)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
case LinkKind.UserId:
|
||||||
|
return (
|
||||||
|
<UserPreview
|
||||||
|
user={await getUser(clientAddress, link.identifier)}
|
||||||
|
userId={link.identifier}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
case LinkKind.Permalink:
|
||||||
|
return (
|
||||||
|
<EventPreview
|
||||||
|
room={await getRoomFromPermalink(clientAddress, link)}
|
||||||
|
event={
|
||||||
|
await getEvent(
|
||||||
|
await client(clientAddress),
|
||||||
|
link.roomLink,
|
||||||
|
link.eventId
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
case LinkKind.GroupId:
|
||||||
|
return (
|
||||||
|
<GroupPreview
|
||||||
|
group={await getGroup(clientAddress, link.identifier)}
|
||||||
|
groupId={link.identifier}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
default:
|
||||||
|
// Todo Implement events
|
||||||
|
return <></>;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
interface PreviewProps extends IProps {
|
||||||
|
client: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Preview: React.FC<PreviewProps> = ({ link, client }: PreviewProps) => {
|
||||||
|
const [content, setContent] = useState(<DefaultPreview link={link} />);
|
||||||
|
|
||||||
|
// TODO: support multiple clients with vias
|
||||||
|
useEffect(() => {
|
||||||
|
(async (): Promise<void> =>
|
||||||
|
setContent(
|
||||||
|
await invite({
|
||||||
|
clientAddress: client,
|
||||||
|
link,
|
||||||
|
})
|
||||||
|
))();
|
||||||
|
}, [link, client]);
|
||||||
|
|
||||||
|
return content;
|
||||||
|
};
|
||||||
|
|
||||||
|
const LinkPreview: React.FC<IProps> = ({ link }: IProps) => {
|
||||||
|
let content: JSX.Element;
|
||||||
|
const [showHSOptions, setShowHSOPtions] = useState(false);
|
||||||
|
|
||||||
|
const hses = useHSs({ link });
|
||||||
|
|
||||||
|
if (!hses.length) {
|
||||||
|
content = (
|
||||||
|
<>
|
||||||
|
<DefaultPreview link={link} />
|
||||||
|
<Toggle
|
||||||
|
checked={showHSOptions}
|
||||||
|
onChange={(): void => setShowHSOPtions(!showHSOptions)}
|
||||||
|
>
|
||||||
|
<span>
|
||||||
|
About
|
||||||
|
<span className="matrixIdentifier">
|
||||||
|
{link.identifier}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</Toggle>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
if (showHSOptions) {
|
||||||
|
content = (
|
||||||
|
<>
|
||||||
|
{content}
|
||||||
|
<HomeserverOptions link={link} />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
content = <Preview link={link} client={hses[0]} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
const [{ clientId }] = useContext(ClientContext);
|
||||||
|
|
||||||
|
// Select which client to link to
|
||||||
|
const displayClientId = clientId
|
||||||
|
? clientId
|
||||||
|
: link.arguments.client
|
||||||
|
? link.arguments.client
|
||||||
|
: null;
|
||||||
|
|
||||||
|
const client = displayClientId ? clientMap[displayClientId] : null;
|
||||||
|
|
||||||
|
const sharer = link.arguments.sharer ? (
|
||||||
|
<WrappedInviterPreview
|
||||||
|
link={{
|
||||||
|
kind: LinkKind.UserId,
|
||||||
|
identifier: link.arguments.sharer,
|
||||||
|
arguments: { vias: [], originalParams: new URLSearchParams() },
|
||||||
|
originalLink: '',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<p style={{ margin: '0 0 10px 0' }}>You're invited to join</p>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<InviteTile client={client} link={link}>
|
||||||
|
{sharer}
|
||||||
|
{content}
|
||||||
|
</InviteTile>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default LinkPreview;
|
22
src/components/MatrixTile.scss
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
/*
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.matrixTile {
|
||||||
|
background: none;
|
||||||
|
row-gap: 8px;
|
||||||
|
|
||||||
|
padding: 0 40px;
|
||||||
|
}
|
23
src/components/MatrixTile.stories.tsx
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
/*
|
||||||
|
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 React from 'react';
|
||||||
|
|
||||||
|
import MatrixTile from './MatrixTile';
|
||||||
|
|
||||||
|
export default { title: 'MatrixTile' };
|
||||||
|
|
||||||
|
export const Default: React.FC = () => <MatrixTile />;
|
51
src/components/MatrixTile.tsx
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
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 React from 'react';
|
||||||
|
import Tile from './Tile';
|
||||||
|
|
||||||
|
import logo from '../imgs/matrix-logo.svg';
|
||||||
|
|
||||||
|
import './MatrixTile.scss';
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
isLink?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const MatrixTile: React.FC<IProps> = ({ isLink }: IProps) => {
|
||||||
|
const copy = isLink ? (
|
||||||
|
<div>
|
||||||
|
This invite uses <a href="https://matrix.org">Matrix</a>, an open
|
||||||
|
network for secure, decentralized communication.
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div>
|
||||||
|
Matrix.to is a stateless URL redirecting service for the{' '}
|
||||||
|
<a href="https://matrix.org">Matrix</a> ecosystem.
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Tile className="matrixTile">
|
||||||
|
<img src={logo} alt="matrix-logo" />
|
||||||
|
{copy}
|
||||||
|
</Tile>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default MatrixTile;
|
|
@ -14,19 +14,17 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export class ConnectionError extends Error {
|
.roomPreview {
|
||||||
constructor(message, isTimeout) {
|
> .avatar {
|
||||||
super(message || "ConnectionError");
|
margin-bottom: 8px;
|
||||||
this.isTimeout = isTimeout;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get name() {
|
> h1 {
|
||||||
return "ConnectionError";
|
font-size: 24px;
|
||||||
|
margin-bottom: 4px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class AbortError extends Error {
|
.roomTopic {
|
||||||
get name() {
|
padding-top: 8px;
|
||||||
return "AbortError";
|
}
|
||||||
}
|
|
||||||
}
|
|
60
src/components/RoomPreview.tsx
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
/*
|
||||||
|
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 React from 'react';
|
||||||
|
import { Room } from '../matrix-cypher';
|
||||||
|
|
||||||
|
import { RoomAvatar } from './Avatar';
|
||||||
|
|
||||||
|
import './RoomPreview.scss';
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
room: Room;
|
||||||
|
}
|
||||||
|
|
||||||
|
const RoomPreview: React.FC<IProps> = ({ room }: IProps) => {
|
||||||
|
const roomAlias = room.canonical_alias
|
||||||
|
? room.canonical_alias
|
||||||
|
: room.aliases
|
||||||
|
? room.aliases[0]
|
||||||
|
: room.room_id;
|
||||||
|
const members =
|
||||||
|
room.num_joined_members > 0 ? (
|
||||||
|
<p>{room.num_joined_members.toLocaleString()} members</p>
|
||||||
|
) : null;
|
||||||
|
return (
|
||||||
|
<div className="roomPreview">
|
||||||
|
<RoomAvatar room={room} />
|
||||||
|
<h1 className="matrixIdentifier">
|
||||||
|
{room.name ? room.name : roomAlias}
|
||||||
|
</h1>
|
||||||
|
{members}
|
||||||
|
<p className="matrixIdentifier">{roomAlias}</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const RoomPreviewWithTopic: React.FC<IProps> = ({ room }: IProps) => {
|
||||||
|
const topic = room.topic ? <p className="roomTopic">{room.topic}</p> : null;
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<RoomPreview room={room} />
|
||||||
|
{topic}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default RoomPreview;
|