Create ViewComments page to display post context
This commit is contained in:
parent
09424ac4eb
commit
4d458734ca
Binary file not shown.
After Width: | Height: | Size: 9.9 KiB |
|
@ -6,9 +6,11 @@ import { createStackNavigator } from "react-navigation-stack";
|
||||||
|
|
||||||
import { registerRootComponent } from 'expo';
|
import { registerRootComponent } from 'expo';
|
||||||
|
|
||||||
|
import ViewPostJsx from "src/components/pages/view-post";
|
||||||
|
import ViewCommentsJsx from "src/components/pages/view-comments.js";
|
||||||
|
|
||||||
import FeedJsx from "src/components/pages/feed";
|
import FeedJsx from "src/components/pages/feed";
|
||||||
import ProfileJsx, { ViewProfileJsx } from "src/components/pages/profile";
|
import ProfileJsx, { ViewProfileJsx } from "src/components/pages/profile";
|
||||||
import ViewPostJsx from "src/components/pages/view-post";
|
|
||||||
import DiscoverJsx from 'src/components/pages/discover';
|
import DiscoverJsx from 'src/components/pages/discover';
|
||||||
import SearchJsx from 'src/components/pages/discover/search';
|
import SearchJsx from 'src/components/pages/discover/search';
|
||||||
import ViewHashtagJsx from 'src/components/pages/discover/view-hashtag';
|
import ViewHashtagJsx from 'src/components/pages/discover/view-hashtag';
|
||||||
|
@ -21,6 +23,7 @@ const Stack = createStackNavigator({
|
||||||
Profile: { screen: ProfileJsx, },
|
Profile: { screen: ProfileJsx, },
|
||||||
Search: { screen: SearchJsx },
|
Search: { screen: SearchJsx },
|
||||||
ViewPost: { screen: ViewPostJsx },
|
ViewPost: { screen: ViewPostJsx },
|
||||||
|
ViewComments: { screen: ViewCommentsJsx },
|
||||||
ViewProfile: { screen: ViewProfileJsx },
|
ViewProfile: { screen: ViewProfileJsx },
|
||||||
ViewHashtag: { screen: ViewHashtagJsx }
|
ViewHashtag: { screen: ViewHashtagJsx }
|
||||||
}, {
|
}, {
|
||||||
|
|
|
@ -9,7 +9,7 @@ import TrayJsx from "src/components/navigation/tray";
|
||||||
|
|
||||||
// Provider for context menus
|
// Provider for context menus
|
||||||
// Allows for establishing global styling of context menus
|
// Allows for establishing global styling of context menus
|
||||||
const ContextJsx = (props) => {
|
export const ContextJsx = (props) => {
|
||||||
return (
|
return (
|
||||||
<MenuProvider customStyles = { providerStyles }>
|
<MenuProvider customStyles = { providerStyles }>
|
||||||
{ props.children }
|
{ props.children }
|
||||||
|
|
|
@ -43,7 +43,9 @@ const FeedJsx = (props) => {
|
||||||
<ScreenWithTrayJsx
|
<ScreenWithTrayJsx
|
||||||
active = "Feed"
|
active = "Feed"
|
||||||
navigation = { props.navigation }>
|
navigation = { props.navigation }>
|
||||||
<TimelineViewJsx posts = { TEST_POSTS } />
|
<TimelineViewJsx
|
||||||
|
navigation = { props.navigation }
|
||||||
|
posts = { TEST_POSTS } />
|
||||||
<div style = { styles.interruptionOuter }>
|
<div style = { styles.interruptionOuter }>
|
||||||
<View style = { styles.interruption }>
|
<View style = { styles.interruption }>
|
||||||
<Image
|
<Image
|
||||||
|
|
|
@ -0,0 +1,361 @@
|
||||||
|
import React, { useEffect, useState } from "react";
|
||||||
|
import { Dimensions, View, Image, TextInput, Text } from "react-native";
|
||||||
|
import { ScrollView } from "react-native-gesture-handler";
|
||||||
|
|
||||||
|
import { timeToAge } from "src/interface/rendering";
|
||||||
|
import { activeOrNot } from "src/interface/interactions";
|
||||||
|
|
||||||
|
import TimelineViewJsx from "src/components/posts/timeline-view";
|
||||||
|
import { ContextJsx } from "src/components/navigation/navigators";
|
||||||
|
import BackBarJsx from "src/components/navigation/back-bar";
|
||||||
|
import { TouchableWithoutFeedback } from "react-native-gesture-handler";
|
||||||
|
|
||||||
|
const TEST_IMAGE = "https://cache.desktopnexus.com/thumbseg/2255/2255124-bigthumbnail.jpg";
|
||||||
|
|
||||||
|
const TEST_CONTEXT = {
|
||||||
|
ancestors: [],
|
||||||
|
descendants: [
|
||||||
|
{
|
||||||
|
id: "1",
|
||||||
|
in_reply_to_id: "0",
|
||||||
|
username: "respondant1",
|
||||||
|
avatar: TEST_IMAGE,
|
||||||
|
content: "This is a comment",
|
||||||
|
favourited: false,
|
||||||
|
created_at: 1596745156000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "2",
|
||||||
|
in_reply_to_id: "0",
|
||||||
|
username: "respondant2",
|
||||||
|
avatar: TEST_IMAGE,
|
||||||
|
content: "This is a comment",
|
||||||
|
favourited: true,
|
||||||
|
created_at: 1596745156000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "3",
|
||||||
|
in_reply_to_id: "2",
|
||||||
|
username: "respondant3",
|
||||||
|
avatar: TEST_IMAGE,
|
||||||
|
content: "This is a comment",
|
||||||
|
favourited: false,
|
||||||
|
created_at: 1596745156000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "4",
|
||||||
|
in_reply_to_id: "2",
|
||||||
|
username: "respondant2",
|
||||||
|
avatar: TEST_IMAGE,
|
||||||
|
content: "This is a comment",
|
||||||
|
favourited: false,
|
||||||
|
created_at: 1596745156000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "5",
|
||||||
|
in_reply_to_id: "1",
|
||||||
|
username: "respondant4",
|
||||||
|
avatar: TEST_IMAGE,
|
||||||
|
content: "This is a comment",
|
||||||
|
favourited: false,
|
||||||
|
created_at: 1596745156000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "6",
|
||||||
|
in_reply_to_id: "4",
|
||||||
|
username: "respondant5",
|
||||||
|
avatar: TEST_IMAGE,
|
||||||
|
content: "This is a comment",
|
||||||
|
favourited: false,
|
||||||
|
created_at: 1596745156000
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
function chunkWhile(arr, fun) {
|
||||||
|
/*
|
||||||
|
* Chunk a list into partitions while fun returns something truthy
|
||||||
|
* > chunkWhile([1,1,1,2,2], (a, b) => a == b)
|
||||||
|
* [[1,1,1], [2,2]]
|
||||||
|
*/
|
||||||
|
|
||||||
|
let parts;
|
||||||
|
|
||||||
|
if (arr == []) {
|
||||||
|
return []
|
||||||
|
} else {
|
||||||
|
parts = [[arr[0]]];
|
||||||
|
}
|
||||||
|
|
||||||
|
let tail = arr.slice(1);
|
||||||
|
|
||||||
|
if (tail == []) {
|
||||||
|
return parts;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < tail.length; i++) {
|
||||||
|
let lastPart = parts[parts.length - 1];
|
||||||
|
if (fun(tail[i], lastPart[lastPart.length - 1])) {
|
||||||
|
// If fun returns something truthy, push tail[i] to the end of the
|
||||||
|
// partition at the end of the new array.
|
||||||
|
parts[parts.length - 1].push(tail[i])
|
||||||
|
} else {
|
||||||
|
// Create a new partition starting with tail[i]
|
||||||
|
parts.push([tail[i]])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return parts;
|
||||||
|
}
|
||||||
|
|
||||||
|
function threadify(descendants, parentID) {
|
||||||
|
/*
|
||||||
|
* Take a list of descendants and sort them into a 2D matrix.
|
||||||
|
* The first item is the direct descendant of parentID post and the rest
|
||||||
|
* are all the descendants of the direct descendant in order of id, the
|
||||||
|
* way Instagram displays conversations in comments.
|
||||||
|
* i.e. [[first level comment, ...descendants]]
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (descendants == []) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort comments in order of increasing reply id
|
||||||
|
const comments = descendants.sort((first, second) => {
|
||||||
|
return first.in_reply_to_id - second.in_reply_to_id;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Return partitions of comments based on their reply id
|
||||||
|
const byReply = chunkWhile(comments, (a, b) => {
|
||||||
|
return a.in_reply_to_id == b.in_reply_to_id;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Start with just the first level comments.
|
||||||
|
// All these elements should be in singleton arrays so they can be
|
||||||
|
// appended to.
|
||||||
|
let sorted = byReply[0].map(x => [x]);
|
||||||
|
|
||||||
|
let sub = byReply.slice(1); // All sub-comments
|
||||||
|
|
||||||
|
// Repeate the procedure until sub is empty (i.e all comments have been
|
||||||
|
// sorted)
|
||||||
|
while (sub.length > 0) {
|
||||||
|
sorted.forEach((thread, threadIndex) => {
|
||||||
|
for (let i = 0; i < thread.length; i++) {
|
||||||
|
const id = thread[i].id;
|
||||||
|
|
||||||
|
// Search for comment groups with that id
|
||||||
|
for(let subIndex = 0; subIndex < sub.length; subIndex++) {
|
||||||
|
// All items in each partition should have the same reply id
|
||||||
|
if(id == sub[subIndex][0].in_reply_to_id) {
|
||||||
|
// Move the newly found thread contents to thread in
|
||||||
|
// sorted
|
||||||
|
sorted[threadIndex] = sorted[threadIndex].concat(sub[subIndex]);
|
||||||
|
sub.splice(subIndex, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return sorted;
|
||||||
|
}
|
||||||
|
|
||||||
|
const CommentJsx = (props) => {
|
||||||
|
const packs = {
|
||||||
|
favourited: {
|
||||||
|
active: require("assets/eva-icons/post-actions/heart-active.png"),
|
||||||
|
inactive: require("assets/eva-icons/post-actions/heart-inactive.png")
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style = { styles.container }>
|
||||||
|
<Image
|
||||||
|
source = { { uri: props.data.avatar } }
|
||||||
|
style = { styles.avatar } />
|
||||||
|
<View style = { styles.contentContainer }>
|
||||||
|
<Text style = { styles.content }>
|
||||||
|
<span style = { styles.bold }>{ props.data.username }</span>
|
||||||
|
{ props.data.content }
|
||||||
|
</Text>
|
||||||
|
<View style = { styles.commentActions }>
|
||||||
|
<View>
|
||||||
|
<Text style = { styles.actionText }>
|
||||||
|
{ timeToAge((new Date()).getTime(), props.data.created_at) }
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
<TouchableWithoutFeedback>
|
||||||
|
<View>
|
||||||
|
<Text style = { [styles.actionText] }>
|
||||||
|
Reply
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
</TouchableWithoutFeedback>
|
||||||
|
<TouchableWithoutFeedback>
|
||||||
|
<Image
|
||||||
|
style = { [styles.heart, styles.action] }
|
||||||
|
source = { activeOrNot(props.data.favourited, packs.favourited) } />
|
||||||
|
</TouchableWithoutFeedback>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const ViewCommentsJsx = (props) => {
|
||||||
|
let [state, setState] = useState({
|
||||||
|
postData: undefined,
|
||||||
|
loaded: false,
|
||||||
|
reply: ""
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
(() => { // Some magical function that will get all the data needed
|
||||||
|
setState({ ...state,
|
||||||
|
descendants: threadify(TEST_CONTEXT.descendants),
|
||||||
|
postData: props.navigation.getParam("postData"),
|
||||||
|
loaded: true,
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ContextJsx>
|
||||||
|
<View style = { { flex: 1 } }>
|
||||||
|
<BackBarJsx navigation = { props.navigation }/>
|
||||||
|
<ScrollView>
|
||||||
|
{ state.loaded ?
|
||||||
|
<View style = { { display: state.loaded ? "block" : "none" } }>
|
||||||
|
<View style = { styles.parentPost }>
|
||||||
|
<CommentJsx
|
||||||
|
data = { state.postData } />
|
||||||
|
</View>
|
||||||
|
<View>
|
||||||
|
{
|
||||||
|
state.descendants.map((thread, i) => {
|
||||||
|
const comment = thread[0];
|
||||||
|
const subs = thread.slice(1);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View key = { i }>
|
||||||
|
<CommentJsx data = { comment }/>
|
||||||
|
{
|
||||||
|
subs.map((sub, j) => {
|
||||||
|
return (
|
||||||
|
<View
|
||||||
|
key = { j }
|
||||||
|
style = { styles.sub }>
|
||||||
|
<CommentJsx
|
||||||
|
data = { sub }/>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
: <View></View>
|
||||||
|
}
|
||||||
|
</ScrollView>
|
||||||
|
<View style = { styles.commentForm }>
|
||||||
|
<Image
|
||||||
|
style = { styles.avatar }
|
||||||
|
source = { { uri: TEST_IMAGE } }/>
|
||||||
|
<TextInput
|
||||||
|
style = { styles.commentInput }
|
||||||
|
placeholder = "Say something..."
|
||||||
|
multiline = { true }
|
||||||
|
onChangeText = { c => setState({...state, reply: c }) }/>
|
||||||
|
<View style = { styles.submitContainer }>
|
||||||
|
<TouchableWithoutFeedback>
|
||||||
|
<Image
|
||||||
|
style = { styles.commentSubmit }
|
||||||
|
source = { require("assets/eva-icons/paper-plane.png") }/>
|
||||||
|
</TouchableWithoutFeedback>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</ContextJsx>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const SCREEN_WIDTH = Dimensions.get("window").width;
|
||||||
|
|
||||||
|
const styles = {
|
||||||
|
bold: {
|
||||||
|
fontWeight: "bold",
|
||||||
|
},
|
||||||
|
container: {
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "row",
|
||||||
|
flexShrink: 1,
|
||||||
|
marginTop: 10,
|
||||||
|
marginBottom: 10,
|
||||||
|
marginRight: 20,
|
||||||
|
},
|
||||||
|
avatar: {
|
||||||
|
marginLeft: 20,
|
||||||
|
marginRight: 20,
|
||||||
|
width: 50,
|
||||||
|
height: 50,
|
||||||
|
borderRadius: "100%"
|
||||||
|
},
|
||||||
|
contentContainer: {
|
||||||
|
flexShrink: 1
|
||||||
|
},
|
||||||
|
parentPost: {
|
||||||
|
borderBottomWidth: 1,
|
||||||
|
borderBottomColor: "#CCC",
|
||||||
|
marginBottom: 10
|
||||||
|
},
|
||||||
|
sub: {
|
||||||
|
marginLeft: SCREEN_WIDTH / 8
|
||||||
|
},
|
||||||
|
commentActions: {
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "row",
|
||||||
|
alignItems: "center",
|
||||||
|
},
|
||||||
|
actionText: {
|
||||||
|
fontSize: 13,
|
||||||
|
color: "#666",
|
||||||
|
paddingRight: 10
|
||||||
|
},
|
||||||
|
heart: {
|
||||||
|
width: 15,
|
||||||
|
height: 15
|
||||||
|
},
|
||||||
|
|
||||||
|
commentForm: {
|
||||||
|
flexDirection: "row",
|
||||||
|
alignItems: "center",
|
||||||
|
backgroundColor: "white",
|
||||||
|
|
||||||
|
borderTopWidth: 1,
|
||||||
|
borderTopColor: "#CCC",
|
||||||
|
|
||||||
|
paddingTop: 10,
|
||||||
|
paddingBottom: 10
|
||||||
|
},
|
||||||
|
commentInput: {
|
||||||
|
borderWidth: 0,
|
||||||
|
padding: 10,
|
||||||
|
flexGrow: 3,
|
||||||
|
marginRight: 20
|
||||||
|
},
|
||||||
|
submitContainer: {
|
||||||
|
marginLeft: "auto",
|
||||||
|
marginRight: 20
|
||||||
|
},
|
||||||
|
commentSubmit: {
|
||||||
|
width: 30,
|
||||||
|
height: 30,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ViewCommentsJsx;
|
|
@ -8,7 +8,9 @@ const ViewPostJsx = (props) => {
|
||||||
<ScreenWithFullNavigationJsx
|
<ScreenWithFullNavigationJsx
|
||||||
active = { props.navigation.getParam("originTab", "Timeline") }
|
active = { props.navigation.getParam("originTab", "Timeline") }
|
||||||
navigation = { props.navigation }>
|
navigation = { props.navigation }>
|
||||||
<PostByIdJsx id = { props.id } />
|
<PostByIdJsx
|
||||||
|
navigation = { props.navigation }
|
||||||
|
id = { props.id } />
|
||||||
</ScreenWithFullNavigationJsx>
|
</ScreenWithFullNavigationJsx>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,8 @@ import {
|
||||||
renderers
|
renderers
|
||||||
} from "react-native-popup-menu";
|
} from "react-native-popup-menu";
|
||||||
|
|
||||||
|
import { pluralize, timeToAge } from "src/interface/rendering"
|
||||||
|
|
||||||
import PostActionBarJsx from "src/components/posts/post-action-bar";
|
import PostActionBarJsx from "src/components/posts/post-action-bar";
|
||||||
|
|
||||||
const SCREEN_WIDTH = Dimensions.get("window").width;
|
const SCREEN_WIDTH = Dimensions.get("window").width;
|
||||||
|
@ -18,14 +20,6 @@ const TEST_IMAGE = "https://cache.desktopnexus.com/thumbseg/2255/2255124-bigthum
|
||||||
// This will be used in RawPostJsx
|
// This will be used in RawPostJsx
|
||||||
const { SlideInMenu } = renderers;
|
const { SlideInMenu } = renderers;
|
||||||
|
|
||||||
function pluralize(n, singular, plural) {
|
|
||||||
if (n < 2) {
|
|
||||||
return singular;
|
|
||||||
} else {
|
|
||||||
return plural;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getAutoHeight(w1, h1, w2) {
|
function getAutoHeight(w1, h1, w2) {
|
||||||
/*
|
/*
|
||||||
Given the original dimensions and the new width, calculate what would
|
Given the original dimensions and the new width, calculate what would
|
||||||
|
@ -45,36 +39,6 @@ function getAutoHeight(w1, h1, w2) {
|
||||||
return w2 * (h1 / w1)
|
return w2 * (h1 / w1)
|
||||||
}
|
}
|
||||||
|
|
||||||
function timeToAge(time1, time2) {
|
|
||||||
/*
|
|
||||||
Output a friendly string to describe the age of a post, where `time1` and
|
|
||||||
`time2` are in milliseconds
|
|
||||||
*/
|
|
||||||
|
|
||||||
const between = (n, lower, upper) => n >= lower && n < upper;
|
|
||||||
|
|
||||||
const diff = time1 - time2;
|
|
||||||
|
|
||||||
if (diff < 60000) {
|
|
||||||
return "Seconds ago"
|
|
||||||
} else if (between(diff, 60000, 3600000)) {
|
|
||||||
const nMin = Math.floor(diff / 60000);
|
|
||||||
return nMin + " " + pluralize(nMin, "minute", "minutes") + " ago";
|
|
||||||
} else if (between(diff, 3600000, 86400000)) {
|
|
||||||
const nHours = Math.floor(diff / 3600000);
|
|
||||||
return nHours + " " + pluralize(nHours, "hour", "hours") + " ago";
|
|
||||||
} else if (between(diff, 86400000, 2629800000)) {
|
|
||||||
const nDays = Math.floor(diff / 86400000);
|
|
||||||
return nDays + " " + pluralize(nDays, "day", "days") + " ago";
|
|
||||||
} else if (between(diff, 2629800000, 31557600000)) {
|
|
||||||
const nMonths = Math.floor(diff / 2629800000);
|
|
||||||
return nMonths + " " + pluralize(nMonths, "month", "months") + " ago";
|
|
||||||
} else {
|
|
||||||
const nYears = Math.floor(diff / 31557600000);
|
|
||||||
return nYears + " " + pluralize(nYears, "year", "years") + " ago";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const RawPostJsx = (props) => {
|
export const RawPostJsx = (props) => {
|
||||||
const repliesCount = props.data.replies_count;
|
const repliesCount = props.data.replies_count;
|
||||||
|
|
||||||
|
@ -125,7 +89,13 @@ export const RawPostJsx = (props) => {
|
||||||
<Text>
|
<Text>
|
||||||
<strong>{ props.data.username }</strong> { props.data.content }
|
<strong>{ props.data.username }</strong> { props.data.content }
|
||||||
</Text>
|
</Text>
|
||||||
<TouchableWithoutFeedback>
|
<TouchableWithoutFeedback
|
||||||
|
onPress = {
|
||||||
|
() => props.navigation.navigate("ViewComments", {
|
||||||
|
originTab: props.navigation.getParam("originTab"),
|
||||||
|
postData: props.data
|
||||||
|
})
|
||||||
|
}>
|
||||||
<View>
|
<View>
|
||||||
<Text style = { styles.comments }>{ commentsText }</Text>
|
<Text style = { styles.comments }>{ commentsText }</Text>
|
||||||
</View>
|
</View>
|
||||||
|
@ -169,7 +139,8 @@ export const PostByDataJsx = (props) => {
|
||||||
<RawPostJsx
|
<RawPostJsx
|
||||||
data = { props.data }
|
data = { props.data }
|
||||||
width = { state.width }
|
width = { state.width }
|
||||||
height = { state.height } />
|
height = { state.height }
|
||||||
|
navigation = { props.navigation }/>
|
||||||
: <View></View> }
|
: <View></View> }
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
@ -210,7 +181,7 @@ export const PostByIdJsx = (props) => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
})();
|
})();
|
||||||
});
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View>
|
<View>
|
||||||
|
@ -218,7 +189,8 @@ export const PostByIdJsx = (props) => {
|
||||||
<RawPostJsx
|
<RawPostJsx
|
||||||
data = { state }
|
data = { state }
|
||||||
width = { state.width }
|
width = { state.width }
|
||||||
height = { state.height } />
|
height = { state.height }
|
||||||
|
navigation = { props.navigation }/>
|
||||||
: <View></View>
|
: <View></View>
|
||||||
}
|
}
|
||||||
</View>
|
</View>
|
||||||
|
|
|
@ -9,7 +9,9 @@ const TimelineViewJsx = (props) => {
|
||||||
{ props.posts.map((post, i) => {
|
{ props.posts.map((post, i) => {
|
||||||
return (
|
return (
|
||||||
<View key = { i } >
|
<View key = { i } >
|
||||||
<PostByDataJsx data = { post } />
|
<PostByDataJsx
|
||||||
|
navigation = { props.navigation }
|
||||||
|
data = { post } />
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}) }
|
}) }
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
export function pluralize(n, singular, plural) {
|
||||||
|
if (n < 2) {
|
||||||
|
return singular;
|
||||||
|
} else {
|
||||||
|
return plural;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function timeToAge(time1, time2) {
|
||||||
|
/*
|
||||||
|
Output a friendly string to describe the age of a post, where `time1` and
|
||||||
|
`time2` are in milliseconds
|
||||||
|
*/
|
||||||
|
|
||||||
|
const between = (n, lower, upper) => n >= lower && n < upper;
|
||||||
|
|
||||||
|
const diff = time1 - time2;
|
||||||
|
|
||||||
|
if (diff < 60000) {
|
||||||
|
return "Seconds ago"
|
||||||
|
} else if (between(diff, 60000, 3600000)) {
|
||||||
|
const nMin = Math.floor(diff / 60000);
|
||||||
|
return nMin + " " + pluralize(nMin, "minute", "minutes") + " ago";
|
||||||
|
} else if (between(diff, 3600000, 86400000)) {
|
||||||
|
const nHours = Math.floor(diff / 3600000);
|
||||||
|
return nHours + " " + pluralize(nHours, "hour", "hours") + " ago";
|
||||||
|
} else if (between(diff, 86400000, 2629800000)) {
|
||||||
|
const nDays = Math.floor(diff / 86400000);
|
||||||
|
return nDays + " " + pluralize(nDays, "day", "days") + " ago";
|
||||||
|
} else if (between(diff, 2629800000, 31557600000)) {
|
||||||
|
const nMonths = Math.floor(diff / 2629800000);
|
||||||
|
return nMonths + " " + pluralize(nMonths, "month", "months") + " ago";
|
||||||
|
} else {
|
||||||
|
const nYears = Math.floor(diff / 31557600000);
|
||||||
|
return nYears + " " + pluralize(nYears, "year", "years") + " ago";
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue