Merge pull request #4597 from urbit/james/big-sigils

chat: 24px sigils
This commit is contained in:
matildepark 2021-03-12 13:13:43 -05:00 committed by GitHub
commit 3922cfb2ef
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 146 additions and 123 deletions

View File

@ -23,9 +23,10 @@ export const Sigil = memo(
size, size,
svgClass = '', svgClass = '',
icon = false, icon = false,
padding = 0 padding = 0,
display = 'inline-block'
}) => { }) => {
const innerSize = Number(size) - 2*padding; const innerSize = Number(size) - 2 * padding;
const paddingPx = `${padding}px`; const paddingPx = `${padding}px`;
const foregroundColor = foreground const foregroundColor = foreground
? foreground ? foreground
@ -34,14 +35,14 @@ export const Sigil = memo(
<Box <Box
backgroundColor={color} backgroundColor={color}
borderRadius={icon ? '1' : '0'} borderRadius={icon ? '1' : '0'}
display='inline-block' display={display}
height={size} height={size}
width={size} width={size}
className={classes} className={classes}
/> />
) : ( ) : (
<Box <Box
display='inline-block' display={display}
borderRadius={icon ? '1' : '0'} borderRadius={icon ? '1' : '0'}
flexBasis={size} flexBasis={size}
backgroundColor={color} backgroundColor={color}

View File

@ -24,7 +24,7 @@ type ChatInputProps = IuseStorage & {
message: string; message: string;
deleteMessage(): void; deleteMessage(): void;
hideAvatars: boolean; hideAvatars: boolean;
} };
interface ChatInputState { interface ChatInputState {
inCodeMode: boolean; inCodeMode: boolean;
@ -60,20 +60,23 @@ class ChatInput extends Component<ChatInputProps, ChatInputState> {
submit(text) { submit(text) {
const { props, state } = this; const { props, state } = this;
const [,,ship,name] = props.station.split('/'); const [, , ship, name] = props.station.split('/');
if (state.inCodeMode) { if (state.inCodeMode) {
this.setState({ this.setState(
inCodeMode: false {
}, async () => { inCodeMode: false
const output = await props.api.graph.eval(text); },
const contents: Content[] = [{ code: { output, expression: text } }]; async () => {
const post = createPost(contents); const output = await props.api.graph.eval(text);
props.api.graph.addPost(ship, name, post); const contents: Content[] = [{ code: { output, expression: text } }];
}); const post = createPost(contents);
props.api.graph.addPost(ship, name, post);
}
);
return; return;
} }
const post = createPost(tokenizeMessage((text))); const post = createPost(tokenizeMessage(text));
props.deleteMessage(); props.deleteMessage();
@ -86,8 +89,8 @@ class ChatInput extends Component<ChatInputProps, ChatInputState> {
this.chatEditor.current.editor.setValue(url); this.chatEditor.current.editor.setValue(url);
this.setState({ uploadingPaste: false }); this.setState({ uploadingPaste: false });
} else { } else {
const [,,ship,name] = props.station.split('/'); const [, , ship, name] = props.station.split('/');
props.api.graph.addPost(ship,name, createPost([{ url }])); props.api.graph.addPost(ship, name, createPost([{ url }]));
} }
} }
@ -110,7 +113,8 @@ class ChatInput extends Component<ChatInputProps, ChatInputState> {
return; return;
} }
Array.from(files).forEach((file) => { Array.from(files).forEach((file) => {
this.props.uploadDefault(file) this.props
.uploadDefault(file)
.then(this.uploadSuccess) .then(this.uploadSuccess)
.catch(this.uploadError); .catch(this.uploadError);
}); });
@ -119,32 +123,40 @@ class ChatInput extends Component<ChatInputProps, ChatInputState> {
render() { render() {
const { props, state } = this; const { props, state } = this;
const color = props.ourContact const color = props.ourContact ? uxToHex(props.ourContact.color) : '000000';
? uxToHex(props.ourContact.color) : '000000';
const sigilClass = props.ourContact const sigilClass = props.ourContact ? '' : 'mix-blend-diff';
? '' : 'mix-blend-diff';
const avatar = ( const avatar =
props.ourContact && props.ourContact && props.ourContact?.avatar && !props.hideAvatars ? (
((props.ourContact?.avatar) && !props.hideAvatars) <BaseImage
)
? <BaseImage
src={props.ourContact.avatar} src={props.ourContact.avatar}
height={16} height={24}
width={16} width={24}
style={{ objectFit: 'cover' }} style={{ objectFit: 'cover' }}
borderRadius={1} borderRadius={1}
display='inline-block' display='inline-block'
/> />
: <Sigil ) : (
ship={window.ship} <Box
size={16} width={24}
color={`#${color}`} height={24}
classes={sigilClass} display='flex'
icon justifyContent='center'
padding={2} alignItems='center'
/>; backgroundColor={`#${color}`}
borderRadius={1}
>
<Sigil
ship={window.ship}
size={16}
color={`#${color}`}
classes={sigilClass}
icon
padding={2}
/>
</Box>
);
return ( return (
<Row <Row
@ -158,7 +170,7 @@ class ChatInput extends Component<ChatInputProps, ChatInputState> {
className='cf' className='cf'
zIndex={0} zIndex={0}
> >
<Row p='2' alignItems='center'> <Row p='12px 8px 12px 12px' alignItems='center'>
{avatar} {avatar}
</Row> </Row>
<ChatEditor <ChatEditor
@ -170,31 +182,23 @@ class ChatInput extends Component<ChatInputProps, ChatInputState> {
onPaste={this.onPaste.bind(this)} onPaste={this.onPaste.bind(this)}
placeholder='Message...' placeholder='Message...'
/> />
<Box <Box mx={2} flexShrink={0} height='16px' width='16px' flexBasis='16px'>
mx={2} {this.props.canUpload ? (
flexShrink={0} this.props.uploading ? (
height='16px' <LoadingSpinner />
width='16px' ) : (
flexBasis='16px' <Icon
> icon='Links'
{this.props.canUpload width='16'
? this.props.uploading height='16'
? <LoadingSpinner /> onClick={() =>
: <Icon icon='Links' this.props.promptUpload().then(this.uploadSuccess)
width="16" }
height="16" />
onClick={() => this.props.promptUpload().then(this.uploadSuccess)} )
/> ) : null}
: null
}
</Box> </Box>
<Box <Box mr={2} flexShrink={0} height='16px' width='16px' flexBasis='16px'>
mr={2}
flexShrink={0}
height='16px'
width='16px'
flexBasis='16px'
>
<Icon <Icon
icon='Dojo' icon='Dojo'
onClick={this.toggleCode} onClick={this.toggleCode}
@ -206,4 +210,6 @@ class ChatInput extends Component<ChatInputProps, ChatInputState> {
} }
} }
export default withLocalState(withStorage(ChatInput, { accept: 'image/*' }), ['hideAvatars']); export default withLocalState(withStorage(ChatInput, { accept: 'image/*' }), [
'hideAvatars'
]);

View File

@ -35,10 +35,10 @@ import RemoteContent from '~/views/components/RemoteContent';
import { Mention } from '~/views/components/MentionText'; import { Mention } from '~/views/components/MentionText';
import styled from 'styled-components'; import styled from 'styled-components';
import useLocalState from '~/logic/state/local'; import useLocalState from '~/logic/state/local';
import useSettingsState, {selectCalmState} from "~/logic/state/settings"; import useSettingsState, { selectCalmState } from '~/logic/state/settings';
import Timestamp from '~/views/components/Timestamp'; import Timestamp from '~/views/components/Timestamp';
import useContactState from '~/logic/state/contact'; import useContactState from '~/logic/state/contact';
import {useIdlingState} from '~/logic/lib/idling'; import { useIdlingState } from '~/logic/lib/idling';
export const DATESTAMP_FORMAT = '[~]YYYY.M.D'; export const DATESTAMP_FORMAT = '[~]YYYY.M.D';
@ -64,39 +64,42 @@ export const DayBreak = ({ when, shimTop = false }: DayBreakProps) => (
</Row> </Row>
); );
export const UnreadMarker = React.forwardRef(({ dayBreak, when, api, association }, ref) => { export const UnreadMarker = React.forwardRef(
const [visible, setVisible] = useState(false); ({ dayBreak, when, api, association }, ref) => {
const idling = useIdlingState(); const [visible, setVisible] = useState(false);
const dismiss = useCallback(() => { const idling = useIdlingState();
api.hark.markCountAsRead(association, '/', 'message'); const dismiss = useCallback(() => {
}, [api, association]); api.hark.markCountAsRead(association, '/', 'message');
}, [api, association]);
useEffect(() => { useEffect(() => {
if(visible && !idling) { if (visible && !idling) {
dismiss(); dismiss();
} }
}, [visible, idling]); }, [visible, idling]);
return ( return (
<Row <Row
position='absolute' position='absolute'
ref={ref} ref={ref}
px={2} px={2}
mt={2} mt={2}
height={5} height={5}
justifyContent='center' justifyContent='center'
alignItems='center' alignItems='center'
width='100%' width='100%'
> >
<Rule borderColor='lightBlue' /> <Rule borderColor='lightBlue' />
<VisibilitySensor onChange={setVisible}> <VisibilitySensor onChange={setVisible}>
<Text color='blue' fontSize={0} flexShrink='0' px={2}> <Text color='blue' fontSize={0} flexShrink='0' px={2}>
New messages below New messages below
</Text> </Text>
</VisibilitySensor> </VisibilitySensor>
<Rule borderColor='lightBlue' /> <Rule borderColor='lightBlue' />
</Row> </Row>
)}); );
}
);
interface ChatMessageProps { interface ChatMessageProps {
msg: Post; msg: Post;
@ -126,8 +129,7 @@ class ChatMessage extends Component<ChatMessageProps> {
this.divRef = React.createRef(); this.divRef = React.createRef();
} }
componentDidMount() { componentDidMount() {}
}
render() { render() {
const { const {
@ -146,7 +148,7 @@ class ChatMessage extends Component<ChatMessageProps> {
history, history,
api, api,
highlighted, highlighted,
fontSize, fontSize
} = this.props; } = this.props;
let { renderSigil } = this.props; let { renderSigil } = this.props;
@ -170,7 +172,6 @@ class ChatMessage extends Component<ChatMessageProps> {
.unix(msg['time-sent'] / 1000) .unix(msg['time-sent'] / 1000)
.format(renderSigil ? 'h:mm A' : 'h:mm'); .format(renderSigil ? 'h:mm A' : 'h:mm');
const messageProps = { const messageProps = {
msg, msg,
timestamp, timestamp,
@ -183,7 +184,7 @@ class ChatMessage extends Component<ChatMessageProps> {
api, api,
scrollWindow, scrollWindow,
highlighted, highlighted,
fontSize, fontSize
}; };
const unreadContainerStyle = { const unreadContainerStyle = {
@ -204,11 +205,11 @@ class ChatMessage extends Component<ChatMessageProps> {
) : null} ) : null}
{renderSigil ? ( {renderSigil ? (
<> <>
<MessageAuthor pb={'2px'} {...messageProps} /> <MessageAuthor pb={1} {...messageProps} />
<Message pl={5} pr={4} {...messageProps} /> <Message pl={'44px'} pr={4} {...messageProps} />
</> </>
) : ( ) : (
<Message pl={5} pr={4} timestampHover {...messageProps} /> <Message pl={'44px'} pr={4} timestampHover {...messageProps} />
)} )}
<Box style={unreadContainerStyle}> <Box style={unreadContainerStyle}>
{isLastRead ? ( {isLastRead ? (
@ -226,7 +227,9 @@ class ChatMessage extends Component<ChatMessageProps> {
} }
} }
export default React.forwardRef((props, ref) => <ChatMessage {...props} innerRef={ref} />); export default React.forwardRef((props, ref) => (
<ChatMessage {...props} innerRef={ref} />
));
export const MessageAuthor = ({ export const MessageAuthor = ({
timestamp, timestamp,
@ -239,9 +242,9 @@ export const MessageAuthor = ({
}) => { }) => {
const osDark = useLocalState((state) => state.dark); const osDark = useLocalState((state) => state.dark);
const theme = useSettingsState(s => s.display.theme); const theme = useSettingsState((s) => s.display.theme);
const dark = theme === 'dark' || (theme === 'auto' && osDark); const dark = theme === 'dark' || (theme === 'auto' && osDark);
const contacts = useContactState(state => state.contacts); const contacts = useContactState((state) => state.contacts);
const datestamp = moment const datestamp = moment
.unix(msg['time-sent'] / 1000) .unix(msg['time-sent'] / 1000)
@ -291,19 +294,30 @@ export const MessageAuthor = ({
display='inline-block' display='inline-block'
style={{ objectFit: 'cover' }} style={{ objectFit: 'cover' }}
src={contact.avatar} src={contact.avatar}
height={16} height={24}
width={16} width={24}
borderRadius={1} borderRadius={1}
/> />
) : ( ) : (
<Sigil <Box
ship={msg.author} width={24}
size={16} height={24}
color={color} display='flex'
classes={sigilClass} justifyContent='center'
icon alignItems='center'
padding={2} backgroundColor={color}
/> borderRadius={1}
>
<Sigil
ship={msg.author}
size={12}
display='block'
color={color}
classes={sigilClass}
icon
padding={0}
/>
</Box>
); );
return ( return (
<Box display='flex' alignItems='center' {...rest}> <Box display='flex' alignItems='center' {...rest}>
@ -311,9 +325,9 @@ export const MessageAuthor = ({
onClick={() => { onClick={() => {
setShowOverlay(true); setShowOverlay(true);
}} }}
height={16} height={24}
pr={2} pr={2}
pl={2} pl={'12px'}
cursor='pointer' cursor='pointer'
position='relative' position='relative'
> >
@ -340,10 +354,10 @@ export const MessageAuthor = ({
pt={1} pt={1}
pb={1} pb={1}
display='flex' display='flex'
alignItems='center' alignItems='baseline'
> >
<Text <Text
fontSize={0} fontSize={1}
mr={2} mr={2}
flexShrink={0} flexShrink={0}
mono={nameMono} mono={nameMono}
@ -385,13 +399,15 @@ export const Message = ({
...rest ...rest
}) => { }) => {
const { hovering, bind } = useHovering(); const { hovering, bind } = useHovering();
const contacts = useContactState(state => state.contacts); const contacts = useContactState((state) => state.contacts);
return ( return (
<Box position='relative' {...rest}> <Box position='relative' {...rest}>
{timestampHover ? ( {timestampHover ? (
<Text <Text
display={hovering ? 'block' : 'none'} display={hovering ? 'block' : 'none'}
position='absolute' position='absolute'
width='40px'
textAlign='center'
left='0' left='0'
top='3px' top='3px'
fontSize={0} fontSize={0}
@ -454,7 +470,7 @@ export const Message = ({
</Box> </Box>
); );
case 'mention': case 'mention':
const first = (i) => (i === 0); const first = (i) => i === 0;
return ( return (
<Mention <Mention
key={i} key={i}