diff --git a/autoload/traqvim/command.vim b/autoload/traqvim/command.vim index 655495a..c774b97 100644 --- a/autoload/traqvim/command.vim +++ b/autoload/traqvim/command.vim @@ -20,6 +20,11 @@ let g:traqvim#command#subcommands = #{ \ impl: function('traqvim#command#message'), \ comp: function('traqvim#command#messageComplete'), \ }, + \ stamp: #{ + \ args: ['add', 'remove'], + \ impl: function('traqvim#command#stamp'), + \ comp: function('traqvim#command#stampComplete'), + \ }, \ pin: #{ \ args: ['create', 'remove'], \ impl: function('traqvim#command#pin'), @@ -114,6 +119,39 @@ function traqvim#command#messageComplete(arglead, cmdline) abort return g:traqvim#command#subcommands.message.args->copy()->filter({_, v -> v =~? '^' . a:arglead}) endfunction +function traqvim#command#stamp(args) abort + if a:args[0] ==# 'add' + call denops#request('traqvim', 'messageAddStamps', [bufnr(), traqvim#message#get_message(), a:args[1:]]) + elseif a:args[0] ==# 'remove' + call denops#request('traqvim', 'messageRemoveStamps', [bufnr(), traqvim#message#get_message(), a:args[1:]]) + endif +endfunction + +function traqvim#command#stampComplete(arglead, cmdline) abort + let cmds = split(a:cmdline) + " addの場合はスタンプ名を補完 + if len(cmds) >= 3 && cmds[2] ==# 'add' + let stamps = denops#request('traqvim', 'getStamps', []) + return stamps->map({_, v -> v['name']})->matchfuzzy(a:arglead) + endif + " removeの場合はスタンプに押されてるスタンプを補完 + if len(cmds) >= 3 && cmds[2] ==# 'remove' + let message = traqvim#message#get_message() + let stamps = message->get('stamps', []) + let ret = [] + " TODO: userIDを取得して自分が押したスタンプだけを補完する + for stamp in stamps + let s = denops#request('traqvim', 'getStamp', [stamp['stampId']]) + let ret += [s['name']] + endfor + return ret->matchfuzzy(a:arglead) + endif + if a:cmdline[strlen(a:cmdline)-1] ==# ' ' && len(split(a:cmdline)) >= 3 + return [] + endif + return g:traqvim#command#subcommands.stamp.args->copy()->filter({_, v -> v =~? '^' . a:arglead}) +endfunction + function traqvim#command#pin(args) abort if a:args[0] ==# 'create' call denops#request('traqvim', 'createPin', [bufnr(), traqvim#message#get_message()]) diff --git a/autoload/traqvim/view.vim b/autoload/traqvim/view.vim index b9299ae..e92cf01 100644 --- a/autoload/traqvim/view.vim +++ b/autoload/traqvim/view.vim @@ -190,6 +190,7 @@ endfunction function traqvim#view#draw_delete_message(bufNum, message) abort call setbufvar(a:bufNum, "&modifiable", 1) + " TODO: このstart, endがスタンプの行をどのように考慮してるか確認しとく let start = a:message.position["start"] let end = a:message.position["end"] if a:message->get('pinned') diff --git a/denops/traqvim/action.ts b/denops/traqvim/action.ts index f642b9c..bda1f2e 100644 --- a/denops/traqvim/action.ts +++ b/denops/traqvim/action.ts @@ -2,12 +2,15 @@ import { assert, bufname, Denops, fn, helper, is, vars } from "./deps.ts"; import { ChannelBuffer, Message } from "./type.d.ts"; import { activity, + addMessageStamp, channelMessageOptions, channelPath, channelTimeline, createPin, deleteMessage, editMessage, + getMessageStamps, + removeMessageStamp, removePin, } from "./model.ts"; import { isMessage } from "./type_check.ts"; @@ -272,3 +275,97 @@ export const actionRemovePin = async ( ); await denops.call("traqvim#view#draw_message_pin", bufNum, message); }; + +export const actionAddMessageStamp = async ( + denops: Denops, + message: Message, + stamp: string, + bufNum: number, +): Promise => { + try { + await addMessageStamp(message.id, stamp); + } catch (e) { + console.error(e); + return; + } + // 既存メッセージの取得 + const timeline = await vars.buffers.get(denops, "channelTimeline"); + assert(timeline, is.ArrayOf(isMessage)); + // 一旦対象メッセージを削除する + await vars.buffers.set( + denops, + "channelTimeline", + timeline.filter((m) => m.id !== message.id), + ); + await denops.call("traqvim#view#draw_delete_message", bufNum, message); + // 更新されたメッセージにあるスタンプを取得 + const updatedMessage = await getMessageStamps(message.id); + const editedTimeline = timeline.map((m) => { + if (m.id === message.id) { + return { + ...m, + stamps: updatedMessage, + }; + } else { + return m; + } + }); + // 編集したものをセット + await vars.buffers.set( + denops, + "channelTimeline", + editedTimeline, + ); + await denops.call( + "traqvim#view#draw_append_message", + bufNum, + editedTimeline.find((m) => m.id === message.id), + ); +}; + +export const actionRemoveMessageStamp = async ( + denops: Denops, + message: Message, + stamp: string, + bufNum: number, +): Promise => { + try { + await removeMessageStamp(message.id, stamp); + } catch (e) { + console.error(e); + return; + } + // 既存メッセージの取得 + const timeline = await vars.buffers.get(denops, "channelTimeline"); + assert(timeline, is.ArrayOf(isMessage)); + // 一旦対象メッセージを削除する + await vars.buffers.set( + denops, + "channelTimeline", + timeline.filter((m) => m.id !== message.id), + ); + await denops.call("traqvim#view#draw_delete_message", bufNum, message); + // 更新されたメッセージにあるスタンプを取得 + const updatedMessage = await getMessageStamps(message.id); + const editedTimeline = timeline.map((m) => { + if (m.id === message.id) { + return { + ...m, + stamps: updatedMessage, + }; + } else { + return m; + } + }); + // 編集したものをセット + await vars.buffers.set( + denops, + "channelTimeline", + editedTimeline, + ); + await denops.call( + "traqvim#view#draw_append_message", + bufNum, + editedTimeline.find((m) => m.id === message.id), + ); +}; diff --git a/denops/traqvim/main.ts b/denops/traqvim/main.ts index 73a3100..584a254 100644 --- a/denops/traqvim/main.ts +++ b/denops/traqvim/main.ts @@ -6,6 +6,8 @@ import { channelUUID, downloadFile, getStamp, + getStampId, + getStamps, getUser, homeChannelId, homeChannelPath, @@ -23,6 +25,7 @@ import { vars, } from "./deps.ts"; import { + actionAddMessageStamp, actionBackChannelMessage, actionCreatePin, actionDeleteMessage, @@ -30,6 +33,7 @@ import { actionForwardChannelMessage, actionOpenActivity, actionOpenChannel, + actionRemoveMessageStamp, actionRemovePin, actionYankMessageLink, actionYankMessageMarkdown, @@ -403,6 +407,44 @@ export async function main(denops: Denops) { await denops.cmd(":bdelete"); return; }, + async messageAddStamps( + bufNum: unknown, + message: unknown, + stampNames: unknown, + ): Promise { + assert(bufNum, is.Number); + assert(message, isMessage); + assert(stampNames, is.ArrayOf(is.String)); + for (const stampName of stampNames) { + const stampId = await getStampId(stampName); + if (stampId === undefined) { + helper.echo(denops, `Stamp not found: ${stampName}`); + continue; + } + const stamp = await getStamp(stampId); + await actionAddMessageStamp(denops, message, stamp.id, bufNum); + } + return; + }, + async messageRemoveStamps( + bufNum: unknown, + message: unknown, + stampNames: unknown, + ): Promise { + assert(bufNum, is.Number); + assert(message, isMessage); + assert(stampNames, is.ArrayOf(is.String)); + for (const stampName of stampNames) { + const stampId = await getStampId(stampName); + if (stampId === undefined) { + helper.echo(denops, `Stamp not found: ${stampName}`); + continue; + } + const stamp = await getStamp(stampId); + await actionRemoveMessageStamp(denops, message, stamp.id, bufNum); + } + return; + }, async createPin( bufNum: unknown, message: unknown, @@ -437,6 +479,9 @@ export async function main(denops: Denops) { assert(userId, is.String); return getUser(userId); }; + denops.dispatcher["getStamps"] = (): Promise => { + return getStamps(); + }; denops.dispatcher["getStamp"] = (stampId: unknown): Promise => { assert(stampId, is.String); return getStamp(stampId); diff --git a/denops/traqvim/model.ts b/denops/traqvim/model.ts index 3462cae..6ce54fa 100644 --- a/denops/traqvim/model.ts +++ b/denops/traqvim/model.ts @@ -275,13 +275,6 @@ export const activity = async (): Promise => { return activitiesConverted; }; -// stamp情報の取得 -export const getStamps = async (): Promise => { - const stampsRes = await api.api.getStamps(); - const stamps = stampsRes.data; - return stamps; -}; - export const sendMessage = async ( channelUUID: string, content: string, @@ -358,6 +351,15 @@ export const removePin = async ( } }; +export const getStamps = async (): Promise => { + const stampsRes = await api.api.getStamps(); + const stamps = stampsRes.data; + stamps.forEach((stamp: traq.Stamp) => { + StampMapCache.set(stamp.id, stamp); + }); + return stamps; +}; + export const getStamp = async ( stampId: string, ): Promise => { @@ -386,3 +388,44 @@ export const downloadFile = async ( } return new Uint8Array(); }; + +export const getStampId = async ( + stampName: string, +): Promise => { + const stamps = await getStamps(); + const stamp = stamps.find((stamp: traq.Stamp) => { + return stamp.name === stampName; + }); + return stamp?.id; +}; + +export const getMessageStamps = async ( + messageId: string, +): Promise => { + const stmpasRes = await api.api.getMessageStamps(messageId); + return stmpasRes.data; +}; + +export const addMessageStamp = async ( + messageId: string, + stampId: string, +): Promise => { + try { + // TODO: postだから失敗するかも + await api.api.addMessageStamp(messageId, stampId); + } catch (e) { + console.error(e); + } +}; + +export const removeMessageStamp = async ( + messageId: string, + stampId: string, +): Promise => { + try { + // TODO: postだから失敗するかも + await api.api.removeMessageStamp(messageId, stampId); + } catch (e) { + console.error(e); + } +};