mirror of
https://github.com/ilyakooo0/urbit.git
synced 2024-12-24 07:26:51 +03:00
Merge branch 'master' of github.com:urbit/interface
This commit is contained in:
commit
65e4a03f7e
@ -60,3 +60,11 @@ h2 {
|
||||
.bg-v-light-gray {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
|
||||
.gray-30 {
|
||||
color: #B1B2B3;
|
||||
}
|
||||
|
||||
.green-medium {
|
||||
color: #2ED196;
|
||||
}
|
||||
|
@ -40,6 +40,14 @@ gulp.task('jsx-transform', function(cb) {
|
||||
.pipe(gulp.dest('dist'));
|
||||
});
|
||||
|
||||
gulp.task('tile-jsx-transform', function(cb) {
|
||||
return gulp.src('tile/**/*.js')
|
||||
.pipe(sucrase({
|
||||
transforms: ['jsx']
|
||||
}))
|
||||
.pipe(gulp.dest('dist'));
|
||||
});
|
||||
|
||||
gulp.task('js-imports', function(cb) {
|
||||
return gulp.src('dist/index.js')
|
||||
.pipe(rollup({
|
||||
@ -72,6 +80,35 @@ gulp.task('js-imports', function(cb) {
|
||||
.on('end', cb);
|
||||
});
|
||||
|
||||
gulp.task('tile-js-imports', function(cb) {
|
||||
return gulp.src('dist/tile.js')
|
||||
.pipe(rollup({
|
||||
plugins: [
|
||||
commonjs({
|
||||
namedExports: {
|
||||
'node_modules/react/index.js': [ 'Component' ],
|
||||
}
|
||||
}),
|
||||
rootImport({
|
||||
root: `${__dirname}/dist/js`,
|
||||
useEntry: 'prepend',
|
||||
extensions: '.js'
|
||||
}),
|
||||
json(),
|
||||
globals(),
|
||||
builtins(),
|
||||
resolve()
|
||||
]
|
||||
}, 'umd'))
|
||||
.on('error', function(e){
|
||||
console.log(e);
|
||||
cb();
|
||||
})
|
||||
.pipe(gulp.dest('./urbit/app/write/js/'))
|
||||
.on('end', cb);
|
||||
});
|
||||
|
||||
|
||||
gulp.task('js-minify', function () {
|
||||
return gulp.src('./urbit/app/write/js/index.js')
|
||||
.pipe(minify())
|
||||
@ -101,13 +138,15 @@ gulp.task('urbit-copy', function () {
|
||||
});
|
||||
|
||||
gulp.task('js-bundle-dev', gulp.series('jsx-transform', 'js-imports'));
|
||||
gulp.task('tile-js-bundle-dev', gulp.series('tile-jsx-transform', 'tile-js-imports'));
|
||||
gulp.task('js-bundle-prod', gulp.series('jsx-transform', 'js-imports', 'js-minify', 'js-cachebust'))
|
||||
|
||||
gulp.task('bundle-dev',
|
||||
gulp.series(
|
||||
gulp.parallel(
|
||||
'css-bundle',
|
||||
'js-bundle-dev'
|
||||
'js-bundle-dev',
|
||||
'tile-js-bundle-dev'
|
||||
),
|
||||
'urbit-copy'
|
||||
)
|
||||
@ -124,7 +163,10 @@ gulp.task('bundle-prod',
|
||||
);
|
||||
|
||||
gulp.task('default', gulp.series('bundle-dev'));
|
||||
|
||||
gulp.task('watch', gulp.series('default', function() {
|
||||
gulp.watch('tile/**/*.js', gulp.parallel('tile-js-bundle-dev'));
|
||||
|
||||
gulp.watch('src/**/*.js', gulp.parallel('js-bundle-dev'));
|
||||
gulp.watch('src/**/*.css', gulp.parallel('css-bundle'));
|
||||
|
||||
|
@ -95,6 +95,18 @@ h4 {
|
||||
color: #B1B2B3;
|
||||
}
|
||||
|
||||
.green {
|
||||
color: #2AA779;
|
||||
}
|
||||
|
||||
.green-medium {
|
||||
color: #2ED196;
|
||||
}
|
||||
|
||||
.red {
|
||||
color: #EE5432;
|
||||
}
|
||||
|
||||
.w-336 {
|
||||
width: 336px;
|
||||
}
|
||||
@ -182,7 +194,11 @@ h4 {
|
||||
|
||||
.h-inner {
|
||||
height: calc(100% - 124px);
|
||||
top: 124px;
|
||||
top: 48px;
|
||||
}
|
||||
|
||||
.h-footer {
|
||||
height: 76px;
|
||||
}
|
||||
|
||||
::placeholder {
|
||||
|
110
apps/publish/src/js/components/comments.js
Normal file
110
apps/publish/src/js/components/comments.js
Normal file
@ -0,0 +1,110 @@
|
||||
import React, { Component } from 'react';
|
||||
import classnames from 'classnames';
|
||||
import { Comment } from '/components/lib/comment';
|
||||
import { CommentBox } from '/components/lib/comment-box';
|
||||
import { Sigil } from '/components/lib/icons/sigil';
|
||||
|
||||
export class Comments extends Component {
|
||||
constructor(props){
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
show: false,
|
||||
commentBody: '',
|
||||
awaiting: false,
|
||||
}
|
||||
|
||||
this.toggleDisplay = this.toggleDisplay.bind(this);
|
||||
this.commentChange = this.commentChange.bind(this);
|
||||
this.postComment = this.postComment.bind(this);
|
||||
}
|
||||
|
||||
commentChange(evt) {
|
||||
this.setState({commentBody: evt.target.value});
|
||||
}
|
||||
|
||||
toggleDisplay() {
|
||||
this.setState({show: !this.state.show});
|
||||
}
|
||||
|
||||
postComment() {
|
||||
let comment = {
|
||||
"new-comment": {
|
||||
who: this.props.ship,
|
||||
coll: this.props.blogId,
|
||||
name: this.props.postId,
|
||||
content: this.state.commentBody,
|
||||
}
|
||||
};
|
||||
console.log("post comment", comment);
|
||||
|
||||
this.setState({
|
||||
awaiting: {
|
||||
ship: this.props.ship,
|
||||
blogId: this.props.blogId,
|
||||
postId: this.props.postId,
|
||||
}
|
||||
});
|
||||
|
||||
this.props.api.action("write", "write-action", comment);
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps, prevState) {
|
||||
if (this.state.awaiting) {
|
||||
if (prevProps.comments != this.props.comments) {
|
||||
this.setState({awaiting: false, commentBody: ''});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
render(){
|
||||
if (this.state.show) {
|
||||
let our = `~${window.ship}`;
|
||||
let comments = this.props.comments.map((comment, i) => {
|
||||
let commentProps = {
|
||||
ship: comment.info.creator,
|
||||
date: comment.info["date-created"],
|
||||
body: comment.body,
|
||||
};
|
||||
return (<Comment {...commentProps} key={i} />);
|
||||
});
|
||||
return (
|
||||
<div className="cb mt3 mb4">
|
||||
<p className="gray-50 body-large b">
|
||||
{this.props.comments.length}
|
||||
<span className="black">
|
||||
Comments
|
||||
</span>
|
||||
</p>
|
||||
<p className="cl body-regular pointer" onClick={this.toggleDisplay}>
|
||||
- Hide Comments
|
||||
</p>
|
||||
|
||||
<CommentBox our={our}
|
||||
action={this.commentChange}
|
||||
enabled={!(Boolean(this.state.awaiting))}
|
||||
post={this.postComment}/>
|
||||
|
||||
|
||||
<div className="flex-col" style={{marginTop: 32}}>
|
||||
{comments}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div className="cb mt3 mb4">
|
||||
<p className="gray-50 body-large b">
|
||||
{this.props.comments.length}
|
||||
<span className="black">
|
||||
Comments
|
||||
</span>
|
||||
</p>
|
||||
<p className="cl body-regular pointer" onClick={this.toggleDisplay}>
|
||||
+ Show Comments
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
64
apps/publish/src/js/components/lib/comment-box.js
Normal file
64
apps/publish/src/js/components/lib/comment-box.js
Normal file
@ -0,0 +1,64 @@
|
||||
import React, { Component } from 'react';
|
||||
import classnames from 'classnames';
|
||||
import { Sigil } from '/components/lib/icons/sigil';
|
||||
|
||||
export class CommentBox extends Component {
|
||||
constructor(props){
|
||||
super(props);
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps, prevState) {
|
||||
if (!prevProps.enabled && this.props.enabled) {
|
||||
if (this.textarea) {
|
||||
this.textarea.value = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.props.enabled) {
|
||||
return (
|
||||
<div className="cb w-100 flex"
|
||||
style={{paddingBottom: 8, marginTop: 32}}>
|
||||
<div className="fl" style={{marginRight: 10}}>
|
||||
<Sigil ship={this.props.our} size={36}/>
|
||||
</div>
|
||||
<div className="flex-col w-100">
|
||||
<textarea className="body-regular-400 w-100"
|
||||
ref={(el) => {this.textarea = el}}
|
||||
style={{resize: "none"}}
|
||||
type="text"
|
||||
name="commentBody"
|
||||
defaultValue=''
|
||||
onChange={this.props.action}>
|
||||
</textarea>
|
||||
<p className="body-regular pointer" onClick={this.props.post}>
|
||||
-> Post
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div className="cb w-100 flex"
|
||||
style={{paddingBottom: 8, marginTop: 32}}>
|
||||
<div className="fl" style={{marginRight: 10}}>
|
||||
<Sigil ship={this.props.our} size={36}/>
|
||||
</div>
|
||||
<div className="flex-col w-100">
|
||||
<textarea className="body-regular-400 w-100"
|
||||
ref={(el) => {this.textarea = el}}
|
||||
style={{resize: "none"}}
|
||||
type="text"
|
||||
name="commentBody"
|
||||
disabled={true}>
|
||||
</textarea>
|
||||
<p className="body-regular gray-50">
|
||||
-> Post
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
58
apps/publish/src/js/components/lib/comment.js
Normal file
58
apps/publish/src/js/components/lib/comment.js
Normal file
@ -0,0 +1,58 @@
|
||||
import React, { Component } from 'react';
|
||||
import classnames from 'classnames';
|
||||
import { Sigil } from '/components/lib/icons/sigil';
|
||||
import moment from 'moment';
|
||||
|
||||
export class Comment 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',
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
render(){
|
||||
let body = this.props.body.split("\n").slice(0, -1).map((line, i) =>{
|
||||
return (<p key={i}>{line}</p>);
|
||||
});
|
||||
|
||||
let date = moment(this.props.date).fromNow();
|
||||
|
||||
return (
|
||||
<div className="cb w-100 flex" style={{paddingBottom: 16}}>
|
||||
<div className="fl" style={{marginRight: 10}}>
|
||||
<Sigil ship={this.props.ship} size={36} />
|
||||
</div>
|
||||
<div className="flex-col fl">
|
||||
<div className="label-small-mono gray-50">
|
||||
<p className="fl" style={{width: 107}}>{this.props.ship}</p>
|
||||
<p className="fl">{date}</p>
|
||||
</div>
|
||||
<div className="cb body-regular-400">
|
||||
{body}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
import React, { Component } from 'react';
|
||||
import { sealDict } from '/components/lib/seal-dict';
|
||||
|
||||
|
||||
export class Sigil extends Component {
|
||||
render() {
|
||||
let prefix = this.props.prefix ? JSON.parse(this.props.prefix) : false;
|
||||
@ -8,7 +9,7 @@ export class Sigil extends Component {
|
||||
return (
|
||||
<div
|
||||
className="bg-black"
|
||||
style={{ flexBasis: 48, padding: 4, paddingBottom: 0 }}>
|
||||
style={{ flexBasis: 35, padding: 4}}>
|
||||
{
|
||||
sealDict.getSeal(this.props.ship, this.props.size, prefix)
|
||||
}
|
||||
@ -16,3 +17,4 @@ export class Sigil extends Component {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,56 +5,72 @@ import _ from 'lodash';
|
||||
const ReactSVGComponents = {
|
||||
svg: p => {
|
||||
return (
|
||||
<svg {...p.attr} version={'1.1'} xmlns={'http://www.w3.org/2000/svg'}>
|
||||
<svg key={Math.random()}
|
||||
version={'1.1'}
|
||||
xmlns={'http://www.w3.org/2000/svg'}
|
||||
{...p.attr}>
|
||||
{ _.map(_.get(p, 'children', []), child => ReactSVGComponents[child.tag](child)) }
|
||||
</svg>
|
||||
)
|
||||
},
|
||||
circle: p => {
|
||||
return (
|
||||
<circle {...p.attr}>
|
||||
<circle
|
||||
key={Math.random()} {...p.attr}>
|
||||
{ _.map(_.get(p, 'children', []), child => ReactSVGComponents[child.tag](child)) }
|
||||
</circle>
|
||||
)
|
||||
},
|
||||
rect: p => {
|
||||
return (
|
||||
<rect {...p.attr}>
|
||||
<rect
|
||||
key={Math.random()}
|
||||
{...p.attr}>
|
||||
{ _.map(_.get(p, 'children', []), child => ReactSVGComponents[child.tag](child)) }
|
||||
</rect>
|
||||
)
|
||||
},
|
||||
path: p => {
|
||||
return (
|
||||
<path {...p.attr}>
|
||||
<path
|
||||
key={Math.random()}
|
||||
{...p.attr}>
|
||||
{ _.map(_.get(p, 'children', []), child => ReactSVGComponents[child.tag](child)) }
|
||||
</path>
|
||||
)
|
||||
},
|
||||
g: p => {
|
||||
return (
|
||||
<g {...p.attr}>
|
||||
<g
|
||||
key={Math.random()}
|
||||
{...p.attr}>
|
||||
{ _.map(_.get(p, 'children', []), child => ReactSVGComponents[child.tag](child)) }
|
||||
</g>
|
||||
)
|
||||
},
|
||||
polygon: p => {
|
||||
return (
|
||||
<polygon {...p.attr}>
|
||||
<polygon
|
||||
key={Math.random()}
|
||||
{...p.attr}>
|
||||
{ _.map(_.get(p, 'children', []), child => ReactSVGComponents[child.tag](child)) }
|
||||
</polygon>
|
||||
)
|
||||
},
|
||||
line: p => {
|
||||
return (
|
||||
<line {...p.attr}>
|
||||
<line
|
||||
key={Math.random()}
|
||||
{...p.attr}>
|
||||
{ _.map(_.get(p, 'children', []), child => ReactSVGComponents[child.tag](child)) }
|
||||
</line>
|
||||
)
|
||||
},
|
||||
polyline: p => {
|
||||
return (
|
||||
<polyline {...p.attr}>
|
||||
<polyline
|
||||
key={Math.random()}
|
||||
{...p.attr}>
|
||||
{ _.map(_.get(p, 'children', []), child => ReactSVGComponents[child.tag](child)) }
|
||||
</polyline>
|
||||
)
|
||||
|
@ -74,7 +74,7 @@ export class NewBlog extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
let data = {
|
||||
let makeBlog = {
|
||||
"new-collection" : {
|
||||
name: blogId,
|
||||
title: blogTitle,
|
||||
@ -84,11 +84,20 @@ export class NewBlog extends Component {
|
||||
},
|
||||
};
|
||||
|
||||
let sendInvites = {
|
||||
invite: {
|
||||
coll: blogId,
|
||||
title: blogTitle,
|
||||
who: this.state.collaborators,
|
||||
}
|
||||
}
|
||||
|
||||
this.setState({
|
||||
awaiting: blogId
|
||||
});
|
||||
|
||||
this.props.api.action("write", "write-action", data);
|
||||
this.props.api.action("write", "write-action", makeBlog);
|
||||
this.props.api.action("write", "write-action", sendInvites);
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps, prevState) {
|
||||
|
@ -12,7 +12,7 @@ class SideTab extends Component {
|
||||
super(props)
|
||||
}
|
||||
|
||||
render(props) {
|
||||
render() {
|
||||
if (this.props.enabled){
|
||||
return (
|
||||
<div className="w1 z-2"
|
||||
|
@ -4,6 +4,7 @@ import { PostPreview } from '/components/post-preview';
|
||||
import moment from 'moment';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { PostBody } from '/components/post-body';
|
||||
import { Comments } from '/components/comments';
|
||||
import { PathControl } from '/components/lib/path-control';
|
||||
import _ from 'lodash';
|
||||
|
||||
@ -92,7 +93,6 @@ export class Post extends Component {
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
this.setState({
|
||||
awaitingEdit: {
|
||||
ship: this.state.ship,
|
||||
@ -134,14 +134,23 @@ export class Post extends Component {
|
||||
],
|
||||
});
|
||||
|
||||
let read = {
|
||||
read: {
|
||||
who: ship,
|
||||
coll: blogId,
|
||||
post: postId,
|
||||
}
|
||||
};
|
||||
this.props.api.action("write", "write-action", read);
|
||||
|
||||
} else {
|
||||
this.setState({
|
||||
awaitingLoad: {
|
||||
ship: ship,
|
||||
blogId: blogId,
|
||||
postId: postId,
|
||||
temporary: true,
|
||||
},
|
||||
temporary: true,
|
||||
});
|
||||
this.props.api.bind(`/collection/${blogId}`, "PUT", ship, "write",
|
||||
this.handleEvent.bind(this),
|
||||
@ -171,11 +180,7 @@ export class Post extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
handleEvent(diff) {
|
||||
console.log("handle event", diff);
|
||||
|
||||
if (diff.data.total) {
|
||||
let blog = diff.data.total.data;
|
||||
let post = blog.posts[this.state.postId].post;
|
||||
@ -198,47 +203,56 @@ export class Post extends Component {
|
||||
{ text: post.info.title, url: postUrl },
|
||||
],
|
||||
});
|
||||
} else if (diff.data.collection) {
|
||||
let newBlog = this.state.blog;
|
||||
newBlog.info = diff.data.collection.data;
|
||||
this.setState({
|
||||
blog: newBlog,
|
||||
});
|
||||
} else if (diff.data.post) {
|
||||
this.setState({
|
||||
post: diff.data.post.data,
|
||||
});
|
||||
} else if (diff.data.comments) {
|
||||
this.setState({
|
||||
comments: diff.data.comments.data,
|
||||
});
|
||||
} else if (diff.data.remove) {
|
||||
// XX TODO Handle this properly
|
||||
}
|
||||
}
|
||||
|
||||
handleError() {
|
||||
console.log("handle error");
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps, prevState) {
|
||||
if (this.state.awaitingEdit) {
|
||||
let ship = this.state.awaitingEdit.ship;
|
||||
let blogId = this.state.awaitingEdit.blogId;
|
||||
let postId = this.state.awaitingEdit.postId;
|
||||
let ship = this.props.ship;
|
||||
let blogId = this.props.blogId;
|
||||
let postId = this.props.postId;
|
||||
|
||||
if (this.state.awaitingEdit.ship == window.ship) {
|
||||
let oldPost = prevState.post;
|
||||
let oldComments = prevState.comments;
|
||||
let oldBlog = prevState.blog;
|
||||
|
||||
let post = _.get(this.props,
|
||||
`pubs[${blogId}].posts[${postId}].post`, false);
|
||||
let post;
|
||||
let comments;
|
||||
let blog;
|
||||
|
||||
if ((post.info.title != oldPost.info.title) ||
|
||||
(post.raw != oldPost.raw)) {
|
||||
|
||||
this.setState({
|
||||
mode: 'view',
|
||||
titleOriginal: post.info.title,
|
||||
bodyOriginal: post.raw,
|
||||
title: post.info.title,
|
||||
body: post.raw,
|
||||
awaitingEdit: false,
|
||||
post: post,
|
||||
});
|
||||
}
|
||||
if (ship === window.ship) {
|
||||
blog = _.get(this.props, `pubs[${blogId}]`, false);
|
||||
post = _.get(blog, `posts[${postId}].post`, false);
|
||||
comments = _.get(blog, `posts[${postId}].comments`, false);
|
||||
} else {
|
||||
blog = _.get(this.props, `subs[${ship}][${blogId}]`, false);
|
||||
post = _.get(blog, `posts[${postId}].post`, false);
|
||||
comments = _.get(blog, `posts[${postId}].comments`, false);
|
||||
}
|
||||
|
||||
let oldPost = prevState.post;
|
||||
|
||||
let post = _.get(this.props,
|
||||
`subs[${ship}][${blogId}].posts[${postId}].post`, false);
|
||||
if (this.state.awaitingEdit &&
|
||||
((post.info.title != oldPost.info.title) ||
|
||||
(post.raw != oldPost.raw))) {
|
||||
|
||||
if ((post.info.title != oldPost.info.title) ||
|
||||
(post.raw != oldPost.raw)) {
|
||||
this.setState({
|
||||
mode: 'view',
|
||||
titleOriginal: post.info.title,
|
||||
@ -249,10 +263,19 @@ export class Post extends Component {
|
||||
post: post,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.state.temporary){
|
||||
if (oldPost != post) {
|
||||
this.setState({post: post});
|
||||
}
|
||||
if (oldComments != comments) {
|
||||
this.setState({comments: comments});
|
||||
}
|
||||
if (oldBlog != blog) {
|
||||
this.setState({blog: blog});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
titleChange(evt){
|
||||
this.setState({title: evt.target.value});
|
||||
@ -263,9 +286,6 @@ export class Post extends Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
console.log("post", this.props);
|
||||
|
||||
|
||||
if (this.state.awaitingLoad) {
|
||||
return (
|
||||
<div>
|
||||
@ -315,18 +335,12 @@ export class Post extends Component {
|
||||
<hr className="gray-50 w-680"/>
|
||||
|
||||
<hr className="gray-50 w-680"/>
|
||||
|
||||
<div className="cb mt3 mb4">
|
||||
<p className="gray-50 body-large b">
|
||||
{this.state.comments.length}
|
||||
<span className="black">
|
||||
Comments
|
||||
</span>
|
||||
</p>
|
||||
<p className="cl body-regular">
|
||||
+ Show Comments
|
||||
</p>
|
||||
</div>
|
||||
<Comments comments={this.state.comments}
|
||||
api={this.props.api}
|
||||
ship={this.props.ship}
|
||||
blogId={this.props.blogId}
|
||||
postId={this.props.postId}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@ -375,18 +389,12 @@ export class Post extends Component {
|
||||
<hr className="gray-50 w-680"/>
|
||||
|
||||
<hr className="gray-50 w-680"/>
|
||||
|
||||
<div className="cb mt3 mb4">
|
||||
<p className="gray-50 body-large b">
|
||||
{this.state.comments.length}
|
||||
<span className="black">
|
||||
Comments
|
||||
</span>
|
||||
</p>
|
||||
<p className="cl body-regular">
|
||||
+ Show Comments
|
||||
</p>
|
||||
</div>
|
||||
<Comments comments={this.state.comments}
|
||||
api={this.props.api}
|
||||
ship={this.props.ship}
|
||||
blogId={this.props.blogId}
|
||||
postId={this.props.postId}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -37,7 +37,7 @@ export class Pubs extends Component {
|
||||
<div className={cls} key={i}>
|
||||
<div className="fl body-regular-400" style={{flexBasis: 336}}>
|
||||
<Link to={data.url}>
|
||||
{data.title}
|
||||
{data.title}
|
||||
</Link>
|
||||
</div>
|
||||
<p className="fl body-regular-400" style={{flexBasis:336}}>
|
||||
@ -62,7 +62,7 @@ export class Pubs extends Component {
|
||||
</div>
|
||||
<div className="w-100 flex">
|
||||
<p className="fl gray-50 body-regular-400" style={{flexBasis:336}}>
|
||||
Title
|
||||
Title
|
||||
</p>
|
||||
<p className="fl gray-50 body-regular-400" style={{flexBasis:336}}>
|
||||
Host
|
||||
|
@ -45,7 +45,7 @@ export class Root extends Component {
|
||||
return (
|
||||
<Skeleton
|
||||
children={
|
||||
<Subs {...this.state} />
|
||||
<Subs {...this.state} api={api}/>
|
||||
}
|
||||
/>
|
||||
);
|
||||
|
@ -9,15 +9,16 @@ export class Skeleton extends Component {
|
||||
constructor(props){
|
||||
super(props);
|
||||
}
|
||||
//Header {...this.props}/>
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="h-100 w-100 absolute">
|
||||
<HeaderBar/>
|
||||
<div className="h-inner overflow-y-scroll">
|
||||
<div className="h-inner">
|
||||
{this.props.children}
|
||||
</div>
|
||||
<div className="h-footer">
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -9,25 +9,73 @@ const HM = withRouter(HeaderMenu);
|
||||
export class Subs extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.accept = this.accept.bind(this);
|
||||
this.reject = this.reject.bind(this);
|
||||
this.unsubscribe = this.unsubscribe.bind(this);
|
||||
}
|
||||
|
||||
buildBlogData() {
|
||||
let invites = this.props.invites.map((inv) => {
|
||||
return {
|
||||
type: 'invite',
|
||||
url: `/~publish/~${inv.who}/${inv.coll}`,
|
||||
host: `~${inv.who}`,
|
||||
title: inv.title,
|
||||
blogId: inv.coll,
|
||||
};
|
||||
})
|
||||
|
||||
let data = Object.keys(this.props.subs).map((ship) => {
|
||||
let perShip = Object.keys(this.props.subs[ship]).map((blogId) => {
|
||||
let blog = this.props.subs[ship][blogId];
|
||||
return {
|
||||
type: 'regular',
|
||||
url: `/~publish/${blog.info.owner}/${blogId}`,
|
||||
title: blog.info.title,
|
||||
host: blog.info.owner,
|
||||
lastUpdated: "idk"
|
||||
lastUpdated: "idk",
|
||||
blogId: blogId,
|
||||
}
|
||||
});
|
||||
return perShip;
|
||||
});
|
||||
let merged = data.flat();
|
||||
return merged;
|
||||
return invites.concat(merged);
|
||||
}
|
||||
|
||||
accept(host, blogId) {
|
||||
console.log("accepted invitation", host, blogId);
|
||||
let subscribe = {
|
||||
subscribe: {
|
||||
who: host.slice(1),
|
||||
coll: blogId,
|
||||
}
|
||||
};
|
||||
this.props.api.action("write", "write-action", subscribe);
|
||||
}
|
||||
|
||||
reject(host, blogId) {
|
||||
console.log("rejected invitation", host, blogId);
|
||||
let reject = {
|
||||
"reject-invite": {
|
||||
who: host.slice(1),
|
||||
coll: blogId,
|
||||
}
|
||||
};
|
||||
this.props.api.action("write", "write-action", reject);
|
||||
}
|
||||
|
||||
unsubscribe(host, blogId) {
|
||||
console.log("unsubscribe", host, blogId);
|
||||
let unsubscribe = {
|
||||
unsubscribe: {
|
||||
who: host.slice(1),
|
||||
coll: blogId,
|
||||
}
|
||||
};
|
||||
this.props.api.action("write", "write-action", unsubscribe);
|
||||
}
|
||||
|
||||
render() {
|
||||
let blogData = this.buildBlogData();
|
||||
@ -37,11 +85,12 @@ export class Subs extends Component {
|
||||
? "bg-v-light-gray"
|
||||
: "bg-white";
|
||||
let cls = "w-100 flex " + bg;
|
||||
if (data.type === 'regular') {
|
||||
return (
|
||||
<div className={cls} key={i}>
|
||||
<div className="fl body-regular-400" style={{flexBasis: 336}}>
|
||||
<Link to={data.url}>
|
||||
{data.title}
|
||||
{data.title}
|
||||
</Link>
|
||||
</div>
|
||||
<p className="fl body-regular-400" style={{flexBasis:336}}>
|
||||
@ -50,8 +99,42 @@ export class Subs extends Component {
|
||||
<p className="fl body-regular-400" style={{flexBasis:336}}>
|
||||
{data.lastUpdated}
|
||||
</p>
|
||||
<p className="fl body-regular-400 pointer"
|
||||
style={{flexBasis:336}}
|
||||
onClick={this.unsubscribe.bind(this, data.host, data.blogId)}>
|
||||
Unsubscribe
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
} else if (data.type === 'invite') {
|
||||
return (
|
||||
<div className={cls} key={i}>
|
||||
<div className="fl body-regular-400" style={{flexBasis: 336}}>
|
||||
<Link to={data.url}>
|
||||
<span className="body-large green-medium"> • </span>
|
||||
<span className="body-regular-400">Invite to</span>
|
||||
<span className="body-regular">{data.title}</span>
|
||||
</Link>
|
||||
</div>
|
||||
<p className="fl body-regular-400" style={{flexBasis:336}}>
|
||||
{data.host}
|
||||
</p>
|
||||
<p className="fl body-regular-400" style={{flexBasis:336}}>
|
||||
</p>
|
||||
<p className="fl body-regular-400" style={{flexBasis:336}}>
|
||||
<span className="green underline pointer"
|
||||
onClick={this.accept.bind(this, data.host, data.blogId)}>
|
||||
Accept
|
||||
</span>
|
||||
<span> </span>
|
||||
<span className="red underline pointer"
|
||||
onClick={this.reject.bind(this, data.host, data.blogId)}>
|
||||
Reject
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@ -66,7 +149,7 @@ export class Subs extends Component {
|
||||
</div>
|
||||
<div className="w-100 flex">
|
||||
<p className="fl gray-50 body-regular-400" style={{flexBasis:336}}>
|
||||
Title
|
||||
Title
|
||||
</p>
|
||||
<p className="fl gray-50 body-regular-400" style={{flexBasis:336}}>
|
||||
Host
|
||||
@ -74,6 +157,8 @@ export class Subs extends Component {
|
||||
<p className="fl gray-50 body-regular-400" style={{flexBasis:336}}>
|
||||
Last Updated
|
||||
</p>
|
||||
<p className="fl gray-50 body-regular-400" style={{flexBasis:336}}>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{blogs}
|
||||
|
312
apps/publish/src/js/reducers/rumor.js
Normal file
312
apps/publish/src/js/reducers/rumor.js
Normal file
@ -0,0 +1,312 @@
|
||||
export class RumorReducer {
|
||||
reduce(json, state){
|
||||
if (json.collection) {
|
||||
this.reduceCollection(json.collection, state);
|
||||
}
|
||||
if (json.post) {
|
||||
this.reducePost(json, state);
|
||||
}
|
||||
if (json.comments) {
|
||||
this.reduceComments(json, state);
|
||||
}
|
||||
if (json.total) {
|
||||
this.reduceTotal(json, state);
|
||||
}
|
||||
if (json.remove) {
|
||||
this.reduceRemove(json.remove, state);
|
||||
}
|
||||
}
|
||||
|
||||
reduceRemove(json, state) {
|
||||
if (json.who === window.ship) {
|
||||
if (json.post) {
|
||||
this.removePost(json, state);
|
||||
delete state.pubs[json.coll].posts[json.post];
|
||||
} else {
|
||||
let postIds = Object.keys(state.pubs[json.coll].posts);
|
||||
postIds.forEach((postId) => {
|
||||
this.removePost({
|
||||
who: json.who,
|
||||
coll: json.coll,
|
||||
post: postId,
|
||||
}, state);
|
||||
});
|
||||
delete state.pubs[json.coll];
|
||||
}
|
||||
} else {
|
||||
if (json.post) {
|
||||
this.removePost(json, state);
|
||||
delete state.subs[json.who][json.coll].posts[json.post];
|
||||
} else {
|
||||
let postIds = Object.keys(state.subs[json.who][json.coll].posts);
|
||||
postIds.forEach((postId) => {
|
||||
this.removePost({
|
||||
who: json.who,
|
||||
coll: json.coll,
|
||||
post: postId,
|
||||
}, state);
|
||||
});
|
||||
delete state.subs[json.who][json.coll];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
removePost(json, state) {
|
||||
this.removeLatest(json, state);
|
||||
this.removeOrder(json, state);
|
||||
this.removeUnread(json, state);
|
||||
}
|
||||
|
||||
removeLatest(json, state) {
|
||||
let idx = _.findIndex(state.latest, json);
|
||||
_.pullAt(state.latest, [idx]);
|
||||
}
|
||||
|
||||
removeUnread(json, state) {
|
||||
let idx = _.findIndex(state.latest, json);
|
||||
_.pullAt(state.latest, [idx]);
|
||||
}
|
||||
|
||||
removeOrder(json, state) {
|
||||
if (json.who === window.ship) {
|
||||
if (state.pubs[json.coll]) {
|
||||
let pinIdx = _.findIndex(state.pubs[json.coll].order.pin, json.post);
|
||||
let unpinIdx = _.findIndex(state.pubs[json.coll].order.unpin, json.post);
|
||||
|
||||
if (pinIdx != -1) {
|
||||
_.pullAt(state.pubs[json.coll].order.pin, [pinIdx]);
|
||||
}
|
||||
if (unpinIdx != -1) {
|
||||
_.pullAt(state.pubs[json.coll].order.unpin, [unpinIdx]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (state.subs[json.who][json.coll]) {
|
||||
let pinIdx =
|
||||
_.findIndex(state.subs[json.who][json.coll].order.pin, json.post);
|
||||
let unpinIdx =
|
||||
_.findIndex(state.subs[json.who][json.coll].order.unpin, json.post);
|
||||
|
||||
if (pinIdx != -1) {
|
||||
_.pullAt(state.subs[json.who][json.coll].order.pin, [pinIdx]);
|
||||
}
|
||||
if (unpinIdx != -1) {
|
||||
_.pullAt(state.subs[json.who][json.coll].order.unpin, [unpinIdx]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
reduceCollection(json, state) {
|
||||
if (json.who === window.ship) {
|
||||
state.pubs[json.coll] = {
|
||||
info: json.data,
|
||||
order: { pin: [], unpin: [] },
|
||||
posts: {},
|
||||
}
|
||||
} else {
|
||||
state.subs[json.who][json.coll] = {
|
||||
info: json.data,
|
||||
order: { pin: [], unpin: [] },
|
||||
posts: {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
reducePost(json, state) {
|
||||
let who = json.post.who;
|
||||
let coll = json.post.coll;
|
||||
let post = json.post.post;
|
||||
let data = json.post.data;
|
||||
|
||||
if (who === window.ship) {
|
||||
if (state.pubs[coll].posts[post]) {
|
||||
state.pubs[coll].posts[post].post = data;
|
||||
} else {
|
||||
state.pubs[coll].posts[post] = {
|
||||
post: data,
|
||||
comments: [],
|
||||
};
|
||||
}
|
||||
} else {
|
||||
if (state.subs[who][coll].posts[post]) {
|
||||
state.subs[who][coll].posts[post].post = data;
|
||||
} else {
|
||||
state.subs[who][coll].posts[post] = {
|
||||
post: data,
|
||||
comments: [],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
this.insertPost(json, state);
|
||||
}
|
||||
|
||||
insertPost(json, state) {
|
||||
this.insertLatest(json, state);
|
||||
this.insertUnread(json, state);
|
||||
this.insertOrder(json, state);
|
||||
}
|
||||
|
||||
insertLatest(json, state) {
|
||||
let newIndex = {
|
||||
post: json.post.post,
|
||||
coll: json.post.coll,
|
||||
who: json.post.who,
|
||||
}
|
||||
let newDate = json.post.data.info["date-created"];
|
||||
|
||||
if (state.latest.length == 0) {
|
||||
state.latest.push(newIndex);
|
||||
return;
|
||||
}
|
||||
|
||||
if (state.latest.indexOf(newIndex) != -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (var i=0; i<state.latest.length; i++) {
|
||||
let postId = state.latest[i].post;
|
||||
let blogId = state.latest[i].coll;
|
||||
let ship = state.latest[i].who;
|
||||
|
||||
if (newIndex.post == postId && newIndex.coll == blogId && newIndex.who == ship) {
|
||||
break;
|
||||
}
|
||||
|
||||
let idate = this.retrievePost(state, blogId, postId, ship).info["date-created"];
|
||||
|
||||
if (newDate >= idate) {
|
||||
state.latest.splice(i, 0, newIndex);
|
||||
break;
|
||||
} else if (i == (state.latest.length - 1)) {
|
||||
state.latest.push(newIndex);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
insertUnread(json, state) {
|
||||
if (json.post.who != window.ship) {
|
||||
state.unread.push({
|
||||
post: json.post.post,
|
||||
coll: json.post.coll,
|
||||
who: json.post.who,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
insertOrder(json, state) {
|
||||
let blogId = json.post.coll;
|
||||
let ship = json.post.who;
|
||||
let blog = this.retrieveColl(state, blogId, ship);
|
||||
let list = json.post.data.info.pinned
|
||||
? blog.order.pin
|
||||
: blog.order.unpin;
|
||||
let newDate = json.post.data.info["date-created"];
|
||||
|
||||
if (list.length == 0) {
|
||||
list.push(json.post.post);
|
||||
}
|
||||
|
||||
if (list.indexOf(json.post.post) != -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (var i=0; i<list.length; i++) {
|
||||
let postId = list[i];
|
||||
if (json.post.post === postId) {
|
||||
break;
|
||||
}
|
||||
|
||||
let idate = this.retrievePost(state, blogId, postId, ship).info["date-created"];
|
||||
|
||||
if (newDate >= idate) {
|
||||
list.splice(i, 0, json.post.post);
|
||||
break;
|
||||
} else if (i == (state.latest.length - 1)) {
|
||||
list.push(json.post.post);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (window.ship == ship) {
|
||||
state.pubs[blogId].order = json.post.data.info.pinned
|
||||
? {pin: list, unpin: blog.order.unpin}
|
||||
: {pin: blog.order.pin, unpin: list};
|
||||
} else {
|
||||
state.subs[ship][blogId].order = json.post.data.info.pinned
|
||||
? {pin: list, unpin: blog.order.unpin}
|
||||
: {pin: blog.order.pin, unpin: list};
|
||||
}
|
||||
}
|
||||
|
||||
retrieveColl(state, coll, who) {
|
||||
if (who === window.ship) {
|
||||
return state.pubs[coll];
|
||||
} else {
|
||||
return state.subs[who][coll];
|
||||
}
|
||||
}
|
||||
|
||||
retrievePost(state, coll, post, who) {
|
||||
if (who === window.ship) {
|
||||
return state.pubs[coll].posts[post].post;
|
||||
} else {
|
||||
return state.subs[who][coll].posts[post].post;
|
||||
}
|
||||
}
|
||||
|
||||
reduceComments(json, state) {
|
||||
let who = json.comments.who;
|
||||
let coll = json.comments.coll;
|
||||
let post = json.comments.post;
|
||||
let data = json.comments.data;
|
||||
|
||||
if (who === window.ship) {
|
||||
if (state.pubs[coll].posts[post]) {
|
||||
state.pubs[coll].posts[post].comments = data;
|
||||
} else {
|
||||
state.pubs[coll].posts[post] = {
|
||||
post: null,
|
||||
comments: data,
|
||||
};
|
||||
}
|
||||
} else {
|
||||
if (state.subs[who][coll].posts[post]) {
|
||||
state.subs[who][coll].posts[post].comments = data;
|
||||
} else {
|
||||
state.subs[who][coll].posts[post] = {
|
||||
post: null,
|
||||
comments: data,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
reduceTotal(json, state) {
|
||||
if (json.total.who == window.ship) {
|
||||
state.pubs[json.total.coll] = json.total.data
|
||||
} else {
|
||||
if (state.subs[json.total.who]) {
|
||||
state.subs[json.total.who][json.total.coll] = json.total.data;
|
||||
} else {
|
||||
state.subs[json.total.who] = {
|
||||
[json.total.coll] : json.total.data
|
||||
}
|
||||
}
|
||||
}
|
||||
let posts = Object.keys(json.total.data.posts);
|
||||
for (var i=0; i<posts.length; i++) {
|
||||
let post = {
|
||||
post: {
|
||||
coll: json.total.coll,
|
||||
post: posts[i],
|
||||
who: json.total.who,
|
||||
data: json.total.data.posts[posts[i]].post,
|
||||
}
|
||||
};
|
||||
this.insertPost(post, state);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,226 +1,24 @@
|
||||
import _ from 'lodash';
|
||||
|
||||
export class UpdateReducer {
|
||||
reduce(json, state){
|
||||
console.log("update-reducer", json, state);
|
||||
if (json.collection) {
|
||||
this.reduceCollection(json, state);
|
||||
}
|
||||
if (json.post) {
|
||||
this.reducePost(json, state);
|
||||
}
|
||||
if (json.comments) {
|
||||
this.reduceComments(json, state);
|
||||
}
|
||||
if (json.total) {
|
||||
this.reduceTotal(json, state);
|
||||
if (json.invite) {
|
||||
this.reduceInvite(json, state);
|
||||
}
|
||||
}
|
||||
|
||||
reduceCollection(json, state) {
|
||||
if (json.collection.who === window.ship) {
|
||||
state.pubs[json.collection.coll] = {
|
||||
info: json.collection.data,
|
||||
order: { pin: [], unpin: [] },
|
||||
posts: {},
|
||||
}
|
||||
} else {
|
||||
state.subs[json.collection.who][json.collection.coll] = {
|
||||
info: json.collection.data,
|
||||
order: { pin: [], unpin: [] },
|
||||
posts: {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
reducePost(json, state) {
|
||||
let who = json.post.who;
|
||||
let coll = json.post.coll;
|
||||
let post = json.post.post;
|
||||
let data = json.post.data;
|
||||
|
||||
if (who === window.ship) {
|
||||
if (state.pubs[coll].posts[post]) {
|
||||
state.pubs[coll].posts[post].post = data;
|
||||
} else {
|
||||
state.pubs[coll].posts[post] = {
|
||||
post: data,
|
||||
comments: [],
|
||||
reduceInvite(json, state) {
|
||||
let val = {
|
||||
title: json.invite.title,
|
||||
coll: json.invite.coll,
|
||||
who: json.invite.who,
|
||||
};
|
||||
}
|
||||
if (json.invite.add) {
|
||||
state.invites.push(val);
|
||||
} else {
|
||||
if (state.subs[who][coll].posts[post]) {
|
||||
state.subs[who][coll].posts[post].post = data;
|
||||
} else {
|
||||
state.subs[who][coll].posts[post] = {
|
||||
post: data,
|
||||
comments: [],
|
||||
};
|
||||
}
|
||||
let idx = _.findIndex(state.invites, val)
|
||||
_.pullAt(state.invites, [idx]);
|
||||
}
|
||||
|
||||
this.insertPost(json, state);
|
||||
}
|
||||
|
||||
insertPost(json, state) {
|
||||
this.insertLatest(json, state);
|
||||
this.insertUnread(json, state);
|
||||
this.insertOrder(json, state);
|
||||
}
|
||||
|
||||
insertLatest(json, state) {
|
||||
let newIndex = {
|
||||
post: json.post.post,
|
||||
coll: json.post.coll,
|
||||
who: json.post.who,
|
||||
}
|
||||
let newDate = json.post.data.info["date-created"];
|
||||
|
||||
if (state.latest.length == 0) {
|
||||
state.latest.push(newIndex);
|
||||
return;
|
||||
}
|
||||
|
||||
if (state.latest.indexOf(newIndex) != -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (var i=0; i<state.latest.length; i++) {
|
||||
let postId = state.latest[i].post;
|
||||
let blogId = state.latest[i].coll;
|
||||
let ship = state.latest[i].who;
|
||||
|
||||
if (newIndex.post == postId && newIndex.coll == blogId && newIndex.who == ship) {
|
||||
break;
|
||||
}
|
||||
|
||||
let idate = this.retrievePost(state, blogId, postId, ship).info["date-created"];
|
||||
|
||||
if (newDate >= idate) {
|
||||
state.latest.splice(i, 0, newIndex);
|
||||
break;
|
||||
} else if (i == (state.latest.length - 1)) {
|
||||
state.latest.push(newIndex);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
insertUnread(json, state) {
|
||||
if (json.post.who != window.ship) {
|
||||
state.unread.push({
|
||||
post: json.post.post,
|
||||
coll: json.post.coll,
|
||||
who: json.post.who,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
insertOrder(json, state) {
|
||||
let blogId = json.post.coll;
|
||||
let ship = json.post.who;
|
||||
let blog = this.retrieveColl(state, blogId, ship);
|
||||
let list = json.post.data.info.pinned
|
||||
? blog.order.pin
|
||||
: blog.order.unpin;
|
||||
let newDate = json.post.data.info["date-created"];
|
||||
|
||||
if (list.length == 0) {
|
||||
list.push(json.post.post);
|
||||
}
|
||||
|
||||
if (list.indexOf(json.post.post) != -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (var i=0; i<list.length; i++) {
|
||||
let postId = list[i];
|
||||
if (json.post.post === postId) {
|
||||
break;
|
||||
}
|
||||
|
||||
let idate = this.retrievePost(state, blogId, postId, ship).info["date-created"];
|
||||
|
||||
if (newDate >= idate) {
|
||||
list.splice(i, 0, json.post.post);
|
||||
break;
|
||||
} else if (i == (state.latest.length - 1)) {
|
||||
list.push(json.post.post);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (window.ship == ship) {
|
||||
state.pubs[blogId].order = json.post.data.info.pinned
|
||||
? {pin: list, unpin: blog.order.unpin}
|
||||
: {pin: blog.order.pin, unpin: list};
|
||||
} else {
|
||||
state.subs[ship][blogId].order = json.post.data.info.pinned
|
||||
? {pin: list, unpin: blog.order.unpin}
|
||||
: {pin: blog.order.pin, unpin: list};
|
||||
}
|
||||
}
|
||||
|
||||
retrieveColl(state, coll, who) {
|
||||
if (who === window.ship) {
|
||||
return state.pubs[coll];
|
||||
} else {
|
||||
return state.subs[who][coll];
|
||||
}
|
||||
}
|
||||
|
||||
retrievePost(state, coll, post, who) {
|
||||
if (who === window.ship) {
|
||||
return state.pubs[coll].posts[post].post;
|
||||
} else {
|
||||
return state.subs[who][coll].posts[post].post;
|
||||
}
|
||||
}
|
||||
|
||||
reduceComments(json, state) {
|
||||
let who = json.comments.who;
|
||||
let coll = json.comments.coll;
|
||||
let post = json.comments.post;
|
||||
let data = json.comments.data;
|
||||
|
||||
if (who === window.ship) {
|
||||
if (state.pubs[coll].posts[post]) {
|
||||
state.pubs[coll].posts[post].comments = data;
|
||||
} else {
|
||||
state.pubs[coll].posts[post] = {
|
||||
post: null,
|
||||
comments: data,
|
||||
};
|
||||
}
|
||||
} else {
|
||||
if (state.subs[who][coll].posts[post]) {
|
||||
state.subs[who][coll].posts[post].comments = data;
|
||||
} else {
|
||||
state.subs[who][coll].posts[post] = {
|
||||
post: null,
|
||||
comments: data,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
reduceTotal(json, state) {
|
||||
if (json.total.who == window.ship) {
|
||||
state.pubs[json.total.coll] = json.total.data
|
||||
} else {
|
||||
state.subs[json.total.who] = {
|
||||
[json.total.coll] : json.total.data
|
||||
}
|
||||
}
|
||||
let posts = Object.keys(json.total.data.posts);
|
||||
for (var i=0; i<posts.length; i++) {
|
||||
let post = {
|
||||
post: {
|
||||
coll: json.total.coll,
|
||||
post: posts[i],
|
||||
who: json.total.who,
|
||||
data: json.total.data.posts[posts[i]].post,
|
||||
}
|
||||
};
|
||||
this.insertPost(post, state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { UpdateReducer } from '/reducers/update';
|
||||
import { RumorReducer } from '/reducers/rumor';
|
||||
|
||||
class Store {
|
||||
constructor() {
|
||||
@ -8,6 +9,7 @@ class Store {
|
||||
console.log("store.state", this.state);
|
||||
|
||||
this.updateReducer = new UpdateReducer();
|
||||
this.rumorReducer = new RumorReducer();
|
||||
|
||||
this.setState = () => {};
|
||||
}
|
||||
@ -19,6 +21,7 @@ class Store {
|
||||
handleEvent(data) {
|
||||
console.log("store.handleEvent", data);
|
||||
this.updateReducer.reduce(data.data, this.state);
|
||||
this.rumorReducer.reduce(data.data, this.state);
|
||||
this.setState(this.state);
|
||||
}
|
||||
|
||||
|
60
apps/publish/tile/tile.js
Normal file
60
apps/publish/tile/tile.js
Normal file
@ -0,0 +1,60 @@
|
||||
import React, { Component } from 'react'
|
||||
import classnames from 'classnames';
|
||||
|
||||
|
||||
export default class PublishTile extends Component {
|
||||
constructor(props){
|
||||
super(props);
|
||||
}
|
||||
|
||||
render(){
|
||||
console.log("tile", this.props);
|
||||
|
||||
let info = [];
|
||||
if (this.props.data.invites > 0) {
|
||||
let text = (this.props.data.invites == 1)
|
||||
? "Invite"
|
||||
: "Invites"
|
||||
info.push(
|
||||
<p key={1}>
|
||||
<span className="green-medium">{this.props.data.invites} </span>
|
||||
{text}
|
||||
</p>
|
||||
);
|
||||
}
|
||||
if (this.props.data.new > 0) {
|
||||
let text = (this.props.data.new == 1)
|
||||
? "New Post"
|
||||
: "New Posts"
|
||||
info.push(
|
||||
<p key={2}>
|
||||
<span className="green-medium">{this.props.data.new} </span>
|
||||
{text}
|
||||
</p>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="w-100 h-100 relative" style={{background: "#1a1a1a"}}>
|
||||
<a className="w-100 h-100 db no-underline" href="/~publish">
|
||||
<p className="gray-30 label-regular b absolute"
|
||||
style={{left: 8, top: 4}}>
|
||||
Publish
|
||||
</p>
|
||||
<img
|
||||
className="absolute"
|
||||
style={{left: 60, top: 66}}
|
||||
src="/~publish/tile.png"
|
||||
width={113}
|
||||
height={102} />
|
||||
<div className="absolute w-100 flex-col body-regular white"
|
||||
style={{verticalAlign: "bottom", bottom: 8, left: 8}}>
|
||||
{info}
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
window.writeTile = PublishTile;
|
@ -22,6 +22,17 @@
|
||||
/~ ~
|
||||
==
|
||||
::
|
||||
/= tile-js
|
||||
/^ octs
|
||||
/; as-octs:mimes:html
|
||||
/| /: /===/app/write/js/tile /js/
|
||||
/~ ~
|
||||
==
|
||||
::
|
||||
/= images
|
||||
/^ (map knot @)
|
||||
/: /===/app/write/img /_ /png/
|
||||
::
|
||||
|%
|
||||
::
|
||||
+$ move [bone card]
|
||||
@ -44,6 +55,7 @@
|
||||
+$ poke
|
||||
$% [%hall-action action:hall]
|
||||
[%write-action action]
|
||||
[%noun @tas path @t]
|
||||
==
|
||||
::
|
||||
+$ diff
|
||||
@ -51,6 +63,7 @@
|
||||
[%json json]
|
||||
[%write-collection collection]
|
||||
[%write-rumor rumor]
|
||||
[%write-update update]
|
||||
==
|
||||
::
|
||||
--
|
||||
@ -131,8 +144,13 @@
|
||||
^- (quip move _this)
|
||||
~& write-prep+act.bol
|
||||
?~ old
|
||||
:_ this
|
||||
[ost.bol %connect / [~ /'~publish'] %write]~
|
||||
:_ this(sat *state)
|
||||
:~ [ost.bol %connect / [~ /'~publish'] %write]
|
||||
:* ost.bol %poke /publish [our.bol %launch]
|
||||
%noun %write /publishtile '/~publish/tile.js'
|
||||
==
|
||||
==
|
||||
::
|
||||
:: [~ this(sat *state)]
|
||||
[~ this(sat (state u.old))]
|
||||
::
|
||||
@ -143,6 +161,9 @@
|
||||
[~ this]
|
||||
?+ a
|
||||
[~ this]
|
||||
::
|
||||
%update-tile
|
||||
[make-tile-moves this]
|
||||
::
|
||||
%test-build
|
||||
=/ schema=schematic:ford
|
||||
@ -309,7 +330,7 @@
|
||||
::
|
||||
=? unread.sat !=(who our.bol)
|
||||
(~(put in unread.sat) who coll post)
|
||||
da-this
|
||||
(da-emil make-tile-moves)
|
||||
::
|
||||
++ da-insert-latest
|
||||
|= [who=@p coll=@tas post=@tas]
|
||||
@ -638,6 +659,7 @@
|
||||
++ poke-write-action
|
||||
|= act=action
|
||||
^- (quip move _this)
|
||||
~& poke+act
|
||||
?- -.act
|
||||
::
|
||||
%new-collection
|
||||
@ -789,15 +811,29 @@
|
||||
|= who=@p
|
||||
^- move
|
||||
[ost.bol %poke /forward [who %write] %write-action new-act]
|
||||
:- ~
|
||||
this(invites.sat (~(put by invites.sat) [src.bol coll.act] title.act))
|
||||
=. invites.sat (~(put by invites.sat) [src.bol coll.act] title.act)
|
||||
:_ this
|
||||
%+ welp make-tile-moves
|
||||
::
|
||||
%+ turn (prey:pubsub:userlib /primary bol)
|
||||
|= [b=bone *]
|
||||
[b %diff %write-update %invite %.y src.bol coll.act title.act]
|
||||
::
|
||||
:: %reject-invite: remove invite from list, acceptance is handled by
|
||||
:: %subscribe action
|
||||
::
|
||||
%reject-invite
|
||||
:- ~
|
||||
this(invites.sat (~(del by invites.sat) [who.act coll.act]))
|
||||
=/ title=(unit @t) (~(get by invites.sat) [who.act coll.act])
|
||||
?~ title
|
||||
[~ this]
|
||||
=. invites.sat (~(del by invites.sat) [who.act coll.act])
|
||||
:_ this
|
||||
%+ welp make-tile-moves
|
||||
::
|
||||
%+ turn (prey:pubsub:userlib /primary bol)
|
||||
|= [b=bone *]
|
||||
^- move
|
||||
[b %diff %write-update %invite %.n who.act coll.act u.title]
|
||||
::
|
||||
:: %serve:
|
||||
::
|
||||
@ -885,7 +921,8 @@
|
||||
=(coll coll.act)
|
||||
==
|
||||
::
|
||||
=/ new-unread=(set [@p @tas @tas])
|
||||
=. unread.sat
|
||||
^- (set [@p @tas @tas])
|
||||
%- sy
|
||||
%+ skip ~(tap in unread.sat)
|
||||
|= [who=@p coll=@tas post=@tas]
|
||||
@ -893,12 +930,13 @@
|
||||
=(coll coll.act)
|
||||
==
|
||||
::
|
||||
:- [[ost.bol %kill /collection/[coll.act] ~] kills]
|
||||
:- %+ welp
|
||||
make-tile-moves
|
||||
[[ost.bol %kill /collection/[coll.act] ~] kills]
|
||||
%= this
|
||||
pubs.sat (~(del by pubs.sat) coll.act)
|
||||
awaiting.sat (~(del by awaiting.sat) coll.act)
|
||||
latest.sat new-latest
|
||||
unread.sat new-unread
|
||||
==
|
||||
::
|
||||
:: %subscribe: sub to a foreign blog; remove invites for that blog
|
||||
@ -906,8 +944,18 @@
|
||||
%subscribe
|
||||
~& write-action+act
|
||||
=/ wir=wire /collection/[coll.act]
|
||||
:_ this(invites.sat (~(del by invites.sat) [who.act coll.act]))
|
||||
=/ title=(unit @t) (~(get by invites.sat) [who.act coll.act])
|
||||
=. invites.sat (~(del by invites.sat) [who.act coll.act])
|
||||
:_ this
|
||||
;: welp
|
||||
make-tile-moves
|
||||
[ost.bol %peer wir [who.act %write] wir]~
|
||||
?~ title ~
|
||||
%+ turn (prey:pubsub:userlib /primary bol)
|
||||
|= [b=bone *]
|
||||
^- move
|
||||
[b %diff %write-update %invite %.n who.act coll.act u.title]
|
||||
==
|
||||
::
|
||||
:: %unsubscribe: unsub from a foreign blog, delete all state related to it
|
||||
::
|
||||
@ -919,7 +967,8 @@
|
||||
=(coll coll.act)
|
||||
==
|
||||
::
|
||||
=/ new-unread=(set [@p @tas @tas])
|
||||
=. unread.sat
|
||||
^- (set [@p @tas @tas])
|
||||
%- sy
|
||||
%+ skim ~(tap in unread.sat)
|
||||
|= [who=@p coll=@tas post=@tas]
|
||||
@ -927,12 +976,22 @@
|
||||
=(coll coll.act)
|
||||
==
|
||||
=/ wir=wire /collection/[coll.act]
|
||||
:- [ost.bol %pull wir [who.act %write] ~]~
|
||||
%= this
|
||||
:_ %= this
|
||||
subs.sat (~(del by subs.sat) who.act coll.act)
|
||||
latest.sat new-latest
|
||||
unread.sat new-unread
|
||||
==
|
||||
:- [ost.bol %pull wir [who.act %write] ~]
|
||||
%+ welp make-tile-moves
|
||||
%+ turn (prey:pubsub:userlib /primary bol)
|
||||
|= [b=bone *]
|
||||
^- move
|
||||
[b %diff %write-rumor %remove who.act coll.act ~]
|
||||
::
|
||||
:: %read: notify that we've seen a post
|
||||
::
|
||||
%read
|
||||
=. unread.sat (~(del in unread.sat) who.act coll.act post.act)
|
||||
[make-tile-moves this]
|
||||
::
|
||||
==
|
||||
::
|
||||
@ -952,6 +1011,16 @@
|
||||
?+ request-line
|
||||
:_ this
|
||||
[ost.bol %http-response not-found:app]~
|
||||
:: images
|
||||
::
|
||||
[[[~ %png] [%'~publish' @t ~]] ~]
|
||||
=/ filename=@t i.t.site.request-line
|
||||
=/ img=(unit @t) (~(get by images) filename)
|
||||
?~ img
|
||||
:_ this
|
||||
[ost.bol %http-response not-found:app]~
|
||||
:_ this
|
||||
[ost.bol %http-response (png-response:app (as-octs:mimes:html u.img))]~
|
||||
:: styling
|
||||
::
|
||||
[[[~ %css] [%'~publish' %index ~]] ~]
|
||||
@ -962,6 +1031,11 @@
|
||||
[[[~ %js] [%'~publish' %index ~]] ~]
|
||||
:_ this
|
||||
[ost.bol %http-response (js-response:app js)]~
|
||||
:: tile js
|
||||
::
|
||||
[[[~ %js] [%'~publish' %tile ~]] ~]
|
||||
:_ this
|
||||
[ost.bol %http-response (js-response:app tile-js)]~
|
||||
:: home page; redirect to recent
|
||||
::
|
||||
[[~ [%'~publish' ~]] ~]
|
||||
@ -1039,6 +1113,26 @@
|
||||
::
|
||||
==
|
||||
::
|
||||
++ make-tile-moves
|
||||
^- (list move)
|
||||
%+ turn (prey:pubsub:userlib /publishtile bol)
|
||||
|= [b=bone *]
|
||||
^- move
|
||||
[b %diff %json make-tile-json]
|
||||
::
|
||||
++ make-tile-json
|
||||
^- json
|
||||
%- pairs:enjs:format
|
||||
:~ invites+(numb:enjs:format ~(wyt by invites.sat))
|
||||
new+(numb:enjs:format ~(wyt in unread.sat))
|
||||
==
|
||||
::
|
||||
++ peer-publishtile
|
||||
|= wir=wire
|
||||
^- (quip move _this)
|
||||
:_ this
|
||||
[ost.bol %diff %json make-tile-json]~
|
||||
::
|
||||
++ peer-primary
|
||||
|= wir=wire
|
||||
~& peer-primary+wir
|
||||
|
BIN
apps/publish/urbit/app/write/img/tile.png
Normal file
BIN
apps/publish/urbit/app/write/img/tile.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
@ -42,6 +42,7 @@
|
||||
subscribe+subscribe
|
||||
unsubscribe+unsubscribe
|
||||
::
|
||||
read+read
|
||||
==
|
||||
::
|
||||
++ new-collection
|
||||
@ -76,13 +77,13 @@
|
||||
::
|
||||
++ delete-post
|
||||
%- ot:dejs
|
||||
coll+(su:dejs sym)
|
||||
:~ coll+(su:dejs sym)
|
||||
post+(su:dejs sym)
|
||||
==
|
||||
::
|
||||
++ delete-comment
|
||||
%- ot:dejs
|
||||
coll+(su:dejs sym)
|
||||
:~ coll+(su:dejs sym)
|
||||
post+(su:dejs sym)
|
||||
comment+(su:dejs sym)
|
||||
==
|
||||
@ -144,34 +145,17 @@
|
||||
%- ~(run in (sy x))
|
||||
|=(w=@ [& w])
|
||||
::
|
||||
++ item-id
|
||||
|= jon=^json
|
||||
^- item-id:write
|
||||
?> ?=(%a -.jon) :: must be array
|
||||
?< ?=(~ +.jon) :: must have at least one item
|
||||
?> ?=([%s @t] -.+.jon) :: first item must be string
|
||||
=/ coll=@tas (slav %tas +.-.+.jon) :: get first item as @tas
|
||||
?~ +.+.jon :: if only one item, return it
|
||||
coll
|
||||
?> ?=([%s @t] -.+.+.jon) :: second item must be string
|
||||
=/ post=@tas (slav %tas +.-.+.+.jon) :: get second item as @tas
|
||||
?~ +.+.+.jon :: if two items, return them
|
||||
[coll post]
|
||||
?> ?=([%s @t] -.+.+.+.jon) :: third item must be string
|
||||
=/ comm=@tas (slav %tas +.-.+.+.+.jon) :: get third item as @tas
|
||||
?> ?=(~ +.+.+.+.jon) :: no fourth item
|
||||
[coll post comm]
|
||||
::
|
||||
++ invite
|
||||
%- ot:dejs
|
||||
:~ coll+(su:dejs sym)
|
||||
title+so:dejs
|
||||
who+(ar:dejs (su:dejs fed:ag))
|
||||
==
|
||||
::
|
||||
++ reject-invite
|
||||
%- ot:dejs
|
||||
:~ coll+(su:dejs sym)
|
||||
who+(su:dejs fed:ag)
|
||||
:~ who+(su:dejs fed:ag)
|
||||
coll+(su:dejs sym)
|
||||
==
|
||||
::
|
||||
++ serve
|
||||
@ -196,6 +180,13 @@
|
||||
coll+(su:dejs sym)
|
||||
==
|
||||
::
|
||||
++ read
|
||||
%- ot:dejs
|
||||
:~ who+(su:dejs fed:ag)
|
||||
coll+(su:dejs sym)
|
||||
post+(su:dejs sym)
|
||||
==
|
||||
::
|
||||
--
|
||||
--
|
||||
--
|
||||
|
@ -43,11 +43,12 @@
|
||||
==
|
||||
::
|
||||
%remove
|
||||
=/ suf=(list [@tas json])
|
||||
%- pairs
|
||||
:~ [%coll s+col.rum]
|
||||
:~ [%who (ship who.rum)]
|
||||
[%coll s+col.rum]
|
||||
[%post ?~(pos.rum ~ s+u.pos.rum)]
|
||||
==
|
||||
::
|
||||
==
|
||||
::
|
||||
--
|
||||
|
26
apps/publish/urbit/mar/write/update.hoon
Normal file
26
apps/publish/urbit/mar/write/update.hoon
Normal file
@ -0,0 +1,26 @@
|
||||
/- *write
|
||||
|_ upd=update
|
||||
++ grab
|
||||
|%
|
||||
++ noun update
|
||||
--
|
||||
++ grow
|
||||
|%
|
||||
++ noun upd
|
||||
++ json
|
||||
=, enjs:format
|
||||
%+ frond -.upd
|
||||
::
|
||||
?- -.upd
|
||||
%invite
|
||||
%- pairs
|
||||
:~ [%who (ship who.upd)]
|
||||
[%add b+add.upd]
|
||||
[%coll s+col.upd]
|
||||
[%title s+title.upd]
|
||||
==
|
||||
::
|
||||
==
|
||||
::
|
||||
--
|
||||
--
|
@ -11,12 +11,14 @@
|
||||
~
|
||||
==
|
||||
^- (list [comment-info:write @t])
|
||||
:: XX sort this list
|
||||
%+ sort
|
||||
%+ turn ~(tap by comments)
|
||||
|= [fil=knot front=(map knot cord) content=wain ~]
|
||||
^- [comment-info:write @t]
|
||||
:- (front-to-comment-info:write front)
|
||||
(of-wain:format (slag 8 content))
|
||||
|= [a=[com=comment-info:write @t] b=[com=comment-info:write @t]]
|
||||
(lte date-created.com.a date-created.com.b)
|
||||
::
|
||||
/_
|
||||
/. /&front&/udon/
|
||||
|
@ -44,7 +44,7 @@
|
||||
==
|
||||
::
|
||||
[%invite coll=@tas title=@t who=(list ship)]
|
||||
[%reject-invite coll=@tas who=ship]
|
||||
[%reject-invite who=@p coll=@tas]
|
||||
::
|
||||
[%serve coll=@tas]
|
||||
[%unserve coll=@tas]
|
||||
@ -52,6 +52,7 @@
|
||||
[%subscribe who=@p coll=@tas]
|
||||
[%unsubscribe who=@p coll=@tas]
|
||||
::
|
||||
[%read who=@p coll=@tas post=@tas]
|
||||
==
|
||||
::
|
||||
+$ collection-info
|
||||
@ -118,4 +119,8 @@
|
||||
[%total who=@p col=@tas dat=collection]
|
||||
[%remove who=@p col=@tas pos=(unit @tas)]
|
||||
==
|
||||
::
|
||||
+$ update
|
||||
$% [%invite add=? who=@p col=@tas title=@t]
|
||||
==
|
||||
--
|
||||
|
Loading…
Reference in New Issue
Block a user