first pass at post page

This commit is contained in:
Isaac Visintainer 2019-06-03 16:56:32 -07:00
parent e08d0afe33
commit 1733aa7b67
7 changed files with 286 additions and 388 deletions

View File

@ -75,6 +75,14 @@ h2 {
width: 336px; width: 336px;
} }
.w-688 {
width: 688px;
}
.w-680 {
width: 680px;
}
.h-80 { .h-80 {
height: 80px; height: 80px;
} }
@ -86,6 +94,8 @@ h2 {
padding: 0; padding: 0;
} }
.b-gray-30 { .b-gray-30 {
border-color: #B1B2B3; border-color: #B1B2B3;
} }

View File

@ -71,8 +71,8 @@ export class Blog extends Component {
} }
render() { render() {
let blog = this.retrieveColl(this.props.blogId, this.props.ship);
let postProps = this.buildPosts(); let postProps = this.buildPosts();
let posts = postProps.map((post) => { let posts = postProps.map((post) => {
return ( return (
<PostPreview <PostPreview
@ -81,9 +81,30 @@ export class Blog extends Component {
); );
}); });
let host = blog.info.owner;
let contributers = host + " and X others"; // XX backend work
let subscribers = "~bitpyx-dildus and X others"; // XX backend work
return ( return (
<div className="flex flex-wrap"> <div className="flex-col">
{posts} <h2>{blog.info.title}</h2>
<div className="flex">
<div style={{flexBasis: 350}}>
<p>Host</p>
<p>{host}</p>
</div>
<div style={{flexBasis: 350}}>
<p>Contributors</p>
<p>{contributers}</p>
</div>
<div style={{flexBasis: 350}}>
<p>Subscribers</p>
<p>{subscribers}</p>
</div>
</div>
<div className="flex flex-wrap">
{posts}
</div>
</div> </div>
); );
} }

View File

@ -0,0 +1,68 @@
import React, { Component } from 'react';
import classnames from 'classnames';
import { PostPreview } from '/components/post-preview';
import moment from 'moment';
import { Link } from 'react-router-dom';
export class PostBody extends Component {
constructor(props){
super(props)
}
renderA(what, node, attr) {
let aStyle = { textDecorationLine: "underline" };
let children = what.map((item, key) => {
if (typeof(item) === 'string') {
return item;
} else {
let newAttr = Object.assign({style: aStyle, key: key}, item.ga);
return this.parseContent(item.c, item.gn, newAttr);
}
});
const element = React.createElement(node, Object.assign({style: aStyle}, attr), children);
return element;
}
renderIMG(what, node, attr) {
let imgStyle = {
width: "100%",
height: "auto"
};
let newAttr = Object.assign({style: imgStyle}, attr);
const element = React.createElement(node, newAttr);
return element;
}
renderDefault(what, node, attr) {
let children = what.map((item, key) => {
if (typeof(item) === 'string') {
return item;
} else {
let newAttr = Object.assign({key: key}, item.ga);
return this.parseContent(item.c, item.gn, newAttr);
}
});
const element = React.createElement(node, attr, children);
return element;
}
parseContent(what, node, attr) {
switch (node) {
case "a":
return this.renderA(what, node, attr);
case "img":
return this.renderIMG(what, node, attr);
default:
return this.renderDefault(what, node, attr);
}
}
render() {
let page = this.parseContent(this.props.body.c, this.props.body.gn, this.props.body.ga);
return page;
}
}

View File

@ -0,0 +1,172 @@
import React, { Component } from 'react';
import classnames from 'classnames';
import { PostPreview } from '/components/post-preview';
import moment from 'moment';
import { Link } from 'react-router-dom';
import { PostBody } from '/components/post-body';
export class Post extends Component {
constructor(props){
super(props)
moment.updateLocale('en', {
relativeTime: {
past: function(input) {
return input === 'just now'
? input
: input + ' ago'
},
s : 'just now',
future : 'in %s',
m : '1m',
mm : '%dm',
h : '1h',
hh : '%dh',
d : '1d',
dd : '%dd',
M : '1 month',
MM : '%d months',
y : '1 year',
yy : '%d years',
}
});
}
buildPosts(){
let blogId = this.props.blogId;
let ship = this.props.ship;
let blog = this.retrieveColl(blogId, ship);
console.log("buildposts", blog);
let pinProps = blog.order.pin.map((post) => {
return this.buildPostPreviewProps(post, blogId, ship, true);
});
let unpinProps = blog.order.unpin.map((post) => {
return this.buildPostPreviewProps(post, blogId, ship, false);
});
return pinProps.concat(unpinProps);
}
buildPostPreviewProps(post, coll, who, pinned){
let pos = this.retrievePost(post, coll, who);
let col = this.retrieveColl(coll, who);
let com = this.retrieveComments(post, coll, who);
return {
postTitle: pos.info.title,
postName: post,
postSnippet: "body snippet",
numComments: com.length,
collectionTitle: col.title,
collectionName: coll,
author: who,
date: pos.info["date-created"],
pinned: pinned,
}
}
retrievePost(post, coll, who) {
if (who === window.ship) {
return this.props.pubs[coll].posts[post].post;
} else {
return this.props.subs[who][coll].posts[post].post;
}
}
retrieveComments(post, coll, who) {
if (who === window.ship) {
return this.props.pubs[coll].posts[post].comments;
} else {
return this.props.subs[who][coll].posts[post].comments;
}
}
retrieveColl(coll, who) {
if (who === window.ship) {
return this.props.pubs[coll];
} else {
return this.props.subs[who][coll];
}
}
render() {
let ship = this.props.ship;
let blog = this.retrieveColl(this.props.blogId, this.props.ship);
let post = this.retrievePost(this.props.postId, this.props.blogId, this.props.ship);
let comments = this.retrieveComments(this.props.postId, this.props.blogId, this.props.ship);
let blogLink = `/~publish/~${this.props.ship}/${this.props.blogId}`;
let blogLinkText = `<- Back to ${blog.info.title}`;
let editLink = `/~publish/~${this.props.ship}/${this.props.blogId}/${this.props.postId}/edit`;
let date = moment(post.info["date-created"]).fromNow();
let authorDate = `${post.info.creator}${date}`;
// change unpin to concatenation of pinned and unpinned
let morePosts = blog.order.unpin.slice(0,20).map((pid) => {
let p = this.retrievePost(pid, this.props.blogId, this.props.ship);
let color = (pid == this.props.postId) ? "black" : "gray-50";
let postLink = `/~publish/~${this.props.ship}/${this.props.blogId}/${pid}`;
return (
<Link to={postLink} className="label-regular">
<p className={color}>{p.info.title} {pid}</p>
</Link>
);
});
return (
<div className="w-688 flex-col center mt4">
<Link to={blogLink}>
<p className="body-regular">
{blogLinkText}
</p>
</Link>
<h2>{post.info.title}</h2>
<div className="mb4">
<p className="fl label-small gray-50">{authorDate}</p>
<Link to={editLink}>
<p className="label-regular gray-50 fr">Edit</p>
</Link>
</div>
<div className="cb">
<PostBody
body={post.body}
/>
</div>
<hr className="gray-50 w-680"/>
<div className="cb mt3 mb4">
<p className="gray-50 body-large b">
{comments.length}
<span className="black">
Comments
</span>
</p>
<p className="cl body-regular">
Show Comments
</p>
</div>
<hr className="gray-50 w-680"/>
<div className="cb flex-col">
<p className="label-regular b mb1">{blog.info.title}</p>
<p className="label-regular gray-30 mb2">Hosted by {blog.info.owner}</p>
{morePosts}
</div>
</div>
);
}
}

View File

@ -1,381 +0,0 @@
import React, { PureComponent } from 'react';
import {
ActivityIndicator,
StyleSheet,
View,
Text,
Image,
Linking,
TouchableOpacity,
Dimensions
} from 'react-native';
import PropTypes from 'prop-types';
import { fontStyles } from '../utils/styles/index';
export class RenderPost extends Component {
static propTypes = {
what: PropTypes.object.isRequired
};
parseStyleAttribute(attr) {
if ('style' in attr) {
return Object.assign({ flex: 1 }, attr.style);
}
return { flex: 1 };
}
renderDIV(what, node, attr) {
let children = what.map((item) => {
if (typeof(item) === 'string') {
let style = this.parseStyleAttribute(attr);
return (
<Text key={Math.random()} style={[ fontStyles.bodySanFrancisco, style ]}>{item}</Text>
);
} else {
return this.parseContent(item.c, item.gn, item.ga);
}
});
return (
<View key={Math.random()}>
{ children }
</View>
);
}
renderP(what, node, attr) {
let pStyle = fontStyles.bodySanFrancisco;
let hasSeen = {
c: false,
t: false
};
let children = what.map((item) => {
if (typeof(item) === 'string') {
if (item.trim().length > 0) {
hasSeen.t = true;
}
let style = this.parseStyleAttribute(attr);
return (
<Text key={Math.random()} style={[pStyle, style ]}>{item}</Text>
);
} else {
hasSeen.c = true;
return this.parseContent(item.c, item.gn, Object.assign({style: pStyle}, item.ga));
}
});
let flexDirection = hasSeen.c && hasSeen.t ? 'row' : 'column';
return (
<Text key={Math.random()} style={{ flexDirection }}>
{ children }
{"\n"}
</Text>
);
}
renderH1(what, node, attr) {
let h1Style = fontStyles.h1WorkSansSemibold;
let children = what.map((item) => {
if (typeof(item) === 'string') {
let style = this.parseStyleAttribute(attr);
return (
<Text key={Math.random()} style={[ style, h1Style ]}>{item}</Text>
);
} else {
return this.parseContent(item.c, item.gn, Object.assign({style: h1Style}, item.ga));
}
});
return (
<Text key={Math.random()}>
{ children }
{"\n"}
</Text>
);
}
renderH2(what, node, attr) {
let h2Style = fontStyles.h2WorkSansSemibold;
let children = what.map((item) => {
if (typeof(item) === 'string') {
let style = this.parseStyleAttribute(attr);
return (
<Text key={Math.random()} style={[ style, h2Style ]}>{item}</Text>
);
} else {
return this.parseContent(item.c, item.gn, Object.assign({style: h2Style}, item.ga));
}
});
return (
<Text key={Math.random()}>
{ children }
{"\n"}
</Text>
);
}
renderH3(what, node, attr) {
let h3Style = fontStyles.h3WorkSansMedium;
let children = what.map((item) => {
if (typeof(item) === 'string') {
let style = this.parseStyleAttribute(attr);
return (
<Text key={Math.random()} style={[ style, h3Style ]}>{item}</Text>
);
} else {
return this.parseContent(item.c, item.gn, Object.assign({style: h3Style}, item.ga));
}
});
return (
<Text key={Math.random()}>
{ children }
{"\n"}
</Text>
);
}
renderH4(what, node, attr) {
let h4Style = fontStyles.h4SanFranciscoMedium;
let children = what.map((item) => {
if (typeof(item) === 'string') {
let style = this.parseStyleAttribute(attr);
return (
<Text key={Math.random()} style={[ style, h4Style ]}>{item}</Text>
);
} else {
return this.parseContent(item.c, item.gn, Object.assign({style: h4Style}, item.ga));
}
});
return (
<Text key={Math.random()}>
{ children }
{"\n"}
</Text>
);
}
renderEM(what, node, attr) {
let emStyle = { fontWeight: '700' };
let children = what.map((item) => {
if (typeof(item) === 'string') {
let style = this.parseStyleAttribute(attr);
return (
<Text key={Math.random()} style={[fontStyles.bodySanFrancisco, style, emStyle ]}>{item}</Text>
);
} else {
return this.parseContent(item.c, item.gn, Object.assign({style: emStyle}, item.ga));
}
});
return (
<Text key={Math.random()}>
{ children }
</Text>
);
}
renderI(what, node, attr) {
let iStyle = { fontStyle: 'italic' };
let children = what.map((item) => {
if (typeof(item) === 'string') {
let style = this.parseStyleAttribute(attr);
return (
<Text key={Math.random()} style={[fontStyles.bodySanFrancisco, style, iStyle ]}>{item}</Text>
);
} else {
return this.parseContent(item.c, item.gn, Object.assign({style: iStyle}, item.ga));
}
});
return (
<Text key={Math.random()}>
{ children }
</Text>
);
}
renderA(what, node, attr) {
let aStyle = { textDecorationLine: 'underline' };
let href = '';
let child = (<Text> </Text>);
what.forEach((item) => {
if (typeof(item) === 'string') {
let style = this.parseStyleAttribute(attr);
href = attr.href;
child = (
<Text key={Math.random()} style={[fontStyles.bodySanFrancisco, style, aStyle ]}>{item}</Text>
);
return null;
} else {
return null;
}
});
return (
<Text onPress={ () => {
Linking.canOpenURL(href).then(supported => {
if (supported) {
Linking.openURL(href);
}
});
}}
key={Math.random()}>
{ child }
</Text>
);
}
renderCODE(what, node, attr) {
let codeStyle = { fontFamily: 'Source Code Pro', backgroundColor: '#f1f1f1' };
let children = what.map((item) => {
if (typeof(item) === 'string') {
let style = this.parseStyleAttribute(attr);
return (
<Text key={Math.random()} style={[ fontStyles.bodySanFrancisco, style, codeStyle ]}>{item}</Text>
);
} else {
return this.parseContent(item.c, item.gn, Object.assign({style: codeStyle}, item.ga));
}
});
return (
<Text key={Math.random()}>
{ children }
</Text>
);
}
renderOL(what, node, attr) {
let children = what.map((item, i) => {
if (typeof(item) === 'string') {
let style = this.parseStyleAttribute(attr);
return (
<Text key={Math.random()} style={[ fontStyles.bodySanFrancisco, style ]}>{item}</Text>
);
} else {
return this.parseContent(item.c, item.gn, Object.assign({ renderOL: true, OLIndex: i + 1 }, item.ga));
}
});
return (
<Text key={Math.random()}>
{ children }
{"\n"}
</Text>
);
}
renderUL(what, node, attr) {
let children = what.map((item) => {
if (typeof(item) === 'string') {
let style = this.parseStyleAttribute(attr);
return (
<Text key={Math.random()} style={[ fontStyles.bodySanFrancisco, style ]}>{item}</Text>
);
} else {
return this.parseContent(item.c, item.gn, Object.assign({ renderUL: true }, item.ga));
}
});
return (
<Text key={Math.random()}>
{ children }
{"\n"}
</Text>
);
}
renderLI(what, node, attr) {
let children = what.map((item) => {
if (typeof(item) === 'string') {
let style = this.parseStyleAttribute(attr);
if ('renderOL' in attr) {
return (
<Text key={Math.random()} style={[ fontStyles.bodySanFrancisco, { paddingLeft: 8 }, style ]}>{attr.OLIndex}. {item}</Text>
);
}
return (
<Text key={Math.random()} style={[ fontStyles.bodySanFrancisco, { paddingLeft: 8 }, style ]}>- {item}</Text>
);
} else {
return this.parseContent(item.c, item.gn, Object.assign(attr, item.ga));
}
});
return (
<Text key={Math.random()}>
{ children }
{"\n"}
</Text>
);
}
renderIMG(what, node, attr) {
let width = Dimensions.get('window').width;
let uri = attr.src.replace("http://", "https://");
return (
<Image key={attr.src}
resizeMode={'contain'}
style={{
width: width,
height: 200
}} source={{
uri
}} />
);
}
parseContent(what, node, attr) {
switch(node) {
case "div":
return this.renderDIV(what, node, attr);
case "p":
return this.renderP(what, node, attr);
case "h1":
return this.renderH1(what, node, attr);
case "h2":
return this.renderH2(what, node, attr);
case "h3":
return this.renderH3(what, node, attr);
case "h4":
return this.renderH4(what, node, attr);
case "ol":
return this.renderOL(what, node, attr);
case "ul":
return this.renderUL(what, node, attr);
case "li":
return this.renderLI(what, node, attr);
case "b":
case "em":
return this.renderEM(what, node, attr);
case "code":
return this.renderCODE(what, node, attr);
case "img":
return this.renderIMG(what, node, attr);
case "i":
return this.renderI(what, node, attr);
case "a":
return this.renderA(what, node, attr);
default:
//console.log(what, attr, node);
return null;
}
}
render() {
console.log(this.props.what.c);
if (!('c' in this.props.what)) {
return (
<View style={{ flex: 1, marginTop: 40, alignItems: 'center' }}>
<ActivityIndicator size="large" color="#000" />
</View>
);
}
return (
<View style={styles.container}>
{ this.parseContent(this.props.what.c, this.props.what.gn, this.props.what.ga) }
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1
}
});

View File

@ -9,6 +9,7 @@ import { store } from '/store';
import { Recent } from '/components/recent'; import { Recent } from '/components/recent';
import { Header } from '/components/header'; import { Header } from '/components/header';
import { Blog } from '/components/blog'; import { Blog } from '/components/blog';
import { Post } from '/components/post';
export class Root extends Component { export class Root extends Component {
constructor(props) { constructor(props) {
@ -22,7 +23,7 @@ export class Root extends Component {
render() { render() {
return ( return (
<div> <div className="fl w-100">
<BrowserRouter> <BrowserRouter>
<Header {...this.state} /> <Header {...this.state} />
<Route exact path="/~publish/recent" <Route exact path="/~publish/recent"
@ -72,11 +73,17 @@ export class Root extends Component {
<Route exact path="/~publish/:ship/:blog/:post" <Route exact path="/~publish/:ship/:blog/:post"
render={ (props) => { render={ (props) => {
return ( return (
<div> <div className="fl w-100">
post page <Post
blogId = {props.match.params.blog}
postId = {props.match.params.post}
ship = {props.match.params.ship.slice(1)}
{...this.state}
/>
</div> </div>
); );
}} /> }} />
</BrowserRouter> </BrowserRouter>
</div> </div>
); );

View File

@ -245,7 +245,8 @@
(~(put by pubs.sat) col.del new) (~(put by pubs.sat) col.del new)
=? subs.sat !=(our.bol who.del) =? subs.sat !=(our.bol who.del)
(~(put by subs.sat) [who.del col.del] new) (~(put by subs.sat) [who.del col.del] new)
=. da-this (da-insert who.del col.del pos.del) =? da-this ?=(~ old)
(da-insert who.del col.del pos.del)
(da-emil (affection del)) (da-emil (affection del))
:: ::
%comments %comments