1
0
mirror of https://github.com/matrix-org/matrix-authentication-service.git synced 2025-11-19 00:26:27 +03:00

Add instance privacy policy, TOS and imprint, and loads of design cleanups

This commit is contained in:
Quentin Gliech
2023-10-24 19:02:28 +02:00
parent 10e31f03fa
commit 8984cc703b
50 changed files with 1077 additions and 604 deletions

View File

@@ -23,7 +23,7 @@ limitations under the License.
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>matrix-authentication-service</title>
<script type="application/javascript">
window.APP_CONFIG = JSON.parse('{"root": "/account/", "graphqlEndpoint": "/graphql"}');
window.APP_CONFIG = JSON.parse('{"root": "/account/", "graphqlEndpoint": "/graphql", "branding": {}}');
(function () {
const query = window.matchMedia("(prefers-color-scheme: dark)");
function handleChange(list) {

View File

@@ -5,6 +5,16 @@
"continue": "Continue",
"save": "Save"
},
"branding": {
"privacy_policy": {
"alt": "Link to the service privacy policy",
"link": "Privacy Policy"
},
"terms_and_conditions": {
"alt": "Link to the service terms and conditions",
"link": "Terms & Conditions"
}
},
"common": {
"add": "Add",
"error": "Error",

View File

@@ -13,27 +13,28 @@
* limitations under the License.
*/
.container {
box-sizing: border-box;
max-width: calc(378px + var(--cpd-space-6x) * 2);
margin: 0 auto;
padding: var(--cpd-space-6x);
}
.legal-footer {
display: flex;
flex-direction: column;
gap: var(--cpd-space-2x);
.footer {
margin-top: var(--cpd-space-6x);
padding: var(--cpd-space-6x) 0;
border-top: 1px solid var(--cpd-color-border-interactive-secondary);
font: var(--cpd-font-body-sm-regular);
letter-spacing: var(--cpd-font-letter-spacing-body-sm);
& nav {
display: flex;
gap: var(--cpd-space-2x);
align-items: center;
justify-content: center;
text-align: center;
}
.footer-links {
margin: var(--cpd-space-4x) 0;
& > ul {
display: flex;
flex-direction: row;
justify-content: center;
gap: var(--cpd-space-2x);
& .separator {
color: var(--cpd-color-text-secondary);
}
}
& .imprint {
color: var(--cpd-color-text-secondary);
text-align: center;
}
}

View File

@@ -0,0 +1,54 @@
// Copyright 2023 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 type { Meta, StoryObj } from "@storybook/react";
import Footer from "./Footer";
const meta = {
title: "UI/Footer",
component: Footer,
tags: ["autodocs"],
} satisfies Meta<typeof Footer>;
export default meta;
type Story = StoryObj<typeof Footer>;
export const Basic: Story = {
args: {
tosUri: "https://matrix.org/legal/terms-and-conditions/",
policyUri: "https://matrix.org/legal/privacy-notice/",
imprint: "The Matrix.org Foundation C.I.C.",
},
};
export const LinksOnly: Story = {
args: {
tosUri: "https://matrix.org/legal/terms-and-conditions/",
policyUri: "https://matrix.org/legal/privacy-notice/",
},
};
export const ImprintOnly: Story = {
args: {
imprint: "The Matrix.org Foundation C.I.C.",
},
};
export const OneLink: Story = {
args: {
tosUri: "https://matrix.org/legal/terms-and-conditions/",
imprint: "The Matrix.org Foundation C.I.C.",
},
};

View File

@@ -0,0 +1,57 @@
// Copyright 2023 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 "@vector-im/compound-web";
import { useTranslation } from "react-i18next";
import styles from "./Footer.module.css";
type Props = {
policyUri?: string;
tosUri?: string;
imprint?: string;
};
const Footer: React.FC<Props> = ({ policyUri, tosUri, imprint }) => {
const { t } = useTranslation();
return (
<footer className={styles.legalFooter}>
{(policyUri || tosUri) && (
<nav>
{policyUri && (
<Link href={policyUri} title={t("branding.privacy_policy.alt")}>
{t("branding.privacy_policy.link")}
</Link>
)}
{policyUri && tosUri && (
<div className={styles.separator} aria-hidden="true">
</div>
)}
{tosUri && (
<Link href={tosUri} title={t("branding.terms_and_conditions.alt")}>
{t("branding.terms_and_conditions.link")}
</Link>
)}
</nav>
)}
{imprint && <p className={styles.imprint}>{imprint}</p>}
</footer>
);
};
export default Footer;

View File

@@ -0,0 +1,15 @@
// Copyright 2023 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.
export { default } from "./Footer";

View File

@@ -0,0 +1,26 @@
/* Copyright 2023 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.
*/
.layout-container {
box-sizing: border-box;
display: flex;
flex-direction: column;
max-width: calc(420px + var(--cpd-space-6x) * 2);
min-height: 100vh;
margin: 0 auto;
padding: var(--cpd-space-6x);
gap: var(--cpd-space-6x);
}

View File

@@ -17,8 +17,8 @@
import { render } from "@testing-library/react";
import { describe, expect, it, vi, afterAll, beforeEach } from "vitest";
import { currentUserIdAtom, GqlResult } from "../atoms";
import { WithLocation } from "../test-utils/WithLocation";
import { currentUserIdAtom, GqlResult } from "../../atoms";
import { WithLocation } from "../../test-utils/WithLocation";
import Layout from "./Layout";

View File

@@ -15,19 +15,21 @@
import { useAtomValue } from "jotai";
import { useTranslation } from "react-i18next";
import { currentUserIdAtom } from "../atoms";
import { isErr, unwrapErr, unwrapOk } from "../result";
import { routeAtom } from "../routing";
import { currentUserIdAtom } from "../../atoms";
import { isErr, unwrapErr, unwrapOk } from "../../result";
import { appConfigAtom, routeAtom } from "../../routing";
import Footer from "../Footer";
import GraphQLError from "../GraphQLError";
import NavBar from "../NavBar";
import NavItem from "../NavItem";
import NotLoggedIn from "../NotLoggedIn";
import UserGreeting from "../UserGreeting";
import GraphQLError from "./GraphQLError";
import styles from "./Layout.module.css";
import NavBar from "./NavBar";
import NavItem from "./NavItem";
import NotLoggedIn from "./NotLoggedIn";
import UserGreeting from "./UserGreeting";
const Layout: React.FC<{ children?: React.ReactNode }> = ({ children }) => {
const route = useAtomValue(routeAtom);
const appConfig = useAtomValue(appConfigAtom);
const result = useAtomValue(currentUserIdAtom);
const { t } = useTranslation();
@@ -45,7 +47,7 @@ const Layout: React.FC<{ children?: React.ReactNode }> = ({ children }) => {
);
return (
<div className={styles.container}>
<div className={styles.layoutContainer}>
{shouldHideNavBar ? null : (
<>
<UserGreeting userId={userId} />
@@ -63,18 +65,11 @@ const Layout: React.FC<{ children?: React.ReactNode }> = ({ children }) => {
<main>{children}</main>
{/* TODO: the footer needs to be reworked to show configurable info not hardcoded links: https://github.com/matrix-org/matrix-authentication-service/issues/1675 */}
{/* <footer className={styles.footer}>
<nav className={styles.footerLinks}>
<ul>
<Link href="https://matrix.org/legal/copyright-notice">Info</Link>
<Link href="https://matrix.org/legal/privacy-notice">Privacy</Link>
<Link href="https://matrix.org/legal/terms-and-conditions">
Terms & Conditions
</Link>
</ul>
</nav>
</footer> */}
<Footer
imprint={appConfig.branding?.imprint}
tosUri={appConfig.branding?.tosUri}
policyUri={appConfig.branding?.policyUri}
/>
</div>
);
};

View File

@@ -0,0 +1,15 @@
// Copyright 2023 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.
export { default } from "./Layout";

View File

@@ -15,7 +15,6 @@
.nav-bar {
border-bottom: var(--cpd-border-width-1) solid var(--cpd-color-gray-400);
margin: var(--cpd-space-6x) 0;
padding: 0 var(--cpd-space-10x);
}

View File

@@ -13,10 +13,6 @@
* limitations under the License.
*/
.alert {
margin-top: var(--cpd-space-4x);
> * {
box-sizing: content-box;
}
}
.alert > * {
box-sizing: content-box;
}

View File

@@ -15,6 +15,11 @@
export type AppConfig = {
root: string;
graphqlEndpoint: string;
branding?: {
tosUri?: string;
policyUri?: string;
imprint?: string;
};
};
interface IWindow {
@@ -22,6 +27,10 @@ interface IWindow {
}
const config: AppConfig = (typeof window !== "undefined" &&
(window as IWindow).APP_CONFIG) || { root: "/", graphqlEndpoint: "/graphql" };
(window as IWindow).APP_CONFIG) || {
root: "/",
graphqlEndpoint: "/graphql",
branding: {},
};
export default config;

View File

@@ -23,6 +23,9 @@
@import "./styles/cpd-form.css";
@import "./styles/cpd-link.css";
@import "./components/Layout/Layout.module.css";
@import "./components/Footer/Footer.module.css";
@config "../tailwind.templates.config.cjs";
@tailwind base;
@@ -30,92 +33,111 @@
@tailwind utilities;
.cpd-text-body-lg-regular {
font: var(--cpd-font-body-lg-regular);
letter-spacing: var(--cpd-font-letter-spacing-body-lg);
font: var(--cpd-font-body-lg-regular);
letter-spacing: var(--cpd-font-letter-spacing-body-lg);
}
.cpd-text-heading-xl-semibold {
font: var(--cpd-font-heading-xl-semibold);
letter-spacing: var(--cpd-font-letter-spacing-heading-xl);
font: var(--cpd-font-heading-xl-semibold);
letter-spacing: var(--cpd-font-letter-spacing-heading-xl);
}
.cpd-text-body-md-regular {
font: var(--cpd-font-body-md-regular);
letter-spacing: var(--cpd-font-letter-spacing-body-md);
font: var(--cpd-font-body-md-regular);
letter-spacing: var(--cpd-font-letter-spacing-body-md);
}
.cpd-text-primary {
color: var(--cpd-color-text-primary);
color: var(--cpd-color-text-primary);
}
.cpd-text-secondary {
color: var(--cpd-color-text-secondary);
color: var(--cpd-color-text-secondary);
}
.consent-client-icon {
display: block;
display: block;
height: var(--cpd-space-16x);
width: var(--cpd-space-16x);
margin: 0 auto;
&.generic {
background-color: var(--cpd-color-bg-subtle-secondary);
border-radius: var(--cpd-radius-pill-effect);
color: var(--cpd-color-icon-primary);
& svg {
margin: var(--cpd-space-4x);
height: var(--cpd-space-8x);
width: var(--cpd-space-8x);
}
}
&.image {
height: var(--cpd-space-16x);
width: var(--cpd-space-16x);
margin: 0 auto;
&.generic {
background-color: var(--cpd-color-bg-subtle-secondary);
border-radius: var(--cpd-radius-pill-effect);
color: var(--cpd-color-icon-primary);
& svg {
margin: var(--cpd-space-4x);
height: var(--cpd-space-8x);
width: var(--cpd-space-8x);
}
}
&.image {
height: var(--cpd-space-16x);
width: var(--cpd-space-16x);
border-radius: var(--cpd-space-2x);
overflow: hidden;
}
border-radius: var(--cpd-space-2x);
overflow: hidden;
}
}
.consent-scope-list {
--border-radius: var(--cpd-space-4x);
& ul {
display: flex;
flex-direction: column;
gap: var(--cpd-space-1x);
--border-radius: var(--cpd-space-4x);
& ul {
display: flex;
flex-direction: column;
gap: var(--cpd-space-1x);
& > li {
font: var(--cpd-font-body-md-regular);
letter-spacing: var(--cpd-font-letter-spacing-body-md);
color: var(--cpd-color-text-primary);
& > li {
font: var(--cpd-font-body-md-regular);
letter-spacing: var(--cpd-font-letter-spacing-body-md);
color: var(--cpd-color-text-primary);
background-color: var(--cpd-color-bg-subtle-secondary);
padding: var(--cpd-space-3x) var(--cpd-space-5x);
display: flex;
gap: var(--cpd-space-4x);
line-height: var(--cpd-space-6x);
background-color: var(--cpd-color-bg-subtle-secondary);
padding: var(--cpd-space-3x) var(--cpd-space-5x);
display: flex;
gap: var(--cpd-space-4x);
line-height: var(--cpd-space-6x);
&:first-of-type {
border-top-left-radius: var(--border-radius);
border-top-right-radius: var(--border-radius);
}
&:first-of-type {
border-top-left-radius: var(--border-radius);
border-top-right-radius: var(--border-radius);
}
&:last-of-type {
border-bottom-left-radius: var(--border-radius);
border-bottom-right-radius: var(--border-radius);
}
&:last-of-type {
border-bottom-left-radius: var(--border-radius);
border-bottom-right-radius: var(--border-radius);
}
& > p {
flex: 1;
}
& > p {
flex: 1;
}
& > svg {
display: block;
height: var(--cpd-space-6x);
width: var(--cpd-space-6x);
color: var(--cpd-color-icon-quaternary);
}
}
& > svg {
display: block;
height: var(--cpd-space-6x);
width: var(--cpd-space-6x);
color: var(--cpd-color-icon-quaternary);
}
}
}
}
}
.separator {
display: flex;
align-items: center;
& hr {
flex: 1;
border: none;
border-top: 1px solid var(--cpd-color-bg-subtle-primary);
}
& p {
margin-inline: var(--cpd-space-4x);
text-transform: uppercase;
font: var(--cpd-font-body-md-regular);
letter-spacing: var(--cpd-font-letter-spacing-body-md);
color: var(--cpd-color-text-primary);
}
}