RN, Reply, Reactなど

This commit is contained in:
Xeltica 2020-07-24 03:53:44 +09:00
parent 1676816373
commit a18b44e7d6
7 changed files with 157 additions and 17 deletions

View File

@ -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;

View File

@ -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<string, string>;
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 => {

View File

@ -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

16
src/views/react.pug Normal file
View File

@ -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") リアクションを押す

7
src/views/renote.pug Normal file
View File

@ -0,0 +1,7 @@
extends _base
block content
h2 このノートをリノートしますか?
+note(note)
+post-form('action/create-note', "コメント(省略可能)", "リノート")
input(type="hidden", name="renoteId", value=note.id)

7
src/views/reply.pug Normal file
View File

@ -0,0 +1,7 @@
extends _base
block content
h2 このノートに返信しますか?
+note(note)
+post-form('action/create-note', "何と返そうか?", "返信")
input(type="hidden", name="replyId", value=note.id)

View File

@ -5,20 +5,22 @@ block content
p: b=getUserName(user)
| &nbsp;
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)