mirror of
https://github.com/ilyakooo0/urbit.git
synced 2024-11-10 18:21:34 +03:00
Merge branch 'release/next-js' into la/release-2021-03-04
This commit is contained in:
commit
f1356ed96d
@ -1,5 +1,5 @@
|
||||
/- *settings
|
||||
/+ verb, dbug, default-agent
|
||||
/+ verb, dbug, default-agent, agentio
|
||||
|%
|
||||
+$ card card:agent:gall
|
||||
+$ versioned-state
|
||||
@ -20,10 +20,14 @@
|
||||
+* this .
|
||||
do ~(. +> bol)
|
||||
def ~(. (default-agent this %|) bol)
|
||||
io ~(. agentio bol)
|
||||
::
|
||||
++ on-init
|
||||
^- (quip card _this)
|
||||
`this
|
||||
=^ cards state
|
||||
(put-entry:do %tutorial %seen b+|)
|
||||
[cards this]
|
||||
|
||||
::
|
||||
++ on-save !>(state)
|
||||
::
|
||||
|
@ -127,11 +127,11 @@ module.exports = {
|
||||
plugins: [
|
||||
new UrbitShipPlugin(urbitrc),
|
||||
new webpack.DefinePlugin({
|
||||
'process.env.TUTORIAL_HOST': JSON.stringify('~hastuc-dibtux'),
|
||||
'process.env.TUTORIAL_HOST': JSON.stringify('~difmex-passed'),
|
||||
'process.env.TUTORIAL_GROUP': JSON.stringify('beginner-island'),
|
||||
'process.env.TUTORIAL_CHAT': JSON.stringify('chat-1704'),
|
||||
'process.env.TUTORIAL_BOOK': JSON.stringify('book-9695'),
|
||||
'process.env.TUTORIAL_LINKS': JSON.stringify('link-2827'),
|
||||
'process.env.TUTORIAL_CHAT': JSON.stringify('introduce-yourself-7010'),
|
||||
'process.env.TUTORIAL_BOOK': JSON.stringify('guides-9684'),
|
||||
'process.env.TUTORIAL_LINKS': JSON.stringify('community-articles-2143'),
|
||||
})
|
||||
|
||||
// new CleanWebpackPlugin(),
|
||||
|
@ -61,12 +61,12 @@ module.exports = {
|
||||
new webpack.DefinePlugin({
|
||||
'process.env.LANDSCAPE_STREAM': JSON.stringify(process.env.LANDSCAPE_STREAM),
|
||||
'process.env.LANDSCAPE_SHORTHASH': JSON.stringify(GIT_DESC),
|
||||
'process.env.TUTORIAL_HOST': JSON.stringify('~hastuc-dibtux'),
|
||||
'process.env.TUTORIAL_HOST': JSON.stringify('~difmex-passed'),
|
||||
'process.env.TUTORIAL_GROUP': JSON.stringify('beginner-island'),
|
||||
'process.env.TUTORIAL_CHAT': JSON.stringify('chat-8401'),
|
||||
'process.env.TUTORIAL_BOOK': JSON.stringify('notebook-9148'),
|
||||
'process.env.TUTORIAL_LINKS': JSON.stringify('links-4353'),
|
||||
})
|
||||
'process.env.TUTORIAL_CHAT': JSON.stringify('introduce-yourself-7010'),
|
||||
'process.env.TUTORIAL_BOOK': JSON.stringify('guides-9684'),
|
||||
'process.env.TUTORIAL_LINKS': JSON.stringify('community-articles-2143'),
|
||||
}),
|
||||
// new HtmlWebpackPlugin({
|
||||
// title: 'Hot Module Replacement',
|
||||
// template: './public/index.html',
|
||||
|
27
pkg/interface/package-lock.json
generated
27
pkg/interface/package-lock.json
generated
@ -1439,9 +1439,9 @@
|
||||
"integrity": "sha512-kBzJueOoGDVF2knGt+Kf5ylvil6+V1qn8/RqAj1S6wUTnfUfAMRzDp4LQI2MxLI8Is0OG3XCErVSOUImU6R3lg=="
|
||||
},
|
||||
"@tlon/indigo-react": {
|
||||
"version": "1.2.17",
|
||||
"resolved": "https://registry.npmjs.org/@tlon/indigo-react/-/indigo-react-1.2.17.tgz",
|
||||
"integrity": "sha512-D53HDLbqkRX3nY5zcXv8DRHw7FhsCGYfY3xa8CbaFfhFupdXBHi96UURi9Qq3sBc4FHgnPj45eJflji7Yj3gYg==",
|
||||
"version": "1.2.19",
|
||||
"resolved": "https://registry.npmjs.org/@tlon/indigo-react/-/indigo-react-1.2.19.tgz",
|
||||
"integrity": "sha512-lcHtPIbKeXVDvqd9dkCswB++CLRB2TsYFoegRU5VX3A886R+larJP81CzmoAwmZiJL3OnwypRklyfAv41F6W2w==",
|
||||
"requires": {
|
||||
"@reach/menu-button": "^0.10.5",
|
||||
"react": "^16.13.1",
|
||||
@ -1775,6 +1775,7 @@
|
||||
"version": "file:../npm/api",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.12.5",
|
||||
"@types/lodash": "^4.14.168",
|
||||
"@urbit/eslint-config": "^1.0.0",
|
||||
"big-integer": "^1.6.48",
|
||||
"lodash": "^4.17.20"
|
||||
@ -1782,26 +1783,36 @@
|
||||
"dependencies": {
|
||||
"@babel/runtime": {
|
||||
"version": "7.12.5",
|
||||
"bundled": true,
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.12.5.tgz",
|
||||
"integrity": "sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg==",
|
||||
"requires": {
|
||||
"regenerator-runtime": "^0.13.4"
|
||||
}
|
||||
},
|
||||
"@types/lodash": {
|
||||
"version": "4.14.168",
|
||||
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.168.tgz",
|
||||
"integrity": "sha512-oVfRvqHV/V6D1yifJbVRU3TMp8OT6o6BG+U9MkwuJ3U8/CsDHvalRpsxBqivn71ztOFZBTfJMvETbqHiaNSj7Q=="
|
||||
},
|
||||
"@urbit/eslint-config": {
|
||||
"version": "1.0.0",
|
||||
"bundled": true
|
||||
"resolved": "https://registry.npmjs.org/@urbit/eslint-config/-/eslint-config-1.0.0.tgz",
|
||||
"integrity": "sha512-Xmzb6MvM7KorlPJEq/hURZZ4BHSVy/7CoQXWogsBSTv5MOZnMqwNKw6yt24k2AO/2UpHwjGptimaNLqFfesJbw=="
|
||||
},
|
||||
"big-integer": {
|
||||
"version": "1.6.48",
|
||||
"bundled": true
|
||||
"resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.48.tgz",
|
||||
"integrity": "sha512-j51egjPa7/i+RdiRuJbPdJ2FIUYYPhvYLjzoYbcMMm62ooO6F94fETG4MTs46zPAF9Brs04OajboA/qTGuz78w=="
|
||||
},
|
||||
"lodash": {
|
||||
"version": "4.17.20",
|
||||
"bundled": true
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
|
||||
"integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA=="
|
||||
},
|
||||
"regenerator-runtime": {
|
||||
"version": "0.13.7",
|
||||
"bundled": true
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz",
|
||||
"integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew=="
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -10,7 +10,7 @@
|
||||
"@reach/tabs": "^0.10.5",
|
||||
"@tlon/indigo-dark": "^1.0.6",
|
||||
"@tlon/indigo-light": "^1.0.6",
|
||||
"@tlon/indigo-react": "1.2.17",
|
||||
"@tlon/indigo-react": "^1.2.19",
|
||||
"@tlon/sigil-js": "^1.4.3",
|
||||
"@urbit/api": "file:../npm/api",
|
||||
"aws-sdk": "^2.830.0",
|
||||
|
23
pkg/interface/src/logic/lib/idling.ts
Normal file
23
pkg/interface/src/logic/lib/idling.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
|
||||
export function useIdlingState() {
|
||||
const [idling, setIdling] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
function blur() {
|
||||
setIdling(true);
|
||||
}
|
||||
function focus() {
|
||||
setIdling(false);
|
||||
}
|
||||
window.addEventListener('blur', blur);
|
||||
window.addEventListener('focus', focus);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('blur', blur);
|
||||
window.removeEventListener('focus', focus);
|
||||
}
|
||||
}, []);
|
||||
|
||||
return idling;
|
||||
}
|
@ -79,7 +79,7 @@ const otherIndex = function(config) {
|
||||
messages: result('Messages', '/~landscape/messages', 'messages', null),
|
||||
logout: result('Log Out', '/~/logout', 'logout', null)
|
||||
};
|
||||
|
||||
other.push(result('Tutorial', '/?tutorial=true', 'tutorial', null));
|
||||
for(let cat of config.categories) {
|
||||
if(idx[cat]) {
|
||||
other.push(idx[cat]);
|
||||
|
@ -12,6 +12,7 @@ export const TUTORIAL_GROUP = process.env.TUTORIAL_GROUP!;
|
||||
export const TUTORIAL_CHAT = process.env.TUTORIAL_CHAT!;
|
||||
export const TUTORIAL_BOOK = process.env.TUTORIAL_BOOK!;
|
||||
export const TUTORIAL_LINKS = process.env.TUTORIAL_LINKS!;
|
||||
export const TUTORIAL_GROUP_RESOURCE = `/ship/${TUTORIAL_HOST}/${TUTORIAL_GROUP}` ;
|
||||
|
||||
interface StepDetail {
|
||||
title: string;
|
||||
@ -26,7 +27,7 @@ interface StepDetail {
|
||||
|
||||
export function hasTutorialGroup(props: { associations: Associations }) {
|
||||
return (
|
||||
`/ship/${TUTORIAL_HOST}/${TUTORIAL_GROUP}` in props.associations.groups
|
||||
TUTORIAL_GROUP_RESOURCE in props.associations.groups
|
||||
);
|
||||
}
|
||||
|
||||
@ -90,7 +91,7 @@ export const progressDetails: Record<TutorialProgress, StepDetail> = {
|
||||
alignY: 'top',
|
||||
arrow: 'East',
|
||||
offsetX: MODAL_WIDTH + 24,
|
||||
offsetY: MODAL_HEIGHT / 2 - 8
|
||||
offsetY: 80,
|
||||
},
|
||||
channels: {
|
||||
title: 'Channels',
|
||||
@ -143,7 +144,7 @@ export const progressDetails: Record<TutorialProgress, StepDetail> = {
|
||||
alignY: 'top',
|
||||
alignX: 'left',
|
||||
arrow: 'North',
|
||||
offsetX: (MODAL_WIDTH / 2) - 16,
|
||||
offsetX: 0,
|
||||
offsetY: -48
|
||||
},
|
||||
profile: {
|
||||
@ -155,17 +156,17 @@ export const progressDetails: Record<TutorialProgress, StepDetail> = {
|
||||
alignX: 'right',
|
||||
arrow: 'South',
|
||||
offsetX: -300 + MODAL_WIDTH / 2,
|
||||
offsetY: -120 + MODAL_HEIGHT / 2
|
||||
offsetY: -60,
|
||||
},
|
||||
leap: {
|
||||
title: 'Leap',
|
||||
description:
|
||||
'Leap allows you to go to a specific channel, message, collection, profile or group simply by typing in a command or selecting a shortcut from the dropdown menu.',
|
||||
url: `/~profile/~${window.ship}`,
|
||||
alignY: 'top',
|
||||
alignX: 'left',
|
||||
arrow: 'North',
|
||||
offsetX: 0.3 *MODAL_HEIGHT,
|
||||
offsetY: -48
|
||||
}
|
||||
alignY: "top",
|
||||
alignX: "left",
|
||||
arrow: "North",
|
||||
offsetX: 76,
|
||||
offsetY: -48,
|
||||
},
|
||||
};
|
||||
|
@ -7,6 +7,7 @@ import { BackgroundConfig, RemoteContentPolicy, TutorialProgress, tutorialProgre
|
||||
|
||||
|
||||
export interface LocalState {
|
||||
theme: "light" | "dark" | "auto";
|
||||
hideAvatars: boolean;
|
||||
hideNicknames: boolean;
|
||||
remoteContentPolicy: RemoteContentPolicy;
|
||||
@ -35,6 +36,7 @@ export const selectLocalState =
|
||||
const useLocalState = create<LocalStateZus>(persist((set, get) => ({
|
||||
dark: false,
|
||||
background: undefined,
|
||||
theme: "auto",
|
||||
hideAvatars: false,
|
||||
hideNicknames: false,
|
||||
hideLeapCats: [],
|
||||
|
@ -11,6 +11,7 @@ export interface SettingsState {
|
||||
backgroundType: 'none' | 'url' | 'color';
|
||||
background?: string;
|
||||
dark: boolean;
|
||||
theme: "light" | "dark" | "auto";
|
||||
};
|
||||
calm: {
|
||||
hideNicknames: boolean;
|
||||
@ -22,7 +23,11 @@ export interface SettingsState {
|
||||
remoteContentPolicy: RemoteContentPolicy;
|
||||
leap: {
|
||||
categories: LeapCategories[];
|
||||
}
|
||||
};
|
||||
tutorial: {
|
||||
seen: boolean;
|
||||
joined?: number;
|
||||
};
|
||||
set: (fn: (state: SettingsState) => void) => void
|
||||
};
|
||||
|
||||
@ -33,11 +38,14 @@ export const selectSettingsState =
|
||||
|
||||
export const selectCalmState = (s: SettingsState) => s.calm;
|
||||
|
||||
export const selectDisplayState = (s: SettingsState) => s.display;
|
||||
|
||||
const useSettingsState = create<SettingsStateZus>((set) => ({
|
||||
display: {
|
||||
backgroundType: 'none',
|
||||
background: undefined,
|
||||
dark: false,
|
||||
theme: "auto"
|
||||
},
|
||||
calm: {
|
||||
hideNicknames: false,
|
||||
@ -55,6 +63,10 @@ const useSettingsState = create<SettingsStateZus>((set) => ({
|
||||
leap: {
|
||||
categories: leapCategories,
|
||||
},
|
||||
tutorial: {
|
||||
seen: false,
|
||||
joined: undefined
|
||||
},
|
||||
set: (fn: (state: SettingsState) => void) => set(produce(fn))
|
||||
}));
|
||||
|
||||
|
@ -68,7 +68,6 @@ const Root = withSettingsState(styled.div`
|
||||
`, ['display']);
|
||||
|
||||
const StatusBarWithRouter = withRouter(StatusBar);
|
||||
|
||||
class App extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
@ -135,13 +134,14 @@ class App extends React.Component {
|
||||
const { state, props } = this;
|
||||
const associations = state.associations ?
|
||||
state.associations : { contacts: {} };
|
||||
const theme = props.dark ? dark : light;
|
||||
const background = this.props.background;
|
||||
const theme =
|
||||
((props.dark && props?.display?.theme == "auto") ||
|
||||
props?.display?.theme == "dark"
|
||||
) ? dark : light;
|
||||
|
||||
const notificationsCount = state.notificationsCount || 0;
|
||||
const doNotDisturb = state.doNotDisturb || false;
|
||||
const ourContact = this.state.contacts[`~${this.ship}`] || null;
|
||||
|
||||
return (
|
||||
<ThemeProvider theme={theme}>
|
||||
<Helmet>
|
||||
@ -196,5 +196,4 @@ class App extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
export default withLocalState(process.env.NODE_ENV === 'production' ? App : hot(App));
|
||||
|
||||
export default withSettingsState(withLocalState(process.env.NODE_ENV === 'production' ? App : hot(App)), ['display']);
|
||||
|
@ -130,7 +130,13 @@ class ChatInput extends Component<ChatInputProps, ChatInputState> {
|
||||
props.ourContact &&
|
||||
((props.ourContact.avatar !== null) && !props.hideAvatars)
|
||||
)
|
||||
? <BaseImage src={props.ourContact.avatar} height={16} width={16} className="dib" />
|
||||
? <BaseImage
|
||||
src={props.ourContact.avatar}
|
||||
height={16}
|
||||
width={16}
|
||||
style={{ objectFit: 'cover' }}
|
||||
display='inline-block'
|
||||
/>
|
||||
: <Sigil
|
||||
ship={window.ship}
|
||||
size={16}
|
||||
|
@ -37,6 +37,7 @@ import styled from 'styled-components';
|
||||
import useLocalState from '~/logic/state/local';
|
||||
import useSettingsState, {selectCalmState} from "~/logic/state/settings";
|
||||
import Timestamp from '~/views/components/Timestamp';
|
||||
import {useIdlingState} from '~/logic/lib/idling';
|
||||
|
||||
export const DATESTAMP_FORMAT = '[~]YYYY.M.D';
|
||||
|
||||
@ -64,16 +65,16 @@ export const DayBreak = ({ when, shimTop = false }: DayBreakProps) => (
|
||||
|
||||
export const UnreadMarker = React.forwardRef(({ dayBreak, when, api, association }, ref) => {
|
||||
const [visible, setVisible] = useState(false);
|
||||
const idling = useIdlingState();
|
||||
const dismiss = useCallback(() => {
|
||||
api.hark.markCountAsRead(association, '/', 'message');
|
||||
}, [api, association]);
|
||||
|
||||
useEffect(() => {
|
||||
if(visible) {
|
||||
console.log('dismissing');
|
||||
if(visible && !idling) {
|
||||
dismiss();
|
||||
}
|
||||
}, [visible]);
|
||||
}, [visible, idling]);
|
||||
|
||||
return (
|
||||
<Row
|
||||
@ -293,6 +294,7 @@ export const MessageAuthor = ({
|
||||
contact?.avatar && !hideAvatars ? (
|
||||
<BaseImage
|
||||
display='inline-block'
|
||||
style={{ objectFit: 'cover' }}
|
||||
src={contact.avatar}
|
||||
height={16}
|
||||
width={16}
|
||||
|
@ -88,8 +88,6 @@ export default class ChatWindow extends Component<
|
||||
|
||||
componentDidMount() {
|
||||
this.calculateUnreadIndex();
|
||||
window.addEventListener('blur', this.handleWindowBlur);
|
||||
window.addEventListener('focus', this.handleWindowFocus);
|
||||
setTimeout(() => {
|
||||
if (this.props.scrollTo) {
|
||||
this.scrollToUnread();
|
||||
@ -99,11 +97,6 @@ export default class ChatWindow extends Component<
|
||||
}, this.INITIALIZATION_MAX_TIME);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
window.removeEventListener('blur', this.handleWindowBlur);
|
||||
window.removeEventListener('focus', this.handleWindowFocus);
|
||||
}
|
||||
|
||||
calculateUnreadIndex() {
|
||||
const { graph, unreadCount } = this.props;
|
||||
const unreadIndex = graph.keys()[unreadCount];
|
||||
@ -113,7 +106,6 @@ export default class ChatWindow extends Component<
|
||||
});
|
||||
return;
|
||||
}
|
||||
console.log(`found unread: ${unreadIndex}`);
|
||||
this.setState({
|
||||
unreadIndex
|
||||
});
|
||||
@ -201,7 +193,6 @@ export default class ChatWindow extends Component<
|
||||
this.calculateUnreadIndex();
|
||||
}
|
||||
this.fetchPending = false;
|
||||
console.log(currSize, graph.size);
|
||||
return currSize === graph.size;
|
||||
}
|
||||
|
||||
|
@ -40,6 +40,18 @@ const renderers = {
|
||||
</Text>
|
||||
);
|
||||
},
|
||||
blockquote: ({ children }) => {
|
||||
return (
|
||||
<Text
|
||||
lineHeight="20px"
|
||||
display="block"
|
||||
borderLeft="1px solid"
|
||||
color="black"
|
||||
paddingLeft={2}>
|
||||
{children}
|
||||
</Text>
|
||||
)
|
||||
},
|
||||
paragraph: ({ children }) => {
|
||||
return (
|
||||
<Text fontSize='1' lineHeight={'20px'}>
|
||||
|
@ -50,12 +50,6 @@ button {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #000;
|
||||
font-weight: 400;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-weight: 400;
|
||||
}
|
||||
@ -80,10 +74,6 @@ h2 {
|
||||
font-family: 'Source Code Pro', monospace;
|
||||
}
|
||||
|
||||
.bg-welcome-green {
|
||||
background-color: #ecf6f2;
|
||||
}
|
||||
|
||||
.c-default {
|
||||
cursor: default;
|
||||
}
|
||||
@ -158,38 +148,11 @@ h2 {
|
||||
/* responsive */
|
||||
|
||||
@media all and (max-width: 34.375em) {
|
||||
.dn-s {
|
||||
display: none;
|
||||
}
|
||||
.flex-basis-full-s {
|
||||
flex-basis: 100%;
|
||||
}
|
||||
.h-100-minus-96-s {
|
||||
height: calc(100% - 96px);
|
||||
}
|
||||
.unread-notice {
|
||||
top: 96px;
|
||||
}
|
||||
}
|
||||
|
||||
@media all and (min-width: 34.375em) and (max-width: 46.875em) {
|
||||
.flex-basis-250-m {
|
||||
flex-basis: 250px;
|
||||
}
|
||||
}
|
||||
|
||||
@media all and (min-width: 46.875em) and (max-width: 60em) {
|
||||
.flex-basis-250-l {
|
||||
flex-basis: 250px;
|
||||
}
|
||||
}
|
||||
|
||||
@media all and (min-width: 60em) {
|
||||
.flex-basis-250-xl {
|
||||
flex-basis: 250px;
|
||||
}
|
||||
}
|
||||
|
||||
blockquote {
|
||||
padding: 0 0 0 16px;
|
||||
margin: 0;
|
||||
@ -345,79 +308,13 @@ pre.CodeMirror-placeholder.CodeMirror-line-like {
|
||||
/* dark */
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.bg-black-d {
|
||||
background-color: black;
|
||||
}
|
||||
.white-d {
|
||||
color: white;
|
||||
}
|
||||
.gray1-d {
|
||||
color: #4d4d4d;
|
||||
}
|
||||
.gray2-d {
|
||||
color: #7f7f7f;
|
||||
}
|
||||
.gray3-d {
|
||||
color: #b1b2b3;
|
||||
}
|
||||
.gray4-d {
|
||||
color: #e6e6e6;
|
||||
}
|
||||
.bg-gray0-d {
|
||||
background-color: #333;
|
||||
}
|
||||
.bg-gray1-d {
|
||||
background-color: #4d4d4d;
|
||||
}
|
||||
.b--gray0-d {
|
||||
border-color: #333;
|
||||
}
|
||||
.b--gray1-d {
|
||||
border-color: #4d4d4d;
|
||||
}
|
||||
.b--gray2-d {
|
||||
border-color: #7f7f7f;
|
||||
}
|
||||
.b--white-d {
|
||||
border-color: #fff;
|
||||
}
|
||||
.b--green2-d {
|
||||
border-color: #2aa779;
|
||||
}
|
||||
.bb-d {
|
||||
border-bottom-width: 1px;
|
||||
border-bottom-style: solid;
|
||||
}
|
||||
.invert-d {
|
||||
filter: invert(1);
|
||||
}
|
||||
.o-80-d {
|
||||
opacity: 0.8;
|
||||
}
|
||||
.focus-b--white-d:focus {
|
||||
border-color: #fff;
|
||||
}
|
||||
a {
|
||||
color: #fff;
|
||||
}
|
||||
.hover-bg-gray1-d:hover {
|
||||
background-color: #4d4d4d;
|
||||
}
|
||||
blockquote {
|
||||
border-left: 1px solid white;
|
||||
}
|
||||
|
||||
.contrast-10-d {
|
||||
filter: contrast(0.1);
|
||||
}
|
||||
|
||||
.bg-none-d {
|
||||
background: none;
|
||||
border-left: 1px solid inherit;
|
||||
}
|
||||
|
||||
/* codemirror */
|
||||
.chat .cm-s-tlon.CodeMirror {
|
||||
color: #fff;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.chat .cm-s-tlon span.cm-def {
|
||||
@ -468,7 +365,7 @@ pre.CodeMirror-placeholder.CodeMirror-line-like {
|
||||
/* set rules w/ both color & bg-color last to preserve legibility */
|
||||
.chat .CodeMirror-selected {
|
||||
background: var(--medium-gray) !important;
|
||||
color: white;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.chat .cm-s-tlon span.cm-comment {
|
||||
|
@ -13,6 +13,7 @@ import Tile from './components/tiles/tile';
|
||||
import Groups from './components/Groups';
|
||||
import ModalButton from './components/ModalButton';
|
||||
import { StatelessAsyncButton } from '~/views/components/StatelessAsyncButton';
|
||||
import { StarIcon } from '~/views/components/StarIcon';
|
||||
import { writeText } from '~/logic/lib/util';
|
||||
import { useModal } from "~/logic/lib/useModal";
|
||||
import { NewGroup } from "~/views/landscape/components/NewGroup";
|
||||
@ -45,6 +46,7 @@ const tutSelector = f.pick(['tutorialProgress', 'nextTutStep', 'hideGroups']);
|
||||
export default function LaunchApp(props) {
|
||||
const history = useHistory();
|
||||
const [hashText, setHashText] = useState(props.baseHash);
|
||||
const [exitingTut, setExitingTut] = useState(false);
|
||||
const hashBox = (
|
||||
<Box
|
||||
position={["relative", "absolute"]}
|
||||
@ -103,10 +105,11 @@ export default function LaunchApp(props) {
|
||||
e.stopPropagation();
|
||||
if(!hasTutorialGroup(props)) {
|
||||
await props.api.groups.join(TUTORIAL_HOST, TUTORIAL_GROUP);
|
||||
await props.api.settings.putEntry('tutorial', 'joined', Date.now());
|
||||
await waiter(hasTutorialGroup);
|
||||
await Promise.all(
|
||||
[TUTORIAL_BOOK, TUTORIAL_CHAT, TUTORIAL_LINKS].map(graph =>
|
||||
props.api.graph.join(TUTORIAL_HOST, graph)));
|
||||
props.api.graph.joinGraph(TUTORIAL_HOST, graph)));
|
||||
|
||||
await waiter(p => {
|
||||
return `/ship/${TUTORIAL_HOST}/${TUTORIAL_CHAT}` in p.associations.graph &&
|
||||
@ -117,26 +120,39 @@ export default function LaunchApp(props) {
|
||||
nextTutStep();
|
||||
dismiss();
|
||||
}
|
||||
return (
|
||||
<Col maxWidth="350px" gapY="2" p="3">
|
||||
<Box position="absolute" left="-16px" top="-16px">
|
||||
<Icon width="32px" height="32px" color="blue" display="block" icon="LargeBullet" />
|
||||
</Box>
|
||||
<Text lineHeight="tall" fontWeight="medium">Welcome</Text>
|
||||
<Text lineHeight="tall">
|
||||
You have been invited to use Landscape, an interface to chat
|
||||
and interact with communities
|
||||
<br />
|
||||
Would you like a tour of Landscape?
|
||||
</Text>
|
||||
<Row gapX="2" justifyContent="flex-end">
|
||||
<Button backgroundColor="washedGray" onClick={onDismiss}>Skip</Button>
|
||||
<StatelessAsyncButton primary onClick={onContinue}>
|
||||
Yes
|
||||
</StatelessAsyncButton>
|
||||
</Row>
|
||||
</Col>
|
||||
)}
|
||||
return exitingTut ? (
|
||||
<Col maxWidth="350px" p="3">
|
||||
<Icon icon="Info" fill="black"></Icon>
|
||||
<Text my="3" lineHeight="tall">
|
||||
You can always restart the tutorial by typing "tutorial" in Leap
|
||||
</Text>
|
||||
<Row gapX="2" justifyContent="flex-end">
|
||||
<Button primary onClick={onDismiss}>Ok</Button>
|
||||
</Row>
|
||||
</Col>
|
||||
) : (
|
||||
<Col maxWidth="350px" p="3">
|
||||
<Box position="absolute" left="-16px" top="-16px">
|
||||
<StarIcon width="32px" height="32px" color="blue" display="block" />
|
||||
</Box>
|
||||
<Text mb="3" lineHeight="tall" fontWeight="medium">Welcome</Text>
|
||||
<Text mb="3" lineHeight="tall">
|
||||
You have been invited to use Landscape, an interface to chat
|
||||
and interact with communities
|
||||
<br />
|
||||
Would you like a tour of Landscape?
|
||||
</Text>
|
||||
<Row gapX="2" justifyContent="flex-end">
|
||||
<Button
|
||||
backgroundColor="washedGray"
|
||||
onClick={() => setExitingTut(true)}
|
||||
>Skip</Button>
|
||||
<StatelessAsyncButton primary onClick={onContinue}>
|
||||
Yes
|
||||
</StatelessAsyncButton>
|
||||
</Row>
|
||||
</Col>
|
||||
)}
|
||||
});
|
||||
const hasLoaded = useMemo(() => Object.keys(props.contacts).length > 0, [props.contacts]);
|
||||
|
||||
|
@ -2,21 +2,26 @@ import React, { useRef } from 'react';
|
||||
import { Box, Text, Col } from '@tlon/indigo-react';
|
||||
import f from 'lodash/fp';
|
||||
import _ from 'lodash';
|
||||
import moment from 'moment';
|
||||
|
||||
import { Associations, Association, Unreads, UnreadStats } from '@urbit/api';
|
||||
import { alphabeticalOrder } from '~/logic/lib/util';
|
||||
import { getUnreadCount, getNotificationCount } from '~/logic/lib/hark';
|
||||
import Tile from '../components/tiles/tile';
|
||||
import { useTutorialModal } from '~/views/components/useTutorialModal';
|
||||
import { TUTORIAL_HOST, TUTORIAL_GROUP } from '~/logic/lib/tutorialModal';
|
||||
import useSettingsState, { selectCalmState } from '~/logic/state/settings';
|
||||
import { TUTORIAL_HOST, TUTORIAL_GROUP, TUTORIAL_GROUP_RESOURCE } from '~/logic/lib/tutorialModal';
|
||||
import useSettingsState, { selectCalmState, SettingsState } from '~/logic/state/settings';
|
||||
|
||||
interface GroupsProps {
|
||||
associations: Associations;
|
||||
}
|
||||
|
||||
const sortGroupsAlph = (a: Association, b: Association) =>
|
||||
alphabeticalOrder(a.metadata.title, b.metadata.title);
|
||||
a.group === TUTORIAL_GROUP_RESOURCE
|
||||
? -1
|
||||
: b.group === TUTORIAL_GROUP_RESOURCE
|
||||
? 1
|
||||
: alphabeticalOrder(a.metadata.title, b.metadata.title);
|
||||
|
||||
const getGraphUnreads = (associations: Associations, unreads: Unreads) => (path: string) =>
|
||||
f.flow(
|
||||
@ -72,6 +77,7 @@ interface GroupProps {
|
||||
unreads: number;
|
||||
first: boolean;
|
||||
}
|
||||
const selectJoined = (s: SettingsState) => s.tutorial.joined;
|
||||
function Group(props: GroupProps) {
|
||||
const { path, title, unreads, updates, first = false } = props;
|
||||
const anchorRef = useRef<HTMLElement>(null);
|
||||
@ -79,14 +85,18 @@ function Group(props: GroupProps) {
|
||||
useTutorialModal(
|
||||
'start',
|
||||
isTutorialGroup,
|
||||
anchorRef.current
|
||||
anchorRef
|
||||
);
|
||||
const { hideUnreads } = useSettingsState(selectCalmState)
|
||||
const joined = useSettingsState(selectJoined);
|
||||
return (
|
||||
<Tile ref={anchorRef} position="relative" bg={isTutorialGroup ? 'lightBlue' : undefined} to={`/~landscape${path}`} gridColumnStart={first ? '1' : null}>
|
||||
<Col height="100%" justifyContent="space-between">
|
||||
<Text>{title}</Text>
|
||||
{!hideUnreads && (<Col>
|
||||
{isTutorialGroup && joined &&
|
||||
(<Text>{Math.floor(moment.duration(moment(joined).add(14, 'days').diff(moment())).as('days'))} days remaining</Text>)
|
||||
}
|
||||
{updates > 0 &&
|
||||
(<Text mt="1" color="blue">{updates} update{updates !== 1 && 's'} </Text>)
|
||||
}
|
||||
|
@ -17,7 +17,6 @@ export default class CustomTile extends React.PureComponent {
|
||||
>
|
||||
<BaseImage
|
||||
position='absolute'
|
||||
className="invert-d"
|
||||
style={{ left: 38, top: 38 }}
|
||||
src='/~launch/img/UnknownCustomTile.png'
|
||||
width='48px'
|
||||
|
@ -53,25 +53,10 @@ button {
|
||||
|
||||
/* dark */
|
||||
@media all and (prefers-color-scheme: dark) {
|
||||
.bg-gray0-d {
|
||||
background-color: #333;
|
||||
}
|
||||
.bg-gray1-d {
|
||||
background-color: #4d4d4d;
|
||||
}
|
||||
.bg-gray2-d {
|
||||
background-color: #7f7f7f;
|
||||
}
|
||||
.b--gray1-d {
|
||||
border-color: #4d4d4d;
|
||||
}
|
||||
.white-d {
|
||||
color: #fff;
|
||||
}
|
||||
.invert-d {
|
||||
filter: invert(1);
|
||||
}
|
||||
.hover-bg-gray1-d:hover {
|
||||
background-color: #4d4d4d;
|
||||
}
|
||||
}
|
||||
|
@ -69,6 +69,7 @@ export function LinkResource(props: LinkResourceProps) {
|
||||
render={(props) => {
|
||||
return (
|
||||
<LinkWindow
|
||||
key={rid}
|
||||
s3={s3}
|
||||
association={resource}
|
||||
contacts={contacts}
|
||||
|
@ -16,23 +16,4 @@
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* responsive */
|
||||
@media all and (max-width: 34.375em) {
|
||||
.dn-s {
|
||||
display: none;
|
||||
}
|
||||
.flex-basis-100-s, .flex-basis-full-s {
|
||||
flex-basis: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@media all and (min-width: 34.375em) {
|
||||
.db-ns {
|
||||
display: block;
|
||||
}
|
||||
.flex-basis-30-ns {
|
||||
flex-basis: 30vw;
|
||||
}
|
||||
}
|
@ -51,7 +51,7 @@ export default function NotificationsScreen(props: any): ReactElement {
|
||||
.map(g => props.associations?.groups?.[g]?.metadata?.title)
|
||||
.join(', ');
|
||||
const anchorRef = useRef<HTMLElement | null>(null);
|
||||
useTutorialModal('notifications', true, anchorRef.current);
|
||||
useTutorialModal('notifications', true, anchorRef);
|
||||
return (
|
||||
<Switch>
|
||||
<Route
|
||||
@ -74,7 +74,8 @@ export default function NotificationsScreen(props: any): ReactElement {
|
||||
borderBottom="1"
|
||||
borderBottomColor="washedGray"
|
||||
>
|
||||
<Text>Updates</Text>
|
||||
|
||||
<Text ref={anchorRef}>Notifications</Text>
|
||||
<Row
|
||||
justifyContent="space-between"
|
||||
>
|
||||
|
@ -47,7 +47,7 @@ export function Profile(props: any): ReactElement {
|
||||
|
||||
const anchorRef = useRef<HTMLElement | null>(null);
|
||||
|
||||
useTutorialModal('profile', ship === `~${window.ship}`, anchorRef.current);
|
||||
useTutorialModal('profile', ship === `~${window.ship}`, anchorRef);
|
||||
|
||||
return (
|
||||
<Center
|
||||
|
@ -3,7 +3,7 @@ import moment from 'moment';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { BigInteger } from 'big-integer';
|
||||
|
||||
import { Box } from '@tlon/indigo-react';
|
||||
import { Box, Text } from '@tlon/indigo-react';
|
||||
import { Graph } from '@urbit/api';
|
||||
|
||||
import { getLatestRevision } from '~/logic/lib/publish';
|
||||
@ -24,13 +24,14 @@ function NavigationItem(props: {
|
||||
textAlign={props.prev ? 'left' : 'right'}
|
||||
>
|
||||
<Link to={props.url}>
|
||||
<Box color="gray" mb={2}>
|
||||
<Text display='block' color="gray">
|
||||
{props.prev ? 'Previous' : 'Next'}
|
||||
</Box>
|
||||
<Box mb={1}>{props.title}</Box>
|
||||
</Text>
|
||||
<Text display='block' lineHeight="tall">{props.title}</Text>
|
||||
<Timestamp
|
||||
stamp={moment(props.date)}
|
||||
time={false}
|
||||
fontSize="1"
|
||||
justifyContent={props.prev ? 'flex-start' : 'flex-end'}
|
||||
/>
|
||||
</Link>
|
||||
|
@ -68,7 +68,15 @@ export function NotePreview(props: NotePreviewProps) {
|
||||
unwrapDisallowed
|
||||
allowedTypes={['text', 'root', 'break', 'paragraph', 'image']}
|
||||
renderers={{
|
||||
image: props => <Image src={props.src} maxHeight='300px' style={{ objectFit: 'cover' }} />
|
||||
image: props => (
|
||||
<Box
|
||||
backgroundImage={`url(${props.src})`}
|
||||
style={{ backgroundSize: 'cover',
|
||||
backgroundPosition: "center" }}
|
||||
>
|
||||
<Image src={props.src} opacity="0" maxHeight="300px"/>
|
||||
</Box>
|
||||
)
|
||||
}}
|
||||
source={snippet}
|
||||
/>
|
||||
|
@ -5,41 +5,6 @@
|
||||
--light-gray: rgba(0,0,0,0.08);
|
||||
}
|
||||
|
||||
.bg-welcome-green {
|
||||
background-color: #ECF6F2;
|
||||
}
|
||||
|
||||
@media all and (max-width: 34.375em) {
|
||||
.dn-s {
|
||||
display: none;
|
||||
}
|
||||
.flex-basis-100-s, .flex-basis-full-s {
|
||||
flex-basis: 100%;
|
||||
}
|
||||
.h-100-m-40-s {
|
||||
height: calc(100% - 40px);
|
||||
}
|
||||
.black-s {
|
||||
color: #000;
|
||||
}
|
||||
}
|
||||
|
||||
@media all and (min-width: 34.375em) {
|
||||
.db-ns {
|
||||
display: block;
|
||||
}
|
||||
.flex-basis-250-ns {
|
||||
flex-basis: 250px;
|
||||
}
|
||||
.h-100-m-40-ns {
|
||||
height: calc(100% - 40px);
|
||||
}
|
||||
}
|
||||
|
||||
.bg-light-green {
|
||||
background: rgba(42, 167, 121, 0.1);
|
||||
}
|
||||
|
||||
.NotebookButton {
|
||||
border-radius:2px;
|
||||
cursor: pointer;
|
||||
@ -207,57 +172,6 @@
|
||||
}
|
||||
|
||||
@media all and (prefers-color-scheme: dark) {
|
||||
.bg-black-d {
|
||||
background-color: black;
|
||||
}
|
||||
.white-d {
|
||||
color: white;
|
||||
}
|
||||
.gray1-d {
|
||||
color: #4d4d4d;
|
||||
}
|
||||
.gray2-d {
|
||||
color: #7f7f7f;
|
||||
}
|
||||
.gray3-d {
|
||||
color: #b1b2b3;
|
||||
}
|
||||
.gray4-d {
|
||||
color: #e6e6e6;
|
||||
}
|
||||
.bg-gray0-d {
|
||||
background-color: #333;
|
||||
}
|
||||
.bg-gray1-d {
|
||||
background-color: #4d4d4d;
|
||||
}
|
||||
.b--gray0-d {
|
||||
border-color: #333;
|
||||
}
|
||||
.b--gray1-d {
|
||||
border-color: #4d4d4d;
|
||||
}
|
||||
.b--gray2-d {
|
||||
border-color: #7f7f7f;
|
||||
}
|
||||
.b--white-d {
|
||||
border-color: #fff;
|
||||
}
|
||||
.invert-d {
|
||||
filter: invert(1);
|
||||
}
|
||||
.o-60-d {
|
||||
opacity: .6;
|
||||
}
|
||||
a {
|
||||
color: #fff;
|
||||
}
|
||||
.focus-b--white-d:focus {
|
||||
border-color: #fff;
|
||||
}
|
||||
.hover-bg-gray1-d:hover {
|
||||
background-color: #4d4d4d;
|
||||
}
|
||||
.options.open {
|
||||
background-color: #4d4d4d;
|
||||
}
|
||||
@ -266,32 +180,32 @@
|
||||
}
|
||||
.publish .cm-s-tlon.CodeMirror {
|
||||
background: unset;
|
||||
color: #fff;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.publish .cm-s-tlon span.cm-def {
|
||||
color: white;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.publish .cm-s-tlon span.cm-variable {
|
||||
color: white;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.publish .cm-s-tlon span.cm-variable-2 {
|
||||
color: white;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.publish .cm-s-tlon span.cm-variable-3,
|
||||
.publish .cm-s-tlon span.cm-type {
|
||||
color: white;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.publish .cm-s-tlon span.cm-property {
|
||||
color: white;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.publish .cm-s-tlon span.cm-operator {
|
||||
color: white;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
|
||||
@ -325,7 +239,7 @@
|
||||
color: black;
|
||||
display: inline-block;
|
||||
padding: 0;
|
||||
background-color: rgba(255,255,255, 0.3);
|
||||
background-color: rgba(0,255,255, 0.3);
|
||||
border-radius: 2px;
|
||||
}
|
||||
}
|
||||
|
@ -78,9 +78,9 @@ export function CalmPrefs(props: {
|
||||
return (
|
||||
<Formik initialValues={initialValues} onSubmit={onSubmit}>
|
||||
<Form>
|
||||
<BackButton/>
|
||||
<Col borderBottom="1" borderBottomColor="washedGray" p="5" pt="4" gapY="5">
|
||||
<BackButton/>
|
||||
<Col gapY="1">
|
||||
<Col gapY="1" mt="0">
|
||||
<Text color="black" fontSize={2} fontWeight="medium">
|
||||
CalmEngine
|
||||
</Text>
|
||||
|
@ -3,6 +3,8 @@ import React from "react";
|
||||
import {
|
||||
Col,
|
||||
Text,
|
||||
Label,
|
||||
ManagedRadioButtonField as Radio
|
||||
} from "@tlon/indigo-react";
|
||||
import { Formik, Form } from "formik";
|
||||
import * as Yup from "yup";
|
||||
@ -20,13 +22,16 @@ const formSchema = Yup.object().shape({
|
||||
.oneOf(["none", "color", "url"], "invalid")
|
||||
.required("Required"),
|
||||
background: Yup.string(),
|
||||
|
||||
theme: Yup.string()
|
||||
.oneOf(["light", "dark", "auto"])
|
||||
.required("Required")
|
||||
});
|
||||
|
||||
interface FormSchema {
|
||||
bgType: BgType;
|
||||
bgColor: string | undefined;
|
||||
bgUrl: string | undefined;
|
||||
theme: string;
|
||||
}
|
||||
|
||||
interface DisplayFormProps {
|
||||
@ -43,6 +48,7 @@ export default function DisplayForm(props: DisplayFormProps) {
|
||||
display: {
|
||||
background,
|
||||
backgroundType,
|
||||
theme
|
||||
}
|
||||
} = useSettingsState(settingsSel);
|
||||
|
||||
@ -63,13 +69,13 @@ export default function DisplayForm(props: DisplayFormProps) {
|
||||
{
|
||||
bgType: backgroundType,
|
||||
bgColor: bgColor || "",
|
||||
bgUrl
|
||||
bgUrl,
|
||||
theme
|
||||
} as FormSchema
|
||||
}
|
||||
onSubmit={async (values, actions) => {
|
||||
let promises = [] as Promise<any>[];
|
||||
promises.push(api.settings.putEntry('display', 'backgroundType', values.bgType));
|
||||
|
||||
promises.push(
|
||||
api.settings.putEntry('display', 'background',
|
||||
values.bgType === "color"
|
||||
@ -79,6 +85,7 @@ export default function DisplayForm(props: DisplayFormProps) {
|
||||
: false
|
||||
));
|
||||
|
||||
promises.push(api.settings.putEntry('display', 'theme', values.theme));
|
||||
await Promise.all(promises);
|
||||
|
||||
actions.setStatus({ success: null });
|
||||
@ -87,9 +94,9 @@ export default function DisplayForm(props: DisplayFormProps) {
|
||||
>
|
||||
{(props) => (
|
||||
<Form>
|
||||
<BackButton/>
|
||||
<Col p="5" pt="4" gapY="5">
|
||||
<BackButton/>
|
||||
<Col gapY="2">
|
||||
<Col gapY="1" mt="0">
|
||||
<Text color="black" fontSize={2} fontWeight="medium">
|
||||
Display Preferences
|
||||
</Text>
|
||||
@ -103,6 +110,10 @@ export default function DisplayForm(props: DisplayFormProps) {
|
||||
api={api}
|
||||
s3={s3}
|
||||
/>
|
||||
<Label>Theme</Label>
|
||||
<Radio name="theme" id="light" label="Light"/>
|
||||
<Radio name="theme" id="dark" label="Dark" />
|
||||
<Radio name="theme" id="auto" label="Auto" />
|
||||
<AsyncButton primary width="fit-content" type="submit">
|
||||
Save
|
||||
</AsyncButton>
|
||||
|
@ -74,9 +74,10 @@ export function LeapSettings(props: { api: GlobalApi; }) {
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<BackButton/>
|
||||
<Col p="5" pt="4" gapY="5">
|
||||
<BackButton/>
|
||||
<Col gapY="1">
|
||||
<Col gapY="1" mt="0">
|
||||
<Text fontSize="2" fontWeight="medium">
|
||||
Leap
|
||||
</Text>
|
||||
@ -97,5 +98,6 @@ export function LeapSettings(props: { api: GlobalApi; }) {
|
||||
</Form>
|
||||
</FormikOnBlur>
|
||||
</Col>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -51,9 +51,10 @@ export function NotificationPreferences(props: {
|
||||
}, [api]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<BackButton/>
|
||||
<Col p="5" pt="4" gapY="5">
|
||||
<BackButton/>
|
||||
<Col gapY="1">
|
||||
<Col gapY="1" mt="0">
|
||||
<Text fontSize="2" fontWeight="medium">
|
||||
Notification Preferences
|
||||
</Text>
|
||||
@ -84,5 +85,6 @@ export function NotificationPreferences(props: {
|
||||
</Form>
|
||||
</FormikOnBlur>
|
||||
</Col>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -64,9 +64,9 @@ export default function S3Form(props: S3FormProps): ReactElement {
|
||||
onSubmit={onSubmit}
|
||||
>
|
||||
<Form>
|
||||
<BackButton/>
|
||||
<Col maxWidth="600px" gapY="5">
|
||||
<BackButton/>
|
||||
<Col gapY="1">
|
||||
<Col gapY="1" mt="0">
|
||||
<Text color="black" fontSize={2} fontWeight="medium">
|
||||
S3 Storage Setup
|
||||
</Text>
|
||||
|
@ -17,9 +17,10 @@ interface SecuritySettingsProps {
|
||||
export default function SecuritySettings({ api }: SecuritySettingsProps) {
|
||||
const [allSessions, setAllSessions] = useState(false);
|
||||
return (
|
||||
<>
|
||||
<BackButton/>
|
||||
<Col gapY="5" p="5" pt="4">
|
||||
<BackButton/>
|
||||
<Col gapY="1">
|
||||
<Col gapY="1" mt="0">
|
||||
<Text fontSize={2} fontWeight="medium">
|
||||
Security Preferences
|
||||
</Text>
|
||||
@ -56,5 +57,6 @@ export default function SecuritySettings({ api }: SecuritySettingsProps) {
|
||||
</form>
|
||||
</Col>
|
||||
</Col>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -47,6 +47,7 @@ export default function Author(props: AuthorProps): ReactElement {
|
||||
<BaseImage
|
||||
display='inline-block'
|
||||
src={contact.avatar}
|
||||
style={{ objectFit: 'cover' }}
|
||||
height={16}
|
||||
width={16}
|
||||
/>
|
||||
|
@ -114,7 +114,7 @@ export function DropdownSearch<C>(props: DropdownSearchProps<C>): ReactElement {
|
||||
search(e.target.value);
|
||||
setQuery(e.target.value);
|
||||
},
|
||||
[setQuery]
|
||||
[search, onChange]
|
||||
);
|
||||
|
||||
const dropdown = useMemo(() => {
|
||||
|
@ -98,6 +98,7 @@ class ProfileOverlay extends PureComponent<
|
||||
contact?.avatar && !hideAvatars ? (
|
||||
<BaseImage
|
||||
display='inline-block'
|
||||
style={{ objectFit: 'cover' }}
|
||||
src={contact.avatar}
|
||||
height={72}
|
||||
width={72}
|
||||
|
@ -3,7 +3,7 @@ import RemoteContent from '~/views/components/RemoteContent';
|
||||
import { hasProvider } from 'oembed-parser';
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
import RemarkDisableTokenizers from 'remark-disable-tokenizers';
|
||||
import { BaseAnchor, Text } from '@tlon/indigo-react';
|
||||
import { Anchor, Text } from '@tlon/indigo-react';
|
||||
import { isValidPatp } from 'urbit-ob';
|
||||
|
||||
import { deSig } from '~/logic/lib/util';
|
||||
@ -37,7 +37,7 @@ const RichText = React.memo(({ disableRemoteContent, ...props }) => (
|
||||
return <RemoteContent className="mw-100" url={linkProps.href} />;
|
||||
}
|
||||
|
||||
return <BaseAnchor target='_blank' rel='noreferrer noopener' borderBottom='1px solid' remoteContentPolicy={remoteContentPolicy} {...linkProps}>{linkProps.children}</BaseAnchor>;
|
||||
return <Anchor target='_blank' rel='noreferrer noopener' borderBottom='1px solid' remoteContentPolicy={remoteContentPolicy} {...linkProps}>{linkProps.children}</Anchor>;
|
||||
},
|
||||
linkReference: (linkProps) => {
|
||||
const linkText = String(linkProps.children[0].props.children);
|
||||
@ -46,6 +46,18 @@ const RichText = React.memo(({ disableRemoteContent, ...props }) => (
|
||||
}
|
||||
return linkText;
|
||||
},
|
||||
blockquote: (blockquoteProps) => {
|
||||
return (
|
||||
<Text
|
||||
lineHeight="20px"
|
||||
display="block"
|
||||
borderLeft="1px solid"
|
||||
color="black"
|
||||
paddingLeft={2} {...props}>
|
||||
{blockquoteProps.children}
|
||||
</Text>
|
||||
)
|
||||
},
|
||||
paragraph: (paraProps) => {
|
||||
return <Text display={props.inline ? 'inline' : 'block'} mb='2' {...props}>{paraProps.children}</Text>;
|
||||
}
|
||||
|
@ -37,14 +37,16 @@ interface InviteSearchProps<I extends string> {
|
||||
maxLength?: number;
|
||||
}
|
||||
|
||||
const getNicknameForShips = (groups: Groups, contacts: Rolodex): readonly [string[], Map<string, string[]>] => {
|
||||
const getNicknameForShips = (groups: Groups, contacts: Rolodex, selected: string[]): readonly [string[], Map<string, string[]>] => {
|
||||
const peerSet = new Set<string>();
|
||||
const nicknames = new Map<string, string[]>();
|
||||
_.forEach(groups, (group, path) => {
|
||||
if (group.members.size > 0) {
|
||||
const groupEntries = group.members.values();
|
||||
for (const member of groupEntries) {
|
||||
peerSet.add(member);
|
||||
if(!selected.includes(member)) {
|
||||
peerSet.add(member);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -116,15 +118,15 @@ export function ShipSearch<I extends string, V extends Value<I>>(
|
||||
|
||||
const inputIdx = useRef(initialValues[id].length);
|
||||
|
||||
const selected: string[] = values[id] ?? [];
|
||||
const selected: string[] = useMemo(() => values[id] ?? [], [values, id]);
|
||||
|
||||
const name = () => `${props.id}[${inputIdx.current}]`;
|
||||
|
||||
const pills = selected.slice(0, inputIdx.current);
|
||||
|
||||
const [peers, nicknames] = useMemo(
|
||||
() => getNicknameForShips(props.groups, props.contacts),
|
||||
[props.contacts, props.groups]
|
||||
() => getNicknameForShips(props.groups, props.contacts, selected),
|
||||
[props.contacts, props.groups, selected]
|
||||
);
|
||||
|
||||
const renderCandidate = useCallback(
|
||||
|
27
pkg/interface/src/views/components/StarIcon.tsx
Normal file
27
pkg/interface/src/views/components/StarIcon.tsx
Normal file
@ -0,0 +1,27 @@
|
||||
import React from "react";
|
||||
import css, { SystemStyleObject } from "@styled-system/css";
|
||||
import styled from "styled-components";
|
||||
import { BaseSVG } from "@tlon/indigo-react";
|
||||
import { PropFunc } from "~/types";
|
||||
|
||||
type StarIconProps = PropFunc<typeof BaseSVG> & SvgProps;
|
||||
|
||||
interface SvgProps {
|
||||
color?: string;
|
||||
}
|
||||
const Svg = styled(BaseSVG)(({ color }: SvgProps) =>
|
||||
css({
|
||||
"& > *": {
|
||||
fill: typeof color === "undefined" ? "inherit" : color || "black",
|
||||
},
|
||||
flexShrink: 0,
|
||||
} as SystemStyleObject)
|
||||
);
|
||||
|
||||
export function StarIcon(props: StarIconProps) {
|
||||
return (
|
||||
<Svg {...props}>
|
||||
<path d="M9.72024 1.7095c.043-.97631 1.31686-1.317634 1.84226-.49363l1.595 2.50165c.3931.61651 1.2933.61651 1.6864 0l1.595-2.50164c.5254-.824012 1.7993-.482689 1.8423.49362l.1305 2.96401c.0322.73046.8118 1.18056 1.4604.84319l2.6322-1.36896c.867-.45092 1.7995.48159 1.3486 1.3486L22.484 8.12852c-.3374.64867.1127 1.42827.8431 1.46044l2.9641.13054c.9763.04299 1.3176 1.3168.4936 1.8422l-2.5017 1.5951c-.6165.3931-.6165 1.2933 0 1.6863l2.5017 1.5951c.824.5254.4827 1.7992-.4936 1.8422l-2.9641.1306c-.7304.0321-1.1805.8117-.8431 1.4604l1.3689 2.6322c.4509.867-.4816 1.7995-1.3486 1.3486l-2.6322-1.369c-.6486-.3374-1.4282.1127-1.4604.8432l-.1305 2.964c-.043.9763-1.3169 1.3176-1.8423.4936l-1.595-2.5016c-.3931-.6165-1.2933-.6165-1.6864 0l-1.595 2.5016c-.5254.824-1.79926.4827-1.84226-.4936l-.13053-2.964c-.03217-.7305-.81177-1.1806-1.46045-.8432l-2.63218 1.369c-.867.4509-1.79951-.4816-1.3486-1.3486l1.36897-2.6322c.33736-.6487-.11274-1.4283-.84319-1.4604l-2.96402-.1306c-.976304-.043-1.317628-1.3168-.49362-1.8422l2.50165-1.5951c.6165-.393.6165-1.2932 0-1.6863l-2.50165-1.5951c-.824006-.5254-.482684-1.79921.49362-1.8422l2.96402-.13054c.73045-.03217 1.18056-.81177.84319-1.46044L4.14848 5.49634c-.45091-.86701.4816-1.79952 1.3486-1.3486L8.12926 5.5167c.64868.33737 1.42828-.11273 1.46045-.84319l.13053-2.96401z" />
|
||||
</Svg>
|
||||
);
|
||||
}
|
@ -48,7 +48,7 @@ const StatusBar = (props) => {
|
||||
|
||||
const anchorRef = useRef(null);
|
||||
|
||||
const leapHighlight = useTutorialModal('leap', true, anchorRef.current);
|
||||
const leapHighlight = useTutorialModal('leap', true, anchorRef);
|
||||
|
||||
const floatLeap = leapHighlight && window.matchMedia('(max-width: 550px)').matches;
|
||||
|
||||
|
@ -111,12 +111,10 @@ export default class VirtualScroller<T> extends Component<VirtualScrollerProps<T
|
||||
scrollbar: 0
|
||||
};
|
||||
|
||||
this.updateVisible = IS_IOS
|
||||
? _.debounce(this.updateVisible.bind(this), 100)
|
||||
: this.updateVisible.bind(this);
|
||||
this.updateVisible = this.updateVisible.bind(this);
|
||||
|
||||
this.invertedKeyHandler = this.invertedKeyHandler.bind(this);
|
||||
this.onScroll = IS_IOS ? _.debounce(this.onScroll.bind(this), 150) : this.onScroll.bind(this);
|
||||
this.onScroll = IS_IOS ? _.debounce(this.onScroll.bind(this), 400) : this.onScroll.bind(this);
|
||||
this.scrollKeyMap = this.scrollKeyMap.bind(this);
|
||||
this.setWindow = this.setWindow.bind(this);
|
||||
}
|
||||
@ -155,10 +153,6 @@ export default class VirtualScroller<T> extends Component<VirtualScrollerProps<T
|
||||
if(size !== prevProps.size || pendingSize !== prevProps.pendingSize) {
|
||||
if(this.scrollLocked) {
|
||||
this.updateVisible(0);
|
||||
if(IS_IOS) {
|
||||
(this.updateVisible as any).flush();
|
||||
|
||||
}
|
||||
this.resetScroll();
|
||||
|
||||
}
|
||||
@ -375,9 +369,6 @@ export default class VirtualScroller<T> extends Component<VirtualScrollerProps<T
|
||||
return;
|
||||
}
|
||||
this.updateVisible(Math.max(offset - this.pageDelta, 0));
|
||||
if(IS_IOS) {
|
||||
(this.updateVisible as any).flush();
|
||||
}
|
||||
requestAnimationFrame(() => {
|
||||
ref = this.childRefs.get(index);
|
||||
this.savedIndex = null;
|
||||
@ -462,8 +453,10 @@ export default class VirtualScroller<T> extends Component<VirtualScrollerProps<T
|
||||
|
||||
const transform = isTop ? 'scale3d(1, 1, 1)' : 'scale3d(1, -1, 1)';
|
||||
|
||||
const atStart = this.props.data.peekLargest()?.[0].eq(visibleItems.peekLargest()?.[0] || bigInt.zero);
|
||||
const atEnd = false;
|
||||
const loaded = this.props.data.size > 0;
|
||||
|
||||
const atStart = loaded && this.props.data.peekLargest()?.[0].eq(visibleItems.peekLargest()?.[0] || bigInt.zero);
|
||||
const atEnd = this.loaded.top;
|
||||
|
||||
return (
|
||||
<>
|
||||
|
@ -100,7 +100,7 @@ export function Omnibox(props: OmniboxProps) {
|
||||
const initialResults = useMemo(() => {
|
||||
return new Map(SEARCHED_CATEGORIES.map((category) => {
|
||||
if (category === 'other') {
|
||||
return ['other', index.get('other')];
|
||||
return ['other', index.get('other').filter(({ app }) => app !== 'tutorial')];
|
||||
}
|
||||
return [category, []];
|
||||
}));
|
||||
@ -133,6 +133,7 @@ export function Omnibox(props: OmniboxProps) {
|
||||
if (defaultApps.includes(app.toLowerCase())
|
||||
|| app === 'profile'
|
||||
|| app === 'messages'
|
||||
|| app === 'tutorial'
|
||||
|| app === 'Links'
|
||||
|| app === 'Terminal'
|
||||
|| app === 'home'
|
||||
|
@ -58,7 +58,10 @@ export class OmniboxResult extends Component {
|
||||
graphic = <Icon display='inline-block' verticalAlign='middle' icon='Inbox' mr='2' size='18px' color={iconFill} />;
|
||||
} else if (icon === 'messages') {
|
||||
graphic = <Icon display='inline-block' verticalAlign='middle' icon='Users' mr='2' size='18px' color={iconFill} />;
|
||||
} else {
|
||||
} else if (icon === 'tutorial') {
|
||||
graphic = <Icon display='inline-block' verticalAlign='middle' icon='Tutorial' mr='2' size='18px' color={iconFill} />;
|
||||
}
|
||||
else {
|
||||
graphic = <Icon display='inline-block' icon='NullIcon' verticalAlign="middle" mr='2' size="16px" color={iconFill} />;
|
||||
}
|
||||
|
||||
|
@ -1,21 +1,25 @@
|
||||
import { useEffect } from 'react';
|
||||
import { TutorialProgress } from '@urbit/api';
|
||||
import useLocalState, { selectLocalState } from '~/logic/state/local';
|
||||
import { useEffect, MutableRefObject } from "react";
|
||||
import { TutorialProgress } from "@urbit/api";
|
||||
import useLocalState, { selectLocalState } from "~/logic/state/local";
|
||||
|
||||
const localSelector = selectLocalState(['tutorialProgress', 'setTutorialRef']);
|
||||
|
||||
export function useTutorialModal(
|
||||
onProgress: TutorialProgress,
|
||||
show: boolean,
|
||||
anchorRef: HTMLElement | null
|
||||
anchorRef: MutableRefObject<HTMLElement | null>
|
||||
) {
|
||||
const { tutorialProgress, setTutorialRef } = useLocalState(localSelector);
|
||||
|
||||
useEffect(() => {
|
||||
if (show && onProgress === tutorialProgress && anchorRef) {
|
||||
setTutorialRef(anchorRef);
|
||||
if (show && (onProgress === tutorialProgress) && anchorRef?.current) {
|
||||
setTutorialRef(anchorRef.current);
|
||||
}
|
||||
}, [onProgress, tutorialProgress, show, anchorRef]);
|
||||
|
||||
return () => {
|
||||
console.log(tutorialProgress);
|
||||
}
|
||||
}, [tutorialProgress, show, anchorRef]);
|
||||
|
||||
return show && onProgress === tutorialProgress;
|
||||
}
|
||||
|
@ -161,7 +161,7 @@ export function GraphPermissions(props: GraphPermissionsProps) {
|
||||
>
|
||||
<Form style={{ display: 'contents' }}>
|
||||
<Col mt="4" flexShrink={0} gapY="5">
|
||||
<Col gapY="1">
|
||||
<Col gapY="1" mt="0">
|
||||
<Text id="permissions" fontWeight="bold" fontSize="2">
|
||||
Permissions
|
||||
</Text>
|
||||
|
@ -6,7 +6,8 @@ import {
|
||||
Box,
|
||||
ManagedTextInputField as Input,
|
||||
ManagedToggleSwitchField as Checkbox,
|
||||
Col
|
||||
Col,
|
||||
Text
|
||||
} from '@tlon/indigo-react';
|
||||
import { Enc } from '@urbit/api';
|
||||
import { Group, GroupPolicy } from '@urbit/api/groups';
|
||||
@ -104,7 +105,7 @@ return null;
|
||||
onSubmit={onSubmit}
|
||||
>
|
||||
<Form>
|
||||
<Box p="4" fontWeight="600" fontSize="2" id="group-details">Group Details</Box>
|
||||
<Box p="4" id="group-details"><Text fontWeight="600" fontSize="2">Group Details</Text></Box>
|
||||
<Col pb="4" px="4" maxWidth="384px" gapY={4}>
|
||||
<Input
|
||||
id="title"
|
||||
|
@ -4,7 +4,7 @@ import {
|
||||
Col,
|
||||
Label,
|
||||
BaseLabel,
|
||||
BaseAnchor
|
||||
Text
|
||||
} from '@tlon/indigo-react';
|
||||
import { GroupNotificationsConfig } from '@urbit/api';
|
||||
import { Association } from '@urbit/api/metadata';
|
||||
@ -28,7 +28,7 @@ export function GroupPersonalSettings(props: {
|
||||
|
||||
return (
|
||||
<Col px="4" pb="4" gapY="4">
|
||||
<BaseAnchor pt="4" fontWeight="600" id="notifications" fontSize="2">Group Notifications</BaseAnchor>
|
||||
<Text pt="4" fontWeight="600" id="notifications" fontSize="2">Group Notifications</Text>
|
||||
<BaseLabel
|
||||
htmlFor="asyncToggle"
|
||||
display="flex"
|
||||
|
@ -20,15 +20,12 @@ export function GroupSummary(props: GroupSummaryProps & PropFunc<typeof Col>): R
|
||||
useTutorialModal(
|
||||
'group-desc',
|
||||
resource === `/ship/${TUTORIAL_HOST}/${TUTORIAL_GROUP}`,
|
||||
anchorRef.current
|
||||
anchorRef
|
||||
);
|
||||
return (
|
||||
<Col {...rest} ref={anchorRef} gapY="4">
|
||||
<Row gapX="2" width="100%">
|
||||
<MetadataIcon
|
||||
borderRadius="1"
|
||||
border="1"
|
||||
borderColor="lightGray"
|
||||
width="40px"
|
||||
height="40px"
|
||||
metadata={metadata}
|
||||
|
@ -178,7 +178,7 @@ export function GroupSwitcher(props: {
|
||||
}
|
||||
>
|
||||
<Row flexGrow={1} alignItems="center" width='100%' minWidth='0' flexShrink={0}>
|
||||
{ metadata && <MetadataIcon flexShrink={0} mr="2" border="1" borderColor="lightGray" borderRadius="1" metadata={metadata} height="24px" width="24px" /> }
|
||||
{ metadata && <MetadataIcon flexShrink={0} mr="2" metadata={metadata} height="24px" width="24px" /> }
|
||||
<Text flexShrink={1} lineHeight="1.1" fontSize='2' fontWeight="700" overflow='hidden' display='inline-block' flexShrink='1' style={{ textOverflow: 'ellipsis', whiteSpace: 'pre' }}>{title}</Text>
|
||||
</Row>
|
||||
</Dropdown>
|
||||
|
@ -22,6 +22,7 @@ import { StatelessAsyncButton } from '~/views/components/StatelessAsyncButton';
|
||||
import { getModuleIcon } from '~/logic/lib/util';
|
||||
import { FormError } from '~/views/components/FormError';
|
||||
import { GroupSummary } from './GroupSummary';
|
||||
import {TUTORIAL_GROUP_RESOURCE} from '~/logic/lib/tutorialModal';
|
||||
|
||||
const formSchema = Yup.object({
|
||||
group: Yup.string()
|
||||
@ -72,6 +73,9 @@ export function JoinGroup(props: JoinGroupProps): ReactElement {
|
||||
|
||||
const onConfirm = useCallback(async (group: string) => {
|
||||
const [,,ship,name] = group.split('/');
|
||||
if(group === TUTORIAL_GROUP_RESOURCE) {
|
||||
await api.settings.putEntry('tutorial', 'joined', Date.now());
|
||||
}
|
||||
await api.groups.join(ship, name);
|
||||
try {
|
||||
await waiter((p: JoinGroupProps) => {
|
||||
|
@ -15,7 +15,7 @@ export function MetadataIcon(props: MetadataIconProps) {
|
||||
const bgColor = metadata.picture ? {} : { bg: `#${uxToHex(metadata.color)}` };
|
||||
|
||||
return (
|
||||
<Box {...bgColor} {...rest} borderRadius={2} overflow="hidden">
|
||||
<Box {...bgColor} {...rest} borderRadius={2} boxShadow="inset 0 0 0 -1px" color="lightGray" overflow="hidden">
|
||||
{metadata.picture && <Image height="100%" src={metadata.picture} />}
|
||||
</Box>
|
||||
);
|
||||
|
@ -10,6 +10,7 @@ import {
|
||||
Row,
|
||||
Text,
|
||||
Icon,
|
||||
Image,
|
||||
Action,
|
||||
StatelessTextInput as Input
|
||||
} from '@tlon/indigo-react';
|
||||
@ -210,13 +211,7 @@ export function Participants(props: {
|
||||
onChange={onSearchChange}
|
||||
/>
|
||||
</Row>
|
||||
<Box
|
||||
display="grid"
|
||||
gridAutoRows={['48px 48px 1px', '48px 1px']}
|
||||
gridTemplateColumns={['48px 1fr', '48px 2fr 1fr', '48px 3fr 1fr']}
|
||||
gridRowGap={2}
|
||||
alignItems="center"
|
||||
>
|
||||
<Col alignItems="center" >
|
||||
{filtered.map((cs, idx) => (
|
||||
<VisibilitySensor
|
||||
key={idx}
|
||||
@ -242,7 +237,7 @@ export function Participants(props: {
|
||||
}
|
||||
</VisibilitySensor>
|
||||
))}
|
||||
</Box>
|
||||
</Col>
|
||||
</Col>
|
||||
</Col>
|
||||
);
|
||||
@ -296,7 +291,13 @@ function Participant(props: {
|
||||
|
||||
const avatar =
|
||||
contact?.avatar !== null && !hideAvatars ? (
|
||||
<img src={contact.avatar} height={32} width={32} className="dib" />
|
||||
<Image
|
||||
src={contact.avatar}
|
||||
height={32}
|
||||
width={32}
|
||||
display='inline-block'
|
||||
style={{ objectFit: 'cover' }}
|
||||
/>
|
||||
) : (
|
||||
<Sigil ship={contact.patp} size={32} color={`#${color}`} />
|
||||
);
|
||||
@ -305,10 +306,12 @@ function Participant(props: {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Row flexDirection={["column", "row"]} gapX="2" alignItems={["flex-start", "center"]} width="100%" justifyContent="space-between" height={["96px", "60px"]}>
|
||||
<Row gapX="4" alignItems="center" height="100%">
|
||||
<Box>{avatar}</Box>
|
||||
<Col justifyContent="center" gapY="1" height="100%" minWidth='0'>
|
||||
<Col alignItems="self-start" justifyContent="center" gapY="1" height="100%" minWidth='0'>
|
||||
{hasNickname && (
|
||||
<Row minWidth='0' flexShrink='1'>
|
||||
<Row minWidth='0' flexShrink={1}>
|
||||
<TruncText title={contact.nickname} color="black">
|
||||
{contact.nickname}
|
||||
</TruncText>
|
||||
@ -318,10 +321,12 @@ function Participant(props: {
|
||||
{cite(contact.patp)}
|
||||
</Text>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row
|
||||
justifyContent="space-between"
|
||||
gridColumn={['1 / 3', 'auto']}
|
||||
width={["100%", "128px"]}
|
||||
alignItems="center"
|
||||
gapX="4"
|
||||
>
|
||||
<Col>
|
||||
<Text color="lightGray" mb="1">
|
||||
@ -380,21 +385,19 @@ function Participant(props: {
|
||||
<Icon display="block" icon="Ellipsis" />
|
||||
</Dropdown>
|
||||
</Row>
|
||||
</Row>
|
||||
<Box
|
||||
borderBottom={1}
|
||||
borderBottomColor="washedGray"
|
||||
gridColumn={['1 / 3', '1 / 4']}
|
||||
width="100%"
|
||||
/>
|
||||
</>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function BlankParticipant({ length }) {
|
||||
const height = [`${length * 97}px`, `${length * 61}px`];
|
||||
return (
|
||||
<Box
|
||||
gridRow={[`auto / span ${3 * length}`, `auto / span ${2 * length}`]}
|
||||
gridColumn={['1 / 3', '1 / 4']}
|
||||
height="100%"
|
||||
/>
|
||||
<Box width="100%" height={height} />
|
||||
);
|
||||
}
|
||||
|
@ -88,7 +88,7 @@ export function ResourceSkeleton(props: ResourceSkeletonProps): ReactElement {
|
||||
display={['block', 'none']}
|
||||
flexShrink={0}
|
||||
>
|
||||
<Link to={`/~landscape${workspace}`}> {'<- Back'}</Link>
|
||||
<Link to={`/~landscape${workspace}`}><Text>{'<- Back'}</Text></Link>
|
||||
</Box>
|
||||
<Box px={1} mr={2} minWidth={0} display="flex" flexShrink={[1, 0]}>
|
||||
<Text
|
||||
|
@ -66,7 +66,7 @@ export function Sidebar(props: SidebarProps): ReactElement {
|
||||
const isAdmin = (role === 'admin') || (workspace?.type === 'home');
|
||||
|
||||
const anchorRef = useRef<HTMLElement | null>(null);
|
||||
useTutorialModal('channels', true, anchorRef.current);
|
||||
useTutorialModal('channels', true, anchorRef);
|
||||
|
||||
return (
|
||||
<ScrollbarLessCol
|
||||
|
@ -49,7 +49,7 @@ export function SidebarItem(props: {
|
||||
useTutorialModal(
|
||||
mod as any,
|
||||
groupPath === `/ship/${TUTORIAL_HOST}/${TUTORIAL_GROUP}`,
|
||||
anchorRef.current
|
||||
anchorRef
|
||||
);
|
||||
const app = apps[appName];
|
||||
const isUnmanaged = groups?.[groupPath]?.hidden || false;
|
||||
|
@ -112,7 +112,8 @@ export function TutorialModal(props: { api: GlobalApi }) {
|
||||
|
||||
const leaveGroup = useCallback(async () => {
|
||||
await props.api.groups.leaveGroup(TUTORIAL_HOST, TUTORIAL_GROUP);
|
||||
}, [props.api]);
|
||||
await dismiss();
|
||||
}, [props.api, dismiss]);
|
||||
|
||||
const progressIdx = progress.findIndex(p => p === tutorialProgress);
|
||||
|
||||
@ -137,19 +138,19 @@ export function TutorialModal(props: { api: GlobalApi }) {
|
||||
return (
|
||||
<Portal>
|
||||
<ModalOverlay dismiss={dismiss} borderRadius="2" maxWidth="270px" backgroundColor="white">
|
||||
<Col p="2" bg="lightBlue">
|
||||
<Col mb="1">
|
||||
<Col p="3" bg="lightBlue">
|
||||
<Col mb="3">
|
||||
<Text lineHeight="tall" fontWeight="bold">
|
||||
Tutorial Finished
|
||||
</Text>
|
||||
<Text fontSize="0" gray>
|
||||
{progressIdx} of {progress.length - 1}
|
||||
{progressIdx} of {progress.length - 2}
|
||||
</Text>
|
||||
</Col>
|
||||
<Text lineHeight="tall">
|
||||
This tutorial is finished. Would you like to leave Beginner Island?
|
||||
</Text>
|
||||
<Row mt="2" gapX="2" justifyContent="flex-end">
|
||||
<Row mt="3" gapX="2" justifyContent="flex-end">
|
||||
<Button backgroundColor="washedGray" onClick={dismiss}>
|
||||
Later
|
||||
</Button>
|
||||
@ -170,8 +171,8 @@ export function TutorialModal(props: { api: GlobalApi }) {
|
||||
if(paused) {
|
||||
return (
|
||||
<ModalOverlay dismiss={bailExit} borderRadius="2" maxWidth="270px" backgroundColor="white">
|
||||
<Col p="2">
|
||||
<Col mb="1">
|
||||
<Col p="3">
|
||||
<Col mb="3">
|
||||
<Text lineHeight="tall" fontWeight="bold">
|
||||
End Tutorial Now?
|
||||
</Text>
|
||||
@ -179,7 +180,7 @@ export function TutorialModal(props: { api: GlobalApi }) {
|
||||
<Text lineHeight="tall">
|
||||
You can always restart the tutorial by typing "tutorial" in Leap.
|
||||
</Text>
|
||||
<Row mt="4" gapX="2" justifyContent="flex-end">
|
||||
<Row mt="3" gapX="2" justifyContent="flex-end">
|
||||
<Button backgroundColor="washedGray" onClick={bailExit}>
|
||||
Cancel
|
||||
</Button>
|
||||
@ -204,8 +205,9 @@ export function TutorialModal(props: { api: GlobalApi }) {
|
||||
{...coords}
|
||||
bg="white"
|
||||
zIndex={50}
|
||||
height={MODAL_HEIGHT_PX}
|
||||
width={['100%', MODAL_WIDTH_PX]}
|
||||
display="flex"
|
||||
flexDirection="column"
|
||||
width={["100%", MODAL_WIDTH_PX]}
|
||||
borderRadius="2"
|
||||
>
|
||||
<Col
|
||||
@ -214,7 +216,7 @@ export function TutorialModal(props: { api: GlobalApi }) {
|
||||
height="100%"
|
||||
width="100%"
|
||||
borderRadius="2"
|
||||
p="2"
|
||||
p="3"
|
||||
bg="lightBlue"
|
||||
|
||||
>
|
||||
@ -229,15 +231,15 @@ export function TutorialModal(props: { api: GlobalApi }) {
|
||||
/>
|
||||
|
||||
<Box
|
||||
right="8px"
|
||||
top="8px"
|
||||
right="16px"
|
||||
top="16px"
|
||||
position="absolute"
|
||||
cursor="pointer"
|
||||
onClick={tryExit}
|
||||
>
|
||||
<Icon icon="X" />
|
||||
</Box>
|
||||
<Col mb="1">
|
||||
<Col mb="3">
|
||||
<Text lineHeight="tall" fontWeight="bold">
|
||||
{title}
|
||||
</Text>
|
||||
@ -247,7 +249,7 @@ export function TutorialModal(props: { api: GlobalApi }) {
|
||||
</Col>
|
||||
|
||||
<Text lineHeight="tall">{description}</Text>
|
||||
<Row gapX="2" mt="2" justifyContent="flex-end">
|
||||
<Row gapX="2" mt="3" justifyContent="flex-end">
|
||||
{ progressIdx > 1 && (
|
||||
<Button bg="washedGray" onClick={prev}>
|
||||
Back
|
||||
|
@ -6,27 +6,6 @@
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
/* responsive */
|
||||
@media all and (max-width: 34.375em) {
|
||||
.dn-s {
|
||||
display: none;
|
||||
}
|
||||
.flex-basis-100-s {
|
||||
flex-basis: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@media all and (min-width: 34.375em) {
|
||||
.db-ns {
|
||||
display: block;
|
||||
}
|
||||
.flex-basis-30-ns {
|
||||
flex-basis: 30vw;
|
||||
}
|
||||
}
|
||||
|
||||
@media all and (prefers-color-scheme: dark) {
|
||||
.o-60-d {
|
||||
opacity: .6;
|
||||
}
|
||||
a, a:any-link {
|
||||
text-decoration: none;
|
||||
}
|
Loading…
Reference in New Issue
Block a user