mirror of
https://github.com/ilyakooo0/urbit.git
synced 2024-12-15 10:02:47 +03:00
Merge pull request #2240 from urbit/m/link-fe
link fe: various fixes and improvements
This commit is contained in:
commit
9e0923f579
@ -7,6 +7,7 @@
|
||||
:: /json/[p]/submissions pages for all groups
|
||||
:: /json/[p]/submissions/[some-group] page for one group
|
||||
:: /json/[p]/discussions/[wood-url]/[some-group] page for url in group
|
||||
:: /json/[n]/submission/[wood-url]/[some-group] nth matching submission
|
||||
::
|
||||
/+ *link, *server, default-agent, verb
|
||||
::
|
||||
@ -83,6 +84,10 @@
|
||||
[%submissions ^]
|
||||
:_ this
|
||||
(give-initial-submissions:do p t.t.t.path)
|
||||
::
|
||||
[%submission @ ^]
|
||||
:_ this
|
||||
(give-specific-submission:do p (break-discussion-path t.t.t.path))
|
||||
::
|
||||
[%discussions @ ^]
|
||||
:_ this
|
||||
@ -258,6 +263,28 @@
|
||||
:- %discussions
|
||||
(build-discussion-path path url.submission)
|
||||
::
|
||||
++ give-specific-submission
|
||||
|= [n=@ud =path =url]
|
||||
:_ [%give %kick ~ ~]~
|
||||
=; =json
|
||||
[%give %fact ~ %json !>(json)]
|
||||
%+ frond:enjs:format 'submission'
|
||||
^- json
|
||||
=; sub=(unit submission)
|
||||
?~ sub ~
|
||||
(submission:en-json u.sub)
|
||||
=/ =submissions
|
||||
=- (~(got by -) path)
|
||||
%+ scry-for (map ^path submissions)
|
||||
[%submissions path]
|
||||
|-
|
||||
?~ submissions ~
|
||||
=* sub i.submissions
|
||||
?. =(url.sub url)
|
||||
$(submissions t.submissions)
|
||||
?: =(0 n) `sub
|
||||
$(n (dec n), submissions t.submissions)
|
||||
::
|
||||
++ give-initial-discussions
|
||||
|= [p=@ud =path =url]
|
||||
^- (list card)
|
||||
|
File diff suppressed because one or more lines are too long
@ -16,6 +16,9 @@ class UrbitApi {
|
||||
decline: this.inviteDecline.bind(this),
|
||||
invite: this.inviteInvite.bind(this)
|
||||
};
|
||||
|
||||
this.bind = this.bind.bind(this);
|
||||
this.bindLinkView = this.bindLinkView.bind(this);
|
||||
}
|
||||
|
||||
bind(path, method, ship = this.authTokens.ship, app, success, fail, quit) {
|
||||
@ -39,6 +42,13 @@ class UrbitApi {
|
||||
});
|
||||
}
|
||||
|
||||
bindLinkView(path, result, fail, quit) {
|
||||
this.bind.bind(this)(
|
||||
path, 'PUT', this.authTokens.ship, 'link-view',
|
||||
result, fail, quit
|
||||
);
|
||||
}
|
||||
|
||||
action(appl, mark, data) {
|
||||
return new Promise((resolve, reject) => {
|
||||
window.urb.poke(ship, appl, mark, data,
|
||||
@ -91,13 +101,82 @@ class UrbitApi {
|
||||
});
|
||||
}
|
||||
|
||||
getComments(path, url) {
|
||||
return this.getCommentsPage.bind(this)(path, url, 0);
|
||||
getCommentsPage(path, url, page) {
|
||||
const strictUrl = this.encodeUrl(url);
|
||||
const endpoint = '/json/' + page + '/discussions/' + strictUrl + path;
|
||||
this.bindLinkView(endpoint,
|
||||
(res) => {
|
||||
if (res.data['initial-discussions']) {
|
||||
// these aren't returned with the response,
|
||||
// so this ensures the reducers know them.
|
||||
res.data['initial-discussions'].path = path;
|
||||
res.data['initial-discussions'].url = url;
|
||||
}
|
||||
store.handleEvent(res);
|
||||
},
|
||||
console.error,
|
||||
()=>{} // no-op on quit
|
||||
);
|
||||
}
|
||||
|
||||
getCommentsPage(path, url, page) {
|
||||
//TODO factor out
|
||||
// encode the url into @ta-safe format, using logic from +wood
|
||||
getPage(path, page) {
|
||||
const endpoint = '/json/' + page + '/submissions' + path;
|
||||
this.bindLinkView(endpoint,
|
||||
(dat)=>{store.handleEvent(dat)},
|
||||
console.error,
|
||||
()=>{} // no-op on quit
|
||||
);
|
||||
}
|
||||
|
||||
getSubmission(path, url, callback) {
|
||||
const strictUrl = this.encodeUrl(url);
|
||||
const endpoint = '/json/0/submission/' + strictUrl + path;
|
||||
this.bindLinkView(endpoint,
|
||||
(res) => {
|
||||
if (res.data.submission) {
|
||||
callback(res.data.submission)
|
||||
} else {
|
||||
console.error('unexpected submission response', res);
|
||||
}
|
||||
},
|
||||
console.error,
|
||||
()=>{} // no-op on quit
|
||||
);
|
||||
}
|
||||
|
||||
linkAction(data) {
|
||||
return this.action("link-store", "link-action", data);
|
||||
}
|
||||
|
||||
postLink(path, url, title) {
|
||||
return this.linkAction({
|
||||
'save': { path, url, title }
|
||||
});
|
||||
}
|
||||
|
||||
postComment(path, url, comment) {
|
||||
return this.linkAction({
|
||||
'note': { path, url, udon: comment }
|
||||
});
|
||||
}
|
||||
|
||||
sidebarToggle() {
|
||||
let sidebarBoolean = true;
|
||||
if (store.state.sidebarShown === true) {
|
||||
sidebarBoolean = false;
|
||||
}
|
||||
store.handleEvent({
|
||||
data: {
|
||||
local: {
|
||||
'sidebarToggle': sidebarBoolean
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//TODO into lib?
|
||||
// encode the url into @ta-safe format, using logic from +wood
|
||||
encodeUrl(url) {
|
||||
let strictUrl = '';
|
||||
for (let i = 0; i < url.length; i++) {
|
||||
const char = url[i];
|
||||
@ -128,61 +207,7 @@ class UrbitApi {
|
||||
}
|
||||
strictUrl = strictUrl + add;
|
||||
}
|
||||
strictUrl = '~.' + strictUrl;
|
||||
|
||||
const endpoint = '/json/' + page + '/discussions/' + strictUrl + path;
|
||||
this.bind.bind(this)(endpoint, 'PUT', this.authTokens.ship, 'link-view',
|
||||
(res) => {
|
||||
if (res.data['initial-discussions']) {
|
||||
// these aren't returned with the response,
|
||||
// so this ensures the reducers know them.
|
||||
res.data['initial-discussions'].path = path;
|
||||
res.data['initial-discussions'].url = url;
|
||||
}
|
||||
store.handleEvent(res);
|
||||
},
|
||||
console.error,
|
||||
()=>{} // no-op on quit
|
||||
);
|
||||
}
|
||||
|
||||
getPage(path, page) {
|
||||
const endpoint = '/json/' + page + '/submissions' + path;
|
||||
this.bind.bind(this)(endpoint, 'PUT', this.authTokens.ship, 'link-view',
|
||||
(dat)=>{store.handleEvent(dat)},
|
||||
console.error,
|
||||
()=>{} // no-op on quit
|
||||
);
|
||||
}
|
||||
|
||||
linkAction(data) {
|
||||
return this.action("link-store", "link-action", data);
|
||||
}
|
||||
|
||||
postLink(path, url, title) {
|
||||
return this.linkAction({
|
||||
'save': { path, url, title }
|
||||
});
|
||||
}
|
||||
|
||||
postComment(path, url, comment, page, index) {
|
||||
return this.linkAction({
|
||||
'note': { path, url, udon: comment }
|
||||
});
|
||||
}
|
||||
|
||||
sidebarToggle() {
|
||||
let sidebarBoolean = true;
|
||||
if (store.state.sidebarShown === true) {
|
||||
sidebarBoolean = false;
|
||||
}
|
||||
store.handleEvent({
|
||||
data: {
|
||||
local: {
|
||||
'sidebarToggle': sidebarBoolean
|
||||
}
|
||||
}
|
||||
});
|
||||
return '~.' + strictUrl;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -12,10 +12,11 @@ export class CommentsPagination extends Component {
|
||||
? "dib"
|
||||
: "dn";
|
||||
|
||||
let nextDisplay = (Number(props.commentPage + 1) < Number(props.total))
|
||||
let nextDisplay = ((Number(props.commentPage) + 1) < Number(props.total))
|
||||
? "dib"
|
||||
: "dn";
|
||||
|
||||
let encodedUrl = window.btoa(props.url);
|
||||
let popout = (props.popout) ? "/popout" : "";
|
||||
|
||||
return (
|
||||
@ -27,6 +28,7 @@ export class CommentsPagination extends Component {
|
||||
+ props.path
|
||||
+ "/" + props.linkPage
|
||||
+ "/" + props.linkIndex
|
||||
+ "/" + encodedUrl
|
||||
+ "/comments" + prevPage}>
|
||||
<- Previous Page
|
||||
</Link>
|
||||
@ -37,6 +39,7 @@ export class CommentsPagination extends Component {
|
||||
+ props.path
|
||||
+ "/" + props.linkPage
|
||||
+ "/" + props.linkIndex
|
||||
+ "/" + encodedUrl
|
||||
+ "/comments" + nextPage}>
|
||||
Next Page ->
|
||||
</Link>
|
||||
|
@ -6,14 +6,18 @@ import { uxToHex } from '../../lib/util';
|
||||
import { api } from '../../api';
|
||||
|
||||
export class Comments extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
let page = "page" + this.props.commentPage;
|
||||
let comments = !!this.props.comments;
|
||||
if ((page !== "page0") &&
|
||||
(!comments || !this.props.comments[page]) &&
|
||||
(this.props.path && this.props.url)
|
||||
let page = this.props.commentPage;
|
||||
if (!this.props.comments ||
|
||||
!this.props.comments[page] ||
|
||||
this.props.comments.local[page]
|
||||
) {
|
||||
this.setState({requested: this.props.commentPage});
|
||||
api.getCommentsPage(
|
||||
this.props.path,
|
||||
this.props.url,
|
||||
@ -21,24 +25,10 @@ export class Comments extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
let page = "page" + this.props.commentPage;
|
||||
if (prevProps !== this.props) {
|
||||
if (!!this.props.comments) {
|
||||
if ((page !== "page0") && !this.props.comments[page] && this.props.url) {
|
||||
api.getCommentsPage(
|
||||
this.props.path,
|
||||
this.props.url,
|
||||
this.props.commentPage);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
let props = this.props;
|
||||
|
||||
let page = "page" + props.commentPage;
|
||||
let page = props.commentPage;
|
||||
|
||||
let commentsObj = !!props.comments
|
||||
? props.comments
|
||||
@ -50,7 +40,7 @@ export class Comments extends Component {
|
||||
|
||||
let total = !!props.comments
|
||||
? props.comments.totalPages
|
||||
: {};
|
||||
: 1;
|
||||
|
||||
let commentsList = Object.keys(commentsPage)
|
||||
.map((entry) => {
|
||||
@ -93,6 +83,7 @@ export class Comments extends Component {
|
||||
popout={props.popout}
|
||||
linkPage={props.linkPage}
|
||||
linkIndex={props.linkIndex}
|
||||
url={props.url}
|
||||
commentPage={props.commentPage}
|
||||
total={total}/>
|
||||
</div>
|
||||
|
@ -45,6 +45,8 @@ export class LinkItem extends Component {
|
||||
hostname = hostname[4];
|
||||
}
|
||||
|
||||
let encodedUrl = window.btoa(props.url);
|
||||
|
||||
let comments = props.comments + " comment" + ((props.comments === 1) ? "" : "s");
|
||||
|
||||
return (
|
||||
@ -68,7 +70,7 @@ export class LinkItem extends Component {
|
||||
: "~" + props.ship}</span>
|
||||
<span className="f9 inter gray2 pr3 v-mid">{this.state.timeSinceLinkPost}</span>
|
||||
<Link to=
|
||||
{"/~link" + props.popout + "/" + props.channel + "/" + props.page + "/" + props.index}
|
||||
{"/~link" + props.popout + "/" + props.channel + "/" + props.page + "/" + props.linkIndex + "/" + encodedUrl}
|
||||
className="v-top">
|
||||
<span className="f9 inter gray2">
|
||||
{comments}
|
||||
|
@ -12,40 +12,38 @@ export class LinkDetail extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
timeSinceLinkPost: this.getTimeSinceLinkPost(),
|
||||
comment: ""
|
||||
timeSinceLinkPost: this.getTimeSinceLinkPost(props.data),
|
||||
comment: "",
|
||||
data: props.data
|
||||
};
|
||||
|
||||
this.setComment = this.setComment.bind(this);
|
||||
}
|
||||
|
||||
updateData(submission) {
|
||||
this.setState({
|
||||
data: submission,
|
||||
timeSinceLinkPost: this.getTimeSinceLinkPost(submission)
|
||||
});
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
// if we have no preloaded data, and we aren't expecting it, get it
|
||||
if (this.props.page != 0 && (!this.props.data || !this.props.data.url)) {
|
||||
api.getPage(this.props.path, this.props.page);
|
||||
|
||||
// if we have preloaded our data,
|
||||
// but no comments, grab the comments
|
||||
} else if (!this.props.comments && this.props.data.url) {
|
||||
api.getCommentsPage(this.props.path, this.props.data.url, this.props.commentPage);
|
||||
if (!this.state.data.title) {
|
||||
api.getSubmission(
|
||||
this.props.path, this.props.url, this.updateData.bind(this)
|
||||
);
|
||||
}
|
||||
|
||||
// start the time-since update timer
|
||||
this.updateTimeSinceNewestMessageInterval = setInterval( () => {
|
||||
this.setState({timeSinceLinkPost: this.getTimeSinceLinkPost()});
|
||||
this.setState({timeSinceLinkPost: this.getTimeSinceLinkPost(this.state.data)});
|
||||
}, 60000);
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
// if we came to this page *directly*,
|
||||
// load the comments -- DidMount will fail
|
||||
if ( (this.props.data.url !== prevProps.data.url) &&
|
||||
(!this.props.comments && this.props.data.url)
|
||||
) {
|
||||
api.getCommentsPage(this.props.path, this.props.data.url, this.props.commentPage);
|
||||
}
|
||||
|
||||
if (this.props.data.timestamp !== prevProps.data.timestamp) {
|
||||
this.setState({timeSinceLinkPost: this.getTimeSinceLinkPost()})
|
||||
if (this.props.url !== prevProps.url) {
|
||||
this.updateData(this.props.data);
|
||||
}
|
||||
}
|
||||
|
||||
@ -56,22 +54,20 @@ export class LinkDetail extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
getTimeSinceLinkPost() {
|
||||
return !!this.props.data.timestamp ?
|
||||
moment.unix(this.props.data.timestamp / 1000).from(moment.utc())
|
||||
getTimeSinceLinkPost(data) {
|
||||
return !!data.time ?
|
||||
moment.unix(data.time / 1000).from(moment.utc())
|
||||
: '';
|
||||
}
|
||||
|
||||
onClickPost() {
|
||||
let url = this.props.data.url || "";
|
||||
let url = this.props.url || "";
|
||||
|
||||
let request = api.postComment(
|
||||
this.props.path,
|
||||
url,
|
||||
this.state.comment,
|
||||
this.props.page,
|
||||
this.props.link
|
||||
);
|
||||
this.state.comment
|
||||
);
|
||||
|
||||
if (request) {
|
||||
this.setState({comment: ""})
|
||||
@ -85,11 +81,12 @@ export class LinkDetail extends Component {
|
||||
render() {
|
||||
let props = this.props;
|
||||
let popout = (props.popout) ? "/popout" : "";
|
||||
let path = props.path + "/" + props.page + "/" + props.link;
|
||||
let routePath = props.path + "/" + props.page + "/" + props.linkIndex + "/" + window.btoa(props.url);
|
||||
|
||||
let ship = props.data.ship || "zod";
|
||||
let title = props.data.title || "";
|
||||
let url = props.data.url || "";
|
||||
const data = this.state.data || props.data;
|
||||
let ship = data.ship || "zod";
|
||||
let title = data.title || "";
|
||||
let url = data.url || "";
|
||||
|
||||
let URLparser = new RegExp(/((?:([\w\d\.-]+)\:\/\/?){1}(?:(www)\.?){0,1}(((?:[\w\d-]+\.)*)([\w\d-]+\.[\w\d]+))){1}(?:\:(\d+)){0,1}((\/(?:(?:[^\/\s\?]+\/)*))(?:([^\?\/\s#]+?(?:.[^\?\s]+){0,1}){0,1}(?:\?([^\s#]+)){0,1})){0,1}(?:#([^#\s]+)){0,1}/);
|
||||
|
||||
@ -99,18 +96,20 @@ export class LinkDetail extends Component {
|
||||
hostname = hostname[4];
|
||||
}
|
||||
|
||||
let commentCount = props.data.commentCount || 0;
|
||||
const commentCount = props.comments
|
||||
? props.comments.totalItems
|
||||
: data.commentCount || 0;
|
||||
|
||||
let comments = commentCount + " comment" + ((commentCount === 1) ? "" : "s");
|
||||
|
||||
let nickname = !!props.members[props.data.ship]
|
||||
? props.members[props.data.ship].nickname
|
||||
let nickname = !!props.members[ship]
|
||||
? props.members[ship].nickname
|
||||
: "";
|
||||
|
||||
let nameClass = nickname ? "inter" : "mono";
|
||||
|
||||
let color = !!props.members[props.data.ship]
|
||||
? uxToHex(props.members[props.data.ship].color)
|
||||
let color = !!props.members[ship]
|
||||
? uxToHex(props.members[ship].color)
|
||||
: "000000";
|
||||
|
||||
let activeClasses = (this.state.comment)
|
||||
@ -135,7 +134,7 @@ export class LinkDetail extends Component {
|
||||
<LinksTabBar
|
||||
{...props}
|
||||
popout={popout}
|
||||
path={path}/>
|
||||
path={routePath}/>
|
||||
</div>
|
||||
<div className="w-100 mt2 flex justify-center overflow-y-scroll ph4 pb4">
|
||||
<div className="w-100 mw7">
|
||||
@ -161,7 +160,7 @@ export class LinkDetail extends Component {
|
||||
<span className="f9 inter gray2 pr3 v-mid">
|
||||
{this.state.timeSinceLinkPost}
|
||||
</span>
|
||||
<Link to={"/~link" + props.path + "/" + props.page + "/" + props.link} className="v-top">
|
||||
<Link to={"/~link" + props.path + "/" + props.page + "/" + props.linkIndex + "/" + window.btoa(props.url)} className="v-top">
|
||||
<span className="f9 inter gray2">
|
||||
{comments}
|
||||
</span>
|
||||
@ -198,9 +197,9 @@ export class LinkDetail extends Component {
|
||||
commentPage={props.commentPage}
|
||||
members={props.members}
|
||||
popout={props.popout}
|
||||
url={props.data.url}
|
||||
url={props.url}
|
||||
linkPage={props.page}
|
||||
linkIndex={props.link}
|
||||
linkIndex={props.linkIndex}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -17,8 +17,11 @@ export class Links extends Component {
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
let linkPage = "page" + this.props.page;
|
||||
if ((this.props.page != 0) && (!this.props.links[linkPage])) {
|
||||
const linkPage = this.props.page;
|
||||
if ( (this.props.page != 0) &&
|
||||
(!this.props.links[linkPage] ||
|
||||
this.props.links.local[linkPage])
|
||||
) {
|
||||
api.getPage(this.props.path, this.props.page);
|
||||
}
|
||||
}
|
||||
@ -27,7 +30,7 @@ export class Links extends Component {
|
||||
let props = this.props;
|
||||
let popout = (props.popout) ? "/popout" : "";
|
||||
let channel = props.path.substr(1);
|
||||
let linkPage = "page" + props.page;
|
||||
let linkPage = props.page;
|
||||
|
||||
let links = !!props.links[linkPage]
|
||||
? props.links[linkPage]
|
||||
@ -42,11 +45,15 @@ export class Links extends Component {
|
||||
: 1;
|
||||
|
||||
let LinkList = Object.keys(links)
|
||||
.map((link) => {
|
||||
.map((linkIndex) => {
|
||||
let linksObj = props.links[linkPage];
|
||||
let { title, url, timestamp, ship, commentCount } = linksObj[link];
|
||||
let { title, url, time, ship } = linksObj[linkIndex];
|
||||
let members = {};
|
||||
|
||||
const commentCount = props.comments[url]
|
||||
? props.comments[url].totalItems
|
||||
: linksObj[linkIndex].commentCount || 0;
|
||||
|
||||
if (!props.members[ship]) {
|
||||
members[ship] = {'nickname': '', 'avatar': 'TODO', 'color': '0x0'};
|
||||
} else {
|
||||
@ -67,12 +74,12 @@ export class Links extends Component {
|
||||
|
||||
return (
|
||||
<LinkItem
|
||||
key={timestamp}
|
||||
key={time}
|
||||
title={title}
|
||||
page={props.page}
|
||||
index={link}
|
||||
linkIndex={linkIndex}
|
||||
url={url}
|
||||
timestamp={timestamp}
|
||||
timestamp={time}
|
||||
nickname={nickname}
|
||||
ship={ship}
|
||||
color={color}
|
||||
@ -113,7 +120,7 @@ export class Links extends Component {
|
||||
<LinksTabBar
|
||||
{...props}
|
||||
popout={popout}
|
||||
path={props.path}/>
|
||||
path={props.path + "/" + props.page}/>
|
||||
</div>
|
||||
<div className="w-100 mt2 flex justify-center overflow-y-scroll ph4 pb4">
|
||||
<div className="w-100 mw7">
|
||||
|
@ -69,7 +69,11 @@ export class Root extends Component {
|
||||
|
||||
let channelLinks = !!links[groupPath]
|
||||
? links[groupPath]
|
||||
: {};
|
||||
: {local: {}};
|
||||
|
||||
let channelComments = !!comments[groupPath]
|
||||
? comments[groupPath]
|
||||
: {};
|
||||
|
||||
return (
|
||||
<Skeleton
|
||||
@ -85,6 +89,7 @@ export class Root extends Component {
|
||||
{...props}
|
||||
members={groupMembers}
|
||||
links={channelLinks}
|
||||
comments={channelComments}
|
||||
page={page}
|
||||
path={groupPath}
|
||||
popout={popout}
|
||||
@ -94,7 +99,7 @@ export class Root extends Component {
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<Route exact path="/~link/(popout)?/:ship/:channel/:page/:index/(comments)?/:commentpage?"
|
||||
<Route exact path="/~link/(popout)?/:ship/:channel/:page/:index/:encodedUrl/(comments)?/:commentpage?"
|
||||
render={ (props) => {
|
||||
let groupPath =
|
||||
`/${props.match.params.ship}/${props.match.params.channel}`;
|
||||
@ -105,15 +110,16 @@ export class Root extends Component {
|
||||
|
||||
let index = props.match.params.index || 0;
|
||||
let page = props.match.params.page || 0;
|
||||
let url = window.atob(props.match.params.encodedUrl);
|
||||
|
||||
let data = !!links[groupPath]
|
||||
? !!links[groupPath]["page" + page]
|
||||
? links[groupPath]["page" + page][index]
|
||||
? !!links[groupPath][page]
|
||||
? links[groupPath][page][index]
|
||||
: {}
|
||||
: {};
|
||||
let coms = !comments[groupPath]
|
||||
? undefined
|
||||
: comments[groupPath][data.url];
|
||||
: comments[groupPath][url];
|
||||
|
||||
let commentPage = props.match.params.commentpage || 0;
|
||||
|
||||
@ -130,7 +136,8 @@ export class Root extends Component {
|
||||
<LinkDetail
|
||||
{...props}
|
||||
page={page}
|
||||
link={index}
|
||||
url={url}
|
||||
linkIndex={index}
|
||||
members={groupMembers}
|
||||
path={groupPath}
|
||||
popout={popout}
|
||||
|
70
pkg/interface/link/src/js/reducers/contact-update.js
Normal file
70
pkg/interface/link/src/js/reducers/contact-update.js
Normal file
@ -0,0 +1,70 @@
|
||||
//NOTE copied from /pkg/interface/contacts/src/js/reducers/contact-update.js
|
||||
|
||||
import _ from 'lodash';
|
||||
|
||||
|
||||
export class ContactUpdateReducer {
|
||||
reduce(json, state) {
|
||||
let data = _.get(json, 'contact-update', false);
|
||||
if (data) {
|
||||
this.create(data, state);
|
||||
this.delete(data, state);
|
||||
this.add(data, state);
|
||||
this.remove(data, state);
|
||||
this.edit(data, state);
|
||||
}
|
||||
}
|
||||
|
||||
create(json, state) {
|
||||
let data = _.get(json, 'create', false);
|
||||
if (data) {
|
||||
state.contacts[data.path] = {};
|
||||
}
|
||||
}
|
||||
|
||||
delete(json, state) {
|
||||
let data = _.get(json, 'delete', false);
|
||||
if (data) {
|
||||
delete state.contacts[data.path];
|
||||
}
|
||||
}
|
||||
|
||||
add(json, state) {
|
||||
let data = _.get(json, 'add', false);
|
||||
if (
|
||||
data &&
|
||||
(data.path in state.contacts)
|
||||
) {
|
||||
state.contacts[data.path][data.ship] = data.contact;
|
||||
}
|
||||
}
|
||||
|
||||
remove(json, state) {
|
||||
let data = _.get(json, 'remove', false);
|
||||
if (
|
||||
data &&
|
||||
(data.path in state.contacts) &&
|
||||
(data.ship in state.contacts[data.path])
|
||||
) {
|
||||
delete state.contacts[data.path][data.ship];
|
||||
}
|
||||
}
|
||||
|
||||
edit(json, state) {
|
||||
let data = _.get(json, 'edit', false);
|
||||
if (
|
||||
data &&
|
||||
(data.path in state.contacts) &&
|
||||
(data.ship in state.contacts[data.path])
|
||||
) {
|
||||
let edit = Object.keys(data['edit-field']);
|
||||
if (edit.length !== 1) {
|
||||
return;
|
||||
}
|
||||
state.contacts[data.path][data.ship][edit[0]] =
|
||||
data['edit-field'][edit[0]];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ import _ from 'lodash';
|
||||
|
||||
export class InitialReducer {
|
||||
reduce(json, state) {
|
||||
let data = _.get(json, 'contact-initial', false);
|
||||
let data = _.get(json, 'contact-initial', false);
|
||||
if (data) {
|
||||
state.contacts = data;
|
||||
}
|
||||
@ -14,24 +14,6 @@ export class InitialReducer {
|
||||
state.groups[group] = new Set(data[group]);
|
||||
}
|
||||
}
|
||||
|
||||
data = _.get(json, 'link', false);
|
||||
if (data) {
|
||||
let name = Object.keys(data)[0];
|
||||
let initial = {};
|
||||
initial[name] = {};
|
||||
initial[name]["total-pages"] = data[name]["total-pages"];
|
||||
initial[name]["total-items"] = data[name]["total-items"];
|
||||
initial[name]["page0"] = data[name]["page"];
|
||||
|
||||
if (!!state.links[name]) {
|
||||
let origin = state.links[name];
|
||||
_.extend(initial[name], origin);
|
||||
} else {
|
||||
state.links[name] = {};
|
||||
}
|
||||
state.links[name] = initial[name];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,8 @@
|
||||
import _ from 'lodash';
|
||||
|
||||
// page size as expected from link-view.
|
||||
// must change in parallel with the +page-size in /app/link-view to
|
||||
// ensure sane behavior.
|
||||
const PAGE_SIZE = 25;
|
||||
|
||||
export class LinkUpdateReducer {
|
||||
@ -24,16 +27,17 @@ export class LinkUpdateReducer {
|
||||
|
||||
for (var path of Object.keys(data)) {
|
||||
const here = data[path];
|
||||
const page = "page" + here.pageNumber;
|
||||
const page = here.pageNumber;
|
||||
|
||||
// if we didn't have any state for this path yet, initialize.
|
||||
if (!state.links[path]) {
|
||||
state.links[path] = {};
|
||||
state.links[path] = {local: {}};
|
||||
}
|
||||
|
||||
// since data contains an up-to-date full version of the page,
|
||||
// we can safely overwrite the one in state.
|
||||
state.links[path][page] = here.page;
|
||||
state.links[path].local[page] = false;
|
||||
state.links[path].totalPages = here.totalPages;
|
||||
state.links[path].totalItems = here.totalItems;
|
||||
}
|
||||
@ -77,20 +81,21 @@ export class LinkUpdateReducer {
|
||||
|
||||
const path = data.path;
|
||||
const url = data.url;
|
||||
const page = "page" + data.pageNumber;
|
||||
const page = data.pageNumber;
|
||||
|
||||
// if we didn't have any state for this path yet, initialize.
|
||||
if (!state.comments[path]) {
|
||||
state.comments[path] = {};
|
||||
}
|
||||
if (!state.comments[path][url]) {
|
||||
state.comments[path][url] = {};
|
||||
state.comments[path][url] = {local: {}};
|
||||
}
|
||||
let here = state.comments[path][url];
|
||||
|
||||
// since data contains an up-to-date full version of the page,
|
||||
// we can safely overwrite the one in state.
|
||||
here[page] = data.page;
|
||||
here.local[page] = false;
|
||||
here.totalPages = data.totalPages;
|
||||
here.totalItems = data.totalItems;
|
||||
}
|
||||
@ -117,24 +122,25 @@ export class LinkUpdateReducer {
|
||||
|
||||
//
|
||||
|
||||
_addNewItems(items, pages = {}, page = 0) {
|
||||
//TODO kinda want to refactor this, have it just be number indexes
|
||||
const i = "page" + page;
|
||||
//TODO but if there's more on the page than just the things we're
|
||||
// pushing onto it, we won't load that in. should do an
|
||||
// additional check (+ maybe load) on page-nav, right?
|
||||
_addNewItems(items, pages = {local: {}}, page = 0) {
|
||||
const i = page;
|
||||
if (!pages[i]) {
|
||||
pages[i] = [];
|
||||
// if we know this page exists in the backend, flag it as "local",
|
||||
// so that we know to initiate a "fetch the rest" request when we want
|
||||
// to display the page.
|
||||
pages.local[i] = (page < pages.totalPages);
|
||||
}
|
||||
pages[i] = items.concat(pages[i]);
|
||||
pages.totalItems = pages.totalItems + items.length;
|
||||
if (pages[i].length <= PAGE_SIZE) {
|
||||
pages.totalPages = page + 1;
|
||||
pages.totalItems = (page * PAGE_SIZE) + pages[i].length;
|
||||
pages.totalPages = Math.ceil(pages.totalItems / PAGE_SIZE);
|
||||
return pages;
|
||||
}
|
||||
// overflow into next page
|
||||
const tail = pages[i].slice(PAGE_SIZE);
|
||||
pages[i].length = PAGE_SIZE;
|
||||
pages.totalItems = pages.totalItems - tail.length;
|
||||
return this._addNewItems(tail, pages, page+1);
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { InitialReducer } from '/reducers/initial';
|
||||
import { ContactUpdateReducer } from '/reducers/contact-update.js';
|
||||
import { PermissionUpdateReducer } from '/reducers/permission-update';
|
||||
import { LinkUpdateReducer } from '/reducers/link-update';
|
||||
import { LocalReducer } from '/reducers/local.js';
|
||||
@ -18,6 +19,7 @@ class Store {
|
||||
};
|
||||
|
||||
this.initialReducer = new InitialReducer();
|
||||
this.contactUpdateReducer = new ContactUpdateReducer();
|
||||
this.permissionUpdateReducer = new PermissionUpdateReducer();
|
||||
this.localReducer = new LocalReducer();
|
||||
this.linkUpdateReducer = new LinkUpdateReducer();
|
||||
@ -38,6 +40,7 @@ class Store {
|
||||
|
||||
console.log('event', json);
|
||||
this.initialReducer.reduce(json, this.state);
|
||||
this.contactUpdateReducer.reduce(json, this.state);
|
||||
this.permissionUpdateReducer.reduce(json, this.state);
|
||||
this.localReducer.reduce(json, this.state);
|
||||
this.linkUpdateReducer.reduce(json, this.state);
|
||||
|
Loading…
Reference in New Issue
Block a user