diff --git a/lib/account.js b/lib/account.js index 3a4c6f5..1e4b526 100644 --- a/lib/account.js +++ b/lib/account.js @@ -30,7 +30,8 @@ import { import { getActivity, createActivity, - deleteActivity + deleteActivity, + getHashtagsFromNote } from './notes.js'; import debug from 'debug'; @@ -484,8 +485,24 @@ export const createNote = async (body, cw, inReplyTo, toUser, editOf) => { } } + // if it contains hashtags, add them + let hashtags = body.match(/#[a-z]+/gi) + if (hashtags) { + hashtags.forEach((tag) => { + tags.push({ + "type": "Hashtag", + "href": `https://${ DOMAIN }/tags/${tag}`, + "name": tag + }); + }); + } + + const mdContent = md.render(processedContent); + + // rewrite links to tags in content + let content = mdContent.replace(/#([a-z]+)/gi, ''); - const content = md.render(processedContent); + content = '' + content + ''; const activityId = `https://${ DOMAIN }/m/${guid}`; const url = `https://${ DOMAIN }/notes/${guid}`; @@ -538,6 +555,7 @@ export const createNote = async (body, cw, inReplyTo, toUser, editOf) => { actor: object.attributedTo, published: new Date(object.published).getTime(), inReplyTo: object.inReplyTo, + hashtags: getHashtagsFromNote(object), }); } } diff --git a/lib/notes.js b/lib/notes.js index c422fe0..1a00261 100644 --- a/lib/notes.js +++ b/lib/notes.js @@ -287,3 +287,7 @@ export const getActivitySince = async (since, excludeSelf = false) => { activitystream: res, } } + +export const getHashtagsFromNote = (note) => { + return (note.tag && note.tag.length > 0) ? note.tag.filter((t) => t.type === 'Hashtag').map((t) => t.name) : []; +} diff --git a/lib/storage.js b/lib/storage.js index 60113ef..29d204b 100644 --- a/lib/storage.js +++ b/lib/storage.js @@ -5,6 +5,7 @@ import debug from 'debug'; const logger = debug('ono:storage'); import md5 from 'md5'; import dotenv from 'dotenv'; +import { getHashtagsFromNote } from './notes.js'; dotenv.config(); export const dataDir = path.resolve('./', '.data/'); @@ -64,6 +65,7 @@ export const addActivityToIndex = (note, type = 'activity') => { actor: note.attributedTo || note.actor, published: new Date(note.published).getTime(), inReplyTo: note.inReplyTo, + hashtags: getHashtagsFromNote(note) }); } export const deleteActivityFromIndex = (id) => { diff --git a/routes/public.js b/routes/public.js index 24e8922..32689a0 100644 --- a/routes/public.js +++ b/routes/public.js @@ -195,4 +195,23 @@ router.get('/notes/:guid', async (req, res) => { }); } } -}); \ No newline at end of file +}); + +router.get('/tags/:tag', async (req, res) => { + // all posts referencing tag by the owner user + const noteIds = INDEX.filter((i) => (i.actor === ActivityPub.actor.id) && i.hashtags.includes('#' + req.params.tag)); + // get full posts + const posts = await Promise.all(noteIds.map(async (p) => { + return await getNote(p.id); + })); + + res.render('public/tag', { + tag: req.params.tag, + layout: 'public', + me: ActivityPub.actor, + actor: ActivityPub.actor, + domain: DOMAIN, + user: USERNAME, + notes: posts + }); +});