diff --git a/apps/publish/src/css/custom.css b/apps/publish/src/css/custom.css index 9580c7131..ce9b48613 100644 --- a/apps/publish/src/css/custom.css +++ b/apps/publish/src/css/custom.css @@ -75,6 +75,14 @@ h2 { width: 336px; } +.w-688 { + width: 688px; +} + +.w-680 { + width: 680px; +} + .h-80 { height: 80px; } @@ -86,6 +94,8 @@ h2 { padding: 0; } + + .b-gray-30 { border-color: #B1B2B3; } diff --git a/apps/publish/src/js/components/blog.js b/apps/publish/src/js/components/blog.js index 5af775b36..ebacee37c 100644 --- a/apps/publish/src/js/components/blog.js +++ b/apps/publish/src/js/components/blog.js @@ -71,8 +71,8 @@ export class Blog extends Component { } render() { + let blog = this.retrieveColl(this.props.blogId, this.props.ship); let postProps = this.buildPosts(); - let posts = postProps.map((post) => { return ( - {posts} +
+

{blog.info.title}

+
+
+

Host

+

{host}

+
+
+

Contributors

+

{contributers}

+
+
+

Subscribers

+

{subscribers}

+
+
+
+ {posts} +
); } diff --git a/apps/publish/src/js/components/post-body.js b/apps/publish/src/js/components/post-body.js new file mode 100644 index 000000000..af8e09151 --- /dev/null +++ b/apps/publish/src/js/components/post-body.js @@ -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; + } +} + diff --git a/apps/publish/src/js/components/post.js b/apps/publish/src/js/components/post.js new file mode 100644 index 000000000..a94f9d8a3 --- /dev/null +++ b/apps/publish/src/js/components/post.js @@ -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 ( + +

{p.info.title} {pid}

+ + ); + }); + + return ( +
+ +

+ {blogLinkText} +

+ + +

{post.info.title}

+ +
+

{authorDate}

+ +

Edit

+ +
+ +
+ +
+ +
+ +
+

+ {comments.length} + + Comments + +

+

+ ↓ Show Comments +

+
+ +
+ +
+

{blog.info.title}

+

Hosted by {blog.info.owner}

+ {morePosts} +
+
+ ); + } +} + diff --git a/apps/publish/src/js/components/renderPost.js b/apps/publish/src/js/components/renderPost.js deleted file mode 100644 index 1e3e99054..000000000 --- a/apps/publish/src/js/components/renderPost.js +++ /dev/null @@ -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 ( - {item} - ); - } else { - return this.parseContent(item.c, item.gn, item.ga); - } - }); - return ( - - { children } - - ); - } - - 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 ( - {item} - ); - } 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 ( - - { children } - {"\n"} - - ); - } - - renderH1(what, node, attr) { - let h1Style = fontStyles.h1WorkSansSemibold; - let children = what.map((item) => { - if (typeof(item) === 'string') { - let style = this.parseStyleAttribute(attr); - return ( - {item} - ); - } else { - return this.parseContent(item.c, item.gn, Object.assign({style: h1Style}, item.ga)); - } - }); - return ( - - { children } - {"\n"} - - ); - } - - renderH2(what, node, attr) { - let h2Style = fontStyles.h2WorkSansSemibold; - let children = what.map((item) => { - if (typeof(item) === 'string') { - let style = this.parseStyleAttribute(attr); - return ( - {item} - ); - } else { - return this.parseContent(item.c, item.gn, Object.assign({style: h2Style}, item.ga)); - } - }); - return ( - - { children } - {"\n"} - - ); - } - - renderH3(what, node, attr) { - let h3Style = fontStyles.h3WorkSansMedium; - let children = what.map((item) => { - if (typeof(item) === 'string') { - let style = this.parseStyleAttribute(attr); - return ( - {item} - ); - } else { - return this.parseContent(item.c, item.gn, Object.assign({style: h3Style}, item.ga)); - } - }); - return ( - - { children } - {"\n"} - - ); - } - - renderH4(what, node, attr) { - let h4Style = fontStyles.h4SanFranciscoMedium; - let children = what.map((item) => { - if (typeof(item) === 'string') { - let style = this.parseStyleAttribute(attr); - return ( - {item} - ); - } else { - return this.parseContent(item.c, item.gn, Object.assign({style: h4Style}, item.ga)); - } - }); - return ( - - { children } - {"\n"} - - ); - } - - renderEM(what, node, attr) { - let emStyle = { fontWeight: '700' }; - let children = what.map((item) => { - if (typeof(item) === 'string') { - let style = this.parseStyleAttribute(attr); - return ( - {item} - ); - } else { - return this.parseContent(item.c, item.gn, Object.assign({style: emStyle}, item.ga)); - } - }); - return ( - - { children } - - ); - } - - renderI(what, node, attr) { - let iStyle = { fontStyle: 'italic' }; - let children = what.map((item) => { - if (typeof(item) === 'string') { - let style = this.parseStyleAttribute(attr); - return ( - {item} - ); - } else { - return this.parseContent(item.c, item.gn, Object.assign({style: iStyle}, item.ga)); - } - }); - return ( - - { children } - - ); - } - - renderA(what, node, attr) { - let aStyle = { textDecorationLine: 'underline' }; - let href = ''; - let child = ( ); - what.forEach((item) => { - if (typeof(item) === 'string') { - let style = this.parseStyleAttribute(attr); - href = attr.href; - child = ( - {item} - ); - return null; - } else { - return null; - } - }); - return ( - { - Linking.canOpenURL(href).then(supported => { - if (supported) { - Linking.openURL(href); - } - }); - }} - key={Math.random()}> - { child } - - ); - - } - - 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 ( - {item} - ); - } else { - return this.parseContent(item.c, item.gn, Object.assign({style: codeStyle}, item.ga)); - } - }); - return ( - - { children } - - ); - } - - renderOL(what, node, attr) { - let children = what.map((item, i) => { - if (typeof(item) === 'string') { - let style = this.parseStyleAttribute(attr); - return ( - {item} - ); - } else { - return this.parseContent(item.c, item.gn, Object.assign({ renderOL: true, OLIndex: i + 1 }, item.ga)); - } - }); - return ( - - { children } - {"\n"} - - ); - } - - renderUL(what, node, attr) { - let children = what.map((item) => { - if (typeof(item) === 'string') { - let style = this.parseStyleAttribute(attr); - return ( - {item} - ); - } else { - return this.parseContent(item.c, item.gn, Object.assign({ renderUL: true }, item.ga)); - } - }); - return ( - - { children } - {"\n"} - - ); - } - - renderLI(what, node, attr) { - let children = what.map((item) => { - if (typeof(item) === 'string') { - let style = this.parseStyleAttribute(attr); - if ('renderOL' in attr) { - return ( - {attr.OLIndex}. {item} - ); - } - - return ( - - {item} - ); - } else { - return this.parseContent(item.c, item.gn, Object.assign(attr, item.ga)); - } - }); - return ( - - { children } - {"\n"} - - ); - } - - renderIMG(what, node, attr) { - let width = Dimensions.get('window').width; - let uri = attr.src.replace("http://", "https://"); - return ( - - ); - } - - 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 ( - - - - ); - } - return ( - - { this.parseContent(this.props.what.c, this.props.what.gn, this.props.what.ga) } - - ); - } -} - -const styles = StyleSheet.create({ - container: { - flex: 1 - } -}); diff --git a/apps/publish/src/js/components/root.js b/apps/publish/src/js/components/root.js index 6a021c6fa..90e91b503 100644 --- a/apps/publish/src/js/components/root.js +++ b/apps/publish/src/js/components/root.js @@ -9,6 +9,7 @@ import { store } from '/store'; import { Recent } from '/components/recent'; import { Header } from '/components/header'; import { Blog } from '/components/blog'; +import { Post } from '/components/post'; export class Root extends Component { constructor(props) { @@ -22,7 +23,7 @@ export class Root extends Component { render() { return ( -
+
{ return ( -
- post page +
+
); }} /> +
); diff --git a/apps/publish/urbit/app/write.hoon b/apps/publish/urbit/app/write.hoon index dfece6d8d..eb4cba5b9 100644 --- a/apps/publish/urbit/app/write.hoon +++ b/apps/publish/urbit/app/write.hoon @@ -245,7 +245,8 @@ (~(put by pubs.sat) col.del new) =? subs.sat !=(our.bol who.del) (~(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)) :: %comments