Add private vanishing messages

This commit is contained in:
Rogerio Chaves 2020-04-08 08:44:23 +02:00
parent 929a46d8cb
commit d207b512a3
No known key found for this signature in database
GPG Key ID: E6AF5440509B1D94
11 changed files with 281 additions and 44 deletions

View File

@ -66,11 +66,17 @@ router.get("/", async (_req, res) => {
res.redirect("/about");
}
const [posts, friends] = await Promise.all([
const [posts, friends, vanishingMessages] = await Promise.all([
queries.getPosts(ssbServer, context.profile),
queries.getFriends(ssbServer, context.profile),
queries.getVanishingMessages(ssbServer, context.profile),
]);
res.render("index", { posts, friends });
res.render("index", {
posts,
friends,
vanishingMessages,
profile: context.profile,
});
});
router.get("/profile/:id", async (req, res) => {
@ -101,12 +107,25 @@ router.post("/publish", async (req, res) => {
router.post("/profile/:id/publish", async (req, res) => {
const id = req.params.id;
const visibility = req.body.visibility;
console.log("req.body", req.body);
await ssbServer.publish({
type: "post",
text: req.body.message,
root: id,
});
if (visibility == "vanishing") {
await ssbServer.private.publish(
{
type: "post",
text: req.body.message,
root: id,
},
[id]
);
} else {
await ssbServer.publish({
type: "post",
text: req.body.message,
root: id,
});
}
res.redirect("/profile/" + id);
});

View File

@ -76,6 +76,7 @@ const getPosts = (ssbServer, profile) =>
{
$filter: {
value: {
private: { $not: true },
content: {
root: profile.id,
},
@ -92,6 +93,7 @@ const getPosts = (ssbServer, profile) =>
$filter: {
value: {
author: profile.id,
private: { $not: true },
content: {
type: "post",
root: { $not: true },
@ -115,6 +117,64 @@ const getPosts = (ssbServer, profile) =>
);
});
const getVanishingMessages = (ssbServer, profile) =>
debug("Fetching vanishing messages") ||
new Promise((resolve, reject) => {
pull(
// @ts-ignore
cat([
ssbServer.query.read({
reverse: true,
query: [
{
$filter: {
value: {
private: true,
content: {
root: profile.id,
},
},
},
},
],
limit: 100,
}),
ssbServer.query.read({
reverse: true,
query: [
{
$filter: {
value: {
author: profile.id,
private: true,
content: {
type: "post",
root: { $not: true },
},
},
},
},
],
limit: 100,
}),
]),
pull.filter(
(msg) =>
msg.value.content.type == "post" &&
(msg.value.content.root ||
msg.value.content.recps.includes(profile.id))
),
paramap(mapProfiles(ssbServer)),
pull.collect((err, msgs) => {
debug("Done fetching vanishing messages");
const entries = msgs.map((x) => x.value);
if (err) return reject(err);
return resolve(entries);
})
);
});
const searchPeople = (ssbServer, search) =>
debug("Searching people") ||
new Promise((resolve, reject) => {
@ -242,4 +302,5 @@ module.exports = {
getFriends,
getAllEntries,
getProfile,
getVanishingMessages,
};

View File

@ -16,7 +16,8 @@ Server.use(require("ssb-master"))
.use(require("ssb-device-address"))
.use(require("ssb-identities"))
.use(require("ssb-peer-invites"))
.use(require("ssb-blobs"));
.use(require("ssb-blobs"))
.use(require("ssb-private"));
const server = Server(config);
console.log("SSB server started at", config.port);

12
app/package-lock.json generated
View File

@ -2711,6 +2711,18 @@
}
}
},
"ssb-private": {
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/ssb-private/-/ssb-private-0.2.3.tgz",
"integrity": "sha512-SiLBKOB1hxkrohzOrRWURlzj6HvPFvr9LLd5P5I5C5KU/RtaWe79nYuFgjUFJFnjWw7X4szCy32/EZMihV1l/Q==",
"requires": {
"base64-url": "^2.2.0",
"explain-error": "^1.0.4",
"flumeview-query": "^6.1.0",
"pull-stream": "^3.6.7",
"ssb-keys": "^7.0.14"
}
},
"ssb-query": {
"version": "2.4.3",
"resolved": "https://registry.npmjs.org/ssb-query/-/ssb-query-2.4.3.tgz",

View File

@ -30,6 +30,7 @@
"ssb-keys": "^7.2.2",
"ssb-master": "^1.0.3",
"ssb-peer-invites": "^2.0.2",
"ssb-private": "^0.2.3",
"ssb-query": "^2.4.3",
"ssb-replicate": "^1.3.2",
"ssb-server": "^15.2.0"

22
app/public/js/index.js Normal file
View File

@ -0,0 +1,22 @@
const messages = document.querySelectorAll(".vanishing-message");
messages.forEach((message) => {
message.addEventListener("click", () => {
const overlay = message.parentElement.querySelector(".overlay");
const modal = message.parentElement.querySelector(".modal");
const closeButton = message.parentElement.querySelector(".modal-close");
overlay.style.display = "block";
modal.style.display = "block";
const onClose = () => {
const parent = modal.parentElement;
parent.parentElement.removeChild(parent);
if (document.querySelectorAll(".vanishing-message").length == 0) {
document.querySelector(".vanishing-messages").style.display = "none";
}
};
overlay.addEventListener("click", onClose);
closeButton.addEventListener("click", onClose);
});
});

View File

@ -1,9 +1,25 @@
* {
box-sizing: border-box;
}
body {
font-family: sans-serif;
margin: 0;
line-height: 1.3em;
word-wrap: break-word;
}
button {
font-size: 14px;
background: #ddd;
color: #5f5f5f;
border-radius: 3px;
padding: 8px 10px;
border: none;
cursor: pointer;
}
button:hover {
background: #959eab;
color: #fff;
}
header {
padding: 0 30px;
border-bottom: 1px solid #666;
@ -70,7 +86,7 @@ h1 {
}
.wall {
padding: 20px;
padding: 0 20px;
flex-grow: 1;
}
@ -106,3 +122,57 @@ h1 {
.profile-pic {
width: 300px;
}
button.vanishing-message {
background: none;
width: 84px;
text-align: center;
border: 1px solid #333;
padding: 10px;
cursor: pointer;
color: #000;
}
.overlay {
display: none;
position: fixed;
z-index: 1;
background: rgba(0, 0, 0, 0.5);
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.modal {
display: none;
position: fixed;
z-index: 2;
background: #fff;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
min-width: 400px;
}
.modal-header {
display: flex;
align-items: center;
padding: 10px;
padding-bottom: 0;
font-weight: bold;
}
.modal-body {
padding: 10px;
}
.modal-footer {
padding: 10px;
background: #fffbf4;
border-top: 1px solid #ddd;
font-size: 12px;
display: flex;
align-items: center;
justify-content: space-between;
}

View File

@ -1,3 +1,4 @@
</main>
<script src="/js/index.js"></script>
</body>
</html>

13
app/views/_post.ejs Normal file
View File

@ -0,0 +1,13 @@
<div class="post">
<div>
<img src="<%= profileImageUrl(post.authorProfile) %>" class="post-profile-pic" />
</div>
<div class="post-content">
<div class="content-header">
<%= post.authorProfile.name %>
</div>
<div class="content-body">
<%= post.content.text.slice(0, 140) %>
</div>
</div>
</div>

View File

@ -1,27 +1,66 @@
<%- include('_header') %>
<h1>Home</h1>
<div class="columns">
<div class="about">
<img class="profile-pic" src="<%= profileImageUrl(profile) %>" />
<h1><%= profile.name %></h1>
<form action="/publish" method="POST">
<textarea name="message"></textarea>
<input type="submit" value="Send" />
</form>
<%= profile.description %>
<% posts.map(entry => { %>
<div><%= entry.authorProfile.name %> (<%= entry.author.slice(0, 8) %>): <%= entry.content.text %></div>
<hr />
<% }) %>
<h2>Friends</h2>
<h2>Friends</h2>
<ul>
<% friends.map(friend => { %>
<li>
<a href="<%= profileUrl(friend.content.contact) %>">
<%= friend.content.contactProfile?.name %> (<%= friend.content.contact.slice(0, 8) %>)
</a>
</li>
<% }) %>
</ul>
<ul>
<% friends.map(friend => { %>
<li>
<a href="<%= profileUrl(friend.content.contact) %>">
<%= friend.content.contactProfile && friend.content.contactProfile.name %> (<%= friend.content.contact.slice(0, 8) %>)
</a>
</li>
<% }) %>
</ul>
</div>
<div class="wall">
<% if (vanishingMessages.length > 0) { %>
<div class="vanishing-messages" style="padding-bottom: 20px">
<h2>Vanishing Messages</h2>
<% vanishingMessages.map(post => { %>
<span>
<button class="vanishing-message">
<div><img src="<%= profileImageUrl(post.authorProfile) %>" class="post-profile-pic" /></div>
<div><%= post.authorProfile.name %></div>
</button>
<div class="overlay"></div>
<div class="modal">
<div class="modal-header">
<img src="<%= profileImageUrl(post.authorProfile) %>" class="post-profile-pic" style="padding-right: 10px" />
<%= post.authorProfile.name %>
</div>
<div class="modal-body">
<%= post.content.text %>
</div>
<div class="modal-footer">
after you close this box the message will be gone forever
<button class="modal-close">Close</button>
</div>
</div>
</span>
<% }) %>
</div>
<% } %>
<h2>Your Wall</h2>
<form action="/publish" method="POST">
<textarea name="message"></textarea>
<input type="submit" value="Send" />
</form>
<% posts.map(post => { %>
<%- include('_post', { post }) %>
<% }) %>
</div>
</div>
<%- include('_footer') %>

View File

@ -22,27 +22,25 @@
<div class="wall">
<h1><%= profile.name %>'s Wall</h1>
<!--
<h2>Leave <%= profile.name %> a message</h2>
<form action="<%= profileUrl(profile.id, "/publish") %>" method="POST">
<div>
<label>
<input type="radio" name="visibility" value="public">
Public
</label>
<label>
<input type="radio" name="visibility" value="vanishing">
Vanishing
</label>
</div>
<textarea name="message"></textarea>
<input type="submit" value="Send" />
</form> -->
</form>
<% posts.map(entry => { %>
<div class="post">
<div>
<img src="<%= profileImageUrl(entry.authorProfile) %>" class="post-profile-pic" />
</div>
<div class="post-content">
<div class="content-header">
<%= entry.authorProfile.name %>
</div>
<div class="content-body">
<%= entry.content.text.slice(0, 140) %>
</div>
</div>
</div>
<% posts.map(post => { %>
<%- include('_post', { post }) %>
<% }) %>
</div>
</div>