Merge pull request #2244 from urbit/m/link-fe-path

link fe: use base64url
This commit is contained in:
matildepark 2020-02-06 19:20:02 -05:00 committed by GitHub
commit 6f65a65d85
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 222 additions and 187 deletions

View File

@ -32309,7 +32309,6 @@
global$2[key$1] = "esm"; global$2[key$1] = "esm";
} }
} }
//# sourceMappingURL=react-router.js.map
/** /**
* The public API for a <Router> that uses HTML5 history. * The public API for a <Router> that uses HTML5 history.
@ -32609,7 +32608,6 @@
style: propTypes.object style: propTypes.object
}); });
} }
//# sourceMappingURL=react-router-dom.js.map
var classnames = createCommonjsModule(function (module) { var classnames = createCommonjsModule(function (module) {
/*! /*!
@ -49765,6 +49763,61 @@
return str.slice(0,-1); return str.slice(0,-1);
} }
// encodes string into base64url,
// by encoding into base64 and replacing non-url-safe characters.
//
function base64urlEncode(string) {
return window.btoa(string)
.split('+').join('-')
.split('/').join('_');
}
// decode base64url. inverse of base64urlEncode above.
//
function base64urlDecode(string) {
return window.atob(
string.split('_').join('/')
.split('-').join('+')
);
}
// encode the string into @ta-safe format, using logic from +wood.
// for example, 'some Chars!' becomes '~.some.~43.hars~21.'
//
function stringToTa(string) {
let out = '';
for (let i = 0; i < string.length; i++) {
const char = string[i];
let add = '';
switch (char) {
case ' ':
add = '.';
break;
case '.':
add = '~.';
break;
case '~':
add = '~~';
break;
default:
const charCode = string.charCodeAt(i);
if (
(charCode >= 97 && charCode <= 122) || // a-z
(charCode >= 48 && charCode <= 57) || // 0-9
char === '-'
) {
add = char;
} else {
//TODO behavior for unicode doesn't match +wood's,
// but we can probably get away with that for now.
add = '~' + charCode.toString(16) + '.';
}
}
out = out + add;
}
return '~.' + out;
}
function uxToHex(ux) { function uxToHex(ux) {
let value = ux.substr(2).replace('.', '').padStart(6, '0'); let value = ux.substr(2).replace('.', '').padStart(6, '0');
return value; return value;
@ -54806,7 +54859,7 @@
} }
getCommentsPage(path, url, page) { getCommentsPage(path, url, page) {
const strictUrl = this.encodeUrl(url); const strictUrl = stringToTa(url);
const endpoint = '/json/' + page + '/discussions/' + strictUrl + path; const endpoint = '/json/' + page + '/discussions/' + strictUrl + path;
this.bindLinkView(endpoint, this.bindLinkView(endpoint,
(res) => { (res) => {
@ -54833,8 +54886,7 @@
} }
getSubmission(path, url, callback) { getSubmission(path, url, callback) {
console.log('api: get submission', path, url); const strictUrl = stringToTa(url);
const strictUrl = this.encodeUrl(url);
const endpoint = '/json/0/submission/' + strictUrl + path; const endpoint = '/json/0/submission/' + strictUrl + path;
this.bindLinkView(endpoint, this.bindLinkView(endpoint,
(res) => { (res) => {
@ -54879,42 +54931,6 @@
}); });
} }
//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];
let add = '';
switch (char) {
case ' ':
add = '.';
break;
case '.':
add = '~.';
break;
case '~':
add = '~~';
break;
default:
const charCode = url.charCodeAt(i);
if (
(charCode >= 97 && charCode <= 122) || // a-z
(charCode >= 48 && charCode <= 57) || // 0-9
char === '-'
) {
add = char;
} else {
//TODO behavior for unicode doesn't match +wood's,
// but we can probably get away with that for now.
add = '~' + charCode.toString(16) + '.';
}
}
strictUrl = strictUrl + add;
}
return '~.' + strictUrl;
}
} }
let api$1 = new UrbitApi(); let api$1 = new UrbitApi();
@ -55150,14 +55166,14 @@
react.createElement('div', { className: "dib f8 pl6", __self: this, __source: {fileName: _jsxFileName$3, lineNumber: 28}} react.createElement('div', { className: "dib f8 pl6", __self: this, __source: {fileName: _jsxFileName$3, lineNumber: 28}}
, react.createElement(Link, { , react.createElement(Link, {
className: "no-underline " + memColor, className: "no-underline " + memColor,
to: `/~link/` + popout + `members` + props.path, __self: this, __source: {fileName: _jsxFileName$3, lineNumber: 29}}, "Members" to: `/~link/` + popout + `members` + props.groupPath, __self: this, __source: {fileName: _jsxFileName$3, lineNumber: 29}}, "Members"
) )
) )
) : ( ) : (
react.createElement('div', { className: "dib", style: { width: 0 }, __self: this, __source: {fileName: _jsxFileName$3, lineNumber: 36}}) react.createElement('div', { className: "dib", style: { width: 0 }, __self: this, __source: {fileName: _jsxFileName$3, lineNumber: 36}})
) )
, react.createElement('a', { href: `/~link/popout` + props.path, target: "_blank", , react.createElement('a', { href: `/~link/popout` + props.groupPath, target: "_blank",
className: "dib fr" , __self: this, __source: {fileName: _jsxFileName$3, lineNumber: 38}} className: "dib fr" , __self: this, __source: {fileName: _jsxFileName$3, lineNumber: 38}}
, react.createElement('img', { , react.createElement('img', {
className: `flex-shrink-0 pr4 dn invert-d ` + hidePopoutIcon, className: `flex-shrink-0 pr4 dn invert-d ` + hidePopoutIcon,
@ -59132,34 +59148,34 @@
hostname = hostname[4]; hostname = hostname[4];
} }
let encodedUrl = window.btoa(props.url); let encodedUrl = base64urlEncode(props.url);
let comments = props.comments + " comment" + ((props.comments === 1) ? "" : "s"); let comments = props.comments + " comment" + ((props.comments === 1) ? "" : "s");
return ( return (
react.createElement('div', { className: "w-100 pv3 flex" , __self: this, __source: {fileName: _jsxFileName$6, lineNumber: 53}} react.createElement('div', { className: "w-100 pv3 flex" , __self: this, __source: {fileName: _jsxFileName$6, lineNumber: 54}}
, react.createElement(Sigil, { , react.createElement(Sigil, {
ship: "~" + props.ship, ship: "~" + props.ship,
size: 36, size: 36,
color: "#" + props.color, __self: this, __source: {fileName: _jsxFileName$6, lineNumber: 54}} color: "#" + props.color, __self: this, __source: {fileName: _jsxFileName$6, lineNumber: 55}}
) )
, react.createElement('div', { className: "flex flex-column ml2" , __self: this, __source: {fileName: _jsxFileName$6, lineNumber: 59}} , react.createElement('div', { className: "flex flex-column ml2" , __self: this, __source: {fileName: _jsxFileName$6, lineNumber: 60}}
, react.createElement('a', { href: props.url, , react.createElement('a', { href: props.url,
className: "w-100 flex" , className: "w-100 flex" ,
target: "_blank", __self: this, __source: {fileName: _jsxFileName$6, lineNumber: 60}} target: "_blank", __self: this, __source: {fileName: _jsxFileName$6, lineNumber: 61}}
, react.createElement('p', { className: "f8 truncate" , __self: this, __source: {fileName: _jsxFileName$6, lineNumber: 63}}, props.title , react.createElement('p', { className: "f8 truncate" , __self: this, __source: {fileName: _jsxFileName$6, lineNumber: 64}}, props.title
, react.createElement('span', { className: "gray2 dib truncate-m mw4-m v-btm ml2" , __self: this, __source: {fileName: _jsxFileName$6, lineNumber: 64}}, hostname, " ↗" ) , react.createElement('span', { className: "gray2 dib truncate-m mw4-m v-btm ml2" , __self: this, __source: {fileName: _jsxFileName$6, lineNumber: 65}}, hostname, " ↗" )
) )
) )
, react.createElement('div', { className: "w-100 pt1" , __self: this, __source: {fileName: _jsxFileName$6, lineNumber: 67}} , react.createElement('div', { className: "w-100 pt1" , __self: this, __source: {fileName: _jsxFileName$6, lineNumber: 68}}
, react.createElement('span', { className: "f9 pr2 v-mid " + mono, __self: this, __source: {fileName: _jsxFileName$6, lineNumber: 68}}, (props.nickname) , react.createElement('span', { className: "f9 pr2 v-mid " + mono, __self: this, __source: {fileName: _jsxFileName$6, lineNumber: 69}}, (props.nickname)
? props.nickname ? props.nickname
: "~" + props.ship) : "~" + props.ship)
, react.createElement('span', { className: "f9 inter gray2 pr3 v-mid" , __self: this, __source: {fileName: _jsxFileName$6, lineNumber: 71}}, this.state.timeSinceLinkPost) , react.createElement('span', { className: "f9 inter gray2 pr3 v-mid" , __self: this, __source: {fileName: _jsxFileName$6, lineNumber: 72}}, this.state.timeSinceLinkPost)
, react.createElement(Link$1, { to: , react.createElement(Link$1, { to:
"/~link" + props.popout + "/" + props.channel + "/" + props.page + "/" + props.linkIndex + "/" + encodedUrl, "/~link" + props.popout + props.groupPath + "/" + props.page + "/" + props.linkIndex + "/" + encodedUrl,
className: "v-top", __self: this, __source: {fileName: _jsxFileName$6, lineNumber: 72}} className: "v-top", __self: this, __source: {fileName: _jsxFileName$6, lineNumber: 73}}
, react.createElement('span', { className: "f9 inter gray2" , __self: this, __source: {fileName: _jsxFileName$6, lineNumber: 75}} , react.createElement('span', { className: "f9 inter gray2" , __self: this, __source: {fileName: _jsxFileName$6, lineNumber: 76}}
, comments , comments
) )
) )
@ -59301,12 +59317,12 @@
return ( return (
react.createElement('div', { className: "w-100 inter relative pv6" , __self: this, __source: {fileName: _jsxFileName$8, lineNumber: 20}} react.createElement('div', { className: "w-100 inter relative pv6" , __self: this, __source: {fileName: _jsxFileName$8, lineNumber: 20}}
, react.createElement('div', { className: prevDisplay + " inter f8", __self: this, __source: {fileName: _jsxFileName$8, lineNumber: 21}} , react.createElement('div', { className: prevDisplay + " inter f8", __self: this, __source: {fileName: _jsxFileName$8, lineNumber: 21}}
, react.createElement(Link$1, { to: "/~link" + props.popout + props.path + prevPage, __self: this, __source: {fileName: _jsxFileName$8, lineNumber: 22}}, "<- Previous Page" , react.createElement(Link$1, { to: "/~link" + props.popout + props.groupPath + prevPage, __self: this, __source: {fileName: _jsxFileName$8, lineNumber: 22}}, "<- Previous Page"
) )
) )
, react.createElement('div', { className: nextDisplay + " inter f8", __self: this, __source: {fileName: _jsxFileName$8, lineNumber: 26}} , react.createElement('div', { className: nextDisplay + " inter f8", __self: this, __source: {fileName: _jsxFileName$8, lineNumber: 26}}
, react.createElement(Link$1, { to: "/~link" + props.popout + props.path + nextPage, __self: this, __source: {fileName: _jsxFileName$8, lineNumber: 27}}, "Next Page ->" , react.createElement(Link$1, { to: "/~link" + props.popout + props.groupPath + nextPage, __self: this, __source: {fileName: _jsxFileName$8, lineNumber: 27}}, "Next Page ->"
) )
) )
@ -59329,14 +59345,13 @@
(!this.props.links[linkPage] || (!this.props.links[linkPage] ||
this.props.links.local[linkPage]) this.props.links.local[linkPage])
) { ) {
api.getPage(this.props.path, this.props.page); api.getPage(this.props.groupPath, this.props.page);
} }
} }
render() { render() {
let props = this.props; let props = this.props;
let popout = (props.popout) ? "/popout" : ""; let popout = (props.popout) ? "/popout" : "";
let channel = props.path.substr(1);
let linkPage = props.page; let linkPage = props.page;
let links = !!props.links[linkPage] let links = !!props.links[linkPage]
@ -59391,58 +59406,58 @@
ship: ship, ship: ship,
color: color, color: color,
comments: commentCount, comments: commentCount,
channel: channel, groupPath: props.groupPath,
popout: popout, __self: this, __source: {fileName: _jsxFileName$9, lineNumber: 76}} popout: popout, __self: this, __source: {fileName: _jsxFileName$9, lineNumber: 75}}
) )
) )
}); });
return ( return (
react.createElement('div', { react.createElement('div', {
className: "h-100 w-100 overflow-hidden flex flex-column" , __self: this, __source: {fileName: _jsxFileName$9, lineNumber: 94}} className: "h-100 w-100 overflow-hidden flex flex-column" , __self: this, __source: {fileName: _jsxFileName$9, lineNumber: 93}}
, react.createElement('div', { , react.createElement('div', {
className: "w-100 dn-m dn-l dn-xl inter pt4 pb6 pl3 f8" , className: "w-100 dn-m dn-l dn-xl inter pt4 pb6 pl3 f8" ,
style: { height: "1rem" }, __self: this, __source: {fileName: _jsxFileName$9, lineNumber: 96}} style: { height: "1rem" }, __self: this, __source: {fileName: _jsxFileName$9, lineNumber: 95}}
, react.createElement(Link$1, { to: "/~link/", __self: this, __source: {fileName: _jsxFileName$9, lineNumber: 99}}, "⟵ All Channels") , react.createElement(Link$1, { to: "/~link/", __self: this, __source: {fileName: _jsxFileName$9, lineNumber: 98}}, "⟵ All Channels")
) )
, react.createElement('div', { , react.createElement('div', {
className: `pl3 pt2 flex relative overflow-x-scroll className: `pl3 pt2 flex relative overflow-x-scroll
overflow-x-auto-l overflow-x-auto-xl flex-shrink-0 overflow-x-auto-l overflow-x-auto-xl flex-shrink-0
bb bn-m bn-l bn-xl b--gray4`, bb bn-m bn-l bn-xl b--gray4`,
style: { height: 48 }, __self: this, __source: {fileName: _jsxFileName$9, lineNumber: 101}} style: { height: 48 }, __self: this, __source: {fileName: _jsxFileName$9, lineNumber: 100}}
, react.createElement(SidebarSwitcher, { , react.createElement(SidebarSwitcher, {
sidebarShown: props.sidebarShown, sidebarShown: props.sidebarShown,
popout: props.popout, __self: this, __source: {fileName: _jsxFileName$9, lineNumber: 106}}) popout: props.popout, __self: this, __source: {fileName: _jsxFileName$9, lineNumber: 105}})
, react.createElement(Link$1, { to: `/~link` + popout + props.path, className: "pt2", __self: this, __source: {fileName: _jsxFileName$9, lineNumber: 109}} , react.createElement(Link$1, { to: `/~link` + popout + props.groupPath, className: "pt2", __self: this, __source: {fileName: _jsxFileName$9, lineNumber: 108}}
, react.createElement('h2', { , react.createElement('h2', {
className: `dib f8 fw4 v-top ` + className: `dib f8 fw4 v-top ` +
(props.path.includes("/~/") (props.groupPath.includes("/~/")
? "" ? ""
: "mono"), __self: this, __source: {fileName: _jsxFileName$9, lineNumber: 110}} : "mono"), __self: this, __source: {fileName: _jsxFileName$9, lineNumber: 109}}
, (props.path.includes("/~/")) , (props.groupPath.includes("/~/"))
? "Private" ? "Private"
: channel : props.groupPath.substr(1)
) )
) )
, react.createElement(LinksTabBar, { , react.createElement(LinksTabBar, {
...props, ...props,
popout: popout, popout: popout,
path: props.path + "/" + props.page, __self: this, __source: {fileName: _jsxFileName$9, lineNumber: 120}}) groupPath: props.groupPath + "/" + props.page, __self: this, __source: {fileName: _jsxFileName$9, lineNumber: 119}})
) )
, react.createElement('div', { className: "w-100 mt2 flex justify-center overflow-y-scroll ph4 pb4" , __self: this, __source: {fileName: _jsxFileName$9, lineNumber: 125}} , react.createElement('div', { className: "w-100 mt2 flex justify-center overflow-y-scroll ph4 pb4" , __self: this, __source: {fileName: _jsxFileName$9, lineNumber: 124}}
, react.createElement('div', { className: "w-100 mw7" , __self: this, __source: {fileName: _jsxFileName$9, lineNumber: 126}} , react.createElement('div', { className: "w-100 mw7" , __self: this, __source: {fileName: _jsxFileName$9, lineNumber: 125}}
, react.createElement('div', { className: "flex", __self: this, __source: {fileName: _jsxFileName$9, lineNumber: 127}} , react.createElement('div', { className: "flex", __self: this, __source: {fileName: _jsxFileName$9, lineNumber: 126}}
, react.createElement(LinkSubmit, { path: props.path, __self: this, __source: {fileName: _jsxFileName$9, lineNumber: 128}}) , react.createElement(LinkSubmit, { groupPath: props.groupPath, __self: this, __source: {fileName: _jsxFileName$9, lineNumber: 127}})
) )
, react.createElement('div', { className: "pb4", __self: this, __source: {fileName: _jsxFileName$9, lineNumber: 130}} , react.createElement('div', { className: "pb4", __self: this, __source: {fileName: _jsxFileName$9, lineNumber: 129}}
, LinkList , LinkList
, react.createElement(Pagination, { , react.createElement(Pagination, {
...props, ...props,
key: props.path + props.page, key: props.groupPath + props.page,
popout: popout, popout: popout,
path: props.path, groupPath: props.groupPath,
currentPage: currentPage, currentPage: currentPage,
totalPages: totalPages, __self: this, __source: {fileName: _jsxFileName$9, lineNumber: 132}} totalPages: totalPages, __self: this, __source: {fileName: _jsxFileName$9, lineNumber: 131}}
) )
) )
) )
@ -59521,31 +59536,31 @@
? "dib" ? "dib"
: "dn"; : "dn";
let encodedUrl = window.btoa(props.url); let encodedUrl = base64urlEncode(props.url);
let popout = (props.popout) ? "/popout" : ""; let popout = (props.popout) ? "/popout" : "";
return ( return (
react.createElement('div', { className: "w-100 relative pt4 pb6" , __self: this, __source: {fileName: _jsxFileName$b, lineNumber: 23}} react.createElement('div', { className: "w-100 relative pt4 pb6" , __self: this, __source: {fileName: _jsxFileName$b, lineNumber: 24}}
, react.createElement(Link$1, { , react.createElement(Link$1, {
className: "pb6 absolute inter f8 left-0 " + prevDisplay, className: "pb6 absolute inter f8 left-0 " + prevDisplay,
to: "/~link" to: "/~link"
+ popout + popout
+ props.path + props.groupPath
+ "/" + props.linkPage + "/" + props.linkPage
+ "/" + props.linkIndex + "/" + props.linkIndex
+ "/" + encodedUrl + "/" + encodedUrl
+ "/comments" + prevPage, __self: this, __source: {fileName: _jsxFileName$b, lineNumber: 24}}, "<- Previous Page" + "/comments" + prevPage, __self: this, __source: {fileName: _jsxFileName$b, lineNumber: 25}}, "<- Previous Page"
) )
, react.createElement(Link$1, { , react.createElement(Link$1, {
className: "pb6 absolute inter f8 right-0 " + nextDisplay, className: "pb6 absolute inter f8 right-0 " + nextDisplay,
to: "/~link" to: "/~link"
+ popout + popout
+ props.path + props.groupPath
+ "/" + props.linkPage + "/" + props.linkPage
+ "/" + props.linkIndex + "/" + props.linkIndex
+ "/" + encodedUrl + "/" + encodedUrl
+ "/comments" + nextPage, __self: this, __source: {fileName: _jsxFileName$b, lineNumber: 35}}, "Next Page ->" + "/comments" + nextPage, __self: this, __source: {fileName: _jsxFileName$b, lineNumber: 36}}, "Next Page ->"
) )
) )
@ -59568,7 +59583,7 @@
) { ) {
this.setState({requested: this.props.commentPage}); this.setState({requested: this.props.commentPage});
api$1.getCommentsPage( api$1.getCommentsPage(
this.props.path, this.props.groupPath,
this.props.url, this.props.url,
this.props.commentPage); this.props.commentPage);
} }
@ -59627,8 +59642,8 @@
react.createElement('div', {__self: this, __source: {fileName: _jsxFileName$c, lineNumber: 78}} react.createElement('div', {__self: this, __source: {fileName: _jsxFileName$c, lineNumber: 78}}
, commentsList , commentsList
, react.createElement(CommentsPagination, { , react.createElement(CommentsPagination, {
key: props.path + props.commentPage, key: props.groupPath + props.commentPage,
path: props.path, groupPath: props.groupPath,
popout: props.popout, popout: props.popout,
linkPage: props.linkPage, linkPage: props.linkPage,
linkIndex: props.linkIndex, linkIndex: props.linkIndex,
@ -59664,7 +59679,7 @@
// if we have no preloaded data, and we aren't expecting it, get it // if we have no preloaded data, and we aren't expecting it, get it
if (!this.state.data.title) { if (!this.state.data.title) {
api$1.getSubmission( api$1.getSubmission(
this.props.path, this.props.url, this.updateData.bind(this) this.props.groupPath, this.props.url, this.updateData.bind(this)
); );
} }
@ -59697,7 +59712,7 @@
let url = this.props.url || ""; let url = this.props.url || "";
let request = api$1.postComment( let request = api$1.postComment(
this.props.path, this.props.groupPath,
url, url,
this.state.comment this.state.comment
); );
@ -59714,7 +59729,6 @@
render() { render() {
let props = this.props; let props = this.props;
let popout = (props.popout) ? "/popout" : ""; let popout = (props.popout) ? "/popout" : "";
let routePath = props.path + "/" + props.page + "/" + props.linkIndex + "/" + window.btoa(props.url);
const data = this.state.data || props.data; const data = this.state.data || props.data;
let ship = data.ship || "zod"; let ship = data.ship || "zod";
@ -59761,13 +59775,13 @@
popout: props.popout, __self: this, __source: {fileName: _jsxFileName$d, lineNumber: 126}}) popout: props.popout, __self: this, __source: {fileName: _jsxFileName$d, lineNumber: 126}})
, react.createElement(Link$1, { , react.createElement(Link$1, {
className: "dib f8 fw4 v-top pt2 gray2" , className: "dib f8 fw4 v-top pt2 gray2" ,
to: "/~link" + popout + props.path + "/" + props.page, __self: this, __source: {fileName: _jsxFileName$d, lineNumber: 129}} to: "/~link" + popout + props.groupPath + "/" + props.page, __self: this, __source: {fileName: _jsxFileName$d, lineNumber: 129}}
, "<- Collection index" , "<- Collection index"
) )
, react.createElement(LinksTabBar, { , react.createElement(LinksTabBar, {
...props, ...props,
popout: popout, popout: popout,
path: routePath, __self: this, __source: {fileName: _jsxFileName$d, lineNumber: 134}}) groupPath: props.groupPath, __self: this, __source: {fileName: _jsxFileName$d, lineNumber: 134}})
) )
, react.createElement('div', { className: "w-100 mt2 flex justify-center overflow-y-scroll ph4 pb4" , __self: this, __source: {fileName: _jsxFileName$d, lineNumber: 139}} , react.createElement('div', { className: "w-100 mt2 flex justify-center overflow-y-scroll ph4 pb4" , __self: this, __source: {fileName: _jsxFileName$d, lineNumber: 139}}
, react.createElement('div', { className: "w-100 mw7" , __self: this, __source: {fileName: _jsxFileName$d, lineNumber: 140}} , react.createElement('div', { className: "w-100 mw7" , __self: this, __source: {fileName: _jsxFileName$d, lineNumber: 140}}
@ -59793,7 +59807,7 @@
, react.createElement('span', { className: "f9 inter gray2 pr3 v-mid" , __self: this, __source: {fileName: _jsxFileName$d, lineNumber: 160}} , react.createElement('span', { className: "f9 inter gray2 pr3 v-mid" , __self: this, __source: {fileName: _jsxFileName$d, lineNumber: 160}}
, this.state.timeSinceLinkPost , this.state.timeSinceLinkPost
) )
, react.createElement(Link$1, { to: "/~link" + props.path + "/" + props.page + "/" + props.linkIndex + "/" + window.btoa(props.url), className: "v-top", __self: this, __source: {fileName: _jsxFileName$d, lineNumber: 163}} , react.createElement(Link$1, { to: "/~link" + props.path + "/" + props.page + "/" + props.linkIndex + "/" + base64urlEncode(props.url), className: "v-top", __self: this, __source: {fileName: _jsxFileName$d, lineNumber: 163}}
, react.createElement('span', { className: "f9 inter gray2" , __self: this, __source: {fileName: _jsxFileName$d, lineNumber: 164}} , react.createElement('span', { className: "f9 inter gray2" , __self: this, __source: {fileName: _jsxFileName$d, lineNumber: 164}}
, comments , comments
) )
@ -59824,8 +59838,8 @@
) )
) )
, react.createElement(Comments, { , react.createElement(Comments, {
path: props.path, groupPath: props.groupPath,
key: props.path + props.commentPage, key: props.groupPath + props.commentPage,
comments: props.comments, comments: props.comments,
commentPage: props.commentPage, commentPage: props.commentPage,
members: props.members, members: props.members,
@ -59867,7 +59881,7 @@
let comments = !!state.comments ? state.comments : {}; let comments = !!state.comments ? state.comments : {};
return ( return (
react.createElement(BrowserRouter, {__self: this, __source: {fileName: _jsxFileName$e, lineNumber: 38}} react.createElement(BrowserRouter, {__self: this, __source: {fileName: _jsxFileName$e, lineNumber: 39}}
, react.createElement(Route, { exact: true, path: "/~link", , react.createElement(Route, { exact: true, path: "/~link",
render: (props) => { render: (props) => {
return ( return (
@ -59876,17 +59890,17 @@
paths: paths, paths: paths,
rightPanelHide: true, rightPanelHide: true,
sidebarShown: true, sidebarShown: true,
links: links, __self: this, __source: {fileName: _jsxFileName$e, lineNumber: 42}} links: links, __self: this, __source: {fileName: _jsxFileName$e, lineNumber: 43}}
, react.createElement('div', { className: "h-100 w-100 overflow-x-hidden flex flex-column bg-white bg-gray0-d dn db-ns" , __self: this, __source: {fileName: _jsxFileName$e, lineNumber: 48}} , react.createElement('div', { className: "h-100 w-100 overflow-x-hidden flex flex-column bg-white bg-gray0-d dn db-ns" , __self: this, __source: {fileName: _jsxFileName$e, lineNumber: 49}}
, react.createElement('div', { className: "pl3 pr3 pt2 dt pb3 w-100 h-100" , __self: this, __source: {fileName: _jsxFileName$e, lineNumber: 49}} , react.createElement('div', { className: "pl3 pr3 pt2 dt pb3 w-100 h-100" , __self: this, __source: {fileName: _jsxFileName$e, lineNumber: 50}}
, react.createElement('p', { className: "f8 pt3 gray2 w-100 h-100 dtc v-mid tc" , __self: this, __source: {fileName: _jsxFileName$e, lineNumber: 50}}, "Collections are shared across groups. To create a new collection, " , react.createElement('p', { className: "f8 pt3 gray2 w-100 h-100 dtc v-mid tc" , __self: this, __source: {fileName: _jsxFileName$e, lineNumber: 51}}, "Collections are shared across groups. To create a new collection, "
, react.createElement('a', { className: "black white-d" , href: "/~contacts", __self: this, __source: {fileName: _jsxFileName$e, lineNumber: 51}}, "create a group" ), "." , react.createElement('a', { className: "black white-d" , href: "/~contacts", __self: this, __source: {fileName: _jsxFileName$e, lineNumber: 52}}, "create a group" ), "."
) )
) )
) )
) )
); );
}, __self: this, __source: {fileName: _jsxFileName$e, lineNumber: 39}} ) }, __self: this, __source: {fileName: _jsxFileName$e, lineNumber: 40}} )
, react.createElement(Route, { exact: true, path: "/~link/(popout)?/:ship/:channel/:page?", , react.createElement(Route, { exact: true, path: "/~link/(popout)?/:ship/:channel/:page?",
render: (props) => { render: (props) => {
// groups/contacts and link channels are the same thing in ver 1 // groups/contacts and link channels are the same thing in ver 1
@ -59916,20 +59930,20 @@
sidebarShown: state.sidebarShown, sidebarShown: state.sidebarShown,
sidebarHideMobile: true, sidebarHideMobile: true,
popout: popout, popout: popout,
links: links, __self: this, __source: {fileName: _jsxFileName$e, lineNumber: 79}} links: links, __self: this, __source: {fileName: _jsxFileName$e, lineNumber: 80}}
, react.createElement(Links, { , react.createElement(Links, {
...props, ...props,
members: groupMembers, members: groupMembers,
links: channelLinks, links: channelLinks,
comments: channelComments, comments: channelComments,
page: page, page: page,
path: groupPath, groupPath: groupPath,
popout: popout, popout: popout,
sidebarShown: state.sidebarShown, __self: this, __source: {fileName: _jsxFileName$e, lineNumber: 88}} sidebarShown: state.sidebarShown, __self: this, __source: {fileName: _jsxFileName$e, lineNumber: 89}}
) )
) )
) )
}, __self: this, __source: {fileName: _jsxFileName$e, lineNumber: 58}} }, __self: this, __source: {fileName: _jsxFileName$e, lineNumber: 59}}
) )
, react.createElement(Route, { exact: true, path: "/~link/(popout)?/:ship/:channel/:page/:index/:encodedUrl/(comments)?/:commentpage?", , react.createElement(Route, { exact: true, path: "/~link/(popout)?/:ship/:channel/:page/:index/:encodedUrl/(comments)?/:commentpage?",
render: (props) => { render: (props) => {
@ -59942,7 +59956,7 @@
let index = props.match.params.index || 0; let index = props.match.params.index || 0;
let page = props.match.params.page || 0; let page = props.match.params.page || 0;
let url = window.atob(props.match.params.encodedUrl); let url = base64urlDecode(props.match.params.encodedUrl);
let data = !!links[groupPath] let data = !!links[groupPath]
? !!links[groupPath][page] ? !!links[groupPath][page]
@ -59964,23 +59978,23 @@
sidebarShown: state.sidebarShown, sidebarShown: state.sidebarShown,
sidebarHideMobile: true, sidebarHideMobile: true,
popout: popout, popout: popout,
links: links, __self: this, __source: {fileName: _jsxFileName$e, lineNumber: 127}} links: links, __self: this, __source: {fileName: _jsxFileName$e, lineNumber: 128}}
, react.createElement(LinkDetail, { , react.createElement(LinkDetail, {
...props, ...props,
page: page, page: page,
url: url, url: url,
linkIndex: index, linkIndex: index,
members: groupMembers, members: groupMembers,
path: groupPath, groupPath: groupPath,
popout: popout, popout: popout,
sidebarShown: state.sidebarShown, sidebarShown: state.sidebarShown,
data: data, data: data,
comments: coms, comments: coms,
commentPage: commentPage, __self: this, __source: {fileName: _jsxFileName$e, lineNumber: 136}} commentPage: commentPage, __self: this, __source: {fileName: _jsxFileName$e, lineNumber: 137}}
) )
) )
) )
}, __self: this, __source: {fileName: _jsxFileName$e, lineNumber: 102}} }, __self: this, __source: {fileName: _jsxFileName$e, lineNumber: 103}}
) )
) )
) )

View File

@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import _ from 'lodash'; import _ from 'lodash';
import { uuid } from '/lib/util'; import { uuid, stringToTa } from '/lib/util';
import { store } from '/store'; import { store } from '/store';
import moment from 'moment'; import moment from 'moment';
@ -102,7 +102,7 @@ class UrbitApi {
} }
getCommentsPage(path, url, page) { getCommentsPage(path, url, page) {
const strictUrl = this.encodeUrl(url); const strictUrl = stringToTa(url);
const endpoint = '/json/' + page + '/discussions/' + strictUrl + path; const endpoint = '/json/' + page + '/discussions/' + strictUrl + path;
this.bindLinkView(endpoint, this.bindLinkView(endpoint,
(res) => { (res) => {
@ -129,7 +129,7 @@ class UrbitApi {
} }
getSubmission(path, url, callback) { getSubmission(path, url, callback) {
const strictUrl = this.encodeUrl(url); const strictUrl = stringToTa(url);
const endpoint = '/json/0/submission/' + strictUrl + path; const endpoint = '/json/0/submission/' + strictUrl + path;
this.bindLinkView(endpoint, this.bindLinkView(endpoint,
(res) => { (res) => {
@ -174,42 +174,6 @@ class UrbitApi {
}); });
} }
//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];
let add = '';
switch (char) {
case ' ':
add = '.';
break;
case '.':
add = '~.';
break;
case '~':
add = '~~';
break;
default:
const charCode = url.charCodeAt(i);
if (
(charCode >= 97 && charCode <= 122) || // a-z
(charCode >= 48 && charCode <= 57) || // 0-9
char === '-'
) {
add = char;
} else {
//TODO behavior for unicode doesn't match +wood's,
// but we can probably get away with that for now.
add = '~' + charCode.toString(16) + '.';
}
}
strictUrl = strictUrl + add;
}
return '~.' + strictUrl;
}
} }
export let api = new UrbitApi(); export let api = new UrbitApi();

View File

@ -1,5 +1,6 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import { Route, Link } from 'react-router-dom'; import { Route, Link } from 'react-router-dom';
import { base64urlEncode } from '../../lib/util';
export class CommentsPagination extends Component { export class CommentsPagination extends Component {
render() { render() {
@ -16,7 +17,7 @@ export class CommentsPagination extends Component {
? "dib" ? "dib"
: "dn"; : "dn";
let encodedUrl = window.btoa(props.url); let encodedUrl = base64urlEncode(props.url);
let popout = (props.popout) ? "/popout" : ""; let popout = (props.popout) ? "/popout" : "";
return ( return (
@ -25,7 +26,7 @@ export class CommentsPagination extends Component {
className={"pb6 absolute inter f8 left-0 " + prevDisplay} className={"pb6 absolute inter f8 left-0 " + prevDisplay}
to={"/~link" to={"/~link"
+ popout + popout
+ props.path + props.groupPath
+ "/" + props.linkPage + "/" + props.linkPage
+ "/" + props.linkIndex + "/" + props.linkIndex
+ "/" + encodedUrl + "/" + encodedUrl
@ -36,7 +37,7 @@ export class CommentsPagination extends Component {
className={"pb6 absolute inter f8 right-0 " + nextDisplay} className={"pb6 absolute inter f8 right-0 " + nextDisplay}
to={"/~link" to={"/~link"
+ popout + popout
+ props.path + props.groupPath
+ "/" + props.linkPage + "/" + props.linkPage
+ "/" + props.linkIndex + "/" + props.linkIndex
+ "/" + encodedUrl + "/" + encodedUrl

View File

@ -19,7 +19,7 @@ export class Comments extends Component {
) { ) {
this.setState({requested: this.props.commentPage}); this.setState({requested: this.props.commentPage});
api.getCommentsPage( api.getCommentsPage(
this.props.path, this.props.groupPath,
this.props.url, this.props.url,
this.props.commentPage); this.props.commentPage);
} }
@ -78,8 +78,8 @@ export class Comments extends Component {
<div> <div>
{commentsList} {commentsList}
<CommentsPagination <CommentsPagination
key={props.path + props.commentPage} key={props.groupPath + props.commentPage}
path={props.path} groupPath={props.groupPath}
popout={props.popout} popout={props.popout}
linkPage={props.linkPage} linkPage={props.linkPage}
linkIndex={props.linkIndex} linkIndex={props.linkIndex}

View File

@ -3,6 +3,7 @@ import moment from 'moment';
import { Sigil } from '/components/lib/icons/sigil'; import { Sigil } from '/components/lib/icons/sigil';
import { Route, Link } from 'react-router-dom'; import { Route, Link } from 'react-router-dom';
import { base64urlEncode } from '../../lib/util';
export class LinkItem extends Component { export class LinkItem extends Component {
constructor(props) { constructor(props) {
@ -45,7 +46,7 @@ export class LinkItem extends Component {
hostname = hostname[4]; hostname = hostname[4];
} }
let encodedUrl = window.btoa(props.url); let encodedUrl = base64urlEncode(props.url);
let comments = props.comments + " comment" + ((props.comments === 1) ? "" : "s"); let comments = props.comments + " comment" + ((props.comments === 1) ? "" : "s");
@ -70,7 +71,7 @@ export class LinkItem extends Component {
: "~" + props.ship}</span> : "~" + props.ship}</span>
<span className="f9 inter gray2 pr3 v-mid">{this.state.timeSinceLinkPost}</span> <span className="f9 inter gray2 pr3 v-mid">{this.state.timeSinceLinkPost}</span>
<Link to= <Link to=
{"/~link" + props.popout + "/" + props.channel + "/" + props.page + "/" + props.linkIndex + "/" + encodedUrl} {"/~link" + props.popout + props.groupPath + "/" + props.page + "/" + props.linkIndex + "/" + encodedUrl}
className="v-top"> className="v-top">
<span className="f9 inter gray2"> <span className="f9 inter gray2">
{comments} {comments}

View File

@ -28,14 +28,14 @@ export class LinksTabBar extends Component {
<div className={"dib f8 pl6"}> <div className={"dib f8 pl6"}>
<Link <Link
className={"no-underline " + memColor} className={"no-underline " + memColor}
to={`/~link/` + popout + `members` + props.path}> to={`/~link/` + popout + `members` + props.groupPath}>
Members Members
</Link> </Link>
</div> </div>
) : ( ) : (
<div className="dib" style={{ width: 0 }}></div> <div className="dib" style={{ width: 0 }}></div>
)} )}
<a href={`/~link/popout` + props.path} target="_blank" <a href={`/~link/popout` + props.groupPath} target="_blank"
className="dib fr"> className="dib fr">
<img <img
className={`flex-shrink-0 pr4 dn invert-d ` + hidePopoutIcon} className={`flex-shrink-0 pr4 dn invert-d ` + hidePopoutIcon}

View File

@ -19,12 +19,12 @@ export class Pagination extends Component {
return ( return (
<div className="w-100 inter relative pv6"> <div className="w-100 inter relative pv6">
<div className={prevDisplay + " inter f8"}> <div className={prevDisplay + " inter f8"}>
<Link to={"/~link" + props.popout + props.path + prevPage}> <Link to={"/~link" + props.popout + props.groupPath + prevPage}>
&#60;- Previous Page &#60;- Previous Page
</Link> </Link>
</div> </div>
<div className={nextDisplay + " inter f8"}> <div className={nextDisplay + " inter f8"}>
<Link to={"/~link" + props.popout + props.path + nextPage}> <Link to={"/~link" + props.popout + props.groupPath + nextPage}>
Next Page -> Next Page ->
</Link> </Link>
</div> </div>

View File

@ -7,6 +7,7 @@ import { Sigil } from '/components/lib/icons/sigil';
import { Comments } from './lib/comments'; import { Comments } from './lib/comments';
import { uxToHex } from '../lib/util'; import { uxToHex } from '../lib/util';
import moment from 'moment' import moment from 'moment'
import { base64urlEncode } from '../lib/util';
export class LinkDetail extends Component { export class LinkDetail extends Component {
constructor(props) { constructor(props) {
@ -31,7 +32,7 @@ export class LinkDetail extends Component {
// if we have no preloaded data, and we aren't expecting it, get it // if we have no preloaded data, and we aren't expecting it, get it
if (!this.state.data.title) { if (!this.state.data.title) {
api.getSubmission( api.getSubmission(
this.props.path, this.props.url, this.updateData.bind(this) this.props.groupPath, this.props.url, this.updateData.bind(this)
); );
} }
@ -64,7 +65,7 @@ export class LinkDetail extends Component {
let url = this.props.url || ""; let url = this.props.url || "";
let request = api.postComment( let request = api.postComment(
this.props.path, this.props.groupPath,
url, url,
this.state.comment this.state.comment
); );
@ -81,7 +82,6 @@ export class LinkDetail extends Component {
render() { render() {
let props = this.props; let props = this.props;
let popout = (props.popout) ? "/popout" : ""; let popout = (props.popout) ? "/popout" : "";
let routePath = props.path + "/" + props.page + "/" + props.linkIndex + "/" + window.btoa(props.url);
const data = this.state.data || props.data; const data = this.state.data || props.data;
let ship = data.ship || "zod"; let ship = data.ship || "zod";
@ -128,13 +128,13 @@ export class LinkDetail extends Component {
popout={props.popout}/> popout={props.popout}/>
<Link <Link
className="dib f8 fw4 v-top pt2 gray2" className="dib f8 fw4 v-top pt2 gray2"
to={"/~link" + popout + props.path + "/" + props.page}> to={"/~link" + popout + props.groupPath + "/" + props.page}>
{"<- Collection index"} {"<- Collection index"}
</Link> </Link>
<LinksTabBar <LinksTabBar
{...props} {...props}
popout={popout} popout={popout}
path={routePath}/> groupPath={props.groupPath}/>
</div> </div>
<div className="w-100 mt2 flex justify-center overflow-y-scroll ph4 pb4"> <div className="w-100 mt2 flex justify-center overflow-y-scroll ph4 pb4">
<div className="w-100 mw7"> <div className="w-100 mw7">
@ -160,7 +160,7 @@ export class LinkDetail extends Component {
<span className="f9 inter gray2 pr3 v-mid"> <span className="f9 inter gray2 pr3 v-mid">
{this.state.timeSinceLinkPost} {this.state.timeSinceLinkPost}
</span> </span>
<Link to={"/~link" + props.path + "/" + props.page + "/" + props.linkIndex + "/" + window.btoa(props.url)} className="v-top"> <Link to={"/~link" + props.path + "/" + props.page + "/" + props.linkIndex + "/" + base64urlEncode(props.url)} className="v-top">
<span className="f9 inter gray2"> <span className="f9 inter gray2">
{comments} {comments}
</span> </span>
@ -191,8 +191,8 @@ export class LinkDetail extends Component {
</button> </button>
</div> </div>
<Comments <Comments
path={props.path} groupPath={props.groupPath}
key={props.path + props.commentPage} key={props.groupPath + props.commentPage}
comments={props.comments} comments={props.comments}
commentPage={props.commentPage} commentPage={props.commentPage}
members={props.members} members={props.members}

View File

@ -22,14 +22,13 @@ export class Links extends Component {
(!this.props.links[linkPage] || (!this.props.links[linkPage] ||
this.props.links.local[linkPage]) this.props.links.local[linkPage])
) { ) {
api.getPage(this.props.path, this.props.page); api.getPage(this.props.groupPath, this.props.page);
} }
} }
render() { render() {
let props = this.props; let props = this.props;
let popout = (props.popout) ? "/popout" : ""; let popout = (props.popout) ? "/popout" : "";
let channel = props.path.substr(1);
let linkPage = props.page; let linkPage = props.page;
let links = !!props.links[linkPage] let links = !!props.links[linkPage]
@ -84,7 +83,7 @@ export class Links extends Component {
ship={ship} ship={ship}
color={color} color={color}
comments={commentCount} comments={commentCount}
channel={channel} groupPath={props.groupPath}
popout={popout} popout={popout}
/> />
) )
@ -106,34 +105,34 @@ export class Links extends Component {
<SidebarSwitcher <SidebarSwitcher
sidebarShown={props.sidebarShown} sidebarShown={props.sidebarShown}
popout={props.popout}/> popout={props.popout}/>
<Link to={`/~link` + popout + props.path} className="pt2"> <Link to={`/~link` + popout + props.groupPath} className="pt2">
<h2 <h2
className={`dib f8 fw4 v-top ` + className={`dib f8 fw4 v-top ` +
(props.path.includes("/~/") (props.groupPath.includes("/~/")
? "" ? ""
: "mono")}> : "mono")}>
{(props.path.includes("/~/")) {(props.groupPath.includes("/~/"))
? "Private" ? "Private"
: channel} : props.groupPath.substr(1)}
</h2> </h2>
</Link> </Link>
<LinksTabBar <LinksTabBar
{...props} {...props}
popout={popout} popout={popout}
path={props.path + "/" + props.page}/> groupPath={props.groupPath + "/" + props.page}/>
</div> </div>
<div className="w-100 mt2 flex justify-center overflow-y-scroll ph4 pb4"> <div className="w-100 mt2 flex justify-center overflow-y-scroll ph4 pb4">
<div className="w-100 mw7"> <div className="w-100 mw7">
<div className="flex"> <div className="flex">
<LinkSubmit path={props.path}/> <LinkSubmit groupPath={props.groupPath}/>
</div> </div>
<div className="pb4"> <div className="pb4">
{LinkList} {LinkList}
<Pagination <Pagination
{...props} {...props}
key={props.path + props.page} key={props.groupPath + props.page}
popout={popout} popout={popout}
path={props.path} groupPath={props.groupPath}
currentPage={currentPage} currentPage={currentPage}
totalPages={totalPages} totalPages={totalPages}
/> />

View File

@ -9,6 +9,7 @@ import { store } from '/store';
import { Skeleton } from '/components/skeleton'; import { Skeleton } from '/components/skeleton';
import { Links } from '/components/links-list'; import { Links } from '/components/links-list';
import { LinkDetail } from '/components/link'; import { LinkDetail } from '/components/link';
import { base64urlDecode } from '../lib/util';
export class Root extends Component { export class Root extends Component {
@ -91,7 +92,7 @@ export class Root extends Component {
links={channelLinks} links={channelLinks}
comments={channelComments} comments={channelComments}
page={page} page={page}
path={groupPath} groupPath={groupPath}
popout={popout} popout={popout}
sidebarShown={state.sidebarShown} sidebarShown={state.sidebarShown}
/> />
@ -110,7 +111,7 @@ export class Root extends Component {
let index = props.match.params.index || 0; let index = props.match.params.index || 0;
let page = props.match.params.page || 0; let page = props.match.params.page || 0;
let url = window.atob(props.match.params.encodedUrl); let url = base64urlDecode(props.match.params.encodedUrl);
let data = !!links[groupPath] let data = !!links[groupPath]
? !!links[groupPath][page] ? !!links[groupPath][page]
@ -139,7 +140,7 @@ export class Root extends Component {
url={url} url={url}
linkIndex={index} linkIndex={index}
members={groupMembers} members={groupMembers}
path={groupPath} groupPath={groupPath}
popout={popout} popout={popout}
sidebarShown={state.sidebarShown} sidebarShown={state.sidebarShown}
data={data} data={data}

View File

@ -14,11 +14,66 @@ export function uuid() {
return str.slice(0,-1); return str.slice(0,-1);
} }
// encodes string into base64url,
// by encoding into base64 and replacing non-url-safe characters.
//
export function base64urlEncode(string) {
return window.btoa(string)
.split('+').join('-')
.split('/').join('_');
}
// decode base64url. inverse of base64urlEncode above.
//
export function base64urlDecode(string) {
return window.atob(
string.split('_').join('/')
.split('-').join('+')
);
}
export function isPatTa(str) { export function isPatTa(str) {
const r = /^[a-z,0-9,\-,\.,_,~]+$/.exec(str) const r = /^[a-z,0-9,\-,\.,_,~]+$/.exec(str)
return !!r; return !!r;
} }
// encode the string into @ta-safe format, using logic from +wood.
// for example, 'some Chars!' becomes '~.some.~43.hars~21.'
//
export function stringToTa(string) {
let out = '';
for (let i = 0; i < string.length; i++) {
const char = string[i];
let add = '';
switch (char) {
case ' ':
add = '.';
break;
case '.':
add = '~.';
break;
case '~':
add = '~~';
break;
default:
const charCode = string.charCodeAt(i);
if (
(charCode >= 97 && charCode <= 122) || // a-z
(charCode >= 48 && charCode <= 57) || // 0-9
char === '-'
) {
add = char;
} else {
//TODO behavior for unicode doesn't match +wood's,
// but we can probably get away with that for now.
add = '~' + charCode.toString(16) + '.';
}
}
out = out + add;
}
return '~.' + out;
}
/* /*
Goes from: Goes from:
~2018.7.17..23.15.09..5be5 // urbit @da ~2018.7.17..23.15.09..5be5 // urbit @da