Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion design/notifications.handlebars
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,4 @@
{{/if}}{{/each}}

app.pollForPosts();
</script>
</script>
2 changes: 2 additions & 0 deletions design/partials/note.handlebars
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
</div>
{{/each}}
<footer>
{{#if note}}
<div class="tools">
<button onclick='return app.replyTo("{{note.id}}","{{{getUsername actor.id}}}")'>↩️</button>
<button class="booster {{#if note.isBoosted}}active{{/if}}" onclick='return app.toggleBoost(this, "{{note.id}}")'>
Expand All @@ -36,6 +37,7 @@
<span class="inactive">☆</span>
</button>
</div>
{{/if}}
<a href="{{note.url}}" class="permalink">{{timesince note.published}}</a>
</footer>
</div>
34 changes: 34 additions & 0 deletions lib/UserEvent.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { once, EventEmitter } from 'events';
import debug from 'debug';

const logger = debug('ono:event');

/**
* UserEvent - a class for handling real-time event updates
*/
export class UserEventClient {

constructor() {
this.emitter = new EventEmitter();
this.ac = new AbortController();
}

sendEvent(e, data) {
logger('SENDING EVENT ' + data);
this.emitter.emit('poll', data);
}

abort() {
this.ac.abort();
this.ac = new AbortController();
}

async waitForEvent() {
const [data] = await once(this.emitter, 'poll', { signal: this.ac.signal });
logger('RECEIVED EVENT ' + data);
}

}

export const UserEvent = new UserEventClient();

7 changes: 7 additions & 0 deletions lib/account.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ import {
getActivity,
createActivity
} from './notes.js';
import {
UserEvent
} from '../lib/UserEvent.js';

import debug from 'debug';
import MarkdownIt from 'markdown-it';
Expand Down Expand Up @@ -116,6 +119,9 @@ export const acceptDM = (dm, inboxUser) => {

writeInboxIndex(inboxIndex);

if (dm.attributedTo !== ActivityPub.actor.id) {
UserEvent.sendEvent('dm');
}
}

export const isMyPost = (activity) => {
Expand Down Expand Up @@ -228,6 +234,7 @@ export const addNotification = (notification) => {
notification: notification,
});
writeNotifications(notifications);
UserEvent.sendEvent('notification');
}

const writeNotifications = (notifications) => {
Expand Down
9 changes: 6 additions & 3 deletions public/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ const getCookie = (name) => {
}

const app = {
firstPoll: true,
newPosts: 0,
newNotifications: 0,
latestPost: (date) => {
Expand Down Expand Up @@ -102,12 +103,14 @@ const app = {

},
pollForPosts: () => {
fetch('/private/poll','get').then((json) => {
fetch('/private/poll' + (app.firstPoll ? '?nowait=1' : ''),'get').then((json) => {
app.firstPoll = false;
const res = JSON.parse(json);
app.alertNewPosts(res);
setTimeout(() => app.pollForPosts(), 30000); // poll every 5 seconds
setTimeout(() => app.pollForPosts(), 1000); // poll every 1 seconds, endpoint will stall until event occurs
}).catch((err) => {
console.error(err);
setTimeout(() => app.pollForPosts(), 1000); // poll every 1 seconds, endpoint will stall until event occurs
});
},
toggleBoost: (el, postId) => {
Expand Down Expand Up @@ -265,4 +268,4 @@ const app = {
}
return false;
}
}
}
21 changes: 17 additions & 4 deletions routes/admin.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,23 +31,36 @@ import {
import {
ActivityPub
} from '../lib/ActivityPub.js';
import {
UserEvent
} from '../lib/UserEvent.js';
const logger = debug('ono:admin');

router.get('/index', async (req, res) => {
res.json(INDEX);
});

router.get('/poll', async (req, res) => {

if (!req.query.nowait) {
req.on('close', function (err){
UserEvent.abort();
return;
});
try {
await UserEvent.waitForEvent();
} catch(e) {
// we got aborted
}
}
const sincePosts = new Date(req.cookies.latestPost).getTime();
const sinceNotifications = parseInt(req.cookies.latestNotification);
const sinceNotifications = parseInt(req.cookies.latestNotification);//.filter((n) => {n.});
// notification mechanism used to indicate there are unread posts, but they shouldn't appear in notifications tab
const notifications = getNotifications().filter((n) => n.time > sinceNotifications);
const inboxIndex = getInboxIndex();
const unreadDM = Object.keys(inboxIndex).filter((k) => {
return !inboxIndex[k].lastRead || inboxIndex[k].lastRead < inboxIndex[k].latest;
})?.length || 0;


const {
activitystream
} = await getActivitySince(sincePosts, true);
Expand Down Expand Up @@ -545,4 +558,4 @@ router.post('/boost', async (req, res) => {
});
}
writeBoosts(boosts);
});
});
8 changes: 5 additions & 3 deletions routes/inbox.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,11 @@ import debug from 'debug';
import {
isIndexed
} from '../lib/storage.js';
import {
UserEvent
} from '../lib/UserEvent.js';
const logger = debug('ono:inbox');



router.post('/', async (req, res) => {

const incomingRequest = req.body;
Expand Down Expand Up @@ -154,6 +155,7 @@ router.post('/', async (req, res) => {
} else if (!incomingRequest.object.inReplyTo) {
// this is a NEW post - most likely from a follower
await createActivity(incomingRequest.object);
UserEvent.sendEvent('msg');
} else {
// this is a reply
// from a following
Expand All @@ -180,4 +182,4 @@ router.post('/', async (req, res) => {
logger('Unknown request format:', incomingRequest);
}
return res.status(200).send();
});
});