Mobile reading secret
This commit is contained in:
parent
912c623048
commit
9fb8e50463
|
@ -14,6 +14,7 @@ const {
|
||||||
uploadPicture,
|
uploadPicture,
|
||||||
identityFilename,
|
identityFilename,
|
||||||
ssbFolder,
|
ssbFolder,
|
||||||
|
isPhone,
|
||||||
} = require("./utils");
|
} = require("./utils");
|
||||||
const queries = require("./queries");
|
const queries = require("./queries");
|
||||||
const serveBlobs = require("./serve-blobs");
|
const serveBlobs = require("./serve-blobs");
|
||||||
|
@ -26,7 +27,6 @@ const sgMail = require("@sendgrid/mail");
|
||||||
const ejs = require("ejs");
|
const ejs = require("ejs");
|
||||||
const cookieEncrypter = require("cookie-encrypter");
|
const cookieEncrypter = require("cookie-encrypter");
|
||||||
const expressLayouts = require("express-ejs-layouts");
|
const expressLayouts = require("express-ejs-layouts");
|
||||||
const isMobile = require("ismobilejs").default;
|
|
||||||
|
|
||||||
let ssbServer;
|
let ssbServer;
|
||||||
let mode = process.env.MODE || "client";
|
let mode = process.env.MODE || "client";
|
||||||
|
@ -85,7 +85,7 @@ const cookieOptions = {
|
||||||
httpOnly: true,
|
httpOnly: true,
|
||||||
signed: true,
|
signed: true,
|
||||||
expires: new Date(253402300000000), // Friday, 31 Dec 9999 23:59:59 GMT, nice date from stackoverflow
|
expires: new Date(253402300000000), // Friday, 31 Dec 9999 23:59:59 GMT, nice date from stackoverflow
|
||||||
sameSite: true,
|
sameSite: "Lax",
|
||||||
};
|
};
|
||||||
app.use(cookieParser(cookieSecret));
|
app.use(cookieParser(cookieSecret));
|
||||||
app.use(cookieEncrypter(cookieSecret));
|
app.use(cookieEncrypter(cookieSecret));
|
||||||
|
@ -160,7 +160,7 @@ router.get("/", { public: true }, async (req, res) => {
|
||||||
if (!req.context.profile) {
|
if (!req.context.profile) {
|
||||||
return res.render("index");
|
return res.render("index");
|
||||||
}
|
}
|
||||||
if (isMobile(req.headers["user-agent"]).phone) {
|
if (isPhone(req)) {
|
||||||
return res.redirect("/mobile");
|
return res.redirect("/mobile");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -178,10 +178,9 @@ router.get("/", { public: true }, async (req, res) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
router.get("/mobile", async (req, res) => {
|
router.get("/mobile", async (req, res) => {
|
||||||
// TODO
|
if (!isPhone(req)) {
|
||||||
// if (!isMobile(req.headers["user-agent"]).phone) {
|
return res.redirect("/");
|
||||||
// return res.redirect("/");
|
}
|
||||||
// }
|
|
||||||
|
|
||||||
const posts = await queries.getPosts(ssbServer, req.context.profile);
|
const posts = await queries.getPosts(ssbServer, req.context.profile);
|
||||||
|
|
||||||
|
@ -192,6 +191,20 @@ router.get("/mobile", async (req, res) => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
router.get("/mobile/secrets", async (req, res) => {
|
||||||
|
if (!isPhone(req)) {
|
||||||
|
return res.redirect("/");
|
||||||
|
}
|
||||||
|
|
||||||
|
const secretMessages = await queries.getSecretMessages(ssbServer, req.context.profile);
|
||||||
|
|
||||||
|
res.render("mobile/secrets", {
|
||||||
|
secretMessages,
|
||||||
|
profile: req.context.profile,
|
||||||
|
layout: "mobile/_layout",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
router.get("/login", { public: true }, (_req, res) => {
|
router.get("/login", { public: true }, (_req, res) => {
|
||||||
res.render("login", { mode });
|
res.render("login", { mode });
|
||||||
});
|
});
|
||||||
|
|
|
@ -3,6 +3,7 @@ const leftpad = require("left-pad"); // I don't believe I'm depending on this
|
||||||
const pull = require("pull-stream");
|
const pull = require("pull-stream");
|
||||||
const split = require("split-buffer");
|
const split = require("split-buffer");
|
||||||
const metrics = require("./metrics");
|
const metrics = require("./metrics");
|
||||||
|
const isMobile = require("ismobilejs").default;
|
||||||
|
|
||||||
module.exports.asyncRouter = (app) => {
|
module.exports.asyncRouter = (app) => {
|
||||||
const debug = require("debug")("router");
|
const debug = require("debug")("router");
|
||||||
|
@ -112,3 +113,7 @@ module.exports.promisePull = (...streams) =>
|
||||||
});
|
});
|
||||||
|
|
||||||
module.exports.mapValues = (x) => x.map((y) => y.value);
|
module.exports.mapValues = (x) => x.map((y) => y.value);
|
||||||
|
|
||||||
|
module.exports.isPhone = (req) => {
|
||||||
|
return isMobile(req.headers["user-agent"]).phone;
|
||||||
|
};
|
||||||
|
|
|
@ -0,0 +1,88 @@
|
||||||
|
/**
|
||||||
|
* Compose Post
|
||||||
|
*/
|
||||||
|
|
||||||
|
const composePost = document.querySelector(".js-compose-post");
|
||||||
|
if (composePost) {
|
||||||
|
const composeButton = document.querySelector(".js-publish-button");
|
||||||
|
composePost.addEventListener("focus", () => {
|
||||||
|
composeButton.style.display = "block";
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Modal
|
||||||
|
*/
|
||||||
|
|
||||||
|
const openModalFor = (elem, onConfirm, afterClose = null) => {
|
||||||
|
const overlay = elem.parentElement.querySelector(".overlay");
|
||||||
|
const modal = elem.parentElement.querySelector(".modal");
|
||||||
|
const confirmButtons = elem.parentElement.querySelectorAll(".modal-confirm");
|
||||||
|
const steps = elem.parentElement.querySelectorAll(".js-step");
|
||||||
|
|
||||||
|
overlay.hidden = false;
|
||||||
|
modal.hidden = false;
|
||||||
|
|
||||||
|
const onClose = () => {
|
||||||
|
overlay.hidden = true;
|
||||||
|
modal.hidden = true;
|
||||||
|
steps.forEach((step) => {
|
||||||
|
step.style.display = "none";
|
||||||
|
});
|
||||||
|
if (steps[0]) steps[0].style.display = "flex";
|
||||||
|
if (afterClose) afterClose();
|
||||||
|
};
|
||||||
|
|
||||||
|
const nextOrConfirm = () => {
|
||||||
|
if (steps.length == 0) {
|
||||||
|
onConfirm();
|
||||||
|
} else {
|
||||||
|
let currentStep;
|
||||||
|
steps.forEach((step, index) => {
|
||||||
|
if (currentStep == index) {
|
||||||
|
step.style.display = "flex";
|
||||||
|
} else if (step.style.display != "none") {
|
||||||
|
currentStep = index;
|
||||||
|
currentStep++;
|
||||||
|
if (currentStep < steps.length) step.style.display = "none";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (currentStep == steps.length) {
|
||||||
|
onConfirm();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
overlay.addEventListener("click", onClose);
|
||||||
|
Array.from(confirmButtons).forEach((button) =>
|
||||||
|
button.addEventListener("click", nextOrConfirm)
|
||||||
|
);
|
||||||
|
|
||||||
|
return { close: onClose };
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Secret Messages Reading
|
||||||
|
*/
|
||||||
|
|
||||||
|
const messages = document.querySelectorAll(".js-secret-message");
|
||||||
|
messages.forEach((message) => {
|
||||||
|
message.addEventListener("click", () => {
|
||||||
|
const afterClose = () => {
|
||||||
|
const parent = message.parentElement;
|
||||||
|
const composeMessage = parent.parentElement.querySelector(
|
||||||
|
".js-compose-secret-message"
|
||||||
|
);
|
||||||
|
composeMessage.style.display = "flex";
|
||||||
|
parent.parentElement.removeChild(parent);
|
||||||
|
};
|
||||||
|
|
||||||
|
const modal = openModalFor(message, () => modal.close(), afterClose);
|
||||||
|
|
||||||
|
fetch("/vanish", {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
||||||
|
body: "keys=" + encodeURIComponent(message.dataset.keys),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -1,6 +1,9 @@
|
||||||
body {
|
body {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
padding-bottom: 70px;
|
padding-bottom: 70px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
nav {
|
nav {
|
||||||
|
@ -42,3 +45,55 @@ nav a.selected {
|
||||||
.post {
|
.post {
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.modal {
|
||||||
|
top: 100%;
|
||||||
|
left: 0;
|
||||||
|
transform: none;
|
||||||
|
min-width: auto;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
animation: slide 0.5s forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes slide {
|
||||||
|
100% {
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-group {
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-body {
|
||||||
|
flex-grow: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-footer {
|
||||||
|
border-bottom: 1px solid #ddd;
|
||||||
|
background: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.secret-chat {
|
||||||
|
width: 100%;
|
||||||
|
max-width: none;
|
||||||
|
min-width: auto;
|
||||||
|
flex-grow: 1;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.secret-button {
|
||||||
|
padding: 8px 14px;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.secret-chat .link-profile-pic {
|
||||||
|
margin-right: 12px;
|
||||||
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
</main>
|
</main>
|
||||||
<script src="/js/index.js"></script>
|
<script src="/js/desktop.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
|
@ -1,25 +1,23 @@
|
||||||
<div style="padding-top: 15px;">
|
<% posts.map(post => { %>
|
||||||
<% posts.map(post => { %>
|
<% if (!post.content.text) return %>
|
||||||
<% if (!post.content.text) return %>
|
<div class="post">
|
||||||
<div class="post">
|
<div>
|
||||||
<div>
|
<a href="<%= profileUrl(post.author) %>">
|
||||||
<a href="<%= profileUrl(post.author) %>">
|
<img src="<%= profileImageUrl(post.authorProfile) %>" class="post-profile-pic" />
|
||||||
<img src="<%= profileImageUrl(post.authorProfile) %>" class="post-profile-pic" />
|
</a>
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div class="post-content">
|
|
||||||
<a href="<%= profileUrl(post.author) %>" class="no-link-style">
|
|
||||||
<b><%= post.authorProfile.name %></b>
|
|
||||||
</a>
|
|
||||||
<% let text = post.content.text %>
|
|
||||||
<% if (typeof dont_cut == "undefined") { %>
|
|
||||||
<% text = post.content.text.slice(0, 140) %>
|
|
||||||
<% if (post.content.text.length > 140) text += "..." %>
|
|
||||||
<% } %>
|
|
||||||
<% text.split("\n").map((line, index) => { %>
|
|
||||||
<%- index > 0 ? "<br />" : "" %><%= line %>
|
|
||||||
<% }) %>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<% }) %>
|
<div class="post-content">
|
||||||
</div>
|
<a href="<%= profileUrl(post.author) %>" class="no-link-style">
|
||||||
|
<b><%= post.authorProfile.name %></b>
|
||||||
|
</a>
|
||||||
|
<% let text = post.content.text %>
|
||||||
|
<% if (typeof dont_cut == "undefined") { %>
|
||||||
|
<% text = post.content.text.slice(0, 140) %>
|
||||||
|
<% if (post.content.text.length > 140) text += "..." %>
|
||||||
|
<% } %>
|
||||||
|
<% text.split("\n").map((line, index) => { %>
|
||||||
|
<%- index > 0 ? "<br />" : "" %><%= line %>
|
||||||
|
<% }) %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<% }) %>
|
|
@ -27,15 +27,15 @@
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<h2 style="margin: 0">Your Wall</h2>
|
<h2 style="margin: 0">Your Wall</h2>
|
||||||
<% if (posts.length > 0) { %>
|
<div style="padding: 15px;">
|
||||||
<%- include('_posts', { posts }) %>
|
<% if (posts.length > 0) { %>
|
||||||
<% } else { %>
|
<%- include('_posts', { posts }) %>
|
||||||
<div style="padding-top: 15px;">
|
<% } else { %>
|
||||||
<div class="post">
|
<div class="post">
|
||||||
You have no posts yet, publish something!
|
You have no posts yet, publish something!
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<% } %>
|
||||||
<% } %>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="friends-communities">
|
<div class="friends-communities">
|
||||||
|
|
|
@ -14,23 +14,23 @@
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
<nav>
|
<nav>
|
||||||
<a href="/" class="<%= context.path == "/mobile" ? "selected" : "" %>">
|
<a href="/mobile" class="<%= context.path == "/mobile" ? "selected" : "" %>">
|
||||||
<div class="nav-icon">🙂</div>
|
<div class="nav-icon">🙂</div>
|
||||||
Profile
|
Profile
|
||||||
</a>
|
</a>
|
||||||
<a href="/secrets">
|
<a href="/mobile/secrets" class="<%= context.path == "/mobile/secrets" ? "selected" : "" %>">
|
||||||
<div class="nav-icon">🤫</div>
|
<div class="nav-icon">🤫</div>
|
||||||
Secrets
|
Secrets
|
||||||
</a>
|
</a>
|
||||||
<a href="/friends">
|
<a href="/mobile/friends" class="<%= context.path == "/mobile/friends" ? "selected" : "" %>">
|
||||||
<div class="nav-icon">👨👧👦</div>
|
<div class="nav-icon">👨👧👦</div>
|
||||||
Friends
|
Friends
|
||||||
</a>
|
</a>
|
||||||
<a href="/communities">
|
<a href="/mobile/communities" class="<%= context.path == "/mobile/communities" ? "selected" : "" %>">
|
||||||
<div class="nav-icon">🌆</div>
|
<div class="nav-icon">🌆</div>
|
||||||
Communities
|
Communities
|
||||||
</a>
|
</a>
|
||||||
<a href="/search">
|
<a href="/mobile/search" class="<%= context.path == "/mobile/search" ? "selected" : "" %>">
|
||||||
<div class="nav-icon">💬</div>
|
<div class="nav-icon">💬</div>
|
||||||
Search
|
Search
|
||||||
</a>
|
</a>
|
||||||
|
@ -38,5 +38,7 @@
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<%- body %>
|
<%- body %>
|
||||||
|
|
||||||
|
<script src="/js/mobile.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
|
@ -8,6 +8,12 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<% if (posts.length > 0) { %>
|
<% if (posts.length > 0) { %>
|
||||||
|
<form action="/publish" method="POST" style="padding: 0px 8px 5px 8px;">
|
||||||
|
<textarea name="message" class="compose-post js-compose-post" placeholder="Post something on your wall..."></textarea>
|
||||||
|
<div class="reverse-columns">
|
||||||
|
<input type="submit" value="Publish" style="display: none; margin: 5px 0" class="js-publish-button" />
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
<%- include('../_posts', { posts }) %>
|
<%- include('../_posts', { posts }) %>
|
||||||
<% } else { %>
|
<% } else { %>
|
||||||
<div style="padding-top: 15px;">
|
<div style="padding-top: 15px;">
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
<div class="secret-chat">
|
||||||
|
<div class="column-side-padding" <%= secretMessages.length > 0 ? "hidden" : "" %> style="margin: 25px 0 20px 0; font-size: 14px;">
|
||||||
|
👻 You don't have any secret messages yet
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<% secretMessages.map(chat => { %>
|
||||||
|
<div>
|
||||||
|
<div>
|
||||||
|
<button class="secret-button js-compose-secret-message" style="<%= chat.messages.length == 0 ? "" : "display: none" %>" data-url="<%= profileUrl(chat.authorProfile.id, "/publish_secret") %>">
|
||||||
|
<img class="link-profile-pic" src="<%= profileImageUrl(chat.authorProfile) %>" class="post-profile-pic" />
|
||||||
|
<div style="flex-grow: 1;">
|
||||||
|
<%= chat.authorProfile.name %> <br />
|
||||||
|
<small>
|
||||||
|
No new secrets
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
<%- include('../secrets/_compose_single', { profile: chat.authorProfile }) %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<button class="secret-button js-secret-message" style="<%= chat.messages.length == 0 ? "display: none" : "" %>" data-keys="<%= chat.messages.map(m => m.key).join(",") %>">
|
||||||
|
<img class="link-profile-pic" src="<%= profileImageUrl(chat.authorProfile) %>" class="post-profile-pic" />
|
||||||
|
<div style="flex-grow: 1;">
|
||||||
|
<%= chat.authorProfile.name %> <br />
|
||||||
|
<small>
|
||||||
|
👁 <%= chat.messages.length == 1 ? "1 new message" : chat.messages.length + " new messages" %>
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
<div class="overlay" hidden></div>
|
||||||
|
<div class="modal" hidden>
|
||||||
|
<% chat.messages.reverse().map((message, index) => { %>
|
||||||
|
<div class="modal-group js-step" <%- index > 0 ? 'style="display: none"' : "" %>>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<% if (index == chat.messages.length - 1) { %>
|
||||||
|
<span></span>
|
||||||
|
<button class="modal-confirm">Close</button>
|
||||||
|
<% } else { %>
|
||||||
|
<span></span>
|
||||||
|
<button class="modal-confirm">Next</button>
|
||||||
|
<% } %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a href="<%= profileUrl(chat.authorProfile.id) %>" class="modal-header">
|
||||||
|
<img src="<%= profileImageUrl(chat.authorProfile) %>" class="post-profile-pic" />
|
||||||
|
<div style="padding-left: 10px"><%= chat.authorProfile.name %></div>
|
||||||
|
</a>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div style="margin-top: -60px"><%= message.value.content.text %></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<% }) %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<% }) %>
|
||||||
|
</div>
|
|
@ -63,15 +63,15 @@
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<h2 style="margin: 0"><%= profile.name %>'s Wall</h2>
|
<h2 style="margin: 0"><%= profile.name %>'s Wall</h2>
|
||||||
<% if (posts.length == 0) { %>
|
<div style="padding-top: 15px;">
|
||||||
<div style="padding-top: 15px">
|
<% if (posts.length == 0) { %>
|
||||||
<div class="post">
|
<div class="post">
|
||||||
No posts yet
|
No posts yet
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<% } else { %>
|
||||||
<% } else { %>
|
<%- include('_posts', { posts }) %>
|
||||||
<%- include('_posts', { posts }) %>
|
<% } %>
|
||||||
<% } %>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="friends-communities">
|
<div class="friends-communities">
|
||||||
|
|
Loading…
Reference in New Issue