mirror of
https://github.com/ilyakooo0/urbit.git
synced 2024-12-03 14:37:05 +03:00
Merge remote-tracking branch 'origin/release/next-userspace' into release/2021-5-27
This commit is contained in:
commit
e510cbeb03
2
.github/actions/glob/Dockerfile
vendored
2
.github/actions/glob/Dockerfile
vendored
@ -1,4 +1,4 @@
|
||||
FROM jaredtobin/janeway:v0.15.0
|
||||
FROM jaredtobin/janeway:v0.15.1
|
||||
COPY entrypoint.sh /entrypoint.sh
|
||||
EXPOSE 22/tcp
|
||||
ENTRYPOINT ["/entrypoint.sh"]
|
||||
|
@ -304,8 +304,10 @@
|
||||
unversioned
|
||||
=/ =resource
|
||||
(de-path:resource t.t.path)
|
||||
=/ requested=@ud
|
||||
(slav %ud i.t.t.t.t.t.path)
|
||||
=/ =mark
|
||||
(append-version:ver (slav %ud i.t.t.t.t.t.path))
|
||||
(append-version:ver (min requested version.config))
|
||||
?. (supported:ver mark)
|
||||
:_ this
|
||||
(fact-init-kick:io version+!>(min-version.config))
|
||||
@ -480,7 +482,7 @@
|
||||
%+ turn ~(tap by paths)
|
||||
|= [fact-ver=@ud paths=(set path)]
|
||||
=/ =mark
|
||||
(append-version:ver fact-ver)
|
||||
(append-version:ver (min version.config fact-ver))
|
||||
(fact:io (convert-from:ver mark q.cage) ~(tap in paths))
|
||||
:: TODO: deprecate
|
||||
++ unversioned
|
||||
|
@ -42,7 +42,7 @@ if(urbitrc.URL) {
|
||||
...devServer,
|
||||
index: '',
|
||||
proxy: {
|
||||
'/~btc/js/bundle/index.js': {
|
||||
'/~btc/js/bundle/index.*.js': {
|
||||
target: 'http://localhost:9000',
|
||||
pathRewrite: (req, path) => {
|
||||
return '/index.js'
|
||||
|
@ -90,8 +90,6 @@ export default class BridgeInvoice extends Component {
|
||||
inputBorder = 'red';
|
||||
}
|
||||
|
||||
console.log('bridge invoice', error);
|
||||
|
||||
return (
|
||||
<>
|
||||
{ this.props.state.broadcastSuccess ?
|
||||
|
@ -30,7 +30,6 @@ export class Root extends Component {
|
||||
this.ship = window.ship;
|
||||
this.state = store.state;
|
||||
store.setStateHandler(this.setState.bind(this));
|
||||
console.log('state', this.state);
|
||||
}
|
||||
|
||||
componentDidMount(){
|
||||
|
@ -6,7 +6,6 @@ export class UpdateReducer {
|
||||
if (!json) {
|
||||
return;
|
||||
}
|
||||
console.log('reduce', json);
|
||||
if (json.providerStatus) {
|
||||
this.reduceProviderStatus(json.providerStatus, state);
|
||||
}
|
||||
|
@ -53,16 +53,16 @@ const appIndex = function (apps) {
|
||||
const applications = [];
|
||||
Object.keys(apps)
|
||||
.filter((e) => {
|
||||
return apps[e]?.type?.basic;
|
||||
return !['weather','clock'].includes(e);
|
||||
})
|
||||
.sort((a, b) => {
|
||||
return a.localeCompare(b);
|
||||
})
|
||||
.map((e) => {
|
||||
const obj = result(
|
||||
apps[e].type.basic.title,
|
||||
apps[e].type.basic.linkedUrl,
|
||||
apps[e].type.basic.title,
|
||||
apps[e].type?.basic?.title || apps[e].type.custom?.tile || e,
|
||||
apps[e]?.type.basic?.linkedUrl || apps[e]?.type.custom?.linkedUrl || '',
|
||||
apps[e]?.type?.basic?.title || apps[e].type.custom?.tile || e,
|
||||
null
|
||||
);
|
||||
applications.push(obj);
|
||||
|
@ -3,27 +3,6 @@ import _ from 'lodash';
|
||||
import { reduceState } from '../state/base';
|
||||
import useContactState, { ContactState } from '../state/contact';
|
||||
|
||||
export const ContactReducer = (json) => {
|
||||
const data: ContactUpdate = _.get(json, 'contact-update', false);
|
||||
if (data) {
|
||||
reduceState<ContactState, ContactUpdate>(useContactState, data, [
|
||||
initial,
|
||||
add,
|
||||
remove,
|
||||
edit,
|
||||
setPublic
|
||||
]);
|
||||
}
|
||||
|
||||
// TODO: better isolation
|
||||
const res = _.get(json, 'resource', false);
|
||||
if (res) {
|
||||
useContactState.setState({
|
||||
nackedContacts: useContactState.getState().nackedContacts.add(`~${res.ship}`)
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const initial = (json: ContactUpdate, state: ContactState): ContactState => {
|
||||
const data = _.get(json, 'initial', false);
|
||||
if (data) {
|
||||
@ -65,12 +44,20 @@ export const edit = (json: ContactUpdate, state: ContactState): ContactState =>
|
||||
}
|
||||
|
||||
const value = data['edit-field'][field];
|
||||
|
||||
if(field === 'add-group') {
|
||||
state.contacts[ship].groups.push(value);
|
||||
if (typeof value !== 'string') {
|
||||
state.contacts[ship].groups.push(`/ship/${Object.values(value).join('/')}`);
|
||||
} else if (!(state.contacts[ship].groups.includes(value))) {
|
||||
state.contacts[ship].groups.push(value);
|
||||
}
|
||||
} else if (field === 'remove-group') {
|
||||
if (typeof value !== 'string') {
|
||||
state.contacts[ship].groups =
|
||||
state.contacts[ship].groups.filter(g => g !== `/ship/${Object.values(value).join('/')}`);
|
||||
} else {
|
||||
state.contacts[ship].groups =
|
||||
state.contacts[ship].groups.filter(g => g !== value);
|
||||
}
|
||||
} else {
|
||||
state.contacts[ship][field] = value;
|
||||
}
|
||||
@ -84,3 +71,23 @@ const setPublic = (json: ContactUpdate, state: ContactState): ContactState => {
|
||||
return state;
|
||||
};
|
||||
|
||||
export const ContactReducer = (json) => {
|
||||
const data: ContactUpdate = _.get(json, 'contact-update', false);
|
||||
if (data) {
|
||||
reduceState<ContactState, ContactUpdate>(useContactState, data, [
|
||||
initial,
|
||||
add,
|
||||
remove,
|
||||
edit,
|
||||
setPublic
|
||||
]);
|
||||
}
|
||||
|
||||
// TODO: better isolation
|
||||
const res = _.get(json, 'resource', false);
|
||||
if (res) {
|
||||
useContactState.setState({
|
||||
nackedContacts: useContactState.getState().nackedContacts.add(`~${res.ship}`)
|
||||
});
|
||||
}
|
||||
};
|
||||
|
@ -65,5 +65,5 @@ RelativeTime.args = {
|
||||
size: 24,
|
||||
sigilPadding: 6,
|
||||
isRelativeTime: true,
|
||||
date: date - 3600000
|
||||
date: Date.now() - 3600000
|
||||
};
|
||||
|
@ -56,9 +56,10 @@ export function ViewProfile(props: any): ReactElement {
|
||||
<Col gapY={3} mb={3} mt={6} alignItems='flex-start'>
|
||||
<Text gray>Pinned Groups</Text>
|
||||
<Col>
|
||||
{contact?.groups.slice().sort(lengthOrder).map(g => (
|
||||
{contact?.groups.slice().sort(lengthOrder).map((g, i) => (
|
||||
<GroupLink
|
||||
api={api}
|
||||
key={i}
|
||||
resource={g}
|
||||
measure={() => {}}
|
||||
/>
|
||||
|
@ -81,12 +81,15 @@ export class OmniboxResult extends Component<OmniboxResultProps, OmniboxResultSt
|
||||
if (
|
||||
defaultApps.includes(icon.toLowerCase()) ||
|
||||
icon.toLowerCase() === 'links' ||
|
||||
icon.toLowerCase() === 'terminal'
|
||||
icon.toLowerCase() === 'terminal' ||
|
||||
icon === 'btc-wallet'
|
||||
) {
|
||||
if (icon === 'Link') {
|
||||
icon = 'Collection';
|
||||
} else if (icon === 'Terminal') {
|
||||
icon = 'Dojo';
|
||||
} else if (icon === 'btc-wallet') {
|
||||
icon = 'Bitcoin';
|
||||
}
|
||||
graphic = (
|
||||
<Icon
|
||||
|
@ -13,7 +13,7 @@ import {
|
||||
Tr,
|
||||
Td
|
||||
} from '@tlon/indigo-react';
|
||||
import { Content } from '@urbit/api';
|
||||
import { Content, CodeContent } from '@urbit/api';
|
||||
import _ from 'lodash';
|
||||
import { BlockContent, Content as AstContent, Parent, Root } from 'ts-mdast';
|
||||
import React from 'react';
|
||||
@ -23,7 +23,6 @@ import { PropFunc } from '~/types';
|
||||
import { PermalinkEmbed } from '~/views/apps/permalinks/embed';
|
||||
import { Mention } from '~/views/components/MentionText';
|
||||
import RemoteContent from '~/views/components/RemoteContent';
|
||||
import CodeContent from './content/code';
|
||||
import { parseTall, parseWide } from './parse';
|
||||
|
||||
type StitchMode = 'merge' | 'block' | 'inline';
|
||||
@ -245,6 +244,7 @@ const renderers = {
|
||||
return (
|
||||
<Text
|
||||
mono
|
||||
fontWeight='inherit'
|
||||
p={1}
|
||||
backgroundColor="washedGray"
|
||||
fontSize={0}
|
||||
@ -309,6 +309,7 @@ const renderers = {
|
||||
className="clamp-message"
|
||||
display="block"
|
||||
borderRadius={1}
|
||||
fontWeight='inherit'
|
||||
mono
|
||||
fontSize={0}
|
||||
backgroundColor="washedGray"
|
||||
|
@ -1,50 +0,0 @@
|
||||
import { Box, Text } from '@tlon/indigo-react';
|
||||
import React, { Component } from 'react';
|
||||
|
||||
export default class CodeContent extends Component {
|
||||
render() {
|
||||
const { props } = this;
|
||||
const content = props.content;
|
||||
|
||||
const outputElement =
|
||||
(Boolean(content.code.output) &&
|
||||
content.code.output.length && content.code.output.length > 0) ?
|
||||
(
|
||||
<Text
|
||||
display='block'
|
||||
fontSize={0}
|
||||
mono
|
||||
p={1}
|
||||
my={0}
|
||||
borderRadius={1}
|
||||
overflow='auto'
|
||||
maxHeight='10em'
|
||||
maxWidth='100%'
|
||||
style={{ whiteSpace: 'pre' }}
|
||||
backgroundColor='washedGray'
|
||||
>
|
||||
{content.code.output.join('\n')}
|
||||
</Text>
|
||||
) : null;
|
||||
|
||||
return (
|
||||
<Box my={2}>
|
||||
<Text
|
||||
display='block'
|
||||
mono
|
||||
my={0}
|
||||
p={1}
|
||||
borderRadius={1}
|
||||
overflow='auto'
|
||||
maxHeight='10em'
|
||||
maxWidth='100%'
|
||||
fontSize={0}
|
||||
style={{ whiteSpace: 'pre' }}
|
||||
>
|
||||
{content.code.expression}
|
||||
</Text>
|
||||
{outputElement}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
}
|
@ -1,189 +0,0 @@
|
||||
import { Anchor, Row, Text } from '@tlon/indigo-react';
|
||||
import React from 'react';
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
import RemarkDisableTokenizers from 'remark-disable-tokenizers';
|
||||
import urbitOb from 'urbit-ob';
|
||||
import { GroupLink } from '~/views/components/GroupLink';
|
||||
|
||||
const DISABLED_BLOCK_TOKENS = [
|
||||
'indentedCode',
|
||||
'atxHeading',
|
||||
'thematicBreak',
|
||||
'list',
|
||||
'setextHeading',
|
||||
'html',
|
||||
'definition',
|
||||
'table'
|
||||
];
|
||||
|
||||
const DISABLED_INLINE_TOKENS = [
|
||||
'autoLink',
|
||||
'url',
|
||||
'email',
|
||||
'reference'
|
||||
];
|
||||
|
||||
const renderers = {
|
||||
inlineCode: ({ language, value }) => {
|
||||
return (
|
||||
<Text
|
||||
mono
|
||||
p={1}
|
||||
backgroundColor='washedGray'
|
||||
fontSize={0}
|
||||
style={{ whiteSpace: 'preWrap' }}
|
||||
>
|
||||
{value}
|
||||
</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'}>
|
||||
{children}
|
||||
</Text>
|
||||
);
|
||||
},
|
||||
code: ({ language, value }) => {
|
||||
return (
|
||||
<Text
|
||||
p={1}
|
||||
className='clamp-message'
|
||||
display='block'
|
||||
borderRadius={1}
|
||||
mono
|
||||
fontSize={0}
|
||||
backgroundColor='washedGray'
|
||||
overflowX='auto'
|
||||
style={{ whiteSpace: 'pre' }}
|
||||
>
|
||||
{value}
|
||||
</Text>
|
||||
);
|
||||
},
|
||||
link: (props) => {
|
||||
return <Anchor src={props.href} borderBottom={1} color="black">{props.children}</Anchor>
|
||||
},
|
||||
list: ({depth, children}) => {
|
||||
return <Text my={2} display='block' fontSize={1} ml={depth ? (2 * depth) : 0} lineHeight={'20px'}>{children}</Text>
|
||||
}
|
||||
};
|
||||
|
||||
const MessageMarkdown = React.memo((props) => {
|
||||
const { source, allowHeaders, allowLists, ...rest } = props;
|
||||
const blockCode = source.split('```');
|
||||
const codeLines = blockCode.map((codes) => codes.split('\n'));
|
||||
let lines = [];
|
||||
if (allowLists) {
|
||||
lines.push(source);
|
||||
} else {
|
||||
lines = codeLines.reduce((acc, val, i) => {
|
||||
if (i % 2 === 1) {
|
||||
return [...acc, `\`\`\`${val.join('\n')}\`\`\``];
|
||||
} else {
|
||||
return [...acc, ...val];
|
||||
}
|
||||
}, []);
|
||||
}
|
||||
|
||||
const modifiedBlockTokens = DISABLED_BLOCK_TOKENS.filter(e => {
|
||||
if (allowHeaders && allowLists) {
|
||||
return (e in ["setextHeading", "atxHeading", "list"])
|
||||
} else if (allowHeaders) {
|
||||
return (e in ["setextHeading", "atxHeading"])
|
||||
} else if (allowLists) {
|
||||
return (e === "list")
|
||||
}
|
||||
})
|
||||
|
||||
return lines.map((line, i) => (
|
||||
<React.Fragment key={i}>
|
||||
{i !== 0 && <Row height={2} />}
|
||||
<ReactMarkdown
|
||||
{...rest}
|
||||
source={line}
|
||||
unwrapDisallowed={true}
|
||||
renderers={renderers}
|
||||
allowNode={(node, index, parent) => {
|
||||
if (
|
||||
node.type === 'blockquote' &&
|
||||
parent.type === 'root' &&
|
||||
node.children.length &&
|
||||
node.children[0].type === 'paragraph' &&
|
||||
node.children[0].position.start.offset < 2
|
||||
) {
|
||||
node.children[0].children[0].value =
|
||||
'>' + node.children[0].children[0].value;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}}
|
||||
plugins={[
|
||||
[
|
||||
RemarkDisableTokenizers,
|
||||
{
|
||||
block: modifiedBlockTokens,
|
||||
inline: DISABLED_INLINE_TOKENS
|
||||
}
|
||||
]
|
||||
]}
|
||||
/>
|
||||
</React.Fragment>
|
||||
));
|
||||
});
|
||||
|
||||
export default function TextContent(props) {
|
||||
const content = props.content;
|
||||
const allowHeaders = props.allowHeaders;
|
||||
const allowLists = props.allowLists;
|
||||
|
||||
const group = content.text.trim().match(
|
||||
/([~][/])?(~[a-z]{3,6})(-[a-z]{6})?([/])(([a-z0-9-])+([/-])?)+/
|
||||
);
|
||||
const isGroupLink =
|
||||
group !== null && // matched possible chatroom
|
||||
group[2].length > 2 && // possible ship?
|
||||
urbitOb.isValidPatp(group[2]) && // valid patp?
|
||||
group[0] === content.text.trim(); // entire message is room name?
|
||||
|
||||
if (isGroupLink) {
|
||||
const resource = `/ship/${content.text.trim()}`;
|
||||
return (
|
||||
<GroupLink
|
||||
resource={resource}
|
||||
api={props.api}
|
||||
pl={2}
|
||||
my={2}
|
||||
border={1}
|
||||
borderRadius={2}
|
||||
borderColor='washedGray'
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<Text
|
||||
flexShrink={0}
|
||||
color='black'
|
||||
fontSize={props.fontSize ? props.fontSize : '14px'}
|
||||
lineHeight={props.lineHeight ? props.lineHeight : '20px'}
|
||||
style={{ overflowWrap: 'break-word' }}
|
||||
>
|
||||
<MessageMarkdown source={content.text} allowHeaders={allowHeaders} allowLists={allowLists} />
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
}
|
@ -53,8 +53,22 @@ export function NewChannel(props: NewChannelProps): ReactElement {
|
||||
const groups = useGroupState(state => state.groups);
|
||||
const waiter = useWaitForProps({ groups }, 5000);
|
||||
|
||||
const channelName = (values) => {
|
||||
if (values.name)
|
||||
return values.name;
|
||||
if (!values.name && workspace?.type === 'messages') {
|
||||
const joinedShips = values.ships
|
||||
.filter(Boolean)
|
||||
.map(ship => `~${deSig(ship)}`)
|
||||
.join(', ')
|
||||
.concat(`, ~${deSig(window.ship)}`);
|
||||
return joinedShips;
|
||||
}
|
||||
return values.moduleType;
|
||||
};
|
||||
|
||||
const onSubmit = async (values: FormSchema, actions) => {
|
||||
const name = values.name ? values.name : values.moduleType;
|
||||
const name = channelName(values);
|
||||
const resId: string =
|
||||
stringToSymbol(values.name) +
|
||||
(workspace?.type !== 'messages'
|
||||
|
@ -1,3 +1,4 @@
|
||||
import _ from 'lodash';
|
||||
import { Box, Col, Icon, Text } from '@tlon/indigo-react';
|
||||
import { Association } from '@urbit/api/metadata';
|
||||
import React, { ReactElement, ReactNode, useCallback, useState } from 'react';
|
||||
@ -8,6 +9,7 @@ import GlobalApi from '~/logic/api/global';
|
||||
import { isWriter } from '~/logic/lib/group';
|
||||
import { getItemTitle } from '~/logic/lib/util';
|
||||
import useContactState from '~/logic/state/contact';
|
||||
import useSettingsState, { selectCalmState } from '~/logic/state/settings';
|
||||
import useGroupState from '~/logic/state/group';
|
||||
import { Dropdown } from '~/views/components/Dropdown';
|
||||
import RichText from '~/views/components/RichText';
|
||||
@ -23,6 +25,35 @@ const TruncatedText = styled(RichText)`
|
||||
}
|
||||
`;
|
||||
|
||||
const participantNames = (str: string, contacts, hideNicknames) => {
|
||||
if (_.includes(str, ',') && _.startsWith(str, '~')) {
|
||||
const names = _.split(str, ', ');
|
||||
return names.map((name, idx) => {
|
||||
if (urbitOb.isValidPatp(name)) {
|
||||
if (contacts[name]?.nickname && !hideNicknames)
|
||||
return (
|
||||
<Text key={name} fontSize={2} fontWeight='600'>
|
||||
{contacts[name]?.nickname}
|
||||
{idx + 1 != names.length ? ', ' : null}
|
||||
</Text>
|
||||
);
|
||||
return (
|
||||
<Text key={name} mono fontSize={2} fontWeight='600'>
|
||||
{name}
|
||||
<Text fontSize={2} fontWeight='600'>
|
||||
{idx + 1 != names.length ? ', ' : null}
|
||||
</Text>
|
||||
</Text>
|
||||
);
|
||||
} else {
|
||||
return name;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
return str;
|
||||
}
|
||||
};
|
||||
|
||||
type ResourceSkeletonProps = {
|
||||
association: Association;
|
||||
api: GlobalApi;
|
||||
@ -40,6 +71,7 @@ export function ResourceSkeleton(props: ResourceSkeletonProps): ReactElement {
|
||||
}
|
||||
const rid = association.resource;
|
||||
const groups = useGroupState(state => state.groups);
|
||||
const { hideNicknames } = useSettingsState(selectCalmState);
|
||||
const group = groups[association.group];
|
||||
let workspace = association.group;
|
||||
const [actionsWidth, setActionsWidth] = useState(0);
|
||||
@ -58,7 +90,7 @@ export function ResourceSkeleton(props: ResourceSkeletonProps): ReactElement {
|
||||
|
||||
const contacts = useContactState(state => state.contacts);
|
||||
|
||||
if (urbitOb.isValidPatp(title)) {
|
||||
if (urbitOb.isValidPatp(title) && !hideNicknames) {
|
||||
recipient = title;
|
||||
title = (contacts?.[title]?.nickname) ? contacts[title].nickname : title;
|
||||
} else {
|
||||
@ -107,13 +139,16 @@ export function ResourceSkeleton(props: ResourceSkeletonProps): ReactElement {
|
||||
ml='1'
|
||||
flexShrink={1}
|
||||
>
|
||||
{title}
|
||||
{workspace === '/messages' && !urbitOb.isValidPatp(title)
|
||||
? participantNames(title, contacts, hideNicknames)
|
||||
: title}
|
||||
</Text>
|
||||
);
|
||||
|
||||
const Description = () => (
|
||||
<TruncatedText
|
||||
mono={workspace === '/messages' && !urbitOb.isValidPatp(title)}
|
||||
display={['none','inline']}
|
||||
mono={workspace === '/messages' && !association?.metadata?.description}
|
||||
color='gray'
|
||||
mb={0}
|
||||
minWidth={0}
|
||||
@ -121,7 +156,7 @@ export function ResourceSkeleton(props: ResourceSkeletonProps): ReactElement {
|
||||
flexShrink={1}
|
||||
disableRemoteContent
|
||||
>
|
||||
{workspace === '/messages'
|
||||
{workspace === '/messages' && !association?.metadata?.description
|
||||
? recipient
|
||||
: association?.metadata?.description}
|
||||
</TruncatedText>
|
||||
|
@ -1,20 +1,22 @@
|
||||
import React, { ReactElement, useRef, ReactNode } from 'react';
|
||||
import _ from 'lodash';
|
||||
import React, { useRef } from 'react';
|
||||
import urbitOb from 'urbit-ob';
|
||||
|
||||
import { Icon, Row, Box, Text, BaseImage } from '@tlon/indigo-react';
|
||||
import { Groups, Association, Rolodex, cite } from '@urbit/api';
|
||||
import { Association, cite } from '@urbit/api';
|
||||
|
||||
import { HoverBoxLink } from '~/views/components/HoverBox';
|
||||
import { Sigil } from '~/logic/lib/sigil';
|
||||
import { useTutorialModal } from '~/views/components/useTutorialModal';
|
||||
import { TUTORIAL_HOST, TUTORIAL_GROUP } from '~/logic/lib/tutorialModal';
|
||||
import { Workspace } from '~/types/workspace';
|
||||
import { useContact } from '~/logic/state/contact';
|
||||
import useContactState, { useContact } from '~/logic/state/contact';
|
||||
import { getItemTitle, getModuleIcon, uxToHex } from '~/logic/lib/util';
|
||||
import useGroupState from '~/logic/state/group';
|
||||
import Dot from '~/views/components/Dot';
|
||||
import { SidebarAppConfigs } from './types';
|
||||
import {useHarkDm} from '~/logic/state/hark';
|
||||
import { useHarkDm } from '~/logic/state/hark';
|
||||
import useSettingsState from '~/logic/state/settings';
|
||||
|
||||
function SidebarItemBase(props: {
|
||||
to: string;
|
||||
@ -23,7 +25,7 @@ function SidebarItemBase(props: {
|
||||
hasUnread: boolean;
|
||||
isSynced?: boolean;
|
||||
children: ReactNode;
|
||||
title: string;
|
||||
title: string | ReactNode;
|
||||
mono?: boolean;
|
||||
}) {
|
||||
const {
|
||||
@ -34,7 +36,7 @@ function SidebarItemBase(props: {
|
||||
hasNotification,
|
||||
hasUnread,
|
||||
isSynced = false,
|
||||
mono = false,
|
||||
mono = false
|
||||
} = props;
|
||||
const color = isSynced ? 'black' : 'lightGray';
|
||||
|
||||
@ -42,7 +44,7 @@ function SidebarItemBase(props: {
|
||||
|
||||
return (
|
||||
<HoverBoxLink
|
||||
//ref={anchorRef}
|
||||
// ref={anchorRef}
|
||||
to={to}
|
||||
bg="white"
|
||||
bgActive="washedGray"
|
||||
@ -102,9 +104,9 @@ export function SidebarDmItem(props: {
|
||||
}) {
|
||||
const { ship, selected = false } = props;
|
||||
const contact = useContact(ship);
|
||||
const title = contact?.nickname || (cite(ship) ?? ship)
|
||||
const title = contact?.nickname || (cite(ship) ?? ship);
|
||||
const hideAvatars = false;
|
||||
const { unreads } = useHarkDm(ship) || { unreads : 0 };
|
||||
const { unreads } = useHarkDm(ship) || { unreads: 0 };
|
||||
const img =
|
||||
contact?.avatar && !hideAvatars ? (
|
||||
<BaseImage
|
||||
@ -128,7 +130,7 @@ export function SidebarDmItem(props: {
|
||||
<SidebarItemBase
|
||||
selected={selected}
|
||||
hasNotification={false}
|
||||
hasUnread={unreads as number > 0}
|
||||
hasUnread={(unreads as number) > 0}
|
||||
to={`/~landscape/messages/dm/${ship}`}
|
||||
title={title}
|
||||
mono={!contact?.nickname}
|
||||
@ -148,7 +150,8 @@ export function SidebarAssociationItem(props: {
|
||||
workspace: Workspace;
|
||||
}) {
|
||||
const { association, path, selected, apps } = props;
|
||||
let title = getItemTitle(association) || '';
|
||||
const title = getItemTitle(association) || '';
|
||||
const color = `#${uxToHex(association?.metadata?.color || '0x0')}`;
|
||||
const appName = association?.['app-name'];
|
||||
let mod = appName;
|
||||
if (association?.metadata?.config && 'graph' in association.metadata.config) {
|
||||
@ -156,7 +159,9 @@ export function SidebarAssociationItem(props: {
|
||||
}
|
||||
const rid = association?.resource;
|
||||
const groupPath = association?.group;
|
||||
const groups = useGroupState((state) => state.groups);
|
||||
const groups = useGroupState(state => state.groups);
|
||||
const { hideNicknames } = useSettingsState(s => s.calm);
|
||||
const contacts = useContactState(s => s.contacts);
|
||||
const anchorRef = useRef<HTMLAnchorElement>(null);
|
||||
useTutorialModal(
|
||||
mod as any,
|
||||
@ -194,19 +199,62 @@ export function SidebarAssociationItem(props: {
|
||||
return null;
|
||||
}
|
||||
|
||||
const participantNames = (str: string, color: string) => {
|
||||
if (_.includes(str, ',') && _.startsWith(str, '~')) {
|
||||
const names = _.split(str, ', ');
|
||||
return names.map((name, idx) => {
|
||||
if (urbitOb.isValidPatp(name)) {
|
||||
if (contacts[name]?.nickname && !hideNicknames)
|
||||
return (
|
||||
<Text key={name} color={color}>
|
||||
{contacts[name]?.nickname}
|
||||
{idx + 1 != names.length ? ', ' : null}
|
||||
</Text>
|
||||
);
|
||||
return (
|
||||
<Text key={name} mono color={color}>
|
||||
{name}
|
||||
<Text color={color}>{idx + 1 != names.length ? ', ' : null}</Text>
|
||||
</Text>
|
||||
);
|
||||
} else {
|
||||
return name;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
return str;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<SidebarItemBase
|
||||
to={to}
|
||||
selected={selected}
|
||||
hasUnread={hasUnread}
|
||||
title={title}
|
||||
title={
|
||||
DM && !urbitOb.isValidPatp(title)
|
||||
? participantNames(title, color)
|
||||
: title
|
||||
}
|
||||
hasNotification={hasNotification}
|
||||
>
|
||||
<Icon
|
||||
display="block"
|
||||
color={isSynced ? 'black' : 'lightGray'}
|
||||
icon={getModuleIcon(mod)}
|
||||
/>
|
||||
{DM ? (
|
||||
<Box
|
||||
flexShrink={0}
|
||||
height={16}
|
||||
width={16}
|
||||
borderRadius={2}
|
||||
backgroundColor={
|
||||
`#${uxToHex(props?.association?.metadata?.color)}` || '#000000'
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
<Icon
|
||||
display="block"
|
||||
color={isSynced ? 'black' : 'lightGray'}
|
||||
icon={getModuleIcon(mod)}
|
||||
/>
|
||||
)}
|
||||
</SidebarItemBase>
|
||||
);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user