mirror of
https://github.com/urbit/shrub.git
synced 2024-12-24 11:24:21 +03:00
commit
3922cfb2ef
@ -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}
|
||||||
|
@ -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'
|
||||||
|
]);
|
||||||
|
@ -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}
|
||||||
|
Loading…
Reference in New Issue
Block a user