Verified Commit edc94677 authored by staltz's avatar staltz

ux: feature: links to forked threads

parent c80ac459
......@@ -6,14 +6,16 @@
import {PureComponent} from 'react';
import {h} from '@cycle/react';
import {FeedId, MsgId, Msg} from 'ssb-typescript';
import {FeedId, MsgId, Msg, PostContent} from 'ssb-typescript';
import {ThreadAndExtras, MsgAndExtras, Likes} from '../ssb/types';
import Message from './messages/Message';
import ExpandThread from './messages/ExpandThread';
import ForkNote from './messages/ForkNote';
export type Props = {
thread: ThreadAndExtras;
selfFeedId: FeedId;
onPressFork?: (ev: {rootMsgId: MsgId}) => void;
onPressLikeCount?: (ev: {msgKey: MsgId; likes: Likes}) => void;
onPressLike?: (ev: {msgKey: MsgId; like: boolean}) => void;
onPressReply?: (ev: {msgKey: MsgId; rootKey: MsgId}) => void;
......@@ -50,12 +52,22 @@ export default class CompactThread extends PureComponent<Props> {
}
public render() {
const {thread, onPressExpand} = this.props;
const {thread, onPressExpand, onPressFork} = this.props;
const first = thread.messages[0];
if (!first) return [];
const rest = thread.messages.slice(1);
const forkedRoot: MsgId | undefined = (first as Msg<PostContent>).value
.content.root;
return [
forkedRoot
? h(ForkNote, {
rootId: forkedRoot,
onPress: () => {
onPressFork?.({rootMsgId: forkedRoot});
},
})
: null,
this.renderMessage(first),
thread.full
? null
......
......@@ -159,7 +159,7 @@ type Props = {
onPressReply?: (ev: {msgKey: MsgId; rootKey: MsgId}) => void;
onPressAuthor?: (ev: {authorFeedId: FeedId}) => void;
onPressEtc?: (msg: Msg) => void;
onPressExpandThread?: (ev: {rootMsgId: MsgId}) => void;
onGoToThread?: (ev: {rootMsgId: MsgId}) => void;
yOffsetAnimVal?: Animated.Value;
};
......@@ -240,7 +240,7 @@ export default class Feed extends PureComponent<Props, State> {
onPressReply,
onPressAuthor,
onPressEtc,
onPressExpandThread,
onGoToThread,
style,
contentContainerStyle,
progressViewOffset,
......@@ -297,7 +297,8 @@ export default class Feed extends PureComponent<Props, State> {
onPressReply,
onPressAuthor,
onPressEtc,
onPressExpand: onPressExpandThread ?? (() => {}),
onPressExpand: onGoToThread ?? (() => {}),
onPressFork: onGoToThread,
}),
h(Separator),
]),
......
......@@ -7,15 +7,17 @@
import {Stream, Subscription, Listener} from 'xstream';
import {Component, ReactElement} from 'react';
import {h} from '@cycle/react';
import {FeedId, Msg, MsgId} from 'ssb-typescript';
import {FeedId, Msg, MsgId, PostContent} from 'ssb-typescript';
import {ThreadAndExtras, MsgAndExtras, Likes} from '../ssb/types';
import Message from './messages/Message';
import PlaceholderMessage from './messages/PlaceholderMessage';
import ForkNote from './messages/ForkNote';
export type Props = {
thread: ThreadAndExtras;
publication$?: Stream<any> | null;
selfFeedId: FeedId;
onPressFork?: (ev: {rootMsgId: MsgId}) => void;
onPressLikeCount?: (ev: {msgKey: MsgId; likes: Likes}) => void;
onPressLike?: (ev: {msgKey: string; like: boolean}) => void;
onPressAuthor?: (ev: {authorFeedId: FeedId}) => void;
......@@ -97,14 +99,35 @@ export default class FullThread extends Component<Props, State> {
}
public render() {
const thread = this.props.thread;
const {thread, onPressFork} = this.props;
if (!thread.messages || thread.messages.length <= 0) return [];
// Render all messages
const children: Array<ReactElement<any>> = thread.messages.map(
this.renderMessage,
);
// Render (top) fork note
if (thread.messages[0]) {
const first = thread.messages[0] as Msg<PostContent>;
const rootId: MsgId | undefined = first.value.content.root;
if (rootId) {
children.unshift(
h(ForkNote, {
rootId,
onPress: () => {
onPressFork?.({rootMsgId: rootId});
},
}),
);
}
}
// Render (bottom) placeholder message
if (this.state.showPlaceholder) {
children.push(h(PlaceholderMessage, {key: 'placeholder'}));
}
return children;
}
}
/* Copyright (C) 2020 The Manyverse Authors.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import {PureComponent, createElement as $} from 'react';
import {MsgId} from 'ssb-typescript';
import {View, Text, StyleSheet} from 'react-native';
import {Palette} from '../../global-styles/palette';
import {Dimensions} from '../../global-styles/dimens';
import {Typography} from '../../global-styles/typography';
export type Props = {
rootId: MsgId;
onPress?: (ev: {rootMsgId: MsgId}) => void;
};
export const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: Palette.backgroundText,
marginBottom: 1,
flexDirection: 'column',
paddingHorizontal: Dimensions.horizontalSpaceBig,
paddingVertical: Dimensions.verticalSpaceNormal,
},
label: {
flexWrap: 'wrap',
overflow: 'visible',
color: Palette.text,
fontSize: Typography.fontSizeNormal,
},
cypherlink: {
color: Palette.textBrand,
textDecorationLine: 'underline',
},
});
export default class ForkNote extends PureComponent<Props> {
constructor(props: Props) {
super(props);
}
public render() {
const {rootId, onPress} = this.props;
return $(
View,
{style: styles.container},
$(Text, {style: styles.label, ellipsizeMode: 'tail', numberOfLines: 1}, [
'Forked from ',
$(
Text,
{
style: styles.cypherlink,
onPress: () => {
onPress?.({rootMsgId: rootId});
},
},
rootId,
),
]),
);
}
}
/* Copyright (C) 2018-2019 The Manyverse Authors.
/* Copyright (C) 2018-2020 The Manyverse Authors.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
......@@ -66,7 +66,7 @@ export default function intent(
.startWith(null as any) as Stream<any>,
goToThread$: xs.merge(
reactSource.select('publicFeed').events('pressExpandThread'),
reactSource.select('publicFeed').events('goToThread'),
reactSource
.select('publicFeed')
.events('pressReply')
......
/* Copyright (C) 2018-2019 The Manyverse Authors.
/* Copyright (C) 2018-2020 The Manyverse Authors.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
......@@ -54,7 +54,7 @@ export default function intent(
.compose(sample(state$)),
goToThread$: xs.merge(
reactSource.select('feed').events('pressExpandThread'),
reactSource.select('feed').events('goToThread'),
reactSource
.select('feed')
.events('pressReply')
......
/* Copyright (C) 2018-2019 The Manyverse Authors.
/* Copyright (C) 2018-2020 The Manyverse Authors.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
......@@ -74,6 +74,10 @@ export default function intent(
likes: Likes;
}>).map(({msgKey, likes}) => ({title: 'Likes', msgKey, ids: likes})),
goToAnotherThread$: reactSource
.select('thread')
.events('pressFork') as Stream<{rootMsgId: MsgId}>,
goToCompose$: reactSource
.select('reply-expand')
.events('press')
......
/* Copyright (C) 2018-2019 The Manyverse Authors.
/* Copyright (C) 2018-2020 The Manyverse Authors.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
......@@ -17,6 +17,7 @@ import {
} from '../accounts';
import {navOptions as profileScreenNavOpts} from '../profile';
import {navOptions as rawMsgScreenNavOpts} from '../raw-msg';
import {navOptions as threadScreenNavOpts} from './index';
import {navOptions as composeScreenNavOpts} from '../compose';
export type Actions = {
......@@ -25,6 +26,7 @@ export type Actions = {
msgKey: MsgId;
ids: Array<FeedId> | null;
}>;
goToAnotherThread$: Stream<{rootMsgId: FeedId}>;
goToProfile$: Stream<{authorFeedId: FeedId}>;
goToRawMsg$: Stream<Msg>;
goToCompose$: Stream<any>;
......@@ -102,6 +104,25 @@ export default function navigation(
} as Command;
});
const toThread$ = actions.goToAnotherThread$
.compose(sampleCombine(state$))
.map(
([ev, state]) =>
({
type: 'push',
layout: {
component: {
name: Screens.Thread,
passProps: {
selfFeedId: state.selfFeedId,
rootMsgId: ev.rootMsgId,
},
options: threadScreenNavOpts,
},
},
} as Command),
);
const toRawMsg$ = actions.goToRawMsg$.map(
msg =>
({
......@@ -120,5 +141,12 @@ export default function navigation(
type: 'pop',
} as PopCommand);
return xs.merge(toAccounts$, toProfile$, toCompose$, toRawMsg$, pop$);
return xs.merge(
toAccounts$,
toProfile$,
toCompose$,
toThread$,
toRawMsg$,
pop$,
);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment