mirror of
https://github.com/urbit/shrub.git
synced 2024-12-11 11:02:25 +03:00
Merge branch 'release/next-js' into lf/more-virt-perf
This commit is contained in:
commit
370b7ad9de
@ -25,10 +25,10 @@ module Urbit.Arvo.Common
|
||||
import Urbit.Prelude
|
||||
|
||||
import Control.Monad.Fail (fail)
|
||||
import Data.Bits
|
||||
import Data.Serialize
|
||||
|
||||
import qualified Network.HTTP.Types.Method as H
|
||||
import qualified Network.Socket as N
|
||||
import qualified Urbit.Ob as Ob
|
||||
|
||||
|
||||
@ -159,6 +159,19 @@ deriveNoun ''JsonNode
|
||||
|
||||
-- Ames Destinations -------------------------------------------------
|
||||
|
||||
serializeToNoun :: Serialize a => a -> Noun
|
||||
serializeToNoun = A . bytesAtom . encode
|
||||
|
||||
serializeParseNoun :: Serialize a => String -> Int -> Noun -> Parser a
|
||||
serializeParseNoun desc len = named (pack desc) . \case
|
||||
A (atomBytes -> bs)
|
||||
-- Atoms lose leading 0s, but since lsb, these become trailing NULs
|
||||
| length bs <= len -> case decode $ bs <> replicate (len - length bs) 0 of
|
||||
Right aa -> pure aa
|
||||
Left msg -> fail msg
|
||||
| otherwise -> fail ("putative " <> desc <> " " <> show bs <> " too long")
|
||||
C{} -> fail ("unexpected cell in " <> desc)
|
||||
|
||||
newtype Patp a = Patp { unPatp :: a }
|
||||
deriving newtype (Eq, Ord, Enum, Real, Integral, Num, ToNoun, FromNoun)
|
||||
|
||||
@ -167,17 +180,29 @@ newtype Port = Port { unPort :: Word16 }
|
||||
deriving newtype (Eq, Ord, Show, Enum, Real, Integral, Num, ToNoun, FromNoun)
|
||||
|
||||
-- @if
|
||||
newtype Ipv4 = Ipv4 { unIpv4 :: Word32 }
|
||||
deriving newtype (Eq, Ord, Enum, Real, Integral, Num, ToNoun, FromNoun)
|
||||
newtype Ipv4 = Ipv4 { unIpv4 :: N.HostAddress }
|
||||
deriving newtype (Eq, Ord, Enum)
|
||||
|
||||
instance Serialize Ipv4 where
|
||||
get = (\a b c d -> Ipv4 $ N.tupleToHostAddress $ (d, c, b, a))
|
||||
<$> getWord8 <*> getWord8 <*> getWord8 <*> getWord8
|
||||
put (Ipv4 (N.hostAddressToTuple -> (a, b, c, d))) = for_ [d, c, b, a] putWord8
|
||||
|
||||
instance ToNoun Ipv4 where
|
||||
toNoun = serializeToNoun
|
||||
|
||||
instance FromNoun Ipv4 where
|
||||
parseNoun = serializeParseNoun "Ipv4" 4
|
||||
|
||||
instance Show Ipv4 where
|
||||
show (Ipv4 i) =
|
||||
show ((shiftR i 24) .&. 0xff) ++ "." ++
|
||||
show ((shiftR i 16) .&. 0xff) ++ "." ++
|
||||
show ((shiftR i 8) .&. 0xff) ++ "." ++
|
||||
show (i .&. 0xff)
|
||||
show (Ipv4 (N.hostAddressToTuple -> (a, b, c, d))) =
|
||||
show a ++ "." ++
|
||||
show b ++ "." ++
|
||||
show c ++ "." ++
|
||||
show d
|
||||
|
||||
-- @is
|
||||
-- should probably use hostAddress6ToTuple here, but no one uses it right now
|
||||
newtype Ipv6 = Ipv6 { unIpv6 :: Word128 }
|
||||
deriving newtype (Eq, Ord, Show, Enum, Real, Integral, Num, ToNoun, FromNoun)
|
||||
|
||||
@ -190,21 +215,14 @@ data AmesAddress = AAIpv4 Ipv4 Port
|
||||
deriving (Eq, Ord, Show)
|
||||
|
||||
instance Serialize AmesAddress where
|
||||
get = AAIpv4 <$> (Ipv4 <$> getWord32le) <*> (Port <$> getWord16le)
|
||||
put (AAIpv4 (Ipv4 ip) (Port port)) = putWord32le ip >> putWord16le port
|
||||
get = AAIpv4 <$> get <*> (Port <$> getWord16le)
|
||||
put (AAIpv4 ip (Port port)) = put ip >> putWord16le port
|
||||
|
||||
instance FromNoun AmesAddress where
|
||||
parseNoun = named "AmesAddress" . \case
|
||||
A (atomBytes -> bs)
|
||||
-- Atoms lose leading 0s, but since lsb, these become trailing NULs
|
||||
| length bs <= 6 -> case decode $ bs <> replicate (6 - length bs) 0 of
|
||||
Right aa -> pure aa
|
||||
Left msg -> fail msg
|
||||
| otherwise -> fail ("putative address " <> show bs <> " too long")
|
||||
C{} -> fail "unexpected cell in ames address"
|
||||
parseNoun = serializeParseNoun "AmesAddress" 6
|
||||
|
||||
instance ToNoun AmesAddress where
|
||||
toNoun = A . bytesAtom . encode
|
||||
toNoun = serializeToNoun
|
||||
|
||||
type AmesDest = Each Galaxy AmesAddress
|
||||
|
||||
|
@ -80,10 +80,6 @@ data ShipClass
|
||||
muk :: ByteString -> Word20
|
||||
muk bs = mugBS bs .&. (2 ^ 20 - 1)
|
||||
|
||||
-- XX check this
|
||||
getAmesAddress :: Get AmesAddress
|
||||
getAmesAddress = AAIpv4 <$> (Ipv4 <$> getWord32le) <*> (Port <$> getWord16le)
|
||||
|
||||
putAmesAddress :: Putter AmesAddress
|
||||
putAmesAddress = \case
|
||||
AAIpv4 (Ipv4 ip) (Port port) -> putWord32le ip >> putWord16le port
|
||||
@ -104,7 +100,7 @@ instance Serialize Packet where
|
||||
guard isAmes
|
||||
|
||||
pktOrigin <- if isRelayed
|
||||
then Just <$> getAmesAddress
|
||||
then Just <$> get
|
||||
else pure Nothing
|
||||
|
||||
-- body
|
||||
@ -157,9 +153,10 @@ instance Serialize Packet where
|
||||
|
||||
putWord32le head
|
||||
case pktOrigin of
|
||||
Just o -> putAmesAddress o
|
||||
Just o -> put o
|
||||
Nothing -> pure ()
|
||||
putByteString body
|
||||
|
||||
where
|
||||
putShipGetRank s@(Ship (LargeKey p q)) = case () of
|
||||
_ | s < 2 ^ 16 -> (0, putWord16le $ fromIntegral s) -- lord
|
||||
|
@ -4,8 +4,12 @@
|
||||
1. Opens a UDP socket and makes sure that it stays open.
|
||||
|
||||
- If can't open the port, wait and try again repeatedly.
|
||||
- If there is an error reading or writting from the open socket,
|
||||
close it and open another.
|
||||
- If there is an error reading to or writing from the open socket,
|
||||
close it and open another, making sure, however, to reuse the
|
||||
same port
|
||||
NOTE: It's not clear what, if anything, closing and reopening
|
||||
the socket does. We're keeping this behavior out of conservatism
|
||||
until we understand it better.
|
||||
|
||||
2. Receives packets from the socket.
|
||||
|
||||
@ -158,7 +162,7 @@ realUdpServ
|
||||
-> HostAddress
|
||||
-> AmesStat
|
||||
-> RIO e UdpServ
|
||||
realUdpServ por hos sat = do
|
||||
realUdpServ startPort hos sat = do
|
||||
logInfo $ displayShow ("AMES", "UDP", "Starting real UDP server.")
|
||||
|
||||
env <- ask
|
||||
@ -202,23 +206,30 @@ realUdpServ por hos sat = do
|
||||
did <- atomically (tryWriteTBQueue qSend (a, b))
|
||||
when (did == False) $ do
|
||||
logWarn "AMES: UDP: Dropping outbound packet because queue is full."
|
||||
let opener por = do
|
||||
logInfo $ displayShow $ ("AMES", "UDP", "Trying to open socket, port",)
|
||||
por
|
||||
sk <- forceBind por hos
|
||||
sn <- io $ getSocketName sk
|
||||
sp <- io $ socketPort sk
|
||||
logInfo $ displayShow $ ("AMES", "UDP", "Got socket", sn, sp)
|
||||
|
||||
tOpen <- async $ forever $ do
|
||||
sk <- forceBind por hos
|
||||
sn <- io $ getSocketName sk
|
||||
let waitForRelease = do
|
||||
atomically (writeTVar vSock (Just sk))
|
||||
broken <- atomically (takeTMVar vFail)
|
||||
logWarn "AMES: UDP: Closing broken socket."
|
||||
io (close broken)
|
||||
|
||||
let waitForRelease = do
|
||||
atomically (writeTVar vSock (Just sk))
|
||||
broken <- atomically (takeTMVar vFail)
|
||||
logWarn "AMES: UDP: Closing broken socket."
|
||||
io (close broken)
|
||||
case sn of
|
||||
(SockAddrInet boundPort _) ->
|
||||
-- When we're on IPv4, maybe port forward at the NAT.
|
||||
rwith (requestPortAccess $ fromIntegral boundPort) $
|
||||
\() -> waitForRelease
|
||||
_ -> waitForRelease
|
||||
|
||||
case sn of
|
||||
(SockAddrInet boundPort _) ->
|
||||
-- When we're on IPv4, maybe port forward at the NAT.
|
||||
rwith (requestPortAccess $ fromIntegral boundPort) $
|
||||
\() -> waitForRelease
|
||||
_ -> waitForRelease
|
||||
opener sp
|
||||
|
||||
tOpen <- async $ opener startPort
|
||||
|
||||
tSend <- async $ forever $ join $ atomically $ do
|
||||
(adr, byt) <- readTBQueue qSend
|
||||
|
@ -37,12 +37,12 @@ textPlain = Path [(MkKnot "text"), (MkKnot "plain")]
|
||||
|
||||
-- | Filter for dotfiles, tempfiles and backup files.
|
||||
validClaySyncPath :: FilePath -> Bool
|
||||
validClaySyncPath fp = hasPeriod && notTildeFile && notDotHash && notDoubleHash
|
||||
validClaySyncPath fp = hasPeriod && notTildeFile && notDotFile && notDoubleHash
|
||||
where
|
||||
fileName = takeFileName fp
|
||||
hasPeriod = elem '.' fileName
|
||||
notTildeFile = not $ "~" `isSuffixOf` fileName
|
||||
notDotHash = not $ ".#" `isPrefixOf` fileName
|
||||
notDotFile = not $ "." `isPrefixOf` fileName
|
||||
notDoubleHash =
|
||||
not $ ("#" `isPrefixOf` fileName) && ("#" `isSuffixOf` fileName)
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
name: urbit-king
|
||||
version: 1.3
|
||||
version: 1.4
|
||||
license: MIT
|
||||
license-file: LICENSE
|
||||
data-files:
|
||||
|
@ -12,6 +12,7 @@ import { Contacts, Content } from '@urbit/api';
|
||||
import { Row, BaseImage, Box, Icon, LoadingSpinner } from '@tlon/indigo-react';
|
||||
import withStorage from '~/views/components/withStorage';
|
||||
import { withLocalState } from '~/logic/state/local';
|
||||
import { MOBILE_BROWSER_REGEX } from "~/logic/lib/util";
|
||||
|
||||
type ChatInputProps = IuseStorage & {
|
||||
api: GlobalApi;
|
||||
@ -30,6 +31,7 @@ interface ChatInputState {
|
||||
inCodeMode: boolean;
|
||||
submitFocus: boolean;
|
||||
uploadingPaste: boolean;
|
||||
currentInput: string;
|
||||
}
|
||||
|
||||
class ChatInput extends Component<ChatInputProps, ChatInputState> {
|
||||
@ -41,7 +43,8 @@ class ChatInput extends Component<ChatInputProps, ChatInputState> {
|
||||
this.state = {
|
||||
inCodeMode: false,
|
||||
submitFocus: false,
|
||||
uploadingPaste: false
|
||||
uploadingPaste: false,
|
||||
currentInput: props.message
|
||||
};
|
||||
|
||||
this.chatEditor = React.createRef();
|
||||
@ -50,6 +53,7 @@ class ChatInput extends Component<ChatInputProps, ChatInputState> {
|
||||
this.toggleCode = this.toggleCode.bind(this);
|
||||
this.uploadSuccess = this.uploadSuccess.bind(this);
|
||||
this.uploadError = this.uploadError.bind(this);
|
||||
this.eventHandler = this.eventHandler.bind(this);
|
||||
}
|
||||
|
||||
toggleCode() {
|
||||
@ -61,6 +65,7 @@ class ChatInput extends Component<ChatInputProps, ChatInputState> {
|
||||
submit(text) {
|
||||
const { props, state } = this;
|
||||
const [, , ship, name] = props.station.split('/');
|
||||
this.setState({ currentInput: '' });
|
||||
if (state.inCodeMode) {
|
||||
this.setState(
|
||||
{
|
||||
@ -120,6 +125,10 @@ class ChatInput extends Component<ChatInputProps, ChatInputState> {
|
||||
});
|
||||
}
|
||||
|
||||
eventHandler(value) {
|
||||
this.setState({ currentInput: value });
|
||||
}
|
||||
|
||||
render() {
|
||||
const { props, state } = this;
|
||||
|
||||
@ -130,6 +139,7 @@ class ChatInput extends Component<ChatInputProps, ChatInputState> {
|
||||
const avatar =
|
||||
props.ourContact && props.ourContact?.avatar && !props.hideAvatars ? (
|
||||
<BaseImage
|
||||
flexShrink={0}
|
||||
src={props.ourContact.avatar}
|
||||
height={24}
|
||||
width={24}
|
||||
@ -170,7 +180,7 @@ class ChatInput extends Component<ChatInputProps, ChatInputState> {
|
||||
className='cf'
|
||||
zIndex={0}
|
||||
>
|
||||
<Row p='12px 4px 12px 12px' alignItems='center'>
|
||||
<Row p='12px 4px 12px 12px' flexShrink={0} alignItems='center'>
|
||||
{avatar}
|
||||
</Row>
|
||||
<ChatEditor
|
||||
@ -180,15 +190,25 @@ class ChatInput extends Component<ChatInputProps, ChatInputState> {
|
||||
onUnmount={props.onUnmount}
|
||||
message={props.message}
|
||||
onPaste={this.onPaste.bind(this)}
|
||||
changeEvent={this.eventHandler}
|
||||
placeholder='Message...'
|
||||
/>
|
||||
<Box mx={2} flexShrink={0} height='16px' width='16px' flexBasis='16px'>
|
||||
<Box mx='12px' flexShrink={0} height='16px' width='16px' flexBasis='16px'>
|
||||
<Icon
|
||||
icon='Dojo'
|
||||
cursor='pointer'
|
||||
onClick={this.toggleCode}
|
||||
color={state.inCodeMode ? 'blue' : 'black'}
|
||||
/>
|
||||
</Box>
|
||||
<Box ml='12px' mr={3} flexShrink={0} height='16px' width='16px' flexBasis='16px'>
|
||||
{this.props.canUpload ? (
|
||||
this.props.uploading ? (
|
||||
<LoadingSpinner />
|
||||
) : (
|
||||
<Icon
|
||||
icon='Attachment'
|
||||
cursor='pointer'
|
||||
width='16'
|
||||
height='16'
|
||||
onClick={() =>
|
||||
@ -198,13 +218,24 @@ class ChatInput extends Component<ChatInputProps, ChatInputState> {
|
||||
)
|
||||
) : null}
|
||||
</Box>
|
||||
<Box mr={2} flexShrink={0} height='16px' width='16px' flexBasis='16px'>
|
||||
<Icon
|
||||
icon='Dojo'
|
||||
onClick={this.toggleCode}
|
||||
color={state.inCodeMode ? 'blue' : 'black'}
|
||||
/>
|
||||
</Box>
|
||||
{MOBILE_BROWSER_REGEX.test(navigator.userAgent) ?
|
||||
<Box
|
||||
ml={2}
|
||||
mr="12px"
|
||||
flexShrink={0}
|
||||
display="flex"
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
width="24px"
|
||||
height="24px"
|
||||
borderRadius="50%"
|
||||
backgroundColor={state.currentInput !== '' ? 'blue' : 'gray'}
|
||||
cursor={state.currentInput !== '' ? 'pointer' : 'default'}
|
||||
onClick={() => this.chatEditor.current.submit()}
|
||||
>
|
||||
<Icon icon="ArrowEast" color="white" />
|
||||
</Box>
|
||||
: null}
|
||||
</Row>
|
||||
);
|
||||
}
|
||||
|
@ -255,6 +255,7 @@ interface ChatMessageProps {
|
||||
}
|
||||
|
||||
function ChatMessage(props: ChatMessageProps) {
|
||||
let { highlighted } = this.props;
|
||||
const {
|
||||
msg,
|
||||
previousMsg,
|
||||
@ -268,7 +269,6 @@ function ChatMessage(props: ChatMessageProps) {
|
||||
isLastMessage,
|
||||
unreadMarkerRef,
|
||||
api,
|
||||
highlighted,
|
||||
showOurContact,
|
||||
fontSize,
|
||||
hideHover
|
||||
@ -281,7 +281,15 @@ function ChatMessage(props: ChatMessageProps) {
|
||||
msg.number === 1
|
||||
);
|
||||
|
||||
const ourMention = msg?.contents?.some((e) => {
|
||||
return e?.mention && e?.mention === window.ship;
|
||||
});
|
||||
|
||||
if (!highlighted) {
|
||||
if (ourMention) {
|
||||
highlighted = true;
|
||||
}
|
||||
}
|
||||
|
||||
const date = useMemo(() => daToUnix(bigInt(msg.index.split('/')[1])), [msg.index]);
|
||||
const nextDate = useMemo(() => nextMsg ? (
|
||||
@ -290,7 +298,7 @@ function ChatMessage(props: ChatMessageProps) {
|
||||
[nextMsg]
|
||||
);
|
||||
|
||||
const dayBreak = useMemo(() =>
|
||||
const dayBreak = useMemo(() =>
|
||||
nextDate &&
|
||||
new Date(date).getDate() !==
|
||||
new Date(nextDate).getDate()
|
||||
@ -572,7 +580,7 @@ export const MessagePlaceholder = ({
|
||||
>
|
||||
<Text
|
||||
display='block'
|
||||
background='gray'
|
||||
background='washedGray'
|
||||
width='24px'
|
||||
height='24px'
|
||||
borderRadius='50%'
|
||||
@ -595,12 +603,13 @@ export const MessagePlaceholder = ({
|
||||
display='inline-block'
|
||||
verticalAlign='middle'
|
||||
fontSize='0'
|
||||
gray
|
||||
washedGray
|
||||
cursor='default'
|
||||
>
|
||||
<Text maxWidth='32rem' display='block'>
|
||||
<Text
|
||||
backgroundColor='gray'
|
||||
backgroundColor='washedGray'
|
||||
borderRadius='2'
|
||||
display='block'
|
||||
width='100%'
|
||||
height='100%'
|
||||
@ -612,10 +621,11 @@ export const MessagePlaceholder = ({
|
||||
mono
|
||||
verticalAlign='middle'
|
||||
fontSize='0'
|
||||
gray
|
||||
washedGray
|
||||
>
|
||||
<Text
|
||||
background='gray'
|
||||
background='washedGray'
|
||||
borderRadius='2'
|
||||
display='block'
|
||||
height='1em'
|
||||
style={{ width: `${((index % 3) + 1) * 3}em` }}
|
||||
@ -626,12 +636,14 @@ export const MessagePlaceholder = ({
|
||||
verticalAlign='middle'
|
||||
fontSize='0'
|
||||
ml='2'
|
||||
gray
|
||||
washedGray
|
||||
borderRadius='2'
|
||||
display={['none', 'inline-block']}
|
||||
className='child'
|
||||
>
|
||||
<Text
|
||||
backgroundColor='gray'
|
||||
backgroundColor='washedGray'
|
||||
borderRadius='2'
|
||||
display='block'
|
||||
width='100%'
|
||||
height='100%'
|
||||
@ -640,7 +652,8 @@ export const MessagePlaceholder = ({
|
||||
</Box>
|
||||
<Text
|
||||
display='block'
|
||||
backgroundColor='gray'
|
||||
backgroundColor='washedGray'
|
||||
borderRadius='2'
|
||||
height='1em'
|
||||
style={{ width: `${(index % 5) * 20}%` }}
|
||||
></Text>
|
||||
|
@ -162,6 +162,7 @@ export default class ChatEditor extends Component {
|
||||
editor.showHint(['test', 'foo']);
|
||||
}
|
||||
if (this.state.message !== '' && value == '') {
|
||||
this.props.changeEvent(value);
|
||||
this.setState({
|
||||
message: value
|
||||
});
|
||||
@ -169,6 +170,7 @@ export default class ChatEditor extends Component {
|
||||
if (value == this.props.message || value == '' || value == ' ') {
|
||||
return;
|
||||
}
|
||||
this.props.changeEvent(value);
|
||||
this.setState({
|
||||
message: value
|
||||
});
|
||||
|
@ -19,7 +19,7 @@ interface LinkItemProps {
|
||||
node: GraphNode;
|
||||
association: Association;
|
||||
resource: string; api: GlobalApi; group: Group; path: string; }
|
||||
export const LinkItem = React.forwardRef((props: LinkItemProps, ref): ReactElement => {
|
||||
export const LinkItem = React.forwardRef((props: LinkItemProps, ref): ReactElement => {
|
||||
const {
|
||||
association,
|
||||
node,
|
||||
@ -85,7 +85,7 @@ export const LinkItem = React.forwardRef((props: LinkItemProps, ref): ReactEleme
|
||||
permalink,
|
||||
'Copy reference'
|
||||
);
|
||||
|
||||
|
||||
const deleteLink = () => {
|
||||
if (confirm('Are you sure you want to delete this link?')) {
|
||||
api.graph.removeNodes(`~${ship}`, name, [node.post.index]);
|
||||
@ -96,7 +96,7 @@ export const LinkItem = React.forwardRef((props: LinkItemProps, ref): ReactEleme
|
||||
const unreads = useHarkState(state => state.unreads);
|
||||
const commColor = (unreads.graph?.[appPath]?.[`/${index}`]?.unreads ?? 0) > 0 ? 'blue' : 'gray';
|
||||
const isUnread = unreads.graph?.[appPath]?.['/']?.unreads?.has(node.post.index);
|
||||
|
||||
|
||||
return (
|
||||
<Box
|
||||
mx="auto"
|
||||
@ -166,9 +166,11 @@ export const LinkItem = React.forwardRef((props: LinkItemProps, ref): ReactEleme
|
||||
<Row minWidth='0' flexShrink={0} width="100%" justifyContent="space-between" py={3} bg="white">
|
||||
<Author
|
||||
showImage
|
||||
isRelativeTime
|
||||
ship={author}
|
||||
date={node.post['time-sent']}
|
||||
group={group}
|
||||
lineHeight="1"
|
||||
/>
|
||||
<Box ml="auto">
|
||||
<Link
|
||||
|
@ -115,11 +115,12 @@ export function Note(props: NoteProps & RouteComponentProps) {
|
||||
<Row alignItems="center">
|
||||
<Author
|
||||
showImage
|
||||
isRelativeTime
|
||||
ship={post?.author}
|
||||
date={post?.['time-sent']}
|
||||
group={group}
|
||||
>
|
||||
<Row px="2" gapX="2" alignItems="flex-end">
|
||||
<Row px="2" gapX="2" alignItems="flex-end" height="14px">
|
||||
<Action bg="white" onClick={doCopy}>{copyDisplay}</Action>
|
||||
{adminLinks}
|
||||
</Row>
|
||||
|
@ -24,6 +24,7 @@ interface AuthorProps {
|
||||
unread?: boolean;
|
||||
api?: GlobalApi;
|
||||
size?: number;
|
||||
lineHeight?: string;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line max-lines-per-function
|
||||
@ -38,10 +39,11 @@ export default function Author(props: AuthorProps & PropFunc<typeof Box>): React
|
||||
group,
|
||||
isRelativeTime,
|
||||
dontShowTime,
|
||||
lineHeight = 'tall',
|
||||
...rest
|
||||
} = props;
|
||||
|
||||
const time = props.time || false;
|
||||
const time = props.time || props.date || false;
|
||||
const size = props.size || 16;
|
||||
const sigilPadding = props.sigilPadding || 2;
|
||||
|
||||
@ -89,7 +91,7 @@ export default function Author(props: AuthorProps & PropFunc<typeof Box>): React
|
||||
) : sigil;
|
||||
|
||||
return (
|
||||
<Row height="20px" {...rest} alignItems='center' width='auto'>
|
||||
<Row {...rest} alignItems='center' width='auto'>
|
||||
<Box
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
@ -110,7 +112,7 @@ export default function Author(props: AuthorProps & PropFunc<typeof Box>): React
|
||||
color='black'
|
||||
fontSize='1'
|
||||
cursor='pointer'
|
||||
lineHeight='tall'
|
||||
lineHeight={lineHeight}
|
||||
fontFamily={showNickname ? 'sans' : 'mono'}
|
||||
fontWeight={showNickname ? '500' : '400'}
|
||||
mr={showNickname ? 0 : "2px"}
|
||||
@ -121,6 +123,7 @@ export default function Author(props: AuthorProps & PropFunc<typeof Box>): React
|
||||
</Box>
|
||||
{ !dontShowTime && time && (
|
||||
<Timestamp
|
||||
height="fit-content"
|
||||
relative={isRelativeTime}
|
||||
stamp={stamp}
|
||||
fontSize={1}
|
||||
|
@ -73,7 +73,7 @@ export function ColorInput(props: ColorInputProps) {
|
||||
height='100%'
|
||||
alignSelf='stretch'
|
||||
onChange={onChange}
|
||||
value={`#${padded}`}
|
||||
value={padded}
|
||||
disabled={disabled || false}
|
||||
type='color'
|
||||
opacity={0}
|
||||
|
@ -35,6 +35,7 @@ interface CommentItemProps {
|
||||
}
|
||||
|
||||
export function CommentItem(props: CommentItemProps): ReactElement {
|
||||
let { highlighted } = props;
|
||||
const { ship, name, api, comment, group } = props;
|
||||
const association = useMetadataState(
|
||||
useCallback(s => s.associations.graph[`/ship/${ship}/${name}`], [ship,name])
|
||||
@ -47,6 +48,16 @@ export function CommentItem(props: CommentItemProps): ReactElement {
|
||||
await api.graph.removeNodes(ship, name, [comment.post?.index]);
|
||||
};
|
||||
|
||||
const ourMention = post?.contents?.some((e) => {
|
||||
return e?.mention && e?.mention === window.ship;
|
||||
});
|
||||
|
||||
if (!highlighted) {
|
||||
if (ourMention) {
|
||||
highlighted = true;
|
||||
}
|
||||
}
|
||||
|
||||
const commentIndexArray = (comment.post?.index || '/').split('/');
|
||||
const commentIndex = commentIndexArray[commentIndexArray.length - 1];
|
||||
|
||||
@ -95,6 +106,7 @@ export function CommentItem(props: CommentItemProps): ReactElement {
|
||||
date={post?.['time-sent']}
|
||||
unread={props.unread}
|
||||
group={group}
|
||||
isRelativeTime
|
||||
>
|
||||
<Row px="2" gapX="2" height="18px">
|
||||
<Action bg="white" onClick={doCopy}>{copyDisplay}</Action>
|
||||
@ -106,7 +118,7 @@ export function CommentItem(props: CommentItemProps): ReactElement {
|
||||
borderRadius="1"
|
||||
p="1"
|
||||
mb="1"
|
||||
backgroundColor={props.highlighted ? 'washedBlue' : 'white'}
|
||||
backgroundColor={highlighted ? 'washedBlue' : 'white'}
|
||||
transcluded={0}
|
||||
api={api}
|
||||
post={post}
|
||||
|
@ -23,6 +23,7 @@ import RichText from './RichText';
|
||||
import { ProfileStatus } from './ProfileStatus';
|
||||
import useSettingsState from '~/logic/state/settings';
|
||||
import {useOutsideClick} from '~/logic/lib/useOutsideClick';
|
||||
import {useCopy} from '~/logic/lib/useCopy';
|
||||
import {useContact} from '~/logic/state/contact';
|
||||
import {useHistory} from 'react-router-dom';
|
||||
import {Portal} from './Portal';
|
||||
@ -59,6 +60,7 @@ const ProfileOverlay = (props: ProfileOverlayProps) => {
|
||||
const hideAvatars = useSettingsState(state => state.calm.hideAvatars);
|
||||
const hideNicknames = useSettingsState(state => state.calm.hideNicknames);
|
||||
const isOwn = useMemo(() => window.ship === ship, [ship]);
|
||||
const { copyDisplay, doCopy, didCopy } = useCopy(`~${ship}`);
|
||||
|
||||
const contact = useContact(`~${ship}`)
|
||||
const color = `#${uxToHex(contact?.color ?? '0x0')}`;
|
||||
@ -188,9 +190,18 @@ const ProfileOverlay = (props: ProfileOverlayProps) => {
|
||||
overflow='hidden'
|
||||
whiteSpace='pre'
|
||||
marginBottom='0'
|
||||
cursor='pointer'
|
||||
display={didCopy ? 'none' : 'block'}
|
||||
onClick={doCopy}
|
||||
>
|
||||
{showNickname ? contact?.nickname : cite(ship)}
|
||||
</Text>
|
||||
<Text
|
||||
fontWeight='600'
|
||||
marginBottom='0'
|
||||
>
|
||||
{copyDisplay}
|
||||
</Text>
|
||||
</Row>
|
||||
{isOwn ? (
|
||||
<ProfileStatus
|
||||
|
@ -48,12 +48,14 @@ class RemoteContent extends Component<RemoteContentProps, RemoteContentState> {
|
||||
this.state = {
|
||||
unfold: props.unfold || false,
|
||||
embed: undefined,
|
||||
noCors: false
|
||||
noCors: false,
|
||||
showArrow: false
|
||||
};
|
||||
this.unfoldEmbed = this.unfoldEmbed.bind(this);
|
||||
this.loadOembed = this.loadOembed.bind(this);
|
||||
this.wrapInLink = this.wrapInLink.bind(this);
|
||||
this.onError = this.onError.bind(this);
|
||||
this.toggleArrow = this.toggleArrow.bind(this);
|
||||
}
|
||||
|
||||
save = () => {
|
||||
@ -128,7 +130,7 @@ return;
|
||||
});
|
||||
}
|
||||
|
||||
wrapInLink(contents, textOnly = false, unfold = false, unfoldEmbed = null, embedContainer = null) {
|
||||
wrapInLink(contents, textOnly = false, unfold = false, unfoldEmbed = null, embedContainer = null, flushPadding = false, noOp = false) {
|
||||
const { style } = this.props;
|
||||
return (
|
||||
<Box borderRadius="1" backgroundColor="washedGray" maxWidth="min(100%, 20rem)">
|
||||
@ -145,8 +147,8 @@ return;
|
||||
)}
|
||||
<BaseAnchor
|
||||
display="flex"
|
||||
p="2"
|
||||
onClick={(e) => { e.stopPropagation(); }}
|
||||
p={flushPadding ? 0 : 2}
|
||||
onClick={(e) => { noOp ? e.preventDefault() : e.stopPropagation() }}
|
||||
href={this.props.url}
|
||||
whiteSpace="nowrap"
|
||||
overflow="hidden"
|
||||
@ -157,7 +159,8 @@ return;
|
||||
style={{ color: 'inherit', textDecoration: 'none', ...style }}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
cursor={noOp ? 'default' : 'pointer'}
|
||||
>
|
||||
{contents}
|
||||
</BaseAnchor>
|
||||
</Row>
|
||||
@ -171,11 +174,16 @@ return;
|
||||
this.setState({ noCors: true });
|
||||
}
|
||||
|
||||
toggleArrow() {
|
||||
this.setState({showArrow: !this.state.showArrow})
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
remoteContentPolicy,
|
||||
url,
|
||||
text,
|
||||
transcluded,
|
||||
renderUrl = true,
|
||||
imageProps = {},
|
||||
audioProps = {},
|
||||
@ -192,22 +200,60 @@ return;
|
||||
const isVideo = VIDEO_REGEX.test(url);
|
||||
const isOembed = hasProvider(url);
|
||||
|
||||
const isTranscluded = () => {
|
||||
return transcluded;
|
||||
}
|
||||
|
||||
if (isImage && remoteContentPolicy.imageShown) {
|
||||
return this.wrapInLink(
|
||||
<BaseImage
|
||||
{...(noCors ? {} : { crossOrigin: "anonymous" })}
|
||||
referrerPolicy="no-referrer"
|
||||
flexShrink={0}
|
||||
src={url}
|
||||
style={style}
|
||||
onLoad={onLoad}
|
||||
onError={this.onError}
|
||||
height="100%"
|
||||
width="100%"
|
||||
objectFit="contain"
|
||||
{...imageProps}
|
||||
{...props}
|
||||
/>
|
||||
<Box
|
||||
position='relative'
|
||||
onMouseEnter={this.toggleArrow}
|
||||
onMouseLeave={this.toggleArrow}
|
||||
>
|
||||
<BaseAnchor
|
||||
position='absolute'
|
||||
top={2}
|
||||
right={2}
|
||||
display={this.state.showArrow ? 'block' : 'none'}
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
}}
|
||||
href={url}
|
||||
>
|
||||
<Box
|
||||
backgroundColor='white'
|
||||
padding={2}
|
||||
borderRadius='50%'
|
||||
display='flex'
|
||||
>
|
||||
<Icon icon='ArrowNorthEast' />
|
||||
</Box>
|
||||
</BaseAnchor>
|
||||
<BaseImage
|
||||
{...(noCors ? {} : { crossOrigin: 'anonymous' })}
|
||||
referrerPolicy='no-referrer'
|
||||
flexShrink={0}
|
||||
src={url}
|
||||
style={style}
|
||||
onLoad={onLoad}
|
||||
onError={this.onError}
|
||||
height='100%'
|
||||
width='100%'
|
||||
objectFit='contain'
|
||||
borderRadius={2}
|
||||
{...imageProps}
|
||||
{...props}
|
||||
/>
|
||||
</Box>,
|
||||
false,
|
||||
false,
|
||||
null,
|
||||
null,
|
||||
true,
|
||||
isTranscluded()
|
||||
);
|
||||
} else if (isAudio && remoteContentPolicy.audioShown) {
|
||||
return (
|
||||
@ -271,7 +317,6 @@ return;
|
||||
display={this.state.unfold ? 'block' : 'none'}
|
||||
className='embed-container'
|
||||
style={style}
|
||||
flexShrink={0}
|
||||
onLoad={this.onLoad}
|
||||
{...oembedProps}
|
||||
{...props}
|
||||
|
@ -134,7 +134,7 @@ const StatusBar = (props) => {
|
||||
mr={2}
|
||||
onClick={() => props.history.push('/~landscape/messages')}
|
||||
>
|
||||
<Icon icon='Users' />
|
||||
<Icon icon='Messages' />
|
||||
</StatusBarItem>
|
||||
<Dropdown
|
||||
dropWidth='250px'
|
||||
|
@ -12,6 +12,7 @@ export type TimestampProps = BoxProps & {
|
||||
date?: boolean;
|
||||
time?: boolean;
|
||||
relative?: boolean;
|
||||
height?: string;
|
||||
};
|
||||
|
||||
const Timestamp = (props: TimestampProps): ReactElement | null => {
|
||||
|
@ -130,7 +130,7 @@ export class OmniboxResult extends Component {
|
||||
<Icon
|
||||
display='inline-block'
|
||||
verticalAlign='middle'
|
||||
icon='Users'
|
||||
icon='Messages'
|
||||
mr='2'
|
||||
size='18px'
|
||||
color={iconFill}
|
||||
|
@ -58,7 +58,11 @@ function GraphContentWideInner(
|
||||
width="fit-content"
|
||||
maxWidth="min(500px, 100%)"
|
||||
>
|
||||
<RemoteContent key={content.url} url={content.url} />
|
||||
<RemoteContent
|
||||
key={content.url}
|
||||
url={content.url}
|
||||
transcluded={transcluded}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
case "mention":
|
||||
|
@ -46,6 +46,7 @@ export function PostHeader(props) {
|
||||
isRelativeTime={true}
|
||||
showTime={false}
|
||||
time={true}
|
||||
lineHeight='1'
|
||||
/>
|
||||
<Dropdown
|
||||
dropWidth="200px"
|
||||
|
@ -65,3 +65,13 @@
|
||||
*/
|
||||
u3_noun
|
||||
u3s_cue_atom(u3_atom a);
|
||||
|
||||
/* u3s_sift_ud_bytes: parse @ud.
|
||||
*/
|
||||
u3_weak
|
||||
u3s_sift_ud_bytes(c3_w len_w, c3_y* byt_y);
|
||||
|
||||
/* u3s_sift_ud: parse @ud.
|
||||
*/
|
||||
u3_weak
|
||||
u3s_sift_ud(u3_atom a);
|
||||
|
@ -5,49 +5,16 @@
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
/* functions
|
||||
*/
|
||||
u3_noun
|
||||
_parse_ud(u3_noun txt) {
|
||||
c3_c* c = u3a_string(txt);
|
||||
static inline u3_noun
|
||||
_parse_ud(u3_noun a)
|
||||
{
|
||||
u3_weak pro;
|
||||
|
||||
// First character must represent a digit
|
||||
c3_c* cur = c;
|
||||
if (cur[0] > '9' || cur[0] < '0') {
|
||||
u3a_free(c);
|
||||
return u3_none;
|
||||
}
|
||||
u3_atom total = cur[0] - '0';
|
||||
cur++;
|
||||
|
||||
int since_last_period = 0;
|
||||
while (cur[0] != 0) {
|
||||
since_last_period++;
|
||||
if (cur[0] == '.') {
|
||||
since_last_period = 0;
|
||||
cur++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cur[0] > '9' || cur[0] < '0') {
|
||||
u3a_free(c);
|
||||
u3z(total);
|
||||
return u3_none;
|
||||
}
|
||||
|
||||
total = u3ka_mul(total, 10);
|
||||
total = u3ka_add(total, cur[0] - '0');
|
||||
cur++;
|
||||
|
||||
if (since_last_period > 3) {
|
||||
u3a_free(c);
|
||||
u3z(total);
|
||||
return u3_none;
|
||||
}
|
||||
if ( u3_none == (pro = u3s_sift_ud(u3x_atom(a))) ) {
|
||||
return u3_nul;
|
||||
}
|
||||
|
||||
u3a_free(c);
|
||||
return u3nc(0, total);
|
||||
return u3nc(u3_nul, pro);
|
||||
}
|
||||
|
||||
static
|
||||
|
@ -313,12 +313,17 @@ u3i_word(c3_w dat_w)
|
||||
u3_atom
|
||||
u3i_chub(c3_d dat_d)
|
||||
{
|
||||
c3_w dat_w[2] = {
|
||||
dat_d & 0xffffffffULL,
|
||||
dat_d >> 32
|
||||
};
|
||||
if ( c3y == u3a_is_cat(dat_d) ) {
|
||||
return (u3_atom)dat_d;
|
||||
}
|
||||
else {
|
||||
c3_w dat_w[2] = {
|
||||
dat_d & 0xffffffffULL,
|
||||
dat_d >> 32
|
||||
};
|
||||
|
||||
return u3i_words(2, dat_w);
|
||||
return u3i_words(2, dat_w);
|
||||
}
|
||||
}
|
||||
|
||||
/* u3i_bytes(): Copy [a] bytes from [b] to an LSB first atom.
|
||||
|
@ -98,28 +98,22 @@ _cm_punt(u3_noun tax)
|
||||
}
|
||||
#endif
|
||||
|
||||
static void _write(int fd, const void *buf, size_t count)
|
||||
{
|
||||
if (count != write(fd, buf, count)){
|
||||
u3l_log("write failed\r\n");
|
||||
c3_assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* _cm_emergency(): write emergency text to stderr, never failing.
|
||||
*/
|
||||
static void
|
||||
_cm_emergency(c3_c* cap_c, c3_l sig_l)
|
||||
{
|
||||
_write(2, "\r\n", 2);
|
||||
_write(2, cap_c, strlen(cap_c));
|
||||
c3_i ret_i;
|
||||
|
||||
ret_i = write(2, "\r\n", 2);
|
||||
ret_i = write(2, cap_c, strlen(cap_c));
|
||||
|
||||
if ( sig_l ) {
|
||||
_write(2, ": ", 2);
|
||||
_write(2, &sig_l, 4);
|
||||
ret_i = write(2, ": ", 2);
|
||||
ret_i = write(2, &sig_l, 4);
|
||||
}
|
||||
_write(2, "\r\n", 2);
|
||||
|
||||
ret_i = write(2, "\r\n", 2);
|
||||
}
|
||||
|
||||
static void _cm_overflow(void *arg1, void *arg2, void *arg3)
|
||||
@ -127,7 +121,7 @@ static void _cm_overflow(void *arg1, void *arg2, void *arg3)
|
||||
(void)(arg1);
|
||||
(void)(arg2);
|
||||
(void)(arg3);
|
||||
siglongjmp(u3_Signal, c3__over);
|
||||
u3m_signal(c3__over);
|
||||
}
|
||||
|
||||
/* _cm_signal_handle(): handle a signal in general.
|
||||
@ -139,7 +133,7 @@ _cm_signal_handle(c3_l sig_l)
|
||||
sigsegv_leave_handler(_cm_overflow, NULL, NULL, NULL);
|
||||
}
|
||||
else {
|
||||
siglongjmp(u3_Signal, sig_l);
|
||||
u3m_signal(sig_l);
|
||||
}
|
||||
}
|
||||
|
||||
@ -661,7 +655,10 @@ u3m_dump(void)
|
||||
c3_i
|
||||
u3m_bail(u3_noun how)
|
||||
{
|
||||
if ( (c3__exit == how) && (u3R == &u3H->rod_u) ) {
|
||||
if ( &(u3H->rod_u) == u3R ) {
|
||||
// XX set exit code
|
||||
//
|
||||
fprintf(stderr, "home: bailing out\r\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
@ -688,8 +685,9 @@ u3m_bail(u3_noun how)
|
||||
//
|
||||
switch ( how ) {
|
||||
case c3__foul:
|
||||
case c3__meme:
|
||||
case c3__oops: {
|
||||
// XX set exit code
|
||||
//
|
||||
fprintf(stderr, "bailing out\r\n");
|
||||
abort();
|
||||
}
|
||||
@ -700,6 +698,9 @@ u3m_bail(u3_noun how)
|
||||
// choice but to use the signal process; and we require the flat
|
||||
// form of how.
|
||||
//
|
||||
// XX JB: these seem unrecoverable, at least wrt memory management,
|
||||
// so they've been disabled above for now
|
||||
//
|
||||
c3_assert(_(u3a_is_cat(how)));
|
||||
u3m_signal(how);
|
||||
}
|
||||
|
@ -1205,16 +1205,11 @@ u3r_mp(mpz_t a_mp,
|
||||
else {
|
||||
u3a_atom* b_u = u3a_to_ptr(b);
|
||||
c3_w len_w = b_u->len_w;
|
||||
c3_d bit_d = (c3_d)len_w << 5;
|
||||
|
||||
// avoid reallocation on import, if possible
|
||||
//
|
||||
if ( (len_w >> 27) ) {
|
||||
mpz_init(a_mp);
|
||||
}
|
||||
else {
|
||||
mpz_init2(a_mp, len_w << 5);
|
||||
}
|
||||
|
||||
mpz_init2(a_mp, (c3_w)c3_min(bit_d, UINT32_MAX));
|
||||
mpz_import(a_mp, len_w, -1, sizeof(c3_w), 0, 0, b_u->buf_w);
|
||||
}
|
||||
}
|
||||
|
@ -856,3 +856,128 @@ u3s_cue_atom(u3_atom a)
|
||||
|
||||
return u3s_cue_bytes((c3_d)len_w, byt_y);
|
||||
}
|
||||
|
||||
#define DIGIT(a) ( ((a) >= '0') && ((a) <= '9') )
|
||||
#define BLOCK(a) ( ('.' == (a)[0]) \
|
||||
&& DIGIT(a[1]) \
|
||||
&& DIGIT(a[2]) \
|
||||
&& DIGIT(a[3]) )
|
||||
|
||||
/* u3s_sift_ud_bytes: parse @ud
|
||||
*/
|
||||
u3_weak
|
||||
u3s_sift_ud_bytes(c3_w len_w, c3_y* byt_y)
|
||||
{
|
||||
c3_y num_y = len_w % 4; // leading digits length
|
||||
c3_s val_s = 0; // leading digits value
|
||||
|
||||
// +ape:ag: just 0
|
||||
//
|
||||
if ( !len_w ) return u3_none;
|
||||
if ( '0' == *byt_y ) return ( 1 == len_w ) ? (u3_noun)0 : u3_none;
|
||||
|
||||
// +ted:ab: leading nonzero (checked above), plus up to 2 digits
|
||||
//
|
||||
#define NEXT() do { \
|
||||
if ( !DIGIT(*byt_y) ) return u3_none; \
|
||||
val_s *= 10; \
|
||||
val_s += *byt_y++ - '0'; \
|
||||
} while (0)
|
||||
|
||||
switch ( num_y ) {
|
||||
case 3: NEXT();
|
||||
case 2: NEXT();
|
||||
case 1: NEXT(); break;
|
||||
case 0: return u3_none;
|
||||
}
|
||||
|
||||
#undef NEXT
|
||||
|
||||
len_w -= num_y;
|
||||
|
||||
// +tid:ab: dot-prefixed 3-digit blocks
|
||||
//
|
||||
// avoid gmp allocation if possible
|
||||
// - 19 decimal digits fit in 64 bits
|
||||
// - 18 digits is 24 bytes with separators
|
||||
//
|
||||
if ( ((1 == num_y) && (24 >= len_w))
|
||||
|| (20 >= len_w) )
|
||||
{
|
||||
c3_d val_d = val_s;
|
||||
|
||||
while ( len_w ) {
|
||||
if ( !BLOCK(byt_y) ) return u3_none;
|
||||
|
||||
byt_y++;
|
||||
|
||||
val_d *= 10;
|
||||
val_d += *byt_y++ - '0';
|
||||
val_d *= 10;
|
||||
val_d += *byt_y++ - '0';
|
||||
val_d *= 10;
|
||||
val_d += *byt_y++ - '0';
|
||||
|
||||
len_w -= 4;
|
||||
}
|
||||
|
||||
return u3i_chub(val_d);
|
||||
}
|
||||
|
||||
{
|
||||
// avoid gmp realloc if possible
|
||||
//
|
||||
mpz_t a_mp;
|
||||
{
|
||||
c3_d bit_d = (c3_d)(len_w / 4) * 10;
|
||||
mpz_init2(a_mp, (c3_w)c3_min(bit_d, UINT32_MAX));
|
||||
mpz_set_ui(a_mp, val_s);
|
||||
}
|
||||
|
||||
while ( len_w ) {
|
||||
if ( !BLOCK(byt_y) ) {
|
||||
mpz_clear(a_mp);
|
||||
return u3_none;
|
||||
}
|
||||
|
||||
byt_y++;
|
||||
|
||||
val_s = *byt_y++ - '0';
|
||||
val_s *= 10;
|
||||
val_s += *byt_y++ - '0';
|
||||
val_s *= 10;
|
||||
val_s += *byt_y++ - '0';
|
||||
|
||||
mpz_mul_ui(a_mp, a_mp, 1000);
|
||||
mpz_add_ui(a_mp, a_mp, val_s);
|
||||
|
||||
len_w -= 4;
|
||||
}
|
||||
|
||||
return u3i_mp(a_mp);
|
||||
}
|
||||
}
|
||||
|
||||
#undef BLOCK
|
||||
#undef DIGIT
|
||||
|
||||
/* u3s_sift_ud: parse @ud.
|
||||
*/
|
||||
u3_weak
|
||||
u3s_sift_ud(u3_atom a)
|
||||
{
|
||||
c3_w len_w = u3r_met(3, a);
|
||||
c3_y* byt_y;
|
||||
|
||||
// XX assumes little-endian
|
||||
//
|
||||
if ( c3y == u3a_is_cat(a) ) {
|
||||
byt_y = (c3_y*)&a;
|
||||
}
|
||||
else {
|
||||
u3a_atom* vat_u = u3a_to_ptr(a);
|
||||
byt_y = (c3_y*)vat_u->buf_w;
|
||||
}
|
||||
|
||||
return u3s_sift_ud_bytes(len_w, byt_y);
|
||||
}
|
||||
|
@ -9,6 +9,108 @@ _setup(void)
|
||||
u3m_pave(c3y);
|
||||
}
|
||||
|
||||
static inline c3_i
|
||||
_ud_good(c3_w num_w, const c3_c* num_c)
|
||||
{
|
||||
u3_weak out;
|
||||
if ( num_w != (out = u3s_sift_ud_bytes(strlen(num_c), (c3_y*)num_c)) ) {
|
||||
if ( u3_none == out ) {
|
||||
fprintf(stderr, "sift_ud: %s fail; expected %u\r\n", num_c, num_w);
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "sift_ud: %s wrong; expected %u: actual %u\r\n", num_c, num_w, out);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline c3_i
|
||||
_ud_fail(const c3_c* num_c)
|
||||
{
|
||||
u3_weak out;
|
||||
if ( u3_none != (out = u3s_sift_ud_bytes(strlen(num_c), (c3_y*)num_c)) ) {
|
||||
u3m_p("out", out);
|
||||
fprintf(stderr, "sift_ud: %s expected fail\r\n", num_c);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static c3_i
|
||||
_test_sift_ud(void)
|
||||
{
|
||||
c3_i ret_i = 1;
|
||||
|
||||
ret_i &= _ud_good(0, "0");
|
||||
ret_i &= _ud_good(1, "1");
|
||||
ret_i &= _ud_good(12, "12");
|
||||
ret_i &= _ud_good(123, "123");
|
||||
ret_i &= _ud_good(1234, "1.234");
|
||||
ret_i &= _ud_good(12345, "12.345");
|
||||
ret_i &= _ud_good(123456, "123.456");
|
||||
ret_i &= _ud_good(1234567, "1.234.567");
|
||||
ret_i &= _ud_good(12345678, "12.345.678");
|
||||
ret_i &= _ud_good(123456789, "123.456.789");
|
||||
ret_i &= _ud_good(100000000, "100.000.000");
|
||||
ret_i &= _ud_good(101101101, "101.101.101");
|
||||
ret_i &= _ud_good(201201201, "201.201.201");
|
||||
ret_i &= _ud_good(302201100, "302.201.100");
|
||||
|
||||
ret_i &= _ud_fail("01");
|
||||
ret_i &= _ud_fail("02");
|
||||
ret_i &= _ud_fail("003");
|
||||
ret_i &= _ud_fail("1234");
|
||||
ret_i &= _ud_fail("1234.5");
|
||||
ret_i &= _ud_fail("1234.567.8");
|
||||
ret_i &= _ud_fail("1234.56..78.");
|
||||
ret_i &= _ud_fail("123.45a");
|
||||
ret_i &= _ud_fail(".123.456");
|
||||
|
||||
{
|
||||
c3_c* num_c = "4.294.967.296";
|
||||
u3_weak out = u3s_sift_ud_bytes(strlen(num_c), (c3_y*)num_c);
|
||||
u3_atom pro = u3qc_bex(32);
|
||||
|
||||
if ( u3_none == out ) {
|
||||
fprintf(stderr, "sift_ud: (bex 32) fail\r\n");
|
||||
ret_i = 0;
|
||||
}
|
||||
|
||||
if ( c3n == u3r_sing(pro, out) ) {
|
||||
u3m_p("out", out);
|
||||
fprintf(stderr, "sift_ud: (bex 32) wrong\r\n");
|
||||
ret_i = 0;
|
||||
}
|
||||
|
||||
u3z(out); u3z(pro);
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
c3_c* num_c = "340.282.366.920.938.463.463.374.607.431.768.211.456";
|
||||
u3_weak out = u3s_sift_ud_bytes(strlen(num_c), (c3_y*)num_c);
|
||||
u3_atom pro = u3qc_bex(128);
|
||||
|
||||
if ( u3_none == out ) {
|
||||
fprintf(stderr, "sift_ud: (bex 128) fail\r\n");
|
||||
ret_i = 0;
|
||||
}
|
||||
|
||||
if ( c3n == u3r_sing(pro, out) ) {
|
||||
u3m_p("out", out);
|
||||
fprintf(stderr, "sift_ud: (bex 128) wrong\r\n");
|
||||
ret_i = 0;
|
||||
}
|
||||
|
||||
u3z(out); u3z(pro);
|
||||
}
|
||||
|
||||
return ret_i;
|
||||
}
|
||||
|
||||
static c3_i
|
||||
_test_en_base16(void)
|
||||
{
|
||||
@ -400,6 +502,11 @@ _test_jets(void)
|
||||
{
|
||||
c3_i ret_i = 1;
|
||||
|
||||
if ( !_test_sift_ud() ) {
|
||||
fprintf(stderr, "test jets: sift_ud: failed\r\n");
|
||||
ret_i = 0;
|
||||
}
|
||||
|
||||
if ( !_test_base16() ) {
|
||||
fprintf(stderr, "test jets: base16: failed\r\n");
|
||||
ret_i = 0;
|
||||
|
@ -559,13 +559,18 @@ _cttp_creq_new(u3_cttp* ctp_u, c3_l num_l, u3_noun hes)
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Parse the url out of the new style url passed to us.
|
||||
// parse the url out of the new style url passed to us.
|
||||
//
|
||||
u3_noun unit_pul = u3do("de-purl:html", u3k(url));
|
||||
if (c3n == u3r_du(unit_pul)) {
|
||||
u3l_log("cttp: url parsing failed\n");
|
||||
|
||||
if ( c3n == u3r_du(unit_pul) ) {
|
||||
c3_c* url_c = u3r_string(url);
|
||||
u3l_log("cttp: unable to parse url:\n %s\n", url_c);
|
||||
c3_free(url_c);
|
||||
u3z(hes);
|
||||
return 0;
|
||||
}
|
||||
|
||||
u3_noun pul = u3t(unit_pul);
|
||||
|
||||
u3_noun hat = u3h(pul); // +hart
|
||||
@ -817,7 +822,7 @@ _cttp_creq_on_head(h2o_http1client_t* cli_u, const c3_c* err_c, c3_i ver_i,
|
||||
*/
|
||||
static h2o_http1client_head_cb
|
||||
_cttp_creq_on_connect(h2o_http1client_t* cli_u, const c3_c* err_c,
|
||||
h2o_iovec_t** vec_p, size_t* vec_t, c3_i* hed_i)
|
||||
h2o_iovec_t** vec_u, size_t* vec_i, c3_i* hed_i)
|
||||
{
|
||||
u3_creq* ceq_u = (u3_creq *)cli_u->data;
|
||||
|
||||
@ -826,11 +831,16 @@ _cttp_creq_on_connect(h2o_http1client_t* cli_u, const c3_c* err_c,
|
||||
return 0;
|
||||
}
|
||||
|
||||
// serialize request (populate rub_u)
|
||||
//
|
||||
_cttp_creq_fire(ceq_u);
|
||||
|
||||
{
|
||||
c3_w len_w;
|
||||
ceq_u->vec_u = _cttp_bods_to_vec(ceq_u->rub_u, &len_w);
|
||||
*vec_t = len_w;
|
||||
*vec_p = ceq_u->vec_u;
|
||||
|
||||
*vec_i = len_w;
|
||||
*vec_u = ceq_u->vec_u;
|
||||
*hed_i = (0 == strcmp(ceq_u->met_c, "HEAD"));
|
||||
}
|
||||
|
||||
@ -842,24 +852,28 @@ _cttp_creq_on_connect(h2o_http1client_t* cli_u, const c3_c* err_c,
|
||||
static void
|
||||
_cttp_creq_connect(u3_creq* ceq_u)
|
||||
{
|
||||
c3_assert(u3_csat_ripe == ceq_u->sat_e);
|
||||
c3_assert(ceq_u->ipf_c);
|
||||
c3_assert( u3_csat_ripe == ceq_u->sat_e );
|
||||
c3_assert( ceq_u->ipf_c );
|
||||
|
||||
h2o_iovec_t ipf_u = h2o_iovec_init(ceq_u->ipf_c, strlen(ceq_u->ipf_c));
|
||||
c3_s por_s = ceq_u->por_s ? ceq_u->por_s :
|
||||
( c3y == ceq_u->sec ) ? 443 : 80;
|
||||
|
||||
// connect by IP
|
||||
h2o_http1client_connect(&ceq_u->cli_u, ceq_u, &ceq_u->ctp_u->ctx_u, ipf_u,
|
||||
por_s, c3y == ceq_u->sec, _cttp_creq_on_connect);
|
||||
|
||||
// set hostname for TLS handshake
|
||||
// set hostname for TLS handshake
|
||||
//
|
||||
if ( ceq_u->hot_c && c3y == ceq_u->sec ) {
|
||||
c3_free(ceq_u->cli_u->ssl.server_name);
|
||||
ceq_u->cli_u->ssl.server_name = strdup(ceq_u->hot_c);
|
||||
}
|
||||
|
||||
_cttp_creq_fire(ceq_u);
|
||||
// connect by IP
|
||||
//
|
||||
{
|
||||
h2o_iovec_t ipf_u = h2o_iovec_init(ceq_u->ipf_c, strlen(ceq_u->ipf_c));
|
||||
c3_t tls_t = ( c3y == ceq_u->sec );
|
||||
c3_s por_s = ( ceq_u->por_s )
|
||||
? ceq_u->por_s
|
||||
: ( tls_t ) ? 443 : 80;
|
||||
|
||||
h2o_http1client_connect(&ceq_u->cli_u, ceq_u, &ceq_u->ctp_u->ctx_u,
|
||||
ipf_u, por_s, tls_t, _cttp_creq_on_connect);
|
||||
}
|
||||
}
|
||||
|
||||
/* _cttp_creq_resolve_cb(): cb upon IP address resolution
|
||||
@ -981,7 +995,6 @@ _cttp_ef_http_client(u3_cttp* ctp_u, u3_noun tag, u3_noun dat)
|
||||
ret_o = c3y;
|
||||
}
|
||||
else {
|
||||
u3l_log("cttp: strange request (unparsable url)\n");
|
||||
ret_o = c3n;
|
||||
}
|
||||
}
|
||||
|
@ -1 +1 @@
|
||||
1.3
|
||||
1.4
|
Loading…
Reference in New Issue
Block a user