mirror of
https://github.com/urbit/shrub.git
synced 2024-11-28 22:33:06 +03:00
interface: add profile screen and settings
This commit is contained in:
parent
d9a9ac991f
commit
bddf9bfdba
34
pkg/interface/package-lock.json
generated
34
pkg/interface/package-lock.json
generated
@ -4679,6 +4679,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"fn-name": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fn-name/-/fn-name-3.0.0.tgz",
|
||||
"integrity": "sha512-eNMNr5exLoavuAMhIUVsOKF79SWd/zG104ef6sxBTSw+cZc6BXdQXDvYcGvp0VbxVVSp1XDUNoz7mg1xMtSznA=="
|
||||
},
|
||||
"follow-redirects": {
|
||||
"version": "1.11.0",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.11.0.tgz",
|
||||
@ -7311,6 +7316,11 @@
|
||||
"react-is": "^16.8.1"
|
||||
}
|
||||
},
|
||||
"property-expr": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.2.tgz",
|
||||
"integrity": "sha512-bc/5ggaYZxNkFKj374aLbEDqVADdYaLcFo8XBkishUWbaAdjlphaBFns9TvRA2pUseVL/wMFmui9X3IdNDU37g=="
|
||||
},
|
||||
"proxy-addr": {
|
||||
"version": "2.0.6",
|
||||
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz",
|
||||
@ -8916,6 +8926,11 @@
|
||||
"xml-reader": "2.4.3"
|
||||
}
|
||||
},
|
||||
"synchronous-promise": {
|
||||
"version": "2.0.13",
|
||||
"resolved": "https://registry.npmjs.org/synchronous-promise/-/synchronous-promise-2.0.13.tgz",
|
||||
"integrity": "sha512-R9N6uDkVsghHePKh1TEqbnLddO2IY25OcsksyFp/qBe7XYd0PVbKEWxhcdMhpLzE1I6skj5l4aEZ3CRxcbArlA=="
|
||||
},
|
||||
"tabbable": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/tabbable/-/tabbable-4.0.0.tgz",
|
||||
@ -9148,6 +9163,11 @@
|
||||
"integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==",
|
||||
"dev": true
|
||||
},
|
||||
"toposort": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz",
|
||||
"integrity": "sha1-riF2gXXRVZ1IvvNUILL0li8JwzA="
|
||||
},
|
||||
"transformation-matrix": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/transformation-matrix/-/transformation-matrix-2.1.1.tgz",
|
||||
@ -11716,6 +11736,20 @@
|
||||
"camelcase": "^5.0.0",
|
||||
"decamelize": "^1.2.0"
|
||||
}
|
||||
},
|
||||
"yup": {
|
||||
"version": "0.29.1",
|
||||
"resolved": "https://registry.npmjs.org/yup/-/yup-0.29.1.tgz",
|
||||
"integrity": "sha512-U7mPIbgfQWI6M3hZCJdGFrr+U0laG28FxMAKIgNvgl7OtyYuUoc4uy9qCWYHZjh49b8T7Ug8NNDdiMIEytcXrQ==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.9.6",
|
||||
"fn-name": "~3.0.0",
|
||||
"lodash": "^4.17.15",
|
||||
"lodash-es": "^4.17.11",
|
||||
"property-expr": "^2.0.2",
|
||||
"synchronous-promise": "^2.0.10",
|
||||
"toposort": "^2.0.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.10.5",
|
||||
"@reach/disclosure": "^0.10.5",
|
||||
"@reach/menu-button": "^0.10.1",
|
||||
"@reach/menu-button": "^0.10.18",
|
||||
"@reach/tabs": "^0.10.5",
|
||||
"@tlon/indigo-light": "^1.0.3",
|
||||
"@tlon/indigo-react": "^1.1.15",
|
||||
@ -30,7 +30,8 @@
|
||||
"styled-system": "^5.1.5",
|
||||
"suncalc": "^1.8.0",
|
||||
"urbit-ob": "^5.0.0",
|
||||
"urbit-sigil-js": "^1.3.2"
|
||||
"urbit-sigil-js": "^1.3.2",
|
||||
"yup": "^0.29.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.9.0",
|
||||
|
@ -15,6 +15,7 @@ import DojoApp from './apps/dojo/app';
|
||||
import GroupsApp from './apps/groups/app';
|
||||
import LinksApp from './apps/links/app';
|
||||
import PublishApp from './apps/publish/app';
|
||||
import Profile from './apps/profile/profile';
|
||||
|
||||
import StatusBar from './components/StatusBar';
|
||||
import ErrorComponent from './components/Error';
|
||||
@ -40,6 +41,13 @@ const Root = styled.div`
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
${p => p.background?.type === 'url' ? `
|
||||
background-image: url('${p.background?.url}');
|
||||
background-size: cover;
|
||||
` : p.background?.type === 'color' ? `
|
||||
background-color: ${p.background.color}
|
||||
` : ``
|
||||
}
|
||||
`;
|
||||
|
||||
const Content = styled.div`
|
||||
@ -88,10 +96,11 @@ class App extends React.Component {
|
||||
const selectedGroups = this.state.selectedGroups ? this.state.selectedGroups : [];
|
||||
const { state } = this;
|
||||
const theme = state.dark ? dark : light;
|
||||
const { background } = state;
|
||||
|
||||
return (
|
||||
<ThemeProvider theme={theme}>
|
||||
<Root>
|
||||
<Root background={background} >
|
||||
<Router>
|
||||
<StatusBarWithRouter props={this.props}
|
||||
associations={associations}
|
||||
@ -163,6 +172,12 @@ class App extends React.Component {
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
path="/~profile"
|
||||
render={ p => (
|
||||
<Profile ship={this.ship} api={this.api} {...state} />
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
render={(props) => (
|
||||
<ErrorComponent {...props} code={404} description="Not Found" />
|
||||
|
205
pkg/interface/src/apps/profile/components/lib/DisplayForm.tsx
Normal file
205
pkg/interface/src/apps/profile/components/lib/DisplayForm.tsx
Normal file
@ -0,0 +1,205 @@
|
||||
import React, { useCallback } from "react";
|
||||
|
||||
import {
|
||||
Input,
|
||||
Box,
|
||||
Center,
|
||||
Col,
|
||||
InputLabel,
|
||||
Radio,
|
||||
Checkbox,
|
||||
Button,
|
||||
} from "@tlon/indigo-react";
|
||||
|
||||
import { Formik, Form } from "formik";
|
||||
import * as Yup from "yup";
|
||||
import _ from "lodash";
|
||||
import GlobalApi from "../../../../api/global";
|
||||
import { BackgroundConfig } from "../../../../types/local-update";
|
||||
import { LaunchState } from "../../../../types/launch-update";
|
||||
|
||||
const tiles = ["publish", "links", "chat", "dojo", "clock", "weather"];
|
||||
|
||||
const formSchema = Yup.object().shape({
|
||||
order: Yup.string()
|
||||
.required("Required")
|
||||
.test(
|
||||
"tiles",
|
||||
"Invalid tile ordering",
|
||||
(o: string = "") =>
|
||||
_.difference(
|
||||
o.split(", ").map((i) => i.trim()),
|
||||
tiles
|
||||
).length === 0
|
||||
),
|
||||
bgType: Yup.string()
|
||||
.oneOf(["none", "color", "url"], "invalid")
|
||||
.required("Required"),
|
||||
bgUrl: Yup.string().url(),
|
||||
bgColor: Yup.string().matches(/#([A-F]|[a-f]|[0-9]){6}/, "Invalid color"),
|
||||
avatars: Yup.boolean(),
|
||||
nicknames: Yup.boolean(),
|
||||
});
|
||||
|
||||
type BgType = "none" | "url" | "color";
|
||||
|
||||
interface FormSchema {
|
||||
order: string;
|
||||
bgType: BgType;
|
||||
bgColor: string | undefined;
|
||||
bgUrl: string | undefined;
|
||||
avatars: boolean;
|
||||
nicknames: boolean;
|
||||
}
|
||||
|
||||
interface DisplayFormProps {
|
||||
api: GlobalApi;
|
||||
launch: LaunchState;
|
||||
dark: boolean;
|
||||
background: BackgroundConfig;
|
||||
hideAvatars: boolean;
|
||||
hideNicknames: boolean;
|
||||
}
|
||||
|
||||
function ImagePicker({ url }: { url: string }) {
|
||||
return (
|
||||
<Center
|
||||
width="250px"
|
||||
height="250px"
|
||||
p={3}
|
||||
backgroundImage={`url('${url}')`}
|
||||
backgroundSize="cover"
|
||||
>
|
||||
<Box>Change</Box>
|
||||
</Center>
|
||||
);
|
||||
}
|
||||
|
||||
function BackgroundPicker({
|
||||
bgType,
|
||||
bgUrl,
|
||||
}: {
|
||||
bgType: BgType;
|
||||
bgUrl?: string;
|
||||
}) {
|
||||
return (
|
||||
<Box>
|
||||
<InputLabel>Landscape Background</InputLabel>
|
||||
<Box display="flex" alignItems="center">
|
||||
<Box mt={3} mr={10}>
|
||||
<Radio label="Image" id="url" name="bgType" />
|
||||
<Radio label="Color" id="color" name="bgType" />
|
||||
<Radio label="None" id="none" name="bgType" />
|
||||
</Box>
|
||||
{bgType === "url" && (
|
||||
<Col>
|
||||
<Input ml={4} type="text" label="URL" id="bgUrl" name="bgUrl" />
|
||||
{/*<ImagePicker url={bgUrl || ''} />*/}
|
||||
</Col>
|
||||
)}
|
||||
{bgType === "color" && (
|
||||
<Input ml={4} type="text" label="Color" id="bgColor" name="bgColor" />
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default function DisplayForm(props: DisplayFormProps) {
|
||||
const { api, launch, background, hideAvatars, hideNicknames } = props;
|
||||
|
||||
const initialOrder = launch.tileOrdering.join(", ");
|
||||
let bgColor, bgUrl;
|
||||
if (background?.type === "url") {
|
||||
bgUrl = background.url;
|
||||
}
|
||||
if (background?.type === "color") {
|
||||
bgColor = background.color;
|
||||
}
|
||||
const bgType = background?.type || "none";
|
||||
|
||||
const logoutAll = useCallback(() => {}, []);
|
||||
|
||||
return (
|
||||
<Formik
|
||||
validationSchema={formSchema}
|
||||
initialValues={
|
||||
{
|
||||
order: initialOrder,
|
||||
bgType,
|
||||
bgColor,
|
||||
bgUrl,
|
||||
avatars: hideAvatars,
|
||||
nicknames: hideNicknames,
|
||||
} as FormSchema
|
||||
}
|
||||
onSubmit={(values, actions) => {
|
||||
api.launch.changeOrder(values.order.split(", "));
|
||||
|
||||
const bgConfig: BackgroundConfig =
|
||||
values.bgType === "color"
|
||||
? { type: "color", color: values.bgColor || "" }
|
||||
: values.bgType === "url"
|
||||
? { type: "url", url: values.bgUrl || "" }
|
||||
: undefined;
|
||||
|
||||
api.local.setBackground(bgConfig);
|
||||
api.local.hideAvatars(values.avatars);
|
||||
api.local.hideNicknames(values.nicknames);
|
||||
api.local.dehydrate();
|
||||
actions.setSubmitting(false);
|
||||
}}
|
||||
>
|
||||
{(props) => (
|
||||
<Form>
|
||||
<Box
|
||||
display="grid"
|
||||
gridTemplateColumns="1fr"
|
||||
gridTemplateRows="auto"
|
||||
gridRowGap={2}
|
||||
>
|
||||
<Box color="black" fontSize={1} mb={4} fontWeight={900}>
|
||||
Display Preferences
|
||||
</Box>
|
||||
|
||||
<Box>
|
||||
<Input
|
||||
label="Home Tile Order"
|
||||
id="order"
|
||||
mt={2}
|
||||
type="text"
|
||||
width={256}
|
||||
/>
|
||||
</Box>
|
||||
<BackgroundPicker
|
||||
bgType={props.values.bgType}
|
||||
bgUrl={props.values.bgUrl}
|
||||
/>
|
||||
<Box mt={3}>
|
||||
<Checkbox
|
||||
mt={3}
|
||||
label="Disable avatars"
|
||||
id="avatars"
|
||||
caption="Do not show user-set avatars"
|
||||
/>
|
||||
<Checkbox
|
||||
mt={3}
|
||||
label="Disable nicknames"
|
||||
id="nicknames"
|
||||
caption="Do not show user-set nicknames"
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
<Button
|
||||
onClick={logoutAll}
|
||||
border={1}
|
||||
borderColor="washedGray"
|
||||
type="submit"
|
||||
>
|
||||
Submit
|
||||
</Button>
|
||||
</Form>
|
||||
)}
|
||||
</Formik>
|
||||
);
|
||||
}
|
193
pkg/interface/src/apps/profile/components/lib/S3Form.tsx
Normal file
193
pkg/interface/src/apps/profile/components/lib/S3Form.tsx
Normal file
@ -0,0 +1,193 @@
|
||||
import React, { useCallback } from "react";
|
||||
|
||||
import {
|
||||
Input,
|
||||
Box,
|
||||
Center,
|
||||
Button,
|
||||
Checkbox,
|
||||
Col,
|
||||
Text,
|
||||
Menu,
|
||||
MenuButton,
|
||||
MenuList,
|
||||
MenuItem,
|
||||
} from "@tlon/indigo-react";
|
||||
|
||||
import { Formik, Form } from "formik";
|
||||
import * as Yup from "yup";
|
||||
import GlobalApi from "../../../../api/global";
|
||||
import { S3State } from "../../../../types/s3-update";
|
||||
|
||||
function BucketList({
|
||||
buckets,
|
||||
selected,
|
||||
api,
|
||||
}: {
|
||||
buckets: Set<string>;
|
||||
selected: string;
|
||||
api: GlobalApi;
|
||||
}) {
|
||||
const _buckets = Array.from(buckets);
|
||||
|
||||
const onSubmit = useCallback(
|
||||
(values: { newBucket: string }) => {
|
||||
api.s3.addBucket(values.newBucket);
|
||||
},
|
||||
[api]
|
||||
);
|
||||
|
||||
const onSelect = useCallback(
|
||||
(bucket: string) => {
|
||||
return function () {
|
||||
api.s3.setCurrentBucket(bucket);
|
||||
};
|
||||
},
|
||||
[api]
|
||||
);
|
||||
|
||||
const onDelete = useCallback(
|
||||
(bucket: string) => {
|
||||
return function () {
|
||||
api.s3.removeBucket(bucket);
|
||||
};
|
||||
},
|
||||
[api]
|
||||
);
|
||||
|
||||
return (
|
||||
<Formik initialValues={{ newBucket: "" }} onSubmit={onSubmit}>
|
||||
<Form>
|
||||
<Col alignItems="start">
|
||||
{_buckets.map((bucket) => (
|
||||
<Box
|
||||
key={bucket}
|
||||
display="flex"
|
||||
justifyContent="space-between"
|
||||
alignItems="center"
|
||||
borderRadius={1}
|
||||
border={1}
|
||||
borderColor="washedGray"
|
||||
fontSize={1}
|
||||
pl={2}
|
||||
mb={2}
|
||||
width="100%"
|
||||
>
|
||||
<Text>{bucket}</Text>
|
||||
{bucket === selected && (
|
||||
<Text p={1} color="green">
|
||||
Active
|
||||
</Text>
|
||||
)}
|
||||
{bucket !== selected && (
|
||||
<Menu>
|
||||
<MenuButton sm>Options</MenuButton>
|
||||
<MenuList>
|
||||
<MenuItem onSelect={onSelect(bucket)}>Make Active</MenuItem>
|
||||
<MenuItem onSelect={onDelete(bucket)}>Delete</MenuItem>
|
||||
</MenuList>
|
||||
</Menu>
|
||||
)}
|
||||
</Box>
|
||||
))}
|
||||
<Input
|
||||
mt={4}
|
||||
type="text"
|
||||
label="New Bucket"
|
||||
id="newBucket"
|
||||
name="newBucket"
|
||||
/>
|
||||
<Button border={1} borderColor="washedGrey" type="submit">
|
||||
Add
|
||||
</Button>
|
||||
</Col>
|
||||
</Form>
|
||||
</Formik>
|
||||
);
|
||||
}
|
||||
|
||||
interface FormSchema {
|
||||
s3bucket: string;
|
||||
s3buckets: string[];
|
||||
s3endpoint: string;
|
||||
s3accessKeyId: string;
|
||||
s3secretAccessKey: string;
|
||||
}
|
||||
|
||||
interface S3FormProps {
|
||||
api: GlobalApi;
|
||||
s3: S3State;
|
||||
}
|
||||
|
||||
export default function S3Form(props: S3FormProps) {
|
||||
const { api, s3 } = props;
|
||||
|
||||
const onSubmit = useCallback(
|
||||
(values: FormSchema) => {
|
||||
if (values.s3secretAccessKey !== s3.credentials?.secretAccessKey) {
|
||||
api.s3.setSecretAccessKey(values.s3secretAccessKey);
|
||||
}
|
||||
|
||||
if (values.s3endpoint !== s3.credentials?.endpoint) {
|
||||
api.s3.setEndpoint(values.s3endpoint);
|
||||
}
|
||||
|
||||
if (values.s3accessKeyId !== s3.credentials?.accessKeyId) {
|
||||
api.s3.setAccessKeyId(values.s3accessKeyId);
|
||||
}
|
||||
},
|
||||
[api]
|
||||
);
|
||||
return (
|
||||
<Box
|
||||
display="grid"
|
||||
gridTemplateColumns="1fr"
|
||||
gridTemplateRows="auto"
|
||||
gridRowGap={4}
|
||||
justifyItems="start"
|
||||
>
|
||||
<Box color="black" fontSize={1} mb={4} mt={7} fontWeight={900}>
|
||||
S3 Credentials
|
||||
</Box>
|
||||
<Formik
|
||||
initialValues={
|
||||
{
|
||||
s3bucket: s3.configuration.currentBucket,
|
||||
s3buckets: Array.from(s3.configuration.buckets),
|
||||
s3endpoint: s3.credentials?.endpoint,
|
||||
s3accessKeyId: s3.credentials?.accessKeyId,
|
||||
s3secretAccessKey: s3.credentials?.secretAccessKey,
|
||||
} as FormSchema
|
||||
}
|
||||
onSubmit={onSubmit}
|
||||
>
|
||||
<Form>
|
||||
<Input width="256px" type="text" label="Endpoint" id="s3endpoint" />
|
||||
<Input
|
||||
width="256px"
|
||||
type="text"
|
||||
label="Access Key ID"
|
||||
id="s3accessKeyId"
|
||||
/>
|
||||
<Input
|
||||
width="256px"
|
||||
type="password"
|
||||
label="Secret Access Key"
|
||||
id="s3secretAccessKey"
|
||||
/>
|
||||
<Button border={1} type="submit">
|
||||
Submit
|
||||
</Button>
|
||||
</Form>
|
||||
</Formik>
|
||||
<Box color="black" fontSize={1} my={2} fontWeight={700}>
|
||||
S3 Buckets
|
||||
</Box>
|
||||
<BucketList
|
||||
buckets={s3.configuration.buckets}
|
||||
selected={s3.configuration.currentBucket}
|
||||
api={api}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
}
|
41
pkg/interface/src/apps/profile/components/lib/Security.tsx
Normal file
41
pkg/interface/src/apps/profile/components/lib/Security.tsx
Normal file
@ -0,0 +1,41 @@
|
||||
import React from "react";
|
||||
import { Box, Button } from "@tlon/indigo-react";
|
||||
|
||||
import GlobalApi from "../../../../api/global";
|
||||
|
||||
interface SecuritySettingsProps {
|
||||
api: GlobalApi;
|
||||
}
|
||||
|
||||
export default function SecuritySettings({ api }: SecuritySettingsProps) {
|
||||
return (
|
||||
<>
|
||||
<Box color="black" fontSize={1} mt={7} mb={2} fontWeight={900}>
|
||||
Security
|
||||
</Box>
|
||||
<Box color="black" fontSize={0} mt={4} fontWeight={700}>
|
||||
Log out of this session
|
||||
</Box>
|
||||
<Box fontSize={0} mt={2} color="gray">
|
||||
You will be logged out of your Urbit on this browser
|
||||
<form method="post" action="/~/logout">
|
||||
<Button narrow mt={2} border={1}>
|
||||
Logout
|
||||
</Button>
|
||||
</form>
|
||||
</Box>
|
||||
<Box color="black" fontSize={0} mt={4} fontWeight={700}>
|
||||
Log out of all sessions
|
||||
</Box>
|
||||
<Box fontSize={0} mt={2} color="gray">
|
||||
You will be logged out of all browsers that have currently logged into
|
||||
your Urbit
|
||||
<form method="post" action="/~/logout">
|
||||
<Button error narrow mt={2} border={1}>
|
||||
Logout Everywhere
|
||||
</Button>
|
||||
</form>
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
}
|
60
pkg/interface/src/apps/profile/components/settings.tsx
Normal file
60
pkg/interface/src/apps/profile/components/settings.tsx
Normal file
@ -0,0 +1,60 @@
|
||||
import React from "react";
|
||||
|
||||
import {
|
||||
Box,
|
||||
Text,
|
||||
Button,
|
||||
Col,
|
||||
Input,
|
||||
InputLabel,
|
||||
Radio,
|
||||
Checkbox,
|
||||
} from "@tlon/indigo-react";
|
||||
import * as Yup from "yup";
|
||||
import { Formik, Form } from "formik";
|
||||
import _ from "lodash";
|
||||
|
||||
import GlobalApi from "../../../api/global";
|
||||
import { StoreState } from "../../../store/type";
|
||||
import DisplayForm from "./lib/DisplayForm";
|
||||
import S3Form from "./lib/S3Form";
|
||||
import SecuritySettings from "./lib/Security";
|
||||
|
||||
type ProfileProps = StoreState & { api: GlobalApi; ship: string };
|
||||
|
||||
export default function Profile({
|
||||
api,
|
||||
launch,
|
||||
s3,
|
||||
dark,
|
||||
hideAvatars,
|
||||
hideNicknames,
|
||||
background,
|
||||
}: ProfileProps) {
|
||||
return (
|
||||
<Col
|
||||
backgroundColor="white"
|
||||
fontSize={2}
|
||||
p={4}
|
||||
m={3}
|
||||
borderRadius={1}
|
||||
maxWidth="300px"
|
||||
>
|
||||
<Box color="black" fontSize={0} mb={4}>
|
||||
Ship Settings
|
||||
</Box>
|
||||
<DisplayForm
|
||||
api={api}
|
||||
launch={launch}
|
||||
dark={dark}
|
||||
hideNicknames={hideNicknames}
|
||||
hideAvatars={hideAvatars}
|
||||
background={background}
|
||||
/>
|
||||
|
||||
<S3Form api={api} s3={s3} />
|
||||
|
||||
<SecuritySettings api={api} />
|
||||
</Col>
|
||||
);
|
||||
}
|
59
pkg/interface/src/apps/profile/profile.tsx
Normal file
59
pkg/interface/src/apps/profile/profile.tsx
Normal file
@ -0,0 +1,59 @@
|
||||
import React from "react";
|
||||
|
||||
import { Box, Col, Center, Icon } from "@tlon/indigo-react";
|
||||
|
||||
import { Sigil } from "../../lib/sigil";
|
||||
|
||||
import Settings from "./components/settings";
|
||||
|
||||
export default function ProfileScreen(props: any) {
|
||||
const { ship, dark } = props;
|
||||
return (
|
||||
<Box height="100%" px={3} pb={3} borderRadius={1}>
|
||||
<Box
|
||||
height="100%"
|
||||
width="100%"
|
||||
display="flex"
|
||||
borderRadius={1}
|
||||
bg="white"
|
||||
border={1}
|
||||
borderColor="washedGray"
|
||||
>
|
||||
<Col collapse borderRight={1} borderColor="washedGray">
|
||||
<Box borderBottom={1} borderBottomColor="washedGray">
|
||||
<Box
|
||||
backgroundColor="black"
|
||||
borderRadius={8}
|
||||
margin={4}
|
||||
height={128}
|
||||
width={128}
|
||||
display="flex"
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
>
|
||||
<Sigil ship={ship} size={80} color={dark ? '#FFFFFF' : '#00000'}/>
|
||||
</Box>
|
||||
</Box>
|
||||
<Box py={4}>
|
||||
<Box
|
||||
display="flex"
|
||||
alignItems="center"
|
||||
verticalAlign="middle"
|
||||
fontSize={0}
|
||||
py={1}
|
||||
px={3}
|
||||
color="blue"
|
||||
backgroundColor="washedBlue"
|
||||
>
|
||||
<Icon mr={2} display="inline-block" icon="Circle" fill="blue" />
|
||||
Ship Settings
|
||||
</Box>
|
||||
</Box>
|
||||
</Col>
|
||||
<Box overflowY="auto" flexGrow={1}>
|
||||
<Settings {...props} />
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
@ -15,6 +15,8 @@ const getLocationName = (basePath) => {
|
||||
return 'Links';
|
||||
else if (basePath === '~publish')
|
||||
return 'Publish';
|
||||
else if (basePath === '~profile')
|
||||
return 'Profile';
|
||||
else
|
||||
return 'Unknown';
|
||||
};
|
||||
@ -40,12 +42,12 @@ const StatusBar = (props) => {
|
||||
return (
|
||||
<div
|
||||
className={
|
||||
'bg-white bg-gray0-d w-100 justify-between relative tc pt3 ' + display
|
||||
'w-100 justify-between relative tc pt3 ' + display
|
||||
}
|
||||
style={{ height: 45 }}
|
||||
>
|
||||
<div className="fl lh-copy absolute left-0 pl4" style={{ top: 8 }}>
|
||||
<Link to="/~groups/me"
|
||||
<div className="bg-white b--gray4 ba bg-gray0-d fl lh-copy absolute left-0 ml4 pl2 pr2 br1" style={{ top: 8 }}>
|
||||
<Link to="/~profile"
|
||||
className="dib v-top" style={{ lineHeight: 0, paddingTop: 6 }}>
|
||||
<Sigil
|
||||
ship={'~' + window.ship}
|
||||
|
Loading…
Reference in New Issue
Block a user