From a18b44e7d61151e14b1ba1f93330d9cc7fd7bdd0 Mon Sep 17 00:00:00 2001 From: Xeltica Date: Fri, 24 Jul 2020 03:53:44 +0900 Subject: [PATCH] =?UTF-8?q?RN,=20Reply,=20React=E3=81=AA=E3=81=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/models/User.ts | 1 + src/router.ts | 104 +++++++++++++++++++++++++++++++++++++---- src/views/_base.pug | 23 ++++++++- src/views/react.pug | 16 +++++++ src/views/renote.pug | 7 +++ src/views/reply.pug | 7 +++ src/views/timeline.pug | 16 ++++--- 7 files changed, 157 insertions(+), 17 deletions(-) create mode 100644 src/views/react.pug create mode 100644 src/views/renote.pug create mode 100644 src/views/reply.pug diff --git a/src/models/User.ts b/src/models/User.ts index bcbf362..792f4b6 100644 --- a/src/models/User.ts +++ b/src/models/User.ts @@ -9,6 +9,7 @@ export interface User { carefulBot: boolean; createdAt: string; description: string | null; + clientData?: { reactions: string[] }; fields: { name: string, value: string }[]; followersCount: number; followingCount: number; diff --git a/src/router.ts b/src/router.ts index c24385b..14a24dc 100644 --- a/src/router.ts +++ b/src/router.ts @@ -1,6 +1,6 @@ import { Context, DefaultState } from 'koa'; import Router from 'koa-router'; -import { signIn, api, i } from './misskey'; +import { signIn, api, i, notesShow } from './misskey'; import { Note } from './models/Note'; import { die } from './die'; @@ -72,6 +72,7 @@ router.get('/stl', async ctx => { await timeline(ctx, host, 'notes/hybrid-timeline', 'ソーシャルタイムライン', token); } }); + router.get('/gtl', async ctx => { const token = ctx.cookies.get('i'); const host = ctx.cookies.get('host'); @@ -88,6 +89,70 @@ router.get('/gtl', async ctx => { } }); +router.get('/renote', async ctx => { + const token = ctx.cookies.get('i'); + const host = ctx.cookies.get('host'); + if (!token || !host) { + await die(ctx, 'ログインしてください'); + return; + } + + if (!ctx.query.noteId) { + await die(ctx, 'noteId required'); + return; + } + + try { + const note = await notesShow(host, ctx.query.noteId); + await ctx.render('renote', { note }); + } catch(e) { + await die(ctx, e.message); + } +}); + +router.get('/reply', async ctx => { + const token = ctx.cookies.get('i'); + const host = ctx.cookies.get('host'); + if (!token || !host) { + await die(ctx, 'ログインしてください'); + return; + } + + if (!ctx.query.noteId) { + await die(ctx, 'noteId required'); + return; + } + + try { + const note = await notesShow(host, ctx.query.noteId); + await ctx.render('reply', { note }); + } catch(e) { + await die(ctx, e.message); + } +}); + +router.get('/react', async ctx => { + const token = ctx.cookies.get('i'); + const host = ctx.cookies.get('host'); + if (!token || !host) { + await die(ctx, 'ログインしてください'); + return; + } + + if (!ctx.query.noteId) { + await die(ctx, 'noteId required'); + return; + } + + try { + const note = await notesShow(host, ctx.query.noteId); + const myself = await i(host, token); + await ctx.render('react', { note, reactions: myself.clientData?.reactions }); + } catch(e) { + await die(ctx, e.message); + } +}); + router.post('/', async ctx => { const { host, @@ -121,15 +186,36 @@ router.post('/action/:action', async ctx => { } const action = ctx.params.action as string; - switch (action) { - case 'create-note': { - const { text } = ctx.request.body; - if (!text) await die(ctx, 'テキストがありません'); - await api(host, 'notes/create', { text, i }); - break; + try { + switch (action) { + case 'create-note': { + const { text, renoteId, replyId } = ctx.request.body; + const opts = { i } as Record; + if (text) opts.text = text; + if (renoteId) opts.renoteId = renoteId; + if (replyId) opts.replyId = replyId; + await api(host, 'notes/create', opts); + break; + } + case 'react': { + const { noteId, reaction, customReaction } = ctx.request.body; + if (!noteId) throw new Error('noteId required'); + if (!reaction) throw new Error('絵文字が指定されていません'); + await api(host, 'notes/reactions/create', { i, noteId, reaction: reaction === 'custom' ? customReaction : reaction }); + break; + } + case 'unreact': { + const { noteId } = ctx.request.body; + if (!noteId) throw new Error('noteId required'); + await api(host, 'notes/reactions/delete', { i, noteId }); + break; + } + } + } catch (e) { + await die(ctx, e.message); + return; } - } - ctx.redirect('back', '/'); + ctx.redirect('/'); }); router.post('/logout', ctx => { diff --git a/src/views/_base.pug b/src/views/_base.pug index c231019..5c1a1eb 100644 --- a/src/views/_base.pug +++ b/src/views/_base.pug @@ -2,7 +2,7 @@ mixin avatar(user) img.avatar(src=user.avatarUrl, alt="avatar for " + user.username style="width: 64px; height: 64px; border-radius: 50%")&attributes(attributes) mixin note-header(note) - div.header&attributes(attributes) + header&attributes(attributes) span.name= getUserName(note.user) span.acct(style="color: gray")= getAcct(note.user) @@ -32,6 +32,27 @@ mixin note(note) .reactions each val, key in note.reactions span(class=(key === note.myReaction ? 'my reaction' : 'reaction'))=`${key} ${val}` + footer + |[ + a(href="/reply?noteId=" + note.id) リプライ + |] [ + a(href="/renote?noteId=" + note.id) リノート + |] + if !note.myReaction + | [ + a(href="/react?noteId=" + note.id) リアクション + | ] + else + form(action="action/unreact", method="post" style="display: inline") + input(type="hidden", name="noteId", value=note.id) + button(type="submit") リアクション解除 + +mixin post-form(url, placeholder, buttonText) + form(action=url, method="post") + textarea(name="text", placeholder=placeholder style="max-width: 100%; min-width: 100%; height: 6em; margin-bottom: 8px") + button(type="submit")= buttonText + block + html diff --git a/src/views/react.pug b/src/views/react.pug new file mode 100644 index 0000000..b06ab5d --- /dev/null +++ b/src/views/react.pug @@ -0,0 +1,16 @@ +extends _base + +block content + h2 このノートにリアクションを押しますか? + +note(note) + form(action="action/react", method="post") + each val in reactions + div: label + input(type="radio", name="reaction" value=val) + != val + div: label + input(type="radio", name="reaction", value="custom") + input(type="text", name="customReaction") + input(type="hidden", name="noteId", value=note.id) + + button(type="submit") リアクションを押す \ No newline at end of file diff --git a/src/views/renote.pug b/src/views/renote.pug new file mode 100644 index 0000000..b81f77a --- /dev/null +++ b/src/views/renote.pug @@ -0,0 +1,7 @@ +extends _base + +block content + h2 このノートをリノートしますか? + +note(note) + +post-form('action/create-note', "コメント(省略可能)", "リノート") + input(type="hidden", name="renoteId", value=note.id) \ No newline at end of file diff --git a/src/views/reply.pug b/src/views/reply.pug new file mode 100644 index 0000000..52557fe --- /dev/null +++ b/src/views/reply.pug @@ -0,0 +1,7 @@ +extends _base + +block content + h2 このノートに返信しますか? + +note(note) + +post-form('action/create-note', "何と返そうか?", "返信") + input(type="hidden", name="replyId", value=note.id) \ No newline at end of file diff --git a/src/views/timeline.pug b/src/views/timeline.pug index 3c44a34..b7ec129 100644 --- a/src/views/timeline.pug +++ b/src/views/timeline.pug @@ -5,20 +5,22 @@ block content p: b=getUserName(user) |   span(style="color: gray")= getAcct(user) - form(action="/action/create-note", method="post") - textarea(name="text", placeholder="今何してる?" style="max-width: 100%; min-width: 100%; height: 6em; margin-bottom: 8px") - button(type="submit") ノート + +post-form("/action/create-note", "今なにしてる?", "ノート") hr div + |[ a(href="/") ホーム - | ・ + |] [ a(href="/ltl") ローカル - | ・ + |] [ a(href="/stl") ソーシャル - | ・ + |] [ a(href="/gtl") グローバル - | ・ + |] [ + a(href="/notifications") 通知 + |] [ a(href="/settings") 設定 + |] h2= timelineName each note in timeline +note(note)