Skip to content

Commit

Permalink
/token endpoint rendering page, wip TokenPageLayout remove Storybook …
Browse files Browse the repository at this point in the history
…context to AuthContext
  • Loading branch information
robinkwilson committed Jun 22, 2021
1 parent 3ea3c27 commit 182c2a6
Show file tree
Hide file tree
Showing 8 changed files with 246 additions and 14 deletions.
6 changes: 6 additions & 0 deletions .storybook/main.js
Expand Up @@ -60,6 +60,12 @@ module.exports = {
]
});

config.module.rules.push({
test: /\.(glb|gltf)$/,
use: ["file-loader"],
include: path.resolve(__dirname, "../")
});

return config;
}
};
6 changes: 3 additions & 3 deletions src/react-components/tokens/RevokeTokenModal.js
Expand Up @@ -11,7 +11,7 @@ import { faExclamationTriangle } from "@fortawesome/free-solid-svg-icons/faExcla
import { faTimes } from "@fortawesome/free-solid-svg-icons/faTimes";
import { Column } from "../layout/Column";

export function RevokeTokenModal({ onClose, revoke }) {
export function RevokeTokenModal({ onClose, onRevoke }) {
return (
<Modal
title={<FormattedMessage id="revoke-token-modal.title" defaultMessage="Revoke token" />}
Expand Down Expand Up @@ -51,7 +51,7 @@ export function RevokeTokenModal({ onClose, revoke }) {
<Button preset="basic" sm onClick={onClose}>
<FormattedMessage id="revoke-token-modal.cancel" defaultMessage="Cancel" />
</Button>
<Button preset="accent1" sm onClick={revoke}>
<Button preset="accent1" sm onClick={onRevoke}>
<FormattedMessage id="revoke-token-modal.revoke" defaultMessage="Revoke" />
</Button>
</Row>
Expand All @@ -62,5 +62,5 @@ export function RevokeTokenModal({ onClose, revoke }) {

RevokeTokenModal.propTypes = {
onClose: PropTypes.func.isRequired,
revoke: PropTypes.func.isRequired
onRevoke: PropTypes.func.isRequired
};
31 changes: 20 additions & 11 deletions src/react-components/tokens/Tokens.stories.js
@@ -1,12 +1,12 @@
import React from "react";
import { CenteredModalWrapper } from "../layout/CenteredModalWrapper";
import styles from "./Tokens.scss";
import { TokenList } from "./TokenList";
import { TokenPageLayout } from "./TokenPageLayout";
import { NoAccess } from "./NoAccess";
import { RevokeTokenModal } from "./RevokeTokenModal";
import { RevealTokenModal } from "./RevealTokenModal";
import { CreateToken } from "./CreateToken";
import { StorybookAuthContextProvider } from "../auth/AuthContext";

export default {
title: "Token/Tokens"
Expand Down Expand Up @@ -71,26 +71,35 @@ const dummyTokens = [

const noop = () => {};

// eslint-disable-next-line react/prop-types
const StorybookWrapper = ({ children }) => {
return (
<StorybookAuthContextProvider>
<TokenPageLayout>{children}</TokenPageLayout>
</StorybookAuthContextProvider>
);
};

export const NoAccessPage = () => {
return (
<TokenPageLayout className={styles.backgroundGray}>
<StorybookWrapper>
<NoAccess />
</TokenPageLayout>
</StorybookWrapper>
);
};

// eslint-disable-next-line react/prop-types
export const TokenListPage = ({ children }) => (
<TokenPageLayout>
<StorybookWrapper>
{children}
<TokenList tokens={dummyTokens} onRevokeToken={noop} />
</TokenPageLayout>
</StorybookWrapper>
);

export const EmptyTokenListPage = () => (
<TokenPageLayout>
<StorybookWrapper>
<TokenList tokens={[]} onRevokeToken={noop} />
</TokenPageLayout>
</StorybookWrapper>
);

export const RevokeTokenModalPage = () => (
Expand All @@ -109,17 +118,17 @@ export function ModalRevokeToken() {
const selectedScopes = ["read_rooms", "write_rooms"];
// eslint-disable-next-line react/prop-types
export const CreateTokenPage = ({ children }) => (
<TokenPageLayout>
<StorybookWrapper>
{children}
<CreateToken scopes={scopes} scopeInfo={scopeInfo} selectedScopes={selectedScopes} />
</TokenPageLayout>
</StorybookWrapper>
);

export const ModalSaveTokenPage = () => (
<TokenPageLayout>
<StorybookWrapper>
<CenteredModalWrapper>
<RevealTokenModal onClose={noop} token={{ token: "abcd1234" }} selectedScopes={["write_rooms"]} />
</CenteredModalWrapper>
<TokenList tokens={dummyTokens} onRevokeToken={noop} />
</TokenPageLayout>
</StorybookWrapper>
);
82 changes: 82 additions & 0 deletions src/react-components/tokens/TokensContainer.js
@@ -0,0 +1,82 @@
import React, { useState, useEffect, useContext } from "react";
import { Token } from "./Token";
import { fetchMyTokens } from "./token-utils";
import { FormattedMessage } from "react-intl";
import styles from "./Token.scss";
import { RevokeTokenModal } from "./RevokeTokenModal";
import { RevealTokenModal } from "./RevealTokenModal";
import { TokenList } from "./TokenList";
import { NoAccess } from "./NoAccess";
import { CenteredModalWrapper } from "../layout/CenteredModalWrapper";
import { AuthContext } from "../auth/AuthContext";

export function TokensContainer() {
const [tokens, setTokens] = useState([]);
// const [showRevealTokenModal, setRevealTokenModal] = useState(false);
// const [showRevokeTokenModal, setShowRevokeTokenModal] = useState(false);
// const [selectedRevokeId, setSelectedRevokeId] = useState();
const auth = useContext(AuthContext); // Re-render when you log in/out.

useEffect(() => {
// async function updateTokens() {
// setTokens(await fetchMyTokens());
// }
// updateTokens();
}, []);

const onRevealTokenModalClose = async ({ createdNewToken }) => {
// setRevealTokenModal(false);
// if (createdNewToken) {
// setTokens(await fetchMyTokens());
// }
};

const onRevokeTokenClose = ({ removedTokenId }) => {
// if (removedTokenId) setTokens(tokens.filter(token => token.id !== removedTokenId));
// setShowRevokeTokenModal(false);
// setSelectedRevokeId("");
};

return (
<div>
{
// {showRevealTokenModal && (
// <CenteredModalWrapper>
// <RevealTokenModal onClose={onRevealTokenModalClose} />
// </CenteredModalWrapper>
// )}
// {showRevokeTokenModal && (
// <CenteredModalWrapper>
// <RevokeTokenModal selectedId={selectedRevokeId} onClose={onRevokeTokenClose} />
// </CenteredModalWrapper>
// )}
}

<button
as="a"
preset="primary"
onClick={() => {
// if (!showRevealTokenModal) setRevealTokenModal(true);
}}
>
<FormattedMessage id="tokens.create-token" defaultMessage="Create Token" />
</button>
{auth?.isAdmin ? <TokenList /> : <NoAccess />}

{tokens.map(t => {
return (
<Token
showRevokeToken={id => {
// if (!showRevokeTokenModal) {
// setSelectedRevokeId(id);
// setShowRevokeTokenModal(true);
// }
}}
key={t.id}
{...t}
/>
);
})}
</div>
);
}
65 changes: 65 additions & 0 deletions src/react-components/tokens/token-utils.js
@@ -0,0 +1,65 @@
import { fetchReticulumAuthenticated, getReticulumFetchUrl } from "../../utils/phoenix-utils.js";

const ENDPOINT = "/api/v2_alpha/credentials";
const CREDENTIALS_ENDPOINT_URL = getReticulumFetchUrl(ENDPOINT);

export function fetchMyTokens() {
return fetchReticulumAuthenticated(ENDPOINT).then(function(tokens) {
console.log(tokens);
return tokens.credentials;
});
}

function getHeaders() {
return {
"content-type": "application/json",
authorization: `bearer ${window.APP.store.state.credentials.token}`
};
}

export async function createToken({ scopes }) {
console.log(scopes);
const res = await fetch(CREDENTIALS_ENDPOINT_URL, {
headers: getHeaders(),
method: "POST",
body: JSON.stringify({
scopes,
subjec_type: "account"
})
});
if (res.ok) {
return res.json();
} else {
console.log(res.status);
console.log(res.statusText);
throw new Error(res.statusText);
}
// TODO handle error case
}

export async function revokeToken({ id }) {
console.log("trying revoke...");
const res = await fetch(`${CREDENTIALS_ENDPOINT_URL}/${id}?revoke`, {
headers: getHeaders(),
method: "PUT",
body: JSON.stringify({
id,
revoke: true
})
});
if (res.ok) {
return res.json();
} else {
console.log(res.status);
console.log(res.statusText);
throw new Error(res.statusText);
}
}

export function fetchAvailableScopes() {
// TODO turn into a fetch
// fetch(`${CREDENTIALS_ENDPOINT}/scopes`, {
// headers: getHeaders()
// })
return ["read_rooms", "write_rooms"];
}
28 changes: 28 additions & 0 deletions src/tokens.html
@@ -0,0 +1,28 @@
<!DOCTYPE html>
<html>

<head>
<!-- DO NOT REMOVE/EDIT THIS COMMENT - META_TAGS -->

<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">

<link rel="shortcut icon" type="image/png" href="/favicon.ico">
<title>Tokens</title>
<link href="https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,400;0,500;0,700;1,400&display=swap" rel="stylesheet">
<script>
if (navigator.doNotTrack !== "1") {
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBeforea,m)
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
}
</script>
</head>

<body>
<div id="ui-root"></div>
</body>

</html>

32 changes: 32 additions & 0 deletions src/tokens.js
@@ -0,0 +1,32 @@
import React from "react";
import ReactDOM from "react-dom";
import { WrappedIntlProvider } from "./react-components/wrapped-intl-provider";
import registerTelemetry from "./telemetry";
import Store from "./storage/store";
import "./utils/theme";
import { AuthContextProvider } from "./react-components/auth/AuthContext";
import { TokensContainer } from "./react-components/tokens/TokensContainer";
import "./assets/stylesheets/globals.scss";
import "./react-components/styles/global.scss";
import { TokenPageLayout } from "./react-components/tokens/TokenPageLayout";
import configs from "./utils/configs";
import { ThemeProvider } from "./react-components/styles/theme";

registerTelemetry("/tokens", "Backend API Tokens Page");

const store = new Store();
window.APP = { store };

function Root() {
return (
<WrappedIntlProvider>
<ThemeProvider store={store}>
<AuthContextProvider store={store}>
<TokenPageLayout>{(true || configs.feature("public_api_access")) && <TokensContainer />}</TokenPageLayout>
</AuthContextProvider>
</ThemeProvider>
</WrappedIntlProvider>
);
}

ReactDOM.render(<Root />, document.getElementById("ui-root"));
10 changes: 10 additions & 0 deletions webpack.config.js
Expand Up @@ -269,6 +269,7 @@ module.exports = async (env, argv) => {
cloud: path.join(__dirname, "src", "cloud.js"),
signin: path.join(__dirname, "src", "signin.js"),
verify: path.join(__dirname, "src", "verify.js"),
tokens: path.join(__dirname, "src", "tokens.js"),
"whats-new": path.join(__dirname, "src", "whats-new.js")
},
output: {
Expand All @@ -293,6 +294,7 @@ module.exports = async (env, argv) => {
{ from: /^\/discord/, to: "/discord.html" },
{ from: /^\/cloud/, to: "/cloud.html" },
{ from: /^\/verify/, to: "/verify.html" },
{ from: /^\/tokens/, to: "/tokens.html" },
{ from: /^\/whats-new/, to: "/whats-new.html" }
]
},
Expand Down Expand Up @@ -601,6 +603,14 @@ module.exports = async (env, argv) => {
removeComments: false
}
}),
new HTMLWebpackPlugin({
filename: "tokens.html",
template: path.join(__dirname, "src", "tokens.html"),
chunks: ["tokens"],
minify: {
removeComments: false
}
}),
new CopyWebpackPlugin([
{
from: "src/hub.service.js",
Expand Down

0 comments on commit 182c2a6

Please sign in to comment.