Started work on Tour

This commit is contained in:
ポール ウェッブ 2018-07-16 17:06:37 -04:00
parent 5347b30403
commit e1323c85e5
12 changed files with 794 additions and 41 deletions

View file

@ -46,6 +46,7 @@ function main() {
app.route("/", page(require("./views/pages/home")(app)));
app.route("/resources", page(require("./views/pages/resources")(app)));
app.route("/tour", page(require("./views/pages/tour")(app)));
app.route("/*", page(require("./views/pages/page")(app)));
return app;

View file

@ -8,7 +8,6 @@
"@octokit/rest": "^15.9.4",
"app-root-path": "^2.1.0",
"async": "^2.6.1",
"chalk": "^2.4.1",
"choo": "^6.13.0",
"choo-async": "^0.1.1",
"choo-bundles": "^0.2.4",
@ -29,6 +28,7 @@
"fastify-ws": "^1.0.0",
"front-matter": "^2.3.0",
"fs-exists-sync": "^0.1.0",
"got": "^8.3.2",
"graceful-fs": "^4.1.11",
"heroku-ssl-redirect": "0.0.4",
"make-promises-safe": "^1.1.0",
@ -41,6 +41,7 @@
"request-promise-native": "^1.0.5",
"slack-node": "^0.2.0",
"socket.io": "^2.1.1",
"turbocolor": "^2.3.0",
"ws": "^5.2.2"
},
"devDependencies": {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,4 +1,8 @@
/* global ws, $ */ "use strict";
/* global $, ws */ "use strict";
const log = console.log; // eslint-disable-line
@ -11,7 +15,23 @@ ws.onmessage = socket => {
break;
default:
console.log(data);
log(data);
break;
}
};
function send(msg) { // eslint-disable-line
socketReady(ws, () => ws.send(msg));
}
function socketReady(socket, callback) {
setTimeout(() => {
if (socket.readyState === 1) {
if (callback !== undefined) callback();
return;
} else {
log("Waiting for websocket connection to come online");
socketReady(socket, callback);
}
}, 5);
}

390
sass/pages/_tour.scss Normal file
View file

@ -0,0 +1,390 @@
.hook {
.loader {
animation: spin 2s linear infinite;
border-radius: 50%;
border-style: solid;
border-top-color: $teal;
&:not(.small):not(.tiny) {
width: 4rem; height: 4rem;
border-width: 6px;
margin-right: auto;
margin-left: auto;
}
&.small {
width: 2rem; height: 2rem;
border-width: 3px;
}
&.tiny {
width: 1rem; height: 1rem;
border-width: 2px;
}
}
}
.hook__navigation {
background-color: $black;
color: $white;
font-size: 1rem;
padding-top: 1rem;
padding-bottom: 1rem;
text-align: center;
}
.hook__navigation__step {
@media (min-width: 501px) {
display: inline-block;
&:not(:last-of-type) {
margin-right: 1rem;
}
span {
width: 3rem; height: 3rem;
display: block;
font-size: 1.25rem;
line-height: 3rem;
}
}
@media (max-width: 500px) {
display: block;
span {
width: 1rem; height: 1rem;
display: inline-block;
font-size: 0.7rem;
line-height: 0.9rem;
position: relative;
top: 2px;
vertical-align: top;
}
}
&:not(.active) {
span {
border-color: rgba($white, 0.1);
}
}
&.active {
color: $teal;
span {
border-color: rgba($teal, 0.3);
}
}
span {
border: 1px solid;
border-radius: 50%;
margin: 0 auto 0.5rem;
}
}
.hook__page {
@extend .page__content;
}
.hook__page__hero {
margin-bottom: 2rem;
border-bottom: 1px solid rgba($black, 0.05);
h1, p {
text-align: center;
}
}
.hook__page__hero__claim,
.hook__page__hero__support {
margin-bottom: 3rem; padding-left: 1rem;
background-color: $white;
border: 1px solid rgba($gray, 0.7);
font-size: 1rem;
@media (min-width: 501px) {
margin-right: auto;
margin-left: auto;
width: 80%;
}
&::after {
@include clearfix;
}
button, input {
line-height: 3rem;
}
span {
color: rgba($black, 0.3);
}
button {
border-left: 1px solid rgba($gray, 0.7);
color: $white;
float: right;
position: relative;
text-align: center;
transition: all 0.2s;
width: 6rem;
&::after {
width: calc(100% + 2px); height: calc(100% + 2px);
top: -1px; left: -1px;
border: 1px solid;
content: "";
position: absolute;
transition: inherit;
}
&:not(:hover) {
background-color: $black;
&::after {
border-color: $black;
}
}
&:hover {
background-color: $teal;
&::after {
border-color: $teal;
}
}
}
}
.hook__page__hero__claim input {
width: calc(100% - 10rem);
}
.hook__page__hero__support {
input[type=number] {
width: 3rem;
}
input[type=text] {
width: calc(100% - 11.5rem);
}
span {
line-height: 3rem;
}
a {
margin-left: 0.5rem;
}
}
.hook__page__content::after {
@include clearfix;
}
.hook__page__content__card {
margin-bottom: 1rem; padding: 1rem;
cursor: pointer;
img {
margin-bottom: 0.5rem;
}
@media (min-width: 501px) {
float: left;
vertical-align: top;
width: 50%;
}
@media (max-width: 500px) {
width: 100%;
}
}
.hook__page__content__meme {
margin-bottom: 2rem;
padding-right: 1rem;
padding-left: 1rem;
@media (min-width: 701px) {
width: 50%;
}
@media (max-width: 700px) {
width: 100%;
}
canvas {
width: 100%; height: 100%;
background-color: rgba($teal, 0.3);
margin-bottom: 1rem;
}
h2.__metadata {
margin-top: 3rem;
}
fieldset {
border: none;
&:not(:last-of-type) {
margin-bottom: 1rem;
}
}
label {
color: rgba($black, 0.3);
display: block;
font-size: 0.8rem;
font-weight: 600;
letter-spacing: 0.05rem;
margin-bottom: 0.025rem;
text-transform: uppercase;
width: 100%;
}
input:not([type="checkbox"]):not([type="submit"]),
select,
textarea {
@media (min-width: 901px) {
font-size: 1.25rem;
}
@media (max-width: 900px) {
font-size: 1.05rem;
}
}
input {
&:not([type="checkbox"]):not([type="file"]):not([type="submit"]) {
border-bottom: 2px solid;
padding-bottom: 0.15rem;
transition: all 0.2s;
width: 100%;
}
&:not([type="file"]):not([type="submit"]) {
&:not(:hover):not(:active) {
border-color: $black;
}
&:hover,
&:active {
border-color: $teal;
}
}
&[type="checkbox"] {
width: 1.25rem; height: 1.25rem;
border: 2px solid;
margin-right: 0.5rem;
position: relative;
top: 0.35rem;
&::after {
width: 100%; height: 100%;
content: "";
font-size: 1.5rem;
line-height: 1rem;
position: absolute;
}
&:not(:checked)::after {
color: transparent;
}
&:checked::after {
color: $teal;
}
}
}
select,
textarea {
border-bottom: 2px solid;
width: 100%;
&:not(:hover):not(:active) {
border-color: $black;
}
&:hover,
&:active {
border-color: $teal;
}
}
select {
background-image: url("../../media/svg/down.svg");
background-position: 99% center;
background-repeat: no-repeat;
background-size: 1rem;
}
textarea {
min-height: 100px;
resize: vertical;
}
}
.hook__page__content__meme__thumbnail {
width: 5rem; height: 5rem;
border-style: solid;
border-width: 2px;
margin-bottom: 1rem;
object-fit: contain;
object-position: center;
&:not(:last-of-type) {
margin-right: 1rem;
}
&:not(.selected) {
border-color: transparent;
}
&.selected {
border-color: $black;
}
}
.hook__page__content__meme__uploader {
@extend .__button-black;
text-align: center;
width: 11rem;
> div:first-of-type {
width: 100%; height: 100%;
top: 0; left: 0;
position: absolute;
}
input {
top: 0; left: 0;
bottom: 0; right: 0;
cursor: pointer;
opacity: 0;
position: absolute;
width: 100%;
z-index: 10;
}
}

View file

@ -23,5 +23,6 @@
"pages/home",
"pages/contributing",
"pages/documentation",
"pages/page"
"pages/page",
"pages/tour"
;

View file

@ -4,7 +4,7 @@
// P A C K A G E S
const chalk = require("chalk");
const color = require("turbocolor");
const cors = require("cors");
const dedent = require("dedent");
@ -15,6 +15,8 @@ const fastify = require("fastify")({
}
});
const got = require("got");
const html = require("choo-async/html");
const octokit = require("@octokit/rest")();
const redis = require("redis");
const local = require("app-root-path").require;
@ -32,18 +34,18 @@ if (typeof process.env.GITHUB_OAUTH_TOKEN !== "undefined") {
type: "oauth",
token: process.env.GITHUB_OAUTH_TOKEN
});
}
} else log(`${color.red("[missing]")} GitHub token`);
if (typeof process.env.REDISCLOUD_URL !== "undefined") {
client = redis.createClient(process.env.REDISCLOUD_URL);
client.on("error", redisError => {
process.env.NODE_ENV === "development" ?
log(`\n${chalk.yellow("Unable to connect to Redis client.")}\nYou may be missing an .env file or your connection was reset.`) :
log(`\n${color.yellow("Unable to connect to Redis client.")}\nYou may be missing an .env file or your connection was reset.`) :
logSlackError("An error occured with Redis", redisError)
;
});
}
} else log(`${color.red("[missing]")} Redis client URL`);
@ -76,15 +78,28 @@ fastify.ready(err => {
fastify.ws.on("connection", socket => {
socket.send(JSON.stringify({ "message": "welcome" }));
socket.on("message", msg => {
if (msg === "landed on homepage") {
generateGitHubFeed(result => {
socket.send(JSON.stringify({
"message": "updated html",
"html": result,
"selector": "#github-feed"
}));
});
socket.on("message", data => {
data = JSON.parse(data);
switch(data.message) {
case "landed on homepage":
generateGitHubFeed(result => {
socket.send(JSON.stringify({
"message": "updated html",
"html": result,
"selector": "#github-feed"
}));
});
break;
case "fetch metadata":
fetchMetadata(data.claim, data.method);
break;
default:
log(data);
break;
}
});
@ -105,7 +120,7 @@ const start = async () => {
}
process.env.NODE_ENV === "development" ?
log(`\n${chalk.green("⚡")} ${fastify.server.address().port}\n`) :
log(`\n${color.green("⚡")} ${fastify.server.address().port}\n`) :
logSlackError(`Server started at port \`${fastify.server.address().port}\``)
;
};
@ -153,3 +168,43 @@ function generateGitHubFeed(displayGitHubFeed) {
});
}
}
function fetchMetadata(claimAddress, resolveMethod) {
if (!claimAddress || !resolveMethod) return;
const allowedMethods = [
"publish",
"resolve",
"wallet_send"
];
if (!allowedMethods.includes(resolveMethod)) return;
/*
component.$http.post("https://lbry.tech/forward", {
method: "resolve",
uri: component.address
}).then(response => {
component.isLoading = false;
component.jsonData = JSON.stringify(response.body, null, " ");
}).catch(error => {
component.isLoading = false;
component.jsonData = JSON.stringify(error, null, " ");
log("Error retrieving metadata for a claim:\n", error);
});
*/
got.post("https://lbry.tech/forward", {
});
return html`
<pre><code class="bash">
<span v-html="highlight('bash', exampleCode)"></span>
# Example code using the daemon
curl "http://localhost:5279" --data "{ 'method': 'resolve', 'params': { 'uri': '${claimAddress}' } }"
</code></pre>
`;
}

View file

@ -57,7 +57,10 @@ module.exports = exports = () => async () => html`
</div>
</section>
<div id="github-feed" class="github-feed"></div>
<div id="github-feed" class="github-feed">
<h3>GitHub</h3>
<h5 class="last-updated">Unable to fetch latest GitHub data</h5>
</div>
<section class="contribute">
<div class="inner-wrap">
@ -98,25 +101,11 @@ module.exports = exports = () => async () => html`
<script>
$(function () {
send("landed on homepage");
send(JSON.stringify({
"message": "landed on homepage"
}));
});
function send(msg) {
socketReady(ws, () => ws.send(msg));
};
function socketReady(socket, callback) {
setTimeout(() => {
if (socket.readyState === 1) {
if (callback !== undefined) callback();
return;
} else {
console.log("Waiting for websocket connection to come online");
socketReady(socket, callback);
}
}, 5);
};
document.getElementsByTagName("body")[0].classList.add("home"); // TODO: make this happen in components/layout
</script>
`;

View file

@ -59,8 +59,8 @@ module.exports = exports = () => async state => {
const markdownFileDetails = fm(markdownFile);
const renderedMarkdown = md.render(partialFinder(markdownFileDetails.body));
let ecosystemScripts = "";
if (path === "overview") ecosystemScripts = "<script>" + fs.readFileSync("./views/partials/ecosystem-scripts.js", "utf-8") + "</script>";
let pageScript = "";
if (path === "overview") pageScript = "<script>" + fs.readFileSync("./views/partials/ecosystem-scripts.js", "utf-8") + "</script>";
return html`
<article class="page" itemtype="http://schema.org/BlogPosting">
@ -75,7 +75,7 @@ module.exports = exports = () => async state => {
<section class="page__content" itemprop="articleBody">
<div class="inner-wrap">
${raw(renderedMarkdown)}
${raw(ecosystemScripts)}
${raw(pageScript)}
</div>
</section>
</article>

257
views/pages/tour.js Normal file
View file

@ -0,0 +1,257 @@
"use strict";
// P A C K A G E
// const dedent = require("dedent");
const fs = require("graceful-fs");
const html = require("choo-async/html");
const raw = require("nanohtml/raw");
// E X P O R T
module.exports = exports = () => async () => html`
<div class="hook" id="hook">
<nav class="hook__navigation" id="hook-navigation">
<div class="inner-wrap"> <!--/ TODO: Save tutorial position to localStorage /-->
<a href="#" class="hook__navigation__step" data-action="go to step 1">
<span class="number">1</span>
Resolve a claim
</a>
<a href="#" class="hook__navigation__step" data-action="go to step 2">
<span class="number">2</span>
Publish content
</a>
<a href="#" class="hook__navigation__step" data-action="go to step 3">
<span class="number">3</span>
Support with LBC
</a>
</div>
</nav>
${step1()}
${step2()}
${step3()}
</div>
<script>${raw(fs.readFileSync("./views/partials/tour-scripts.js", "utf-8"))}</script>
`;
function step1() {
/**
Step 1 loading steps:
- exampleCode !== ""
<pre><code class="bash"><span v-html="highlight('bash', exampleCode)"></span></code></pre>
- isLoading
<div class="loader"></div>
- jsonData
<p style="text-align: center;">Success! Here is the response for <strong>lbry://{{ address }}</strong>:</p>
<pre><code class="json"><span v-html="highlight('json', jsonData)"></span></code></pre>
<a href="#" class="__button-black" data-action="go to step 2" title="Proceed to step two">Go to next step</a>
- TODO:
Create message for error
*/
return html`
<section class="hook__page" id="step1-page">
<header class="hook__page__hero">
<div class="inner-wrap">
<h1>Learn the LBRY protocol by examples</h1>
<p>Let's start by getting the associated metadata for a claim.</p>
<div class="hook__page__hero__claim">
<span>lbry://</span><input id="fetch-claim-uri" type="text" placeholder="&thinsp;Claim URI goes here"/>
<button data-action="fetch metadata" class="button" title="Execute claim" type="button">Execute</button>
</div>
</div>
</header>
<div class="hook__page__content inner-wrap" id="step1-placeholder"></div>
<div class="hook__page__content inner-wrap" id="step1-selections"> <!--/ Hide when "isLoading" || "jsonData" is active /-->
<p style="text-align: center;">&hellip;or select a live example from below</p>
<div class="hook__page__content__card">
<img src="https://spee.ch/0654f2e2322ccfefa02a956d252df9ac7611d8b0/placeholder-itsadisaster.jpeg" data-action="choose claim" data-claim-id="itsadisaster">
<div data-action="choose claim" data-claim-id="itsadisaster">
<h4>It's a Disaster</h4>
<p class="account">Anonymous</p>
</div>
</div>
<div class="hook__page__content__card">
<img src="https://spee.ch/b1bd330e048fc22dc7bf941c33dd8245eef492c1/unbubbled.png" data-action="choose claim" data-claim-id="unbubbled1-1">
<div data-action="choose claim" data-claim-id="unbubbled1-1">
<h4>Unbubbled with Jamie King, Ep1.1 &mdash; Bitcoin, Boom or Bust</h4>
<p class="account">@Unbubbled</p>
</div>
</div>
<div class="hook__page__content__card">
<img src="https://spee.ch/9880947df41af880bc19724ceddd1cce957a07e2/placeholder-fortninte.jpeg" data-action="choose claim" data-claim-id="fortnite-top-stream-moments-nickatnyte">
<div data-action="choose claim" data-claim-id="fortnite-top-stream-moments-nickatnyte">
<h4>FORTNITE TOP STREAM MOMENTS &mdash; Nickatnyte &amp; GamingwithMolt</h4>
<p class="account">@nickatnyte</p>
</div>
</div>
<div class="hook__page__content__card">
<img src="https://spee.ch/a3b8258478ad88954f42f6ac3427eab380720f60/placeholder-lbrymine.png" data-action="choose claim" data-claim-id="six">
<div data-action="choose claim" data-claim-id="six">
<h4>LBRY Coin (LBC) GPU Miner for AMD and NVIDIA</h4>
<p class="account">Anonymous</p>
</div>
</div>
</div>
</section>
`;
}
function step2() {
/**
Step 2 loading steps:
...
*/
const images = [
{
src: "../carlsagan2.jpg",
alt: "Carl Sagan"
},
{
src: "../doge-meme.jpg",
alt: "Doge"
},
{
src: "../lbry-green.png",
alt: "LBRY Logo With Green Background"
}
];
const renderedImages = [];
for (const image of images) {
renderedImages.push(`<img src="${image.src}" class="hook__page__content__meme__thumbnail" alt="${image.alt}"/>`);
}
return html`
<section class="hook__page" id="step2-page" style="display: none;"> <!--/ v-images-loaded="imagesLoaded" /-->
<header class="hook__page__hero">
<div class="inner-wrap">
<h1>Publish your content on the blockchain</h1>
<p>Upload an image to the blockchain and you are able to view it on the <a href="http://explorer.lbry.io" title="LBRY Blockchain Explorer" target="_blank" rel="noopener noreferrer">LBRY Blockchain Explorer</a>.</p>
</div>
</header>
<div class="hook__page__content inner-wrap">
<template v-if="!isLoading">
<div class="hook__page__content__meme left">
<img v-bind:src="backgroundImage" id="base-image" style="height: 0; visibility: hidden;" alt="Base image for LBRY meme creator"/>
<canvas id="meme-canvas" width="400" height="300">Sorry, canvas is not supported</canvas>
<!--/ <img v-for="image in images" v-bind:src="image.src" v-on:click="chooseImage(image.src)" class="hook__page__content__meme__thumbnail" v-bind:class="{'selected': backgroundImage === image.src}" v-bind:alt="image.alt"/> /-->
${renderedImages}
</div>
<form class="hook__page__content__meme right"> <!--/ v-on:submit.prevent="submit" /-->
<h2>Image Text</h2>
<fieldset>
<label for="meme-top-line">Top line</label>
<input name="meme-top-line" id="meme-top-line" type="text" placeholder="Top line" required/> <!--/ v-model="topLine" /-->
</fieldset>
<fieldset>
<label for="meme-bottom-line">Bottom line</label>
<input name="meme-bottom-line" id="meme-bottom-line" type="text" placeholder="Bottom line" required/> <!--/ v-model="bottomLine" /-->
</fieldset>
<h2 class="__metadata">Metadata</h2>
<fieldset>
<label for="meme-title">Title</label>
<input name="meme-title" id="meme-title" type="text" placeholder="Title" required/> <!--/ v-model="title" /-->
</fieldset>
<fieldset>
<label for="meme-description">Description</label>
<textarea name="meme-description" id="meme-description" type="text" placeholder="Description" spellcheck="false" required>{{ description }}</textarea> <!--/ v-model="description" /-->
</fieldset>
<fieldset>
<label for="meme-language">Language</label>
<select name="meme-language" id="meme-language"> <!--/ v-model="language" /-->
<option value="ar">Arabic</option>
<option value="zh">Chinese (Mandarin)</option>
<option value="en">English</option>
<option value="fr">French</option>
<option value="de">German</option>
<option value="it">Italian</option>
<option value="jp">Japanese</option>
<option value="ru">Russian</option>
<option value="es">Spanish</option>
<option value="">Not specified</option>
</select>
</fieldset>
<fieldset>
<label for="meme-license">License</label>
<select name="meme-license" id="meme-license" required> <!--/ v-model="license" /-->
<option value="Public Domain">Public Domain</option>
<option value="Creative Commons Attribution 4.0 International">Creative Commons Attribution 4.0 International</option>
<option value="Creative Commons Attribution-ShareAlike 4.0 International">Creative Commons Attribution-ShareAlike 4.0 International</option>
<option value="Creative Commons Attribution-NoDerivatives 4.0 International">Creative Commons Attribution-NoDerivatives 4.0 International</option>
<option value="Creative Commons Attribution-NonCommercial 4.0 International">Creative Commons Attribution-NonCommercial 4.0 International</option>
<option value="Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International">Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International</option>
<option value="Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International">Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International</option>
<option value="None">None</option>
</select>
</fieldset>
<fieldset>
<label><input type="checkbox" name="nsfw"/>NSFW</label> <!--/ v-model="nsfw" /-->
</fieldset>
<fieldset>
<input type="submit" class="__button-black" value="Submit"/>
</fieldset>
</form>
</template>
<pre v-if="exampleCode !== ''" style="clear: both;"><code class="bash"><span v-html="highlight('bash', exampleCode)"></span></code></pre>
<div class="loader" v-if="isLoading"></div>
<div v-if="jsonData">
<p style="text-align: center;">Success!<br/>
<a class="__button-black" v-bind:href="'http://explorer.lbry.io/tx/'+txid">See the transaction on explorer.lbry.io</a>
</p>
<p style="text-align: center;">Here is the raw response:</p>
<pre><code class="json"><span v-html="highlight('json', jsonData)"></span></code></pre>
</div>
</div>
</section>
`;
}
function step3() {
//
}

View file

@ -0,0 +1,39 @@
/* global $, send */ "use strict";
$("#fetch-claim-uri").val(""); // reset
$("[data-action]").on("click", event => {
event.preventDefault();
const data = event.currentTarget.dataset;
switch(data.action) {
case "fetch metadata":
if (!$("#fetch-claim-uri").val()) return;
send(JSON.stringify({
"claim": $("#fetch-claim-uri").val(),
"message": "fetch metadata",
"method": "resolve"
}));
$("#step1-placeholder").html("<div class=\"loader\"></div>");
$("#step1-selections").hide();
break;
case "choose claim":
console.log(data.claimId);
break;
default:
break;
}
});
send(JSON.stringify({
"message": "Landed on Tour"
}));