diff --git a/src/misskey.ts b/src/misskey.ts index 1486702..de25757 100644 --- a/src/misskey.ts +++ b/src/misskey.ts @@ -23,6 +23,15 @@ export function usersShow(host: string, userId: string): Promise { return api(host, 'users/show', { userId }); } +export function usersShowByName(host: string, username: string, userHost?: string): Promise { + const opts: Record = {}; + opts.username = username; + if (userHost) { + opts.host = userHost; + } + return api(host, 'users/show', opts); +} + export function notesShow(host: string, noteId: string): Promise { return api(host, 'notes/show', { noteId }); } \ No newline at end of file diff --git a/src/router.ts b/src/router.ts index 95a60c6..e3c99be 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, notesShow } from './misskey'; +import { signIn, api, i, notesShow, usersShowByName } from './misskey'; import { Note } from './models/Note'; import { die } from './die'; @@ -21,11 +21,11 @@ for (const [ name, title ] of staticRouting) { async function timeline(ctx: Context, host: string, endpoint: string, timelineName: string, token: string) { const user = await i(host, token); - const timeline = await api(host, endpoint, { i: token }); + const notes = await api(host, endpoint, { i: token }); await ctx.render('timeline', { title: timelineName + ' - Simpkey', - user, timeline, timelineName + user, notes, timelineName }); } @@ -102,7 +102,7 @@ router.get('/notifications', async ctx => { }); -router.get('/renote', async ctx => { +router.get('/renote/:noteId', async ctx => { const token = ctx.cookies.get('i'); const host = ctx.cookies.get('host'); if (!token || !host) { @@ -110,20 +110,15 @@ router.get('/renote', async ctx => { return; } - if (!ctx.query.noteId) { - await die(ctx, 'noteId required'); - return; - } - try { - const note = await notesShow(host, ctx.query.noteId); + const note = await notesShow(host, ctx.params.noteId); await ctx.render('renote', { note }); } catch(e) { await die(ctx, e.message); } }); -router.get('/reply', async ctx => { +router.get('/reply/:noteId', async ctx => { const token = ctx.cookies.get('i'); const host = ctx.cookies.get('host'); if (!token || !host) { @@ -131,20 +126,15 @@ router.get('/reply', async ctx => { return; } - if (!ctx.query.noteId) { - await die(ctx, 'noteId required'); - return; - } - try { - const note = await notesShow(host, ctx.query.noteId); + const note = await notesShow(host, ctx.params.noteId); await ctx.render('reply', { note }); } catch(e) { await die(ctx, e.message); } }); -router.get('/react', async ctx => { +router.get('/react/:noteId', async ctx => { const token = ctx.cookies.get('i'); const host = ctx.cookies.get('host'); if (!token || !host) { @@ -152,13 +142,8 @@ router.get('/react', async ctx => { return; } - if (!ctx.query.noteId) { - await die(ctx, 'noteId required'); - return; - } - try { - const note = await notesShow(host, ctx.query.noteId); + const note = await notesShow(host, ctx.params.noteId); const myself = await i(host, token); await ctx.render('react', { note, reactions: myself.clientData?.reactions }); } catch(e) { @@ -166,6 +151,23 @@ router.get('/react', async ctx => { } }); +router.get('/@:acct', async ctx => { + const i = ctx.cookies.get('i'); + const host = ctx.cookies.get('host'); + if (!i || !host) { + await die(ctx, 'ログインしてください'); + return; + } + + const acct = ctx.params.acct.split('@'); + const username = acct[0]; + const remoteHost = acct[1]; + + const user = await usersShowByName(host, username, remoteHost); + const notes = await api(host, 'users/notes', { i, userId: user.id }); + await ctx.render('user', { user, notes }); +}); + router.post('/', async ctx => { const { host, diff --git a/src/views/_components.pug b/src/views/_components.pug index b728842..2bf6efa 100644 --- a/src/views/_components.pug +++ b/src/views/_components.pug @@ -4,14 +4,24 @@ mixin avatar(user) mixin note-header(note) header&attributes(attributes) - span.name= getUserName(note.user) - span.acct(style="color: gray")= getAcct(note.user) + a(href="/" + getAcct(note.user)) + span.name= getUserName(note.user) + span.acct(style="color: gray")= getAcct(note.user) mixin sub-note(note) .sub-note +avatar(note.user) +note-header(note) - p= note.text + p + if note.cw + details + summary !{note.cw} (!{note.text.length}文字) + != note.text + else + != note.text + aside + span.visibility= getVisibility(note) + a.date(href="/notes/" + note.id)= new Date(note.createdAt).toLocaleString() mixin note(note) if note.reply @@ -37,19 +47,19 @@ mixin note(note) +sub-note(note.renote) aside span.visibility= getVisibility(note) - span.date= new Date(note.createdAt).toLocaleString() + a.date(href="/notes/" + note.id)= new Date(note.createdAt).toLocaleString() .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="/reply/" + note.id) リプライ !{note.repliesCount} |] [ - a(href="/renote?noteId=" + note.id) リノート + a(href="/renote/" + note.id) リノート !{note.renoteCount} |] if !note.myReaction | [ - a(href="/react?noteId=" + note.id) リアクション + a(href="/react/" + note.id) リアクション | ] else form(action="action/unreact", method="post" style="display: inline") @@ -76,4 +86,37 @@ mixin nav() a(href="/notifications") 通知 |] [ a(href="/settings") 設定 - |] \ No newline at end of file + |] + +mixin user-header(user, detail = false) + +avatar(user) + .name: b=getUserName(user) + |   + span(style="color: gray")= getAcct(user) + if detail + if user.description + .description= user.description + if user.birthday + .birthday 🎂 !{user.birthday} + if user.location + .birthday 📍!{user.location} + if user.sex + .sex 性別: !{user.sex} + if user.sex + .sex 性別: !{user.sex} + dl + each field in user.fields + dt= field.name + dd= field.value + + .count + a.notes(href="/" + getAcct(user)) !{user.notesCount} ノート + | + a.following(href="/" + getAcct(user) + "/following") !{user.followingCount} フォロー + | + a.followers(href="/" + getAcct(user) + "/followers") !{user.followersCount} フォロワー + +mixin timeline(notes) + each note in notes + hr + +note(note) \ No newline at end of file diff --git a/src/views/react.pug b/src/views/react.pug index b5dc3cf..4a2ca30 100644 --- a/src/views/react.pug +++ b/src/views/react.pug @@ -3,7 +3,7 @@ extends _base block content h2 このノートにリアクションを押しますか? +sub-note(note) - form(action="action/react", method="post") + form(action="/action/react", method="post") each val in reactions div: label input(type="radio", name="reaction" value=val) diff --git a/src/views/renote.pug b/src/views/renote.pug index 31db883..1913e18 100644 --- a/src/views/renote.pug +++ b/src/views/renote.pug @@ -3,5 +3,5 @@ extends _base block content h2 このノートをリノートしますか? +sub-note(note) - +post-form('action/create-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 index 8a692c8..a6e90d0 100644 --- a/src/views/reply.pug +++ b/src/views/reply.pug @@ -3,5 +3,5 @@ extends _base block content h2 このノートに返信しますか? +sub-note(note) - +post-form('action/create-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 d35577f..a1fd426 100644 --- a/src/views/timeline.pug +++ b/src/views/timeline.pug @@ -4,12 +4,8 @@ block header +nav block content - p: b=getUserName(user) - |   - span(style="color: gray")= getAcct(user) + +user-header(user) +post-form("/action/create-note", "今なにしてる?", "ノート") hr h2= timelineName - each note in timeline - hr - +note(note) \ No newline at end of file + +timeline(notes) \ No newline at end of file diff --git a/src/views/user.pug b/src/views/user.pug new file mode 100644 index 0000000..0962980 --- /dev/null +++ b/src/views/user.pug @@ -0,0 +1,6 @@ +extends _base + +block content + +user-header(user, true) + hr + +timeline(notes) \ No newline at end of file