mirror of
https://github.com/misskey-dev/misskey.git
synced 2024-12-29 03:28:29 +01:00
parent
c7106d250c
commit
a1b490afa7
167 changed files with 4440 additions and 1762 deletions
|
@ -33,7 +33,7 @@ common:
|
||||||
confused: "Confused"
|
confused: "Confused"
|
||||||
pudding: "Pudding"
|
pudding: "Pudding"
|
||||||
|
|
||||||
post_categories:
|
note_categories:
|
||||||
music: "Music"
|
music: "Music"
|
||||||
game: "Video Game"
|
game: "Video Game"
|
||||||
anime: "Anime"
|
anime: "Anime"
|
||||||
|
@ -124,7 +124,7 @@ common:
|
||||||
show-result: "Show result"
|
show-result: "Show result"
|
||||||
voted: "Voted"
|
voted: "Voted"
|
||||||
|
|
||||||
mk-post-menu:
|
mk-note-menu:
|
||||||
pin: "Pin"
|
pin: "Pin"
|
||||||
pinned: "Pinned"
|
pinned: "Pinned"
|
||||||
select: "Select category"
|
select: "Select category"
|
||||||
|
@ -211,7 +211,7 @@ ch:
|
||||||
textarea: "Write here"
|
textarea: "Write here"
|
||||||
upload: "Upload"
|
upload: "Upload"
|
||||||
drive: "Drive"
|
drive: "Drive"
|
||||||
post: "Do"
|
note: "Do"
|
||||||
posting: "Doing"
|
posting: "Doing"
|
||||||
|
|
||||||
desktop:
|
desktop:
|
||||||
|
@ -304,8 +304,8 @@ desktop:
|
||||||
settings: "Settings"
|
settings: "Settings"
|
||||||
signout: "Sign out"
|
signout: "Sign out"
|
||||||
|
|
||||||
mk-ui-header-post-button:
|
mk-ui-header-note-button:
|
||||||
post: "Compose new Post"
|
note: "Compose new Post"
|
||||||
|
|
||||||
mk-ui-header-notifications:
|
mk-ui-header-notifications:
|
||||||
title: "Notifications"
|
title: "Notifications"
|
||||||
|
@ -350,18 +350,18 @@ desktop:
|
||||||
no-users: "No muted users"
|
no-users: "No muted users"
|
||||||
|
|
||||||
mk-post-form:
|
mk-post-form:
|
||||||
post-placeholder: "What's happening?"
|
note-placeholder: "What's happening?"
|
||||||
reply-placeholder: "Reply to this post..."
|
reply-placeholder: "Reply to this note..."
|
||||||
quote-placeholder: "Quote this post..."
|
quote-placeholder: "Quote this note..."
|
||||||
post: "Post"
|
note: "Post"
|
||||||
reply: "Reply"
|
reply: "Reply"
|
||||||
repost: "Repost"
|
renote: "Renote"
|
||||||
posted: "Posted!"
|
posted: "Posted!"
|
||||||
replied: "Replied!"
|
replied: "Replied!"
|
||||||
reposted: "Reposted!"
|
reposted: "Reposted!"
|
||||||
post-failed: "Failed to post"
|
note-failed: "Failed to note"
|
||||||
reply-failed: "Failed to reply"
|
reply-failed: "Failed to reply"
|
||||||
repost-failed: "Failed to repost"
|
renote-failed: "Failed to renote"
|
||||||
posting: "Posting"
|
posting: "Posting"
|
||||||
attach-media-from-local: "Attach media from your pc"
|
attach-media-from-local: "Attach media from your pc"
|
||||||
attach-media-from-drive: "Attach media from the drive"
|
attach-media-from-drive: "Attach media from the drive"
|
||||||
|
@ -371,14 +371,14 @@ desktop:
|
||||||
text-remain: "{} chars remaining"
|
text-remain: "{} chars remaining"
|
||||||
|
|
||||||
mk-post-form-window:
|
mk-post-form-window:
|
||||||
post: "New post"
|
note: "New note"
|
||||||
reply: "Reply"
|
reply: "Reply"
|
||||||
attaches: "{} media attached"
|
attaches: "{} media attached"
|
||||||
uploading-media: "Uploading {} media"
|
uploading-media: "Uploading {} media"
|
||||||
|
|
||||||
mk-post-page:
|
mk-note-page:
|
||||||
prev: "Previous post"
|
prev: "Previous note"
|
||||||
next: "Next post"
|
next: "Next note"
|
||||||
|
|
||||||
mk-settings:
|
mk-settings:
|
||||||
profile: "Profile"
|
profile: "Profile"
|
||||||
|
@ -390,10 +390,10 @@ desktop:
|
||||||
other: "Other"
|
other: "Other"
|
||||||
license: "License"
|
license: "License"
|
||||||
|
|
||||||
mk-timeline-post:
|
mk-timeline-note:
|
||||||
reposted-by: "Reposted by {}"
|
reposted-by: "Reposted by {}"
|
||||||
reply: "Reply"
|
reply: "Reply"
|
||||||
repost: "Repost"
|
renote: "Renote"
|
||||||
add-reaction: "Add your reaction"
|
add-reaction: "Add your reaction"
|
||||||
detail: "Show detail"
|
detail: "Show detail"
|
||||||
|
|
||||||
|
@ -448,7 +448,7 @@ desktop:
|
||||||
|
|
||||||
mk-post-form-home-widget:
|
mk-post-form-home-widget:
|
||||||
title: "Post"
|
title: "Post"
|
||||||
post: "Post"
|
note: "Post"
|
||||||
placeholder: "What's happening?"
|
placeholder: "What's happening?"
|
||||||
|
|
||||||
mk-access-log-home-widget:
|
mk-access-log-home-widget:
|
||||||
|
@ -463,16 +463,16 @@ desktop:
|
||||||
have-a-nice-day: "Have a nice day!"
|
have-a-nice-day: "Have a nice day!"
|
||||||
next: "Next"
|
next: "Next"
|
||||||
|
|
||||||
mk-repost-form:
|
mk-renote-form:
|
||||||
quote: "Quote..."
|
quote: "Quote..."
|
||||||
cancel: "Cancel"
|
cancel: "Cancel"
|
||||||
repost: "Repost"
|
renote: "Renote"
|
||||||
reposting: "Reposting..."
|
reposting: "Reposting..."
|
||||||
success: "Reposted!"
|
success: "Reposted!"
|
||||||
failure: "Failed to Repost"
|
failure: "Failed to Renote"
|
||||||
|
|
||||||
mk-repost-form-window:
|
mk-renote-form-window:
|
||||||
title: "Are you sure you want to repost this post?"
|
title: "Are you sure you want to renote this note?"
|
||||||
|
|
||||||
mk-user:
|
mk-user:
|
||||||
last-used-at: "Last used at"
|
last-used-at: "Last used at"
|
||||||
|
@ -541,10 +541,10 @@ mobile:
|
||||||
notifications: "Notifications"
|
notifications: "Notifications"
|
||||||
read-all: "Are you sure you want to mark all unread notifications as read?"
|
read-all: "Are you sure you want to mark all unread notifications as read?"
|
||||||
|
|
||||||
mk-post-page:
|
mk-note-page:
|
||||||
title: "Post"
|
title: "Post"
|
||||||
prev: "Previous post"
|
prev: "Previous note"
|
||||||
next: "Next post"
|
next: "Next note"
|
||||||
|
|
||||||
mk-search-page:
|
mk-search-page:
|
||||||
search: "Search"
|
search: "Search"
|
||||||
|
@ -606,33 +606,33 @@ mobile:
|
||||||
unfollow: "Unfollow"
|
unfollow: "Unfollow"
|
||||||
|
|
||||||
mk-home-timeline:
|
mk-home-timeline:
|
||||||
empty-timeline: "There is no posts"
|
empty-timeline: "There is no notes"
|
||||||
|
|
||||||
mk-notifications:
|
mk-notifications:
|
||||||
more: "More"
|
more: "More"
|
||||||
empty: "No notifications"
|
empty: "No notifications"
|
||||||
|
|
||||||
mk-post-detail:
|
mk-note-detail:
|
||||||
reply: "Reply"
|
reply: "Reply"
|
||||||
reaction: "Reaction"
|
reaction: "Reaction"
|
||||||
|
|
||||||
mk-post-form:
|
mk-post-form:
|
||||||
submit: "Post"
|
submit: "Post"
|
||||||
reply-placeholder: "Reply to this post..."
|
reply-placeholder: "Reply to this note..."
|
||||||
post-placeholder: "What's happening?"
|
note-placeholder: "What's happening?"
|
||||||
|
|
||||||
mk-search-posts:
|
mk-search-notes:
|
||||||
empty: "There is no post related to the 「{}」"
|
empty: "There is no note related to the 「{}」"
|
||||||
|
|
||||||
mk-sub-post-content:
|
mk-sub-note-content:
|
||||||
media-count: "{} media"
|
media-count: "{} media"
|
||||||
poll: "Poll"
|
poll: "Poll"
|
||||||
|
|
||||||
mk-timeline-post:
|
mk-timeline-note:
|
||||||
reposted-by: "Reposted by {}"
|
reposted-by: "Reposted by {}"
|
||||||
|
|
||||||
mk-timeline:
|
mk-timeline:
|
||||||
empty: "No posts"
|
empty: "No notes"
|
||||||
load-more: "More"
|
load-more: "More"
|
||||||
|
|
||||||
mk-ui-nav:
|
mk-ui-nav:
|
||||||
|
@ -652,21 +652,21 @@ mobile:
|
||||||
no-users: "No following."
|
no-users: "No following."
|
||||||
|
|
||||||
mk-user-timeline:
|
mk-user-timeline:
|
||||||
no-posts: "This user seems never post"
|
no-notes: "This user seems never note"
|
||||||
no-posts-with-media: "There is no posts with media"
|
no-notes-with-media: "There is no notes with media"
|
||||||
load-more: "More"
|
load-more: "More"
|
||||||
|
|
||||||
mk-user:
|
mk-user:
|
||||||
follows-you: "Follows you"
|
follows-you: "Follows you"
|
||||||
following: "Following"
|
following: "Following"
|
||||||
followers: "Followers"
|
followers: "Followers"
|
||||||
posts: "Posts"
|
notes: "Posts"
|
||||||
overview: "Overview"
|
overview: "Overview"
|
||||||
timeline: "Timeline"
|
timeline: "Timeline"
|
||||||
media: "Media"
|
media: "Media"
|
||||||
|
|
||||||
mk-user-overview:
|
mk-user-overview:
|
||||||
recent-posts: "Recent posts"
|
recent-notes: "Recent notes"
|
||||||
images: "Images"
|
images: "Images"
|
||||||
activity: "Activity"
|
activity: "Activity"
|
||||||
keywords: "Keywords"
|
keywords: "Keywords"
|
||||||
|
@ -675,9 +675,9 @@ mobile:
|
||||||
followers-you-know: "Followers you know"
|
followers-you-know: "Followers you know"
|
||||||
last-used-at: "Last used at"
|
last-used-at: "Last used at"
|
||||||
|
|
||||||
mk-user-overview-posts:
|
mk-user-overview-notes:
|
||||||
loading: "Loading"
|
loading: "Loading"
|
||||||
no-posts: "No posts"
|
no-notes: "No notes"
|
||||||
|
|
||||||
mk-user-overview-photos:
|
mk-user-overview-photos:
|
||||||
loading: "Loading"
|
loading: "Loading"
|
||||||
|
@ -703,7 +703,7 @@ mobile:
|
||||||
load-more: "More"
|
load-more: "More"
|
||||||
|
|
||||||
stats:
|
stats:
|
||||||
posts-count: "Number of all posts"
|
notes-count: "Number of all notes"
|
||||||
users-count: "Number of all users"
|
users-count: "Number of all users"
|
||||||
|
|
||||||
status:
|
status:
|
||||||
|
|
|
@ -33,7 +33,7 @@ common:
|
||||||
confused: "こまこまのこまり"
|
confused: "こまこまのこまり"
|
||||||
pudding: "Pudding"
|
pudding: "Pudding"
|
||||||
|
|
||||||
post_categories:
|
note_categories:
|
||||||
music: "音楽"
|
music: "音楽"
|
||||||
game: "ゲーム"
|
game: "ゲーム"
|
||||||
anime: "アニメ"
|
anime: "アニメ"
|
||||||
|
@ -124,7 +124,7 @@ common:
|
||||||
show-result: "結果を見る"
|
show-result: "結果を見る"
|
||||||
voted: "投票済み"
|
voted: "投票済み"
|
||||||
|
|
||||||
mk-post-menu:
|
mk-note-menu:
|
||||||
pin: "ピン留め"
|
pin: "ピン留め"
|
||||||
pinned: "ピン留めしました"
|
pinned: "ピン留めしました"
|
||||||
select: "カテゴリを選択"
|
select: "カテゴリを選択"
|
||||||
|
@ -211,7 +211,7 @@ ch:
|
||||||
textarea: "書いて"
|
textarea: "書いて"
|
||||||
upload: "アップロード"
|
upload: "アップロード"
|
||||||
drive: "ドライブ"
|
drive: "ドライブ"
|
||||||
post: "やる"
|
note: "やる"
|
||||||
posting: "やってます"
|
posting: "やってます"
|
||||||
|
|
||||||
desktop:
|
desktop:
|
||||||
|
@ -304,8 +304,8 @@ desktop:
|
||||||
settings: "設定"
|
settings: "設定"
|
||||||
signout: "サインアウト"
|
signout: "サインアウト"
|
||||||
|
|
||||||
mk-ui-header-post-button:
|
mk-ui-header-note-button:
|
||||||
post: "新規投稿"
|
note: "新規投稿"
|
||||||
|
|
||||||
mk-ui-header-notifications:
|
mk-ui-header-notifications:
|
||||||
title: "通知"
|
title: "通知"
|
||||||
|
@ -350,18 +350,18 @@ desktop:
|
||||||
no-users: "ミュートしているユーザーはいません"
|
no-users: "ミュートしているユーザーはいません"
|
||||||
|
|
||||||
mk-post-form:
|
mk-post-form:
|
||||||
post-placeholder: "いまどうしてる?"
|
note-placeholder: "いまどうしてる?"
|
||||||
reply-placeholder: "この投稿への返信..."
|
reply-placeholder: "この投稿への返信..."
|
||||||
quote-placeholder: "この投稿を引用..."
|
quote-placeholder: "この投稿を引用..."
|
||||||
post: "投稿"
|
note: "投稿"
|
||||||
reply: "返信"
|
reply: "返信"
|
||||||
repost: "Repost"
|
renote: "Renote"
|
||||||
posted: "投稿しました!"
|
posted: "投稿しました!"
|
||||||
replied: "返信しました!"
|
replied: "返信しました!"
|
||||||
reposted: "Repostしました!"
|
reposted: "Renoteしました!"
|
||||||
post-failed: "投稿に失敗しました"
|
note-failed: "投稿に失敗しました"
|
||||||
reply-failed: "返信に失敗しました"
|
reply-failed: "返信に失敗しました"
|
||||||
repost-failed: "Repostに失敗しました"
|
renote-failed: "Renoteに失敗しました"
|
||||||
posting: "投稿中"
|
posting: "投稿中"
|
||||||
attach-media-from-local: "PCからメディアを添付"
|
attach-media-from-local: "PCからメディアを添付"
|
||||||
attach-media-from-drive: "ドライブからメディアを添付"
|
attach-media-from-drive: "ドライブからメディアを添付"
|
||||||
|
@ -371,12 +371,12 @@ desktop:
|
||||||
text-remain: "のこり{}文字"
|
text-remain: "のこり{}文字"
|
||||||
|
|
||||||
mk-post-form-window:
|
mk-post-form-window:
|
||||||
post: "新規投稿"
|
note: "新規投稿"
|
||||||
reply: "返信"
|
reply: "返信"
|
||||||
attaches: "添付: {}メディア"
|
attaches: "添付: {}メディア"
|
||||||
uploading-media: "{}個のメディアをアップロード中"
|
uploading-media: "{}個のメディアをアップロード中"
|
||||||
|
|
||||||
mk-post-page:
|
mk-note-page:
|
||||||
prev: "前の投稿"
|
prev: "前の投稿"
|
||||||
next: "次の投稿"
|
next: "次の投稿"
|
||||||
|
|
||||||
|
@ -390,10 +390,10 @@ desktop:
|
||||||
other: "その他"
|
other: "その他"
|
||||||
license: "ライセンス"
|
license: "ライセンス"
|
||||||
|
|
||||||
mk-timeline-post:
|
mk-timeline-note:
|
||||||
reposted-by: "{}がRepost"
|
reposted-by: "{}がRenote"
|
||||||
reply: "返信"
|
reply: "返信"
|
||||||
repost: "Repost"
|
renote: "Renote"
|
||||||
add-reaction: "リアクション"
|
add-reaction: "リアクション"
|
||||||
detail: "詳細"
|
detail: "詳細"
|
||||||
|
|
||||||
|
@ -448,7 +448,7 @@ desktop:
|
||||||
|
|
||||||
mk-post-form-home-widget:
|
mk-post-form-home-widget:
|
||||||
title: "投稿"
|
title: "投稿"
|
||||||
post: "投稿"
|
note: "投稿"
|
||||||
placeholder: "いまどうしてる?"
|
placeholder: "いまどうしてる?"
|
||||||
|
|
||||||
mk-access-log-home-widget:
|
mk-access-log-home-widget:
|
||||||
|
@ -463,16 +463,16 @@ desktop:
|
||||||
have-a-nice-day: "良い一日を!"
|
have-a-nice-day: "良い一日を!"
|
||||||
next: "次"
|
next: "次"
|
||||||
|
|
||||||
mk-repost-form:
|
mk-renote-form:
|
||||||
quote: "引用する..."
|
quote: "引用する..."
|
||||||
cancel: "キャンセル"
|
cancel: "キャンセル"
|
||||||
repost: "Repost"
|
renote: "Renote"
|
||||||
reposting: "しています..."
|
reposting: "しています..."
|
||||||
success: "Repostしました!"
|
success: "Renoteしました!"
|
||||||
failure: "Repostに失敗しました"
|
failure: "Renoteに失敗しました"
|
||||||
|
|
||||||
mk-repost-form-window:
|
mk-renote-form-window:
|
||||||
title: "この投稿をRepostしますか?"
|
title: "この投稿をRenoteしますか?"
|
||||||
|
|
||||||
mk-user:
|
mk-user:
|
||||||
last-used-at: "最終アクセス"
|
last-used-at: "最終アクセス"
|
||||||
|
@ -541,7 +541,7 @@ mobile:
|
||||||
notifications: "通知"
|
notifications: "通知"
|
||||||
read-all: "すべての通知を既読にしますか?"
|
read-all: "すべての通知を既読にしますか?"
|
||||||
|
|
||||||
mk-post-page:
|
mk-note-page:
|
||||||
title: "投稿"
|
title: "投稿"
|
||||||
prev: "前の投稿"
|
prev: "前の投稿"
|
||||||
next: "次の投稿"
|
next: "次の投稿"
|
||||||
|
@ -612,24 +612,24 @@ mobile:
|
||||||
more: "もっと見る"
|
more: "もっと見る"
|
||||||
empty: "ありません!"
|
empty: "ありません!"
|
||||||
|
|
||||||
mk-post-detail:
|
mk-note-detail:
|
||||||
reply: "返信"
|
reply: "返信"
|
||||||
reaction: "リアクション"
|
reaction: "リアクション"
|
||||||
|
|
||||||
mk-post-form:
|
mk-post-form:
|
||||||
submit: "投稿"
|
submit: "投稿"
|
||||||
reply-placeholder: "この投稿への返信..."
|
reply-placeholder: "この投稿への返信..."
|
||||||
post-placeholder: "いまどうしてる?"
|
note-placeholder: "いまどうしてる?"
|
||||||
|
|
||||||
mk-search-posts:
|
mk-search-notes:
|
||||||
empty: "「{}」に関する投稿は見つかりませんでした。"
|
empty: "「{}」に関する投稿は見つかりませんでした。"
|
||||||
|
|
||||||
mk-sub-post-content:
|
mk-sub-note-content:
|
||||||
media-count: "{}個のメディア"
|
media-count: "{}個のメディア"
|
||||||
poll: "投票"
|
poll: "投票"
|
||||||
|
|
||||||
mk-timeline-post:
|
mk-timeline-note:
|
||||||
reposted-by: "{}がRepost"
|
reposted-by: "{}がRenote"
|
||||||
|
|
||||||
mk-timeline:
|
mk-timeline:
|
||||||
empty: "表示するものがありません"
|
empty: "表示するものがありません"
|
||||||
|
@ -652,21 +652,21 @@ mobile:
|
||||||
no-users: "フォロー中のユーザーはいないようです。"
|
no-users: "フォロー中のユーザーはいないようです。"
|
||||||
|
|
||||||
mk-user-timeline:
|
mk-user-timeline:
|
||||||
no-posts: "このユーザーはまだ投稿していないようです。"
|
no-notes: "このユーザーはまだ投稿していないようです。"
|
||||||
no-posts-with-media: "メディア付き投稿はありません。"
|
no-notes-with-media: "メディア付き投稿はありません。"
|
||||||
load-more: "もっとみる"
|
load-more: "もっとみる"
|
||||||
|
|
||||||
mk-user:
|
mk-user:
|
||||||
follows-you: "フォローされています"
|
follows-you: "フォローされています"
|
||||||
following: "フォロー"
|
following: "フォロー"
|
||||||
followers: "フォロワー"
|
followers: "フォロワー"
|
||||||
posts: "投稿"
|
notes: "投稿"
|
||||||
overview: "概要"
|
overview: "概要"
|
||||||
timeline: "タイムライン"
|
timeline: "タイムライン"
|
||||||
media: "メディア"
|
media: "メディア"
|
||||||
|
|
||||||
mk-user-overview:
|
mk-user-overview:
|
||||||
recent-posts: "最近の投稿"
|
recent-notes: "最近の投稿"
|
||||||
images: "画像"
|
images: "画像"
|
||||||
activity: "アクティビティ"
|
activity: "アクティビティ"
|
||||||
keywords: "キーワード"
|
keywords: "キーワード"
|
||||||
|
@ -675,9 +675,9 @@ mobile:
|
||||||
followers-you-know: "知り合いのフォロワー"
|
followers-you-know: "知り合いのフォロワー"
|
||||||
last-used-at: "最終ログイン"
|
last-used-at: "最終ログイン"
|
||||||
|
|
||||||
mk-user-overview-posts:
|
mk-user-overview-notes:
|
||||||
loading: "読み込み中"
|
loading: "読み込み中"
|
||||||
no-posts: "投稿はありません"
|
no-notes: "投稿はありません"
|
||||||
|
|
||||||
mk-user-overview-photos:
|
mk-user-overview-photos:
|
||||||
loading: "読み込み中"
|
loading: "読み込み中"
|
||||||
|
@ -703,7 +703,7 @@ mobile:
|
||||||
load-more: "もっと"
|
load-more: "もっと"
|
||||||
|
|
||||||
stats:
|
stats:
|
||||||
posts-count: "投稿の数"
|
notes-count: "投稿の数"
|
||||||
users-count: "アカウントの数"
|
users-count: "アカウントの数"
|
||||||
|
|
||||||
status:
|
status:
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
<template v-for="p in app.permission">
|
<template v-for="p in app.permission">
|
||||||
<li v-if="p == 'account-read'">アカウントの情報を見る。</li>
|
<li v-if="p == 'account-read'">アカウントの情報を見る。</li>
|
||||||
<li v-if="p == 'account-write'">アカウントの情報を操作する。</li>
|
<li v-if="p == 'account-write'">アカウントの情報を操作する。</li>
|
||||||
<li v-if="p == 'post-write'">投稿する。</li>
|
<li v-if="p == 'note-write'">投稿する。</li>
|
||||||
<li v-if="p == 'like-write'">いいねしたりいいね解除する。</li>
|
<li v-if="p == 'like-write'">いいねしたりいいね解除する。</li>
|
||||||
<li v-if="p == 'following-write'">フォローしたりフォロー解除する。</li>
|
<li v-if="p == 'following-write'">フォローしたりフォロー解除する。</li>
|
||||||
<li v-if="p == 'drive-read'">ドライブを見る。</li>
|
<li v-if="p == 'drive-read'">ドライブを見る。</li>
|
||||||
|
|
|
@ -15,11 +15,11 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="body">
|
<div class="body">
|
||||||
<p v-if="postsFetching">読み込み中<mk-ellipsis/></p>
|
<p v-if="notesFetching">読み込み中<mk-ellipsis/></p>
|
||||||
<div v-if="!postsFetching">
|
<div v-if="!notesFetching">
|
||||||
<p v-if="posts == null || posts.length == 0">まだ投稿がありません</p>
|
<p v-if="notes == null || notes.length == 0">まだ投稿がありません</p>
|
||||||
<template v-if="posts != null">
|
<template v-if="notes != null">
|
||||||
<mk-channel-post each={ post in posts.slice().reverse() } post={ post } form={ parent.refs.form }/>
|
<mk-channel-note each={ note in notes.slice().reverse() } note={ note } form={ parent.refs.form }/>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -62,9 +62,9 @@
|
||||||
|
|
||||||
this.id = this.opts.id;
|
this.id = this.opts.id;
|
||||||
this.fetching = true;
|
this.fetching = true;
|
||||||
this.postsFetching = true;
|
this.notesFetching = true;
|
||||||
this.channel = null;
|
this.channel = null;
|
||||||
this.posts = null;
|
this.notes = null;
|
||||||
this.connection = new ChannelStream(this.id);
|
this.connection = new ChannelStream(this.id);
|
||||||
this.unreadCount = 0;
|
this.unreadCount = 0;
|
||||||
|
|
||||||
|
@ -95,9 +95,9 @@
|
||||||
});
|
});
|
||||||
|
|
||||||
// 投稿読み込み
|
// 投稿読み込み
|
||||||
this.$root.$data.os.api('channels/posts', {
|
this.$root.$data.os.api('channels/notes', {
|
||||||
channelId: this.id
|
channelId: this.id
|
||||||
}).then(posts => {
|
}).then(notes => {
|
||||||
if (fetched) {
|
if (fetched) {
|
||||||
Progress.done();
|
Progress.done();
|
||||||
} else {
|
} else {
|
||||||
|
@ -106,26 +106,26 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
this.update({
|
this.update({
|
||||||
postsFetching: false,
|
notesFetching: false,
|
||||||
posts: posts
|
notes: notes
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
this.connection.on('post', this.onPost);
|
this.connection.on('note', this.onNote);
|
||||||
document.addEventListener('visibilitychange', this.onVisibilitychange, false);
|
document.addEventListener('visibilitychange', this.onVisibilitychange, false);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.on('unmount', () => {
|
this.on('unmount', () => {
|
||||||
this.connection.off('post', this.onPost);
|
this.connection.off('note', this.onNote);
|
||||||
this.connection.close();
|
this.connection.close();
|
||||||
document.removeEventListener('visibilitychange', this.onVisibilitychange);
|
document.removeEventListener('visibilitychange', this.onVisibilitychange);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.onPost = post => {
|
this.onNote = note => {
|
||||||
this.posts.unshift(post);
|
this.notes.unshift(note);
|
||||||
this.update();
|
this.update();
|
||||||
|
|
||||||
if (document.hidden && this.$root.$data.os.isSignedIn && post.userId !== this.$root.$data.os.i.id) {
|
if (document.hidden && this.$root.$data.os.isSignedIn && note.userId !== this.$root.$data.os.i.id) {
|
||||||
this.unreadCount++;
|
this.unreadCount++;
|
||||||
document.title = `(${this.unreadCount}) ${this.channel.title} | Misskey`;
|
document.title = `(${this.unreadCount}) ${this.channel.title} | Misskey`;
|
||||||
}
|
}
|
||||||
|
@ -162,19 +162,19 @@
|
||||||
</script>
|
</script>
|
||||||
</mk-channel>
|
</mk-channel>
|
||||||
|
|
||||||
<mk-channel-post>
|
<mk-channel-note>
|
||||||
<header>
|
<header>
|
||||||
<a class="index" @click="reply">{ post.index }:</a>
|
<a class="index" @click="reply">{ note.index }:</a>
|
||||||
<a class="name" href={ _URL_ + '/@' + acct }><b>{ getUserName(post.user) }</b></a>
|
<a class="name" href={ _URL_ + '/@' + acct }><b>{ getUserName(note.user) }</b></a>
|
||||||
<mk-time time={ post.createdAt }/>
|
<mk-time time={ note.createdAt }/>
|
||||||
<mk-time time={ post.createdAt } mode="detail"/>
|
<mk-time time={ note.createdAt } mode="detail"/>
|
||||||
<span>ID:<i>{ acct }</i></span>
|
<span>ID:<i>{ acct }</i></span>
|
||||||
</header>
|
</header>
|
||||||
<div>
|
<div>
|
||||||
<a v-if="post.reply">>>{ post.reply.index }</a>
|
<a v-if="note.reply">>>{ note.reply.index }</a>
|
||||||
{ post.text }
|
{ note.text }
|
||||||
<div class="media" v-if="post.media">
|
<div class="media" v-if="note.media">
|
||||||
<template each={ file in post.media }>
|
<template each={ file in note.media }>
|
||||||
<a href={ file.url } target="_blank">
|
<a href={ file.url } target="_blank">
|
||||||
<img src={ file.url + '?thumbnail&size=512' } alt={ file.name } title={ file.name }/>
|
<img src={ file.url + '?thumbnail&size=512' } alt={ file.name } title={ file.name }/>
|
||||||
</a>
|
</a>
|
||||||
|
@ -232,18 +232,18 @@
|
||||||
import getAcct from '../../../../acct/render';
|
import getAcct from '../../../../acct/render';
|
||||||
import getUserName from '../../../../renderers/get-user-name';
|
import getUserName from '../../../../renderers/get-user-name';
|
||||||
|
|
||||||
this.post = this.opts.post;
|
this.note = this.opts.note;
|
||||||
this.form = this.opts.form;
|
this.form = this.opts.form;
|
||||||
this.acct = getAcct(this.post.user);
|
this.acct = getAcct(this.note.user);
|
||||||
this.name = getUserName(this.post.user);
|
this.name = getUserName(this.note.user);
|
||||||
|
|
||||||
this.reply = () => {
|
this.reply = () => {
|
||||||
this.form.update({
|
this.form.update({
|
||||||
reply: this.post
|
reply: this.note
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
</mk-channel-post>
|
</mk-channel-note>
|
||||||
|
|
||||||
<mk-channel-form>
|
<mk-channel-form>
|
||||||
<p v-if="reply"><b>>>{ reply.index }</b> ({ getUserName(reply.user) }): <a @click="clearReply">[x]</a></p>
|
<p v-if="reply"><b>>>{ reply.index }</b> ({ getUserName(reply.user) }): <a @click="clearReply">[x]</a></p>
|
||||||
|
@ -251,8 +251,8 @@
|
||||||
<div class="actions">
|
<div class="actions">
|
||||||
<button @click="selectFile">%fa:upload%%i18n:ch.tags.mk-channel-form.upload%</button>
|
<button @click="selectFile">%fa:upload%%i18n:ch.tags.mk-channel-form.upload%</button>
|
||||||
<button @click="drive">%fa:cloud%%i18n:ch.tags.mk-channel-form.drive%</button>
|
<button @click="drive">%fa:cloud%%i18n:ch.tags.mk-channel-form.drive%</button>
|
||||||
<button :class="{ wait: wait }" ref="submit" disabled={ wait || (refs.text.value.length == 0) } @click="post">
|
<button :class="{ wait: wait }" ref="submit" disabled={ wait || (refs.text.value.length == 0) } @click="note">
|
||||||
<template v-if="!wait">%fa:paper-plane%</template>{ wait ? '%i18n:ch.tags.mk-channel-form.posting%' : '%i18n:ch.tags.mk-channel-form.post%' }<mk-ellipsis v-if="wait"/>
|
<template v-if="!wait">%fa:paper-plane%</template>{ wait ? '%i18n:ch.tags.mk-channel-form.posting%' : '%i18n:ch.tags.mk-channel-form.note%' }<mk-ellipsis v-if="wait"/>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<mk-uploader ref="uploader"/>
|
<mk-uploader ref="uploader"/>
|
||||||
|
@ -321,7 +321,7 @@
|
||||||
this.$refs.text.value = '';
|
this.$refs.text.value = '';
|
||||||
};
|
};
|
||||||
|
|
||||||
this.post = () => {
|
this.note = () => {
|
||||||
this.update({
|
this.update({
|
||||||
wait: true
|
wait: true
|
||||||
});
|
});
|
||||||
|
@ -330,7 +330,7 @@
|
||||||
? this.files.map(f => f.id)
|
? this.files.map(f => f.id)
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
this.$root.$data.os.api('posts/create', {
|
this.$root.$data.os.api('notes/create', {
|
||||||
text: this.$refs.text.value == '' ? undefined : this.$refs.text.value,
|
text: this.$refs.text.value == '' ? undefined : this.$refs.text.value,
|
||||||
mediaIds: files,
|
mediaIds: files,
|
||||||
replyId: this.reply ? this.reply.id : undefined,
|
replyId: this.reply ? this.reply.id : undefined,
|
||||||
|
|
|
@ -49,7 +49,7 @@ export type API = {
|
||||||
|
|
||||||
post: (opts?: {
|
post: (opts?: {
|
||||||
reply?: any;
|
reply?: any;
|
||||||
repost?: any;
|
renote?: any;
|
||||||
}) => void;
|
}) => void;
|
||||||
|
|
||||||
notify: (message: string) => void;
|
notify: (message: string) => void;
|
||||||
|
@ -312,7 +312,7 @@ export default class MiOS extends EventEmitter {
|
||||||
// Finish init
|
// Finish init
|
||||||
callback();
|
callback();
|
||||||
|
|
||||||
//#region Post
|
//#region Note
|
||||||
|
|
||||||
// Init service worker
|
// Init service worker
|
||||||
if (this.shouldRegisterSw) this.registerSw();
|
if (this.shouldRegisterSw) this.registerSw();
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import getPostSummary from '../../../../renderers/get-post-summary';
|
import getNoteSummary from '../../../../renderers/get-note-summary';
|
||||||
import getReactionEmoji from '../../../../renderers/get-reaction-emoji';
|
import getReactionEmoji from '../../../../renderers/get-reaction-emoji';
|
||||||
import getUserName from '../../../../renderers/get-user-name';
|
import getUserName from '../../../../renderers/get-user-name';
|
||||||
|
|
||||||
|
@ -23,28 +23,28 @@ export default function(type, data): Notification {
|
||||||
case 'mention':
|
case 'mention':
|
||||||
return {
|
return {
|
||||||
title: `${getUserName(data.user)}さんから:`,
|
title: `${getUserName(data.user)}さんから:`,
|
||||||
body: getPostSummary(data),
|
body: getNoteSummary(data),
|
||||||
icon: data.user.avatarUrl + '?thumbnail&size=64'
|
icon: data.user.avatarUrl + '?thumbnail&size=64'
|
||||||
};
|
};
|
||||||
|
|
||||||
case 'reply':
|
case 'reply':
|
||||||
return {
|
return {
|
||||||
title: `${getUserName(data.user)}さんから返信:`,
|
title: `${getUserName(data.user)}さんから返信:`,
|
||||||
body: getPostSummary(data),
|
body: getNoteSummary(data),
|
||||||
icon: data.user.avatarUrl + '?thumbnail&size=64'
|
icon: data.user.avatarUrl + '?thumbnail&size=64'
|
||||||
};
|
};
|
||||||
|
|
||||||
case 'quote':
|
case 'quote':
|
||||||
return {
|
return {
|
||||||
title: `${getUserName(data.user)}さんが引用:`,
|
title: `${getUserName(data.user)}さんが引用:`,
|
||||||
body: getPostSummary(data),
|
body: getNoteSummary(data),
|
||||||
icon: data.user.avatarUrl + '?thumbnail&size=64'
|
icon: data.user.avatarUrl + '?thumbnail&size=64'
|
||||||
};
|
};
|
||||||
|
|
||||||
case 'reaction':
|
case 'reaction':
|
||||||
return {
|
return {
|
||||||
title: `${getUserName(data.user)}: ${getReactionEmoji(data.reaction)}:`,
|
title: `${getUserName(data.user)}: ${getReactionEmoji(data.reaction)}:`,
|
||||||
body: getPostSummary(data.post),
|
body: getNoteSummary(data.note),
|
||||||
icon: data.user.avatarUrl + '?thumbnail&size=64'
|
icon: data.user.avatarUrl + '?thumbnail&size=64'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -19,8 +19,8 @@ export default function(qs: string) {
|
||||||
case 'reply':
|
case 'reply':
|
||||||
q['reply'] = value == 'null' ? null : value == 'true';
|
q['reply'] = value == 'null' ? null : value == 'true';
|
||||||
break;
|
break;
|
||||||
case 'repost':
|
case 'renote':
|
||||||
q['repost'] = value == 'null' ? null : value == 'true';
|
q['renote'] = value == 'null' ? null : value == 'true';
|
||||||
break;
|
break;
|
||||||
case 'media':
|
case 'media':
|
||||||
q['media'] = value == 'null' ? null : value == 'true';
|
q['media'] = value == 'null' ? null : value == 'true';
|
||||||
|
|
|
@ -4,7 +4,7 @@ import signin from './signin.vue';
|
||||||
import signup from './signup.vue';
|
import signup from './signup.vue';
|
||||||
import forkit from './forkit.vue';
|
import forkit from './forkit.vue';
|
||||||
import nav from './nav.vue';
|
import nav from './nav.vue';
|
||||||
import postHtml from './post-html';
|
import noteHtml from './note-html';
|
||||||
import poll from './poll.vue';
|
import poll from './poll.vue';
|
||||||
import pollEditor from './poll-editor.vue';
|
import pollEditor from './poll-editor.vue';
|
||||||
import reactionIcon from './reaction-icon.vue';
|
import reactionIcon from './reaction-icon.vue';
|
||||||
|
@ -29,7 +29,7 @@ Vue.component('mk-signin', signin);
|
||||||
Vue.component('mk-signup', signup);
|
Vue.component('mk-signup', signup);
|
||||||
Vue.component('mk-forkit', forkit);
|
Vue.component('mk-forkit', forkit);
|
||||||
Vue.component('mk-nav', nav);
|
Vue.component('mk-nav', nav);
|
||||||
Vue.component('mk-post-html', postHtml);
|
Vue.component('mk-note-html', noteHtml);
|
||||||
Vue.component('mk-poll', poll);
|
Vue.component('mk-poll', poll);
|
||||||
Vue.component('mk-poll-editor', pollEditor);
|
Vue.component('mk-poll-editor', pollEditor);
|
||||||
Vue.component('mk-reaction-icon', reactionIcon);
|
Vue.component('mk-reaction-icon', reactionIcon);
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
<img src="/assets/desktop/messaging/delete.png" alt="Delete"/>
|
<img src="/assets/desktop/messaging/delete.png" alt="Delete"/>
|
||||||
</button>
|
</button>
|
||||||
<div class="content" v-if="!message.isDeleted">
|
<div class="content" v-if="!message.isDeleted">
|
||||||
<mk-post-html class="text" v-if="message.text" ref="text" :text="message.text" :i="os.i"/>
|
<mk-note-html class="text" v-if="message.text" ref="text" :text="message.text" :i="os.i"/>
|
||||||
<div class="file" v-if="message.file">
|
<div class="file" v-if="message.file">
|
||||||
<a :href="message.file.url" target="_blank" :title="message.file.name">
|
<a :href="message.file.url" target="_blank" :title="message.file.name">
|
||||||
<img v-if="message.file.type.split('/')[0] == 'image'" :src="message.file.url" :alt="message.file.name"/>
|
<img v-if="message.file.type.split('/')[0] == 'image'" :src="message.file.url" :alt="message.file.name"/>
|
||||||
|
|
|
@ -9,7 +9,7 @@ const flatten = list => list.reduce(
|
||||||
(a, b) => a.concat(Array.isArray(b) ? flatten(b) : b), []
|
(a, b) => a.concat(Array.isArray(b) ? flatten(b) : b), []
|
||||||
);
|
);
|
||||||
|
|
||||||
export default Vue.component('mk-post-html', {
|
export default Vue.component('mk-note-html', {
|
||||||
props: {
|
props: {
|
||||||
text: {
|
text: {
|
||||||
type: String,
|
type: String,
|
|
@ -1,8 +1,8 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="mk-post-menu">
|
<div class="mk-note-menu">
|
||||||
<div class="backdrop" ref="backdrop" @click="close"></div>
|
<div class="backdrop" ref="backdrop" @click="close"></div>
|
||||||
<div class="popover" :class="{ compact }" ref="popover">
|
<div class="popover" :class="{ compact }" ref="popover">
|
||||||
<button v-if="post.userId == os.i.id" @click="pin">%i18n:common.tags.mk-post-menu.pin%</button>
|
<button v-if="note.userId == os.i.id" @click="pin">%i18n:common.tags.mk-note-menu.pin%</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -12,7 +12,7 @@ import Vue from 'vue';
|
||||||
import * as anime from 'animejs';
|
import * as anime from 'animejs';
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
props: ['post', 'source', 'compact'],
|
props: ['note', 'source', 'compact'],
|
||||||
mounted() {
|
mounted() {
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
const popover = this.$refs.popover as any;
|
const popover = this.$refs.popover as any;
|
||||||
|
@ -51,7 +51,7 @@ export default Vue.extend({
|
||||||
methods: {
|
methods: {
|
||||||
pin() {
|
pin() {
|
||||||
(this as any).api('i/pin', {
|
(this as any).api('i/pin', {
|
||||||
postId: this.post.id
|
noteId: this.note.id
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
this.$destroy();
|
this.$destroy();
|
||||||
});
|
});
|
||||||
|
@ -83,7 +83,7 @@ export default Vue.extend({
|
||||||
<style lang="stylus" scoped>
|
<style lang="stylus" scoped>
|
||||||
$border-color = rgba(27, 31, 35, 0.15)
|
$border-color = rgba(27, 31, 35, 0.15)
|
||||||
|
|
||||||
.mk-post-menu
|
.mk-note-menu
|
||||||
position initial
|
position initial
|
||||||
|
|
||||||
> .backdrop
|
> .backdrop
|
|
@ -22,7 +22,7 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
props: ['post'],
|
props: ['note'],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
showResult: false
|
showResult: false
|
||||||
|
@ -30,7 +30,7 @@ export default Vue.extend({
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
poll(): any {
|
poll(): any {
|
||||||
return this.post.poll;
|
return this.note.poll;
|
||||||
},
|
},
|
||||||
total(): number {
|
total(): number {
|
||||||
return this.poll.choices.reduce((a, b) => a + b.votes, 0);
|
return this.poll.choices.reduce((a, b) => a + b.votes, 0);
|
||||||
|
@ -48,8 +48,8 @@ export default Vue.extend({
|
||||||
},
|
},
|
||||||
vote(id) {
|
vote(id) {
|
||||||
if (this.poll.choices.some(c => c.isVoted)) return;
|
if (this.poll.choices.some(c => c.isVoted)) return;
|
||||||
(this as any).api('posts/polls/vote', {
|
(this as any).api('notes/polls/vote', {
|
||||||
postId: this.post.id,
|
noteId: this.note.id,
|
||||||
choice: id
|
choice: id
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
this.poll.choices.forEach(c => {
|
this.poll.choices.forEach(c => {
|
||||||
|
|
|
@ -25,7 +25,7 @@ import * as anime from 'animejs';
|
||||||
const placeholder = '%i18n:common.tags.mk-reaction-picker.choose-reaction%';
|
const placeholder = '%i18n:common.tags.mk-reaction-picker.choose-reaction%';
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
props: ['post', 'source', 'compact', 'cb'],
|
props: ['note', 'source', 'compact', 'cb'],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
title: placeholder
|
title: placeholder
|
||||||
|
@ -68,8 +68,8 @@ export default Vue.extend({
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
react(reaction) {
|
react(reaction) {
|
||||||
(this as any).api('posts/reactions/create', {
|
(this as any).api('notes/reactions/create', {
|
||||||
postId: this.post.id,
|
noteId: this.note.id,
|
||||||
reaction: reaction
|
reaction: reaction
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
if (this.cb) this.cb();
|
if (this.cb) this.cb();
|
||||||
|
|
|
@ -17,10 +17,10 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
props: ['post'],
|
props: ['note'],
|
||||||
computed: {
|
computed: {
|
||||||
reactions(): number {
|
reactions(): number {
|
||||||
return this.post.reactionCounts;
|
return this.note.reactionCounts;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,21 +1,21 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="mk-welcome-timeline">
|
<div class="mk-welcome-timeline">
|
||||||
<div v-for="post in posts">
|
<div v-for="note in notes">
|
||||||
<router-link class="avatar-anchor" :to="`/@${getAcct(post.user)}`" v-user-preview="post.user.id">
|
<router-link class="avatar-anchor" :to="`/@${getAcct(note.user)}`" v-user-preview="note.user.id">
|
||||||
<img class="avatar" :src="`${post.user.avatarUrl}?thumbnail&size=96`" alt="avatar"/>
|
<img class="avatar" :src="`${note.user.avatarUrl}?thumbnail&size=96`" alt="avatar"/>
|
||||||
</router-link>
|
</router-link>
|
||||||
<div class="body">
|
<div class="body">
|
||||||
<header>
|
<header>
|
||||||
<router-link class="name" :to="`/@${getAcct(post.user)}`" v-user-preview="post.user.id">{{ getUserName(post.user) }}</router-link>
|
<router-link class="name" :to="`/@${getAcct(note.user)}`" v-user-preview="note.user.id">{{ getUserName(note.user) }}</router-link>
|
||||||
<span class="username">@{{ getAcct(post.user) }}</span>
|
<span class="username">@{{ getAcct(note.user) }}</span>
|
||||||
<div class="info">
|
<div class="info">
|
||||||
<router-link class="created-at" :to="`/@${getAcct(post.user)}/${post.id}`">
|
<router-link class="created-at" :to="`/@${getAcct(note.user)}/${note.id}`">
|
||||||
<mk-time :time="post.createdAt"/>
|
<mk-time :time="note.createdAt"/>
|
||||||
</router-link>
|
</router-link>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
<div class="text">
|
<div class="text">
|
||||||
<mk-post-html :text="post.text"/>
|
<mk-note-html :text="note.text"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -31,7 +31,7 @@ export default Vue.extend({
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
fetching: true,
|
fetching: true,
|
||||||
posts: []
|
notes: []
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
@ -42,14 +42,14 @@ export default Vue.extend({
|
||||||
getUserName,
|
getUserName,
|
||||||
fetch(cb?) {
|
fetch(cb?) {
|
||||||
this.fetching = true;
|
this.fetching = true;
|
||||||
(this as any).api('posts', {
|
(this as any).api('notes', {
|
||||||
reply: false,
|
reply: false,
|
||||||
repost: false,
|
renote: false,
|
||||||
media: false,
|
media: false,
|
||||||
poll: false,
|
poll: false,
|
||||||
bot: false
|
bot: false
|
||||||
}).then(posts => {
|
}).then(notes => {
|
||||||
this.posts = posts;
|
this.notes = notes;
|
||||||
this.fetching = false;
|
this.fetching = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
import PostFormWindow from '../views/components/post-form-window.vue';
|
import PostFormWindow from '../views/components/post-form-window.vue';
|
||||||
import RepostFormWindow from '../views/components/repost-form-window.vue';
|
import RenoteFormWindow from '../views/components/renote-form-window.vue';
|
||||||
|
|
||||||
export default function(opts) {
|
export default function(opts) {
|
||||||
const o = opts || {};
|
const o = opts || {};
|
||||||
if (o.repost) {
|
if (o.renote) {
|
||||||
const vm = new RepostFormWindow({
|
const vm = new RenoteFormWindow({
|
||||||
propsData: {
|
propsData: {
|
||||||
repost: o.repost
|
renote: o.renote
|
||||||
}
|
}
|
||||||
}).$mount();
|
}).$mount();
|
||||||
document.body.appendChild(vm.$el);
|
document.body.appendChild(vm.$el);
|
||||||
|
|
|
@ -28,7 +28,7 @@ import MkSelectDrive from './views/pages/selectdrive.vue';
|
||||||
import MkDrive from './views/pages/drive.vue';
|
import MkDrive from './views/pages/drive.vue';
|
||||||
import MkHomeCustomize from './views/pages/home-customize.vue';
|
import MkHomeCustomize from './views/pages/home-customize.vue';
|
||||||
import MkMessagingRoom from './views/pages/messaging-room.vue';
|
import MkMessagingRoom from './views/pages/messaging-room.vue';
|
||||||
import MkPost from './views/pages/post.vue';
|
import MkNote from './views/pages/note.vue';
|
||||||
import MkSearch from './views/pages/search.vue';
|
import MkSearch from './views/pages/search.vue';
|
||||||
import MkOthello from './views/pages/othello.vue';
|
import MkOthello from './views/pages/othello.vue';
|
||||||
|
|
||||||
|
@ -57,7 +57,7 @@ init(async (launch) => {
|
||||||
{ path: '/othello', component: MkOthello },
|
{ path: '/othello', component: MkOthello },
|
||||||
{ path: '/othello/:game', component: MkOthello },
|
{ path: '/othello/:game', component: MkOthello },
|
||||||
{ path: '/@:user', component: MkUser },
|
{ path: '/@:user', component: MkUser },
|
||||||
{ path: '/@:user/:post', component: MkPost }
|
{ path: '/@:user/:note', component: MkNote }
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -114,8 +114,8 @@ function registerNotifications(stream: HomeStreamManager) {
|
||||||
setTimeout(n.close.bind(n), 5000);
|
setTimeout(n.close.bind(n), 5000);
|
||||||
});
|
});
|
||||||
|
|
||||||
connection.on('mention', post => {
|
connection.on('mention', note => {
|
||||||
const _n = composeNotification('mention', post);
|
const _n = composeNotification('mention', note);
|
||||||
const n = new Notification(_n.title, {
|
const n = new Notification(_n.title, {
|
||||||
body: _n.body,
|
body: _n.body,
|
||||||
icon: _n.icon
|
icon: _n.icon
|
||||||
|
@ -123,8 +123,8 @@ function registerNotifications(stream: HomeStreamManager) {
|
||||||
setTimeout(n.close.bind(n), 6000);
|
setTimeout(n.close.bind(n), 6000);
|
||||||
});
|
});
|
||||||
|
|
||||||
connection.on('reply', post => {
|
connection.on('reply', note => {
|
||||||
const _n = composeNotification('reply', post);
|
const _n = composeNotification('reply', note);
|
||||||
const n = new Notification(_n.title, {
|
const n = new Notification(_n.title, {
|
||||||
body: _n.body,
|
body: _n.body,
|
||||||
icon: _n.icon
|
icon: _n.icon
|
||||||
|
@ -132,8 +132,8 @@ function registerNotifications(stream: HomeStreamManager) {
|
||||||
setTimeout(n.close.bind(n), 6000);
|
setTimeout(n.close.bind(n), 6000);
|
||||||
});
|
});
|
||||||
|
|
||||||
connection.on('quote', post => {
|
connection.on('quote', note => {
|
||||||
const _n = composeNotification('quote', post);
|
const _n = composeNotification('quote', note);
|
||||||
const n = new Notification(_n.title, {
|
const n = new Notification(_n.title, {
|
||||||
body: _n.body,
|
body: _n.body,
|
||||||
icon: _n.icon
|
icon: _n.icon
|
||||||
|
|
|
@ -29,7 +29,7 @@ import Vue from 'vue';
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
props: ['data'],
|
props: ['data'],
|
||||||
created() {
|
created() {
|
||||||
this.data.forEach(d => d.total = d.posts + d.replies + d.reposts);
|
this.data.forEach(d => d.total = d.notes + d.replies + d.renotes);
|
||||||
const peak = Math.max.apply(null, this.data.map(d => d.total));
|
const peak = Math.max.apply(null, this.data.map(d => d.total));
|
||||||
|
|
||||||
let x = 0;
|
let x = 0;
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
<template>
|
<template>
|
||||||
<svg :viewBox="`0 0 ${ viewBoxX } ${ viewBoxY }`" preserveAspectRatio="none" @mousedown.prevent="onMousedown">
|
<svg :viewBox="`0 0 ${ viewBoxX } ${ viewBoxY }`" preserveAspectRatio="none" @mousedown.prevent="onMousedown">
|
||||||
<title>Black ... Total<br/>Blue ... Posts<br/>Red ... Replies<br/>Green ... Reposts</title>
|
<title>Black ... Total<br/>Blue ... Notes<br/>Red ... Replies<br/>Green ... Renotes</title>
|
||||||
<polyline
|
<polyline
|
||||||
:points="pointsPost"
|
:points="pointsNote"
|
||||||
fill="none"
|
fill="none"
|
||||||
stroke-width="1"
|
stroke-width="1"
|
||||||
stroke="#41ddde"/>
|
stroke="#41ddde"/>
|
||||||
|
@ -12,7 +12,7 @@
|
||||||
stroke-width="1"
|
stroke-width="1"
|
||||||
stroke="#f7796c"/>
|
stroke="#f7796c"/>
|
||||||
<polyline
|
<polyline
|
||||||
:points="pointsRepost"
|
:points="pointsRenote"
|
||||||
fill="none"
|
fill="none"
|
||||||
stroke-width="1"
|
stroke-width="1"
|
||||||
stroke="#a1de41"/>
|
stroke="#a1de41"/>
|
||||||
|
@ -48,24 +48,24 @@ export default Vue.extend({
|
||||||
viewBoxY: 60,
|
viewBoxY: 60,
|
||||||
zoom: 1,
|
zoom: 1,
|
||||||
pos: 0,
|
pos: 0,
|
||||||
pointsPost: null,
|
pointsNote: null,
|
||||||
pointsReply: null,
|
pointsReply: null,
|
||||||
pointsRepost: null,
|
pointsRenote: null,
|
||||||
pointsTotal: null
|
pointsTotal: null
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
this.data.reverse();
|
this.data.reverse();
|
||||||
this.data.forEach(d => d.total = d.posts + d.replies + d.reposts);
|
this.data.forEach(d => d.total = d.notes + d.replies + d.renotes);
|
||||||
this.render();
|
this.render();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
render() {
|
render() {
|
||||||
const peak = Math.max.apply(null, this.data.map(d => d.total));
|
const peak = Math.max.apply(null, this.data.map(d => d.total));
|
||||||
if (peak != 0) {
|
if (peak != 0) {
|
||||||
this.pointsPost = this.data.map((d, i) => `${(i * this.zoom) + this.pos},${(1 - (d.posts / peak)) * this.viewBoxY}`).join(' ');
|
this.pointsNote = this.data.map((d, i) => `${(i * this.zoom) + this.pos},${(1 - (d.notes / peak)) * this.viewBoxY}`).join(' ');
|
||||||
this.pointsReply = this.data.map((d, i) => `${(i * this.zoom) + this.pos},${(1 - (d.replies / peak)) * this.viewBoxY}`).join(' ');
|
this.pointsReply = this.data.map((d, i) => `${(i * this.zoom) + this.pos},${(1 - (d.replies / peak)) * this.viewBoxY}`).join(' ');
|
||||||
this.pointsRepost = this.data.map((d, i) => `${(i * this.zoom) + this.pos},${(1 - (d.reposts / peak)) * this.viewBoxY}`).join(' ');
|
this.pointsRenote = this.data.map((d, i) => `${(i * this.zoom) + this.pos},${(1 - (d.renotes / peak)) * this.viewBoxY}`).join(' ');
|
||||||
this.pointsTotal = this.data.map((d, i) => `${(i * this.zoom) + this.pos},${(1 - (d.total / peak)) * this.viewBoxY}`).join(' ');
|
this.pointsTotal = this.data.map((d, i) => `${(i * this.zoom) + this.pos},${(1 - (d.total / peak)) * this.viewBoxY}`).join(' ');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -4,23 +4,23 @@ import ui from './ui.vue';
|
||||||
import uiNotification from './ui-notification.vue';
|
import uiNotification from './ui-notification.vue';
|
||||||
import home from './home.vue';
|
import home from './home.vue';
|
||||||
import timeline from './timeline.vue';
|
import timeline from './timeline.vue';
|
||||||
import posts from './posts.vue';
|
import notes from './notes.vue';
|
||||||
import subPostContent from './sub-post-content.vue';
|
import subNoteContent from './sub-note-content.vue';
|
||||||
import window from './window.vue';
|
import window from './window.vue';
|
||||||
import postFormWindow from './post-form-window.vue';
|
import noteFormWindow from './post-form-window.vue';
|
||||||
import repostFormWindow from './repost-form-window.vue';
|
import renoteFormWindow from './renote-form-window.vue';
|
||||||
import analogClock from './analog-clock.vue';
|
import analogClock from './analog-clock.vue';
|
||||||
import ellipsisIcon from './ellipsis-icon.vue';
|
import ellipsisIcon from './ellipsis-icon.vue';
|
||||||
import mediaImage from './media-image.vue';
|
import mediaImage from './media-image.vue';
|
||||||
import mediaImageDialog from './media-image-dialog.vue';
|
import mediaImageDialog from './media-image-dialog.vue';
|
||||||
import mediaVideo from './media-video.vue';
|
import mediaVideo from './media-video.vue';
|
||||||
import notifications from './notifications.vue';
|
import notifications from './notifications.vue';
|
||||||
import postForm from './post-form.vue';
|
import noteForm from './post-form.vue';
|
||||||
import repostForm from './repost-form.vue';
|
import renoteForm from './renote-form.vue';
|
||||||
import followButton from './follow-button.vue';
|
import followButton from './follow-button.vue';
|
||||||
import postPreview from './post-preview.vue';
|
import notePreview from './note-preview.vue';
|
||||||
import drive from './drive.vue';
|
import drive from './drive.vue';
|
||||||
import postDetail from './post-detail.vue';
|
import noteDetail from './note-detail.vue';
|
||||||
import settings from './settings.vue';
|
import settings from './settings.vue';
|
||||||
import calendar from './calendar.vue';
|
import calendar from './calendar.vue';
|
||||||
import activity from './activity.vue';
|
import activity from './activity.vue';
|
||||||
|
@ -34,23 +34,23 @@ Vue.component('mk-ui', ui);
|
||||||
Vue.component('mk-ui-notification', uiNotification);
|
Vue.component('mk-ui-notification', uiNotification);
|
||||||
Vue.component('mk-home', home);
|
Vue.component('mk-home', home);
|
||||||
Vue.component('mk-timeline', timeline);
|
Vue.component('mk-timeline', timeline);
|
||||||
Vue.component('mk-posts', posts);
|
Vue.component('mk-notes', notes);
|
||||||
Vue.component('mk-sub-post-content', subPostContent);
|
Vue.component('mk-sub-note-content', subNoteContent);
|
||||||
Vue.component('mk-window', window);
|
Vue.component('mk-window', window);
|
||||||
Vue.component('mk-post-form-window', postFormWindow);
|
Vue.component('mk-post-form-window', noteFormWindow);
|
||||||
Vue.component('mk-repost-form-window', repostFormWindow);
|
Vue.component('mk-renote-form-window', renoteFormWindow);
|
||||||
Vue.component('mk-analog-clock', analogClock);
|
Vue.component('mk-analog-clock', analogClock);
|
||||||
Vue.component('mk-ellipsis-icon', ellipsisIcon);
|
Vue.component('mk-ellipsis-icon', ellipsisIcon);
|
||||||
Vue.component('mk-media-image', mediaImage);
|
Vue.component('mk-media-image', mediaImage);
|
||||||
Vue.component('mk-media-image-dialog', mediaImageDialog);
|
Vue.component('mk-media-image-dialog', mediaImageDialog);
|
||||||
Vue.component('mk-media-video', mediaVideo);
|
Vue.component('mk-media-video', mediaVideo);
|
||||||
Vue.component('mk-notifications', notifications);
|
Vue.component('mk-notifications', notifications);
|
||||||
Vue.component('mk-post-form', postForm);
|
Vue.component('mk-post-form', noteForm);
|
||||||
Vue.component('mk-repost-form', repostForm);
|
Vue.component('mk-renote-form', renoteForm);
|
||||||
Vue.component('mk-follow-button', followButton);
|
Vue.component('mk-follow-button', followButton);
|
||||||
Vue.component('mk-post-preview', postPreview);
|
Vue.component('mk-note-preview', notePreview);
|
||||||
Vue.component('mk-drive', drive);
|
Vue.component('mk-drive', drive);
|
||||||
Vue.component('mk-post-detail', postDetail);
|
Vue.component('mk-note-detail', noteDetail);
|
||||||
Vue.component('mk-settings', settings);
|
Vue.component('mk-settings', settings);
|
||||||
Vue.component('mk-calendar', calendar);
|
Vue.component('mk-calendar', calendar);
|
||||||
Vue.component('mk-activity', activity);
|
Vue.component('mk-activity', activity);
|
||||||
|
|
|
@ -7,12 +7,12 @@
|
||||||
<div class="fetching" v-if="fetching">
|
<div class="fetching" v-if="fetching">
|
||||||
<mk-ellipsis-icon/>
|
<mk-ellipsis-icon/>
|
||||||
</div>
|
</div>
|
||||||
<p class="empty" v-if="posts.length == 0 && !fetching">
|
<p class="empty" v-if="notes.length == 0 && !fetching">
|
||||||
%fa:R comments%
|
%fa:R comments%
|
||||||
<span v-if="mode == 'all'">あなた宛ての投稿はありません。</span>
|
<span v-if="mode == 'all'">あなた宛ての投稿はありません。</span>
|
||||||
<span v-if="mode == 'following'">あなたがフォローしているユーザーからの言及はありません。</span>
|
<span v-if="mode == 'following'">あなたがフォローしているユーザーからの言及はありません。</span>
|
||||||
</p>
|
</p>
|
||||||
<mk-posts :posts="posts" ref="timeline"/>
|
<mk-notes :notes="notes" ref="timeline"/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ export default Vue.extend({
|
||||||
fetching: true,
|
fetching: true,
|
||||||
moreFetching: false,
|
moreFetching: false,
|
||||||
mode: 'all',
|
mode: 'all',
|
||||||
posts: []
|
notes: []
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
|
@ -56,23 +56,23 @@ export default Vue.extend({
|
||||||
},
|
},
|
||||||
fetch(cb?) {
|
fetch(cb?) {
|
||||||
this.fetching = true;
|
this.fetching = true;
|
||||||
this.posts = [];
|
this.notes = [];
|
||||||
(this as any).api('posts/mentions', {
|
(this as any).api('notes/mentions', {
|
||||||
following: this.mode == 'following'
|
following: this.mode == 'following'
|
||||||
}).then(posts => {
|
}).then(notes => {
|
||||||
this.posts = posts;
|
this.notes = notes;
|
||||||
this.fetching = false;
|
this.fetching = false;
|
||||||
if (cb) cb();
|
if (cb) cb();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
more() {
|
more() {
|
||||||
if (this.moreFetching || this.fetching || this.posts.length == 0) return;
|
if (this.moreFetching || this.fetching || this.notes.length == 0) return;
|
||||||
this.moreFetching = true;
|
this.moreFetching = true;
|
||||||
(this as any).api('posts/mentions', {
|
(this as any).api('notes/mentions', {
|
||||||
following: this.mode == 'following',
|
following: this.mode == 'following',
|
||||||
untilId: this.posts[this.posts.length - 1].id
|
untilId: this.notes[this.notes.length - 1].id
|
||||||
}).then(posts => {
|
}).then(notes => {
|
||||||
this.posts = this.posts.concat(posts);
|
this.notes = this.notes.concat(notes);
|
||||||
this.moreFetching = false;
|
this.moreFetching = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,24 +1,24 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="sub" :title="title">
|
<div class="sub" :title="title">
|
||||||
<router-link class="avatar-anchor" :to="`/@${acct}`">
|
<router-link class="avatar-anchor" :to="`/@${acct}`">
|
||||||
<img class="avatar" :src="`${post.user.avatarUrl}?thumbnail&size=64`" alt="avatar" v-user-preview="post.userId"/>
|
<img class="avatar" :src="`${note.user.avatarUrl}?thumbnail&size=64`" alt="avatar" v-user-preview="note.userId"/>
|
||||||
</router-link>
|
</router-link>
|
||||||
<div class="main">
|
<div class="main">
|
||||||
<header>
|
<header>
|
||||||
<div class="left">
|
<div class="left">
|
||||||
<router-link class="name" :to="`/@${acct}`" v-user-preview="post.userId">{{ getUserName(post.user) }}</router-link>
|
<router-link class="name" :to="`/@${acct}`" v-user-preview="note.userId">{{ getUserName(note.user) }}</router-link>
|
||||||
<span class="username">@{{ acct }}</span>
|
<span class="username">@{{ acct }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="right">
|
<div class="right">
|
||||||
<router-link class="time" :to="`/@${acct}/${post.id}`">
|
<router-link class="time" :to="`/@${acct}/${note.id}`">
|
||||||
<mk-time :time="post.createdAt"/>
|
<mk-time :time="note.createdAt"/>
|
||||||
</router-link>
|
</router-link>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
<div class="body">
|
<div class="body">
|
||||||
<mk-post-html v-if="post.text" :text="post.text" :i="os.i" :class="$style.text"/>
|
<mk-note-html v-if="note.text" :text="note.text" :i="os.i" :class="$style.text"/>
|
||||||
<div class="media" v-if="post.media > 0">
|
<div class="media" v-if="note.media > 0">
|
||||||
<mk-media-list :media-list="post.media"/>
|
<mk-media-list :media-list="note.media"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -32,16 +32,16 @@ import getAcct from '../../../../../acct/render';
|
||||||
import getUserName from '../../../../../renderers/get-user-name';
|
import getUserName from '../../../../../renderers/get-user-name';
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
props: ['post'],
|
props: ['note'],
|
||||||
computed: {
|
computed: {
|
||||||
acct() {
|
acct() {
|
||||||
return getAcct(this.post.user);
|
return getAcct(this.note.user);
|
||||||
},
|
},
|
||||||
name() {
|
name() {
|
||||||
return getUserName(this.post.user);
|
return getUserName(this.note.user);
|
||||||
},
|
},
|
||||||
title(): string {
|
title(): string {
|
||||||
return dateStringify(this.post.createdAt);
|
return dateStringify(this.note.createdAt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
448
src/client/app/desktop/views/components/note-detail.vue
Normal file
448
src/client/app/desktop/views/components/note-detail.vue
Normal file
|
@ -0,0 +1,448 @@
|
||||||
|
<template>
|
||||||
|
<div class="mk-note-detail" :title="title">
|
||||||
|
<button
|
||||||
|
class="read-more"
|
||||||
|
v-if="p.reply && p.reply.replyId && context == null"
|
||||||
|
title="会話をもっと読み込む"
|
||||||
|
@click="fetchContext"
|
||||||
|
:disabled="contextFetching"
|
||||||
|
>
|
||||||
|
<template v-if="!contextFetching">%fa:ellipsis-v%</template>
|
||||||
|
<template v-if="contextFetching">%fa:spinner .pulse%</template>
|
||||||
|
</button>
|
||||||
|
<div class="context">
|
||||||
|
<x-sub v-for="note in context" :key="note.id" :note="note"/>
|
||||||
|
</div>
|
||||||
|
<div class="reply-to" v-if="p.reply">
|
||||||
|
<x-sub :note="p.reply"/>
|
||||||
|
</div>
|
||||||
|
<div class="renote" v-if="isRenote">
|
||||||
|
<p>
|
||||||
|
<router-link class="avatar-anchor" :to="`/@${acct}`" v-user-preview="note.userId">
|
||||||
|
<img class="avatar" :src="`${note.user.avatarUrl}?thumbnail&size=32`" alt="avatar"/>
|
||||||
|
</router-link>
|
||||||
|
%fa:retweet%
|
||||||
|
<router-link class="name" :href="`/@${acct}`">{{ getUserName(note.user) }}</router-link>
|
||||||
|
がRenote
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<article>
|
||||||
|
<router-link class="avatar-anchor" :to="`/@${pAcct}`">
|
||||||
|
<img class="avatar" :src="`${p.user.avatarUrl}?thumbnail&size=64`" alt="avatar" v-user-preview="p.user.id"/>
|
||||||
|
</router-link>
|
||||||
|
<header>
|
||||||
|
<router-link class="name" :to="`/@${pAcct}`" v-user-preview="p.user.id">{{ getUserName(p.user) }}</router-link>
|
||||||
|
<span class="username">@{{ pAcct }}</span>
|
||||||
|
<router-link class="time" :to="`/@${pAcct}/${p.id}`">
|
||||||
|
<mk-time :time="p.createdAt"/>
|
||||||
|
</router-link>
|
||||||
|
</header>
|
||||||
|
<div class="body">
|
||||||
|
<mk-note-html :class="$style.text" v-if="p.text" :text="p.text" :i="os.i"/>
|
||||||
|
<div class="media" v-if="p.media.length > 0">
|
||||||
|
<mk-media-list :media-list="p.media"/>
|
||||||
|
</div>
|
||||||
|
<mk-poll v-if="p.poll" :note="p"/>
|
||||||
|
<mk-url-preview v-for="url in urls" :url="url" :key="url"/>
|
||||||
|
<div class="tags" v-if="p.tags && p.tags.length > 0">
|
||||||
|
<router-link v-for="tag in p.tags" :key="tag" :to="`/search?q=#${tag}`">{{ tag }}</router-link>
|
||||||
|
</div>
|
||||||
|
<a class="location" v-if="p.geo" :href="`http://maps.google.com/maps?q=${p.geo.coordinates[1]},${p.geo.coordinates[0]}`" target="_blank">%fa:map-marker-alt% 位置情報</a>
|
||||||
|
<div class="map" v-if="p.geo" ref="map"></div>
|
||||||
|
<div class="renote" v-if="p.renote">
|
||||||
|
<mk-note-preview :note="p.renote"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<footer>
|
||||||
|
<mk-reactions-viewer :note="p"/>
|
||||||
|
<button @click="reply" title="返信">
|
||||||
|
%fa:reply%<p class="count" v-if="p.repliesCount > 0">{{ p.repliesCount }}</p>
|
||||||
|
</button>
|
||||||
|
<button @click="renote" title="Renote">
|
||||||
|
%fa:retweet%<p class="count" v-if="p.renoteCount > 0">{{ p.renoteCount }}</p>
|
||||||
|
</button>
|
||||||
|
<button :class="{ reacted: p.myReaction != null }" @click="react" ref="reactButton" title="リアクション">
|
||||||
|
%fa:plus%<p class="count" v-if="p.reactions_count > 0">{{ p.reactions_count }}</p>
|
||||||
|
</button>
|
||||||
|
<button @click="menu" ref="menuButton">
|
||||||
|
%fa:ellipsis-h%
|
||||||
|
</button>
|
||||||
|
</footer>
|
||||||
|
</article>
|
||||||
|
<div class="replies" v-if="!compact">
|
||||||
|
<x-sub v-for="note in replies" :key="note.id" :note="note"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import Vue from 'vue';
|
||||||
|
import dateStringify from '../../../common/scripts/date-stringify';
|
||||||
|
import getAcct from '../../../../../acct/render';
|
||||||
|
import getUserName from '../../../../../renderers/get-user-name';
|
||||||
|
import parse from '../../../../../text/parse';
|
||||||
|
|
||||||
|
import MkPostFormWindow from './post-form-window.vue';
|
||||||
|
import MkRenoteFormWindow from './renote-form-window.vue';
|
||||||
|
import MkNoteMenu from '../../../common/views/components/note-menu.vue';
|
||||||
|
import MkReactionPicker from '../../../common/views/components/reaction-picker.vue';
|
||||||
|
import XSub from './note-detail.sub.vue';
|
||||||
|
|
||||||
|
export default Vue.extend({
|
||||||
|
components: {
|
||||||
|
XSub
|
||||||
|
},
|
||||||
|
|
||||||
|
props: {
|
||||||
|
note: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
compact: {
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
context: [],
|
||||||
|
contextFetching: false,
|
||||||
|
replies: []
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
isRenote(): boolean {
|
||||||
|
return (this.note.renote &&
|
||||||
|
this.note.text == null &&
|
||||||
|
this.note.mediaIds == null &&
|
||||||
|
this.note.poll == null);
|
||||||
|
},
|
||||||
|
p(): any {
|
||||||
|
return this.isRenote ? this.note.renote : this.note;
|
||||||
|
},
|
||||||
|
reactionsCount(): number {
|
||||||
|
return this.p.reactionCounts
|
||||||
|
? Object.keys(this.p.reactionCounts)
|
||||||
|
.map(key => this.p.reactionCounts[key])
|
||||||
|
.reduce((a, b) => a + b)
|
||||||
|
: 0;
|
||||||
|
},
|
||||||
|
title(): string {
|
||||||
|
return dateStringify(this.p.createdAt);
|
||||||
|
},
|
||||||
|
acct(): string {
|
||||||
|
return getAcct(this.note.user);
|
||||||
|
},
|
||||||
|
name(): string {
|
||||||
|
return getUserName(this.note.user);
|
||||||
|
},
|
||||||
|
pAcct(): string {
|
||||||
|
return getAcct(this.p.user);
|
||||||
|
},
|
||||||
|
pName(): string {
|
||||||
|
return getUserName(this.p.user);
|
||||||
|
},
|
||||||
|
urls(): string[] {
|
||||||
|
if (this.p.text) {
|
||||||
|
const ast = parse(this.p.text);
|
||||||
|
return ast
|
||||||
|
.filter(t => (t.type == 'url' || t.type == 'link') && !t.silent)
|
||||||
|
.map(t => t.url);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
// Get replies
|
||||||
|
if (!this.compact) {
|
||||||
|
(this as any).api('notes/replies', {
|
||||||
|
noteId: this.p.id,
|
||||||
|
limit: 8
|
||||||
|
}).then(replies => {
|
||||||
|
this.replies = replies;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw map
|
||||||
|
if (this.p.geo) {
|
||||||
|
const shouldShowMap = (this as any).os.isSignedIn ? (this as any).os.i.account.clientSettings.showMaps : true;
|
||||||
|
if (shouldShowMap) {
|
||||||
|
(this as any).os.getGoogleMaps().then(maps => {
|
||||||
|
const uluru = new maps.LatLng(this.p.geo.coordinates[1], this.p.geo.coordinates[0]);
|
||||||
|
const map = new maps.Map(this.$refs.map, {
|
||||||
|
center: uluru,
|
||||||
|
zoom: 15
|
||||||
|
});
|
||||||
|
new maps.Marker({
|
||||||
|
position: uluru,
|
||||||
|
map: map
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
fetchContext() {
|
||||||
|
this.contextFetching = true;
|
||||||
|
|
||||||
|
// Fetch context
|
||||||
|
(this as any).api('notes/context', {
|
||||||
|
noteId: this.p.replyId
|
||||||
|
}).then(context => {
|
||||||
|
this.contextFetching = false;
|
||||||
|
this.context = context.reverse();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
reply() {
|
||||||
|
(this as any).os.new(MkPostFormWindow, {
|
||||||
|
reply: this.p
|
||||||
|
});
|
||||||
|
},
|
||||||
|
renote() {
|
||||||
|
(this as any).os.new(MkRenoteFormWindow, {
|
||||||
|
note: this.p
|
||||||
|
});
|
||||||
|
},
|
||||||
|
react() {
|
||||||
|
(this as any).os.new(MkReactionPicker, {
|
||||||
|
source: this.$refs.reactButton,
|
||||||
|
note: this.p
|
||||||
|
});
|
||||||
|
},
|
||||||
|
menu() {
|
||||||
|
(this as any).os.new(MkNoteMenu, {
|
||||||
|
source: this.$refs.menuButton,
|
||||||
|
note: this.p
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="stylus" scoped>
|
||||||
|
@import '~const.styl'
|
||||||
|
|
||||||
|
.mk-note-detail
|
||||||
|
margin 0
|
||||||
|
padding 0
|
||||||
|
overflow hidden
|
||||||
|
text-align left
|
||||||
|
background #fff
|
||||||
|
border solid 1px rgba(0, 0, 0, 0.1)
|
||||||
|
border-radius 8px
|
||||||
|
|
||||||
|
> .read-more
|
||||||
|
display block
|
||||||
|
margin 0
|
||||||
|
padding 10px 0
|
||||||
|
width 100%
|
||||||
|
font-size 1em
|
||||||
|
text-align center
|
||||||
|
color #999
|
||||||
|
cursor pointer
|
||||||
|
background #fafafa
|
||||||
|
outline none
|
||||||
|
border none
|
||||||
|
border-bottom solid 1px #eef0f2
|
||||||
|
border-radius 6px 6px 0 0
|
||||||
|
|
||||||
|
&:hover
|
||||||
|
background #f6f6f6
|
||||||
|
|
||||||
|
&:active
|
||||||
|
background #f0f0f0
|
||||||
|
|
||||||
|
&:disabled
|
||||||
|
color #ccc
|
||||||
|
|
||||||
|
> .context
|
||||||
|
> *
|
||||||
|
border-bottom 1px solid #eef0f2
|
||||||
|
|
||||||
|
> .renote
|
||||||
|
color #9dbb00
|
||||||
|
background linear-gradient(to bottom, #edfde2 0%, #fff 100%)
|
||||||
|
|
||||||
|
> p
|
||||||
|
margin 0
|
||||||
|
padding 16px 32px
|
||||||
|
|
||||||
|
.avatar-anchor
|
||||||
|
display inline-block
|
||||||
|
|
||||||
|
.avatar
|
||||||
|
vertical-align bottom
|
||||||
|
min-width 28px
|
||||||
|
min-height 28px
|
||||||
|
max-width 28px
|
||||||
|
max-height 28px
|
||||||
|
margin 0 8px 0 0
|
||||||
|
border-radius 6px
|
||||||
|
|
||||||
|
[data-fa]
|
||||||
|
margin-right 4px
|
||||||
|
|
||||||
|
.name
|
||||||
|
font-weight bold
|
||||||
|
|
||||||
|
& + article
|
||||||
|
padding-top 8px
|
||||||
|
|
||||||
|
> .reply-to
|
||||||
|
border-bottom 1px solid #eef0f2
|
||||||
|
|
||||||
|
> article
|
||||||
|
padding 28px 32px 18px 32px
|
||||||
|
|
||||||
|
&:after
|
||||||
|
content ""
|
||||||
|
display block
|
||||||
|
clear both
|
||||||
|
|
||||||
|
&:hover
|
||||||
|
> .main > footer > button
|
||||||
|
color #888
|
||||||
|
|
||||||
|
> .avatar-anchor
|
||||||
|
display block
|
||||||
|
width 60px
|
||||||
|
height 60px
|
||||||
|
|
||||||
|
> .avatar
|
||||||
|
display block
|
||||||
|
width 60px
|
||||||
|
height 60px
|
||||||
|
margin 0
|
||||||
|
border-radius 8px
|
||||||
|
vertical-align bottom
|
||||||
|
|
||||||
|
> header
|
||||||
|
position absolute
|
||||||
|
top 28px
|
||||||
|
left 108px
|
||||||
|
width calc(100% - 108px)
|
||||||
|
|
||||||
|
> .name
|
||||||
|
display inline-block
|
||||||
|
margin 0
|
||||||
|
line-height 24px
|
||||||
|
color #777
|
||||||
|
font-size 18px
|
||||||
|
font-weight 700
|
||||||
|
text-align left
|
||||||
|
text-decoration none
|
||||||
|
|
||||||
|
&:hover
|
||||||
|
text-decoration underline
|
||||||
|
|
||||||
|
> .username
|
||||||
|
display block
|
||||||
|
text-align left
|
||||||
|
margin 0
|
||||||
|
color #ccc
|
||||||
|
|
||||||
|
> .time
|
||||||
|
position absolute
|
||||||
|
top 0
|
||||||
|
right 32px
|
||||||
|
font-size 1em
|
||||||
|
color #c0c0c0
|
||||||
|
|
||||||
|
> .body
|
||||||
|
padding 8px 0
|
||||||
|
|
||||||
|
> .renote
|
||||||
|
margin 8px 0
|
||||||
|
|
||||||
|
> .mk-note-preview
|
||||||
|
padding 16px
|
||||||
|
border dashed 1px #c0dac6
|
||||||
|
border-radius 8px
|
||||||
|
|
||||||
|
> .location
|
||||||
|
margin 4px 0
|
||||||
|
font-size 12px
|
||||||
|
color #ccc
|
||||||
|
|
||||||
|
> .map
|
||||||
|
width 100%
|
||||||
|
height 300px
|
||||||
|
|
||||||
|
&:empty
|
||||||
|
display none
|
||||||
|
|
||||||
|
> .mk-url-preview
|
||||||
|
margin-top 8px
|
||||||
|
|
||||||
|
> .tags
|
||||||
|
margin 4px 0 0 0
|
||||||
|
|
||||||
|
> *
|
||||||
|
display inline-block
|
||||||
|
margin 0 8px 0 0
|
||||||
|
padding 2px 8px 2px 16px
|
||||||
|
font-size 90%
|
||||||
|
color #8d969e
|
||||||
|
background #edf0f3
|
||||||
|
border-radius 4px
|
||||||
|
|
||||||
|
&:before
|
||||||
|
content ""
|
||||||
|
display block
|
||||||
|
position absolute
|
||||||
|
top 0
|
||||||
|
bottom 0
|
||||||
|
left 4px
|
||||||
|
width 8px
|
||||||
|
height 8px
|
||||||
|
margin auto 0
|
||||||
|
background #fff
|
||||||
|
border-radius 100%
|
||||||
|
|
||||||
|
&:hover
|
||||||
|
text-decoration none
|
||||||
|
background #e2e7ec
|
||||||
|
|
||||||
|
> footer
|
||||||
|
font-size 1.2em
|
||||||
|
|
||||||
|
> button
|
||||||
|
margin 0 28px 0 0
|
||||||
|
padding 8px
|
||||||
|
background transparent
|
||||||
|
border none
|
||||||
|
font-size 1em
|
||||||
|
color #ddd
|
||||||
|
cursor pointer
|
||||||
|
|
||||||
|
&:hover
|
||||||
|
color #666
|
||||||
|
|
||||||
|
> .count
|
||||||
|
display inline
|
||||||
|
margin 0 0 0 8px
|
||||||
|
color #999
|
||||||
|
|
||||||
|
&.reacted
|
||||||
|
color $theme-color
|
||||||
|
|
||||||
|
> .replies
|
||||||
|
> *
|
||||||
|
border-top 1px solid #eef0f2
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style lang="stylus" module>
|
||||||
|
.text
|
||||||
|
cursor default
|
||||||
|
display block
|
||||||
|
margin 0
|
||||||
|
padding 0
|
||||||
|
overflow-wrap break-word
|
||||||
|
font-size 1.5em
|
||||||
|
color #717171
|
||||||
|
</style>
|
|
@ -1,18 +1,18 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="mk-post-preview" :title="title">
|
<div class="mk-note-preview" :title="title">
|
||||||
<router-link class="avatar-anchor" :to="`/@${acct}`">
|
<router-link class="avatar-anchor" :to="`/@${acct}`">
|
||||||
<img class="avatar" :src="`${post.user.avatarUrl}?thumbnail&size=64`" alt="avatar" v-user-preview="post.userId"/>
|
<img class="avatar" :src="`${note.user.avatarUrl}?thumbnail&size=64`" alt="avatar" v-user-preview="note.userId"/>
|
||||||
</router-link>
|
</router-link>
|
||||||
<div class="main">
|
<div class="main">
|
||||||
<header>
|
<header>
|
||||||
<router-link class="name" :to="`/@${acct}`" v-user-preview="post.userId">{{ name }}</router-link>
|
<router-link class="name" :to="`/@${acct}`" v-user-preview="note.userId">{{ name }}</router-link>
|
||||||
<span class="username">@{{ acct }}</span>
|
<span class="username">@{{ acct }}</span>
|
||||||
<router-link class="time" :to="`/@${acct}/${post.id}`">
|
<router-link class="time" :to="`/@${acct}/${note.id}`">
|
||||||
<mk-time :time="post.createdAt"/>
|
<mk-time :time="note.createdAt"/>
|
||||||
</router-link>
|
</router-link>
|
||||||
</header>
|
</header>
|
||||||
<div class="body">
|
<div class="body">
|
||||||
<mk-sub-post-content class="text" :post="post"/>
|
<mk-sub-note-content class="text" :note="note"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -25,23 +25,23 @@ import getAcct from '../../../../../acct/render';
|
||||||
import getUserName from '../../../../../renderers/get-user-name';
|
import getUserName from '../../../../../renderers/get-user-name';
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
props: ['post'],
|
props: ['note'],
|
||||||
computed: {
|
computed: {
|
||||||
acct() {
|
acct() {
|
||||||
return getAcct(this.post.user);
|
return getAcct(this.note.user);
|
||||||
},
|
},
|
||||||
name() {
|
name() {
|
||||||
return getUserName(this.post.user);
|
return getUserName(this.note.user);
|
||||||
},
|
},
|
||||||
title(): string {
|
title(): string {
|
||||||
return dateStringify(this.post.createdAt);
|
return dateStringify(this.note.createdAt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="stylus" scoped>
|
<style lang="stylus" scoped>
|
||||||
.mk-post-preview
|
.mk-note-preview
|
||||||
font-size 0.9em
|
font-size 0.9em
|
||||||
background #fff
|
background #fff
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="sub" :title="title">
|
<div class="sub" :title="title">
|
||||||
<router-link class="avatar-anchor" :to="`/@${acct}`">
|
<router-link class="avatar-anchor" :to="`/@${acct}`">
|
||||||
<img class="avatar" :src="`${post.user.avatarUrl}?thumbnail&size=64`" alt="avatar" v-user-preview="post.userId"/>
|
<img class="avatar" :src="`${note.user.avatarUrl}?thumbnail&size=64`" alt="avatar" v-user-preview="note.userId"/>
|
||||||
</router-link>
|
</router-link>
|
||||||
<div class="main">
|
<div class="main">
|
||||||
<header>
|
<header>
|
||||||
<router-link class="name" :to="`/@${acct}`" v-user-preview="post.userId">{{ name }}</router-link>
|
<router-link class="name" :to="`/@${acct}`" v-user-preview="note.userId">{{ name }}</router-link>
|
||||||
<span class="username">@{{ acct }}</span>
|
<span class="username">@{{ acct }}</span>
|
||||||
<router-link class="created-at" :to="`/@${acct}/${post.id}`">
|
<router-link class="created-at" :to="`/@${acct}/${note.id}`">
|
||||||
<mk-time :time="post.createdAt"/>
|
<mk-time :time="note.createdAt"/>
|
||||||
</router-link>
|
</router-link>
|
||||||
</header>
|
</header>
|
||||||
<div class="body">
|
<div class="body">
|
||||||
<mk-sub-post-content class="text" :post="post"/>
|
<mk-sub-note-content class="text" :note="note"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -25,16 +25,16 @@ import getAcct from '../../../../../acct/render';
|
||||||
import getUserName from '../../../../../renderers/get-user-name';
|
import getUserName from '../../../../../renderers/get-user-name';
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
props: ['post'],
|
props: ['note'],
|
||||||
computed: {
|
computed: {
|
||||||
acct() {
|
acct() {
|
||||||
return getAcct(this.post.user);
|
return getAcct(this.note.user);
|
||||||
},
|
},
|
||||||
name(): string {
|
name(): string {
|
||||||
return getUserName(this.post.user);
|
return getUserName(this.note.user);
|
||||||
},
|
},
|
||||||
title(): string {
|
title(): string {
|
||||||
return dateStringify(this.post.createdAt);
|
return dateStringify(this.note.createdAt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
596
src/client/app/desktop/views/components/notes.note.vue
Normal file
596
src/client/app/desktop/views/components/notes.note.vue
Normal file
|
@ -0,0 +1,596 @@
|
||||||
|
<template>
|
||||||
|
<div class="note" tabindex="-1" :title="title" @keydown="onKeydown">
|
||||||
|
<div class="reply-to" v-if="p.reply">
|
||||||
|
<x-sub :note="p.reply"/>
|
||||||
|
</div>
|
||||||
|
<div class="renote" v-if="isRenote">
|
||||||
|
<p>
|
||||||
|
<router-link class="avatar-anchor" :to="`/@${acct}`" v-user-preview="note.userId">
|
||||||
|
<img class="avatar" :src="`${note.user.avatarUrl}?thumbnail&size=32`" alt="avatar"/>
|
||||||
|
</router-link>
|
||||||
|
%fa:retweet%
|
||||||
|
<span>{{ '%i18n:desktop.tags.mk-timeline-note.reposted-by%'.substr(0, '%i18n:desktop.tags.mk-timeline-note.reposted-by%'.indexOf('{')) }}</span>
|
||||||
|
<a class="name" :href="`/@${acct}`" v-user-preview="note.userId">{{ getUserName(note.user) }}</a>
|
||||||
|
<span>{{ '%i18n:desktop.tags.mk-timeline-note.reposted-by%'.substr('%i18n:desktop.tags.mk-timeline-note.reposted-by%'.indexOf('}') + 1) }}</span>
|
||||||
|
</p>
|
||||||
|
<mk-time :time="note.createdAt"/>
|
||||||
|
</div>
|
||||||
|
<article>
|
||||||
|
<router-link class="avatar-anchor" :to="`/@${acct}`">
|
||||||
|
<img class="avatar" :src="`${p.user.avatarUrl}?thumbnail&size=64`" alt="avatar" v-user-preview="p.user.id"/>
|
||||||
|
</router-link>
|
||||||
|
<div class="main">
|
||||||
|
<header>
|
||||||
|
<router-link class="name" :to="`/@${acct}`" v-user-preview="p.user.id">{{ acct }}</router-link>
|
||||||
|
<span class="is-bot" v-if="p.user.host === null && p.user.account.isBot">bot</span>
|
||||||
|
<span class="username">@{{ acct }}</span>
|
||||||
|
<div class="info">
|
||||||
|
<span class="app" v-if="p.app">via <b>{{ p.app.name }}</b></span>
|
||||||
|
<span class="mobile" v-if="p.viaMobile">%fa:mobile-alt%</span>
|
||||||
|
<router-link class="created-at" :to="url">
|
||||||
|
<mk-time :time="p.createdAt"/>
|
||||||
|
</router-link>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
<div class="body">
|
||||||
|
<p class="channel" v-if="p.channel">
|
||||||
|
<a :href="`${_CH_URL_}/${p.channel.id}`" target="_blank">{{ p.channel.title }}</a>:
|
||||||
|
</p>
|
||||||
|
<div class="text">
|
||||||
|
<a class="reply" v-if="p.reply">%fa:reply%</a>
|
||||||
|
<mk-note-html v-if="p.textHtml" :text="p.text" :i="os.i" :class="$style.text"/>
|
||||||
|
<a class="rp" v-if="p.renote">RP:</a>
|
||||||
|
</div>
|
||||||
|
<div class="media" v-if="p.media.length > 0">
|
||||||
|
<mk-media-list :media-list="p.media"/>
|
||||||
|
</div>
|
||||||
|
<mk-poll v-if="p.poll" :note="p" ref="pollViewer"/>
|
||||||
|
<div class="tags" v-if="p.tags && p.tags.length > 0">
|
||||||
|
<router-link v-for="tag in p.tags" :key="tag" :to="`/search?q=#${tag}`">{{ tag }}</router-link>
|
||||||
|
</div>
|
||||||
|
<a class="location" v-if="p.geo" :href="`http://maps.google.com/maps?q=${p.geo.coordinates[1]},${p.geo.coordinates[0]}`" target="_blank">%fa:map-marker-alt% 位置情報</a>
|
||||||
|
<div class="map" v-if="p.geo" ref="map"></div>
|
||||||
|
<div class="renote" v-if="p.renote">
|
||||||
|
<mk-note-preview :note="p.renote"/>
|
||||||
|
</div>
|
||||||
|
<mk-url-preview v-for="url in urls" :url="url" :key="url"/>
|
||||||
|
</div>
|
||||||
|
<footer>
|
||||||
|
<mk-reactions-viewer :note="p" ref="reactionsViewer"/>
|
||||||
|
<button @click="reply" title="%i18n:desktop.tags.mk-timeline-note.reply%">
|
||||||
|
%fa:reply%<p class="count" v-if="p.repliesCount > 0">{{ p.repliesCount }}</p>
|
||||||
|
</button>
|
||||||
|
<button @click="renote" title="%i18n:desktop.tags.mk-timeline-note.renote%">
|
||||||
|
%fa:retweet%<p class="count" v-if="p.renoteCount > 0">{{ p.renoteCount }}</p>
|
||||||
|
</button>
|
||||||
|
<button :class="{ reacted: p.myReaction != null }" @click="react" ref="reactButton" title="%i18n:desktop.tags.mk-timeline-note.add-reaction%">
|
||||||
|
%fa:plus%<p class="count" v-if="p.reactions_count > 0">{{ p.reactions_count }}</p>
|
||||||
|
</button>
|
||||||
|
<button @click="menu" ref="menuButton">
|
||||||
|
%fa:ellipsis-h%
|
||||||
|
</button>
|
||||||
|
<button title="%i18n:desktop.tags.mk-timeline-note.detail">
|
||||||
|
<template v-if="!isDetailOpened">%fa:caret-down%</template>
|
||||||
|
<template v-if="isDetailOpened">%fa:caret-up%</template>
|
||||||
|
</button>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
<div class="detail" v-if="isDetailOpened">
|
||||||
|
<mk-note-status-graph width="462" height="130" :note="p"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import Vue from 'vue';
|
||||||
|
import dateStringify from '../../../common/scripts/date-stringify';
|
||||||
|
import getAcct from '../../../../../acct/render';
|
||||||
|
import getUserName from '../../../../../renderers/get-user-name';
|
||||||
|
import parse from '../../../../../text/parse';
|
||||||
|
|
||||||
|
import MkPostFormWindow from './post-form-window.vue';
|
||||||
|
import MkRenoteFormWindow from './renote-form-window.vue';
|
||||||
|
import MkNoteMenu from '../../../common/views/components/note-menu.vue';
|
||||||
|
import MkReactionPicker from '../../../common/views/components/reaction-picker.vue';
|
||||||
|
import XSub from './notes.note.sub.vue';
|
||||||
|
|
||||||
|
function focus(el, fn) {
|
||||||
|
const target = fn(el);
|
||||||
|
if (target) {
|
||||||
|
if (target.hasAttribute('tabindex')) {
|
||||||
|
target.focus();
|
||||||
|
} else {
|
||||||
|
focus(target, fn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Vue.extend({
|
||||||
|
components: {
|
||||||
|
XSub
|
||||||
|
},
|
||||||
|
|
||||||
|
props: ['note'],
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
isDetailOpened: false,
|
||||||
|
connection: null,
|
||||||
|
connectionId: null
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
acct(): string {
|
||||||
|
return getAcct(this.p.user);
|
||||||
|
},
|
||||||
|
name(): string {
|
||||||
|
return getUserName(this.p.user);
|
||||||
|
},
|
||||||
|
isRenote(): boolean {
|
||||||
|
return (this.note.renote &&
|
||||||
|
this.note.text == null &&
|
||||||
|
this.note.mediaIds == null &&
|
||||||
|
this.note.poll == null);
|
||||||
|
},
|
||||||
|
p(): any {
|
||||||
|
return this.isRenote ? this.note.renote : this.note;
|
||||||
|
},
|
||||||
|
reactionsCount(): number {
|
||||||
|
return this.p.reactionCounts
|
||||||
|
? Object.keys(this.p.reactionCounts)
|
||||||
|
.map(key => this.p.reactionCounts[key])
|
||||||
|
.reduce((a, b) => a + b)
|
||||||
|
: 0;
|
||||||
|
},
|
||||||
|
title(): string {
|
||||||
|
return dateStringify(this.p.createdAt);
|
||||||
|
},
|
||||||
|
url(): string {
|
||||||
|
return `/@${this.acct}/${this.p.id}`;
|
||||||
|
},
|
||||||
|
urls(): string[] {
|
||||||
|
if (this.p.text) {
|
||||||
|
const ast = parse(this.p.text);
|
||||||
|
return ast
|
||||||
|
.filter(t => (t.type == 'url' || t.type == 'link') && !t.silent)
|
||||||
|
.map(t => t.url);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
created() {
|
||||||
|
if ((this as any).os.isSignedIn) {
|
||||||
|
this.connection = (this as any).os.stream.getConnection();
|
||||||
|
this.connectionId = (this as any).os.stream.use();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
this.capture(true);
|
||||||
|
|
||||||
|
if ((this as any).os.isSignedIn) {
|
||||||
|
this.connection.on('_connected_', this.onStreamConnected);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw map
|
||||||
|
if (this.p.geo) {
|
||||||
|
const shouldShowMap = (this as any).os.isSignedIn ? (this as any).os.i.account.clientSettings.showMaps : true;
|
||||||
|
if (shouldShowMap) {
|
||||||
|
(this as any).os.getGoogleMaps().then(maps => {
|
||||||
|
const uluru = new maps.LatLng(this.p.geo.coordinates[1], this.p.geo.coordinates[0]);
|
||||||
|
const map = new maps.Map(this.$refs.map, {
|
||||||
|
center: uluru,
|
||||||
|
zoom: 15
|
||||||
|
});
|
||||||
|
new maps.Marker({
|
||||||
|
position: uluru,
|
||||||
|
map: map
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
beforeDestroy() {
|
||||||
|
this.decapture(true);
|
||||||
|
|
||||||
|
if ((this as any).os.isSignedIn) {
|
||||||
|
this.connection.off('_connected_', this.onStreamConnected);
|
||||||
|
(this as any).os.stream.dispose(this.connectionId);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
capture(withHandler = false) {
|
||||||
|
if ((this as any).os.isSignedIn) {
|
||||||
|
this.connection.send({
|
||||||
|
type: 'capture',
|
||||||
|
id: this.p.id
|
||||||
|
});
|
||||||
|
if (withHandler) this.connection.on('note-updated', this.onStreamNoteUpdated);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
decapture(withHandler = false) {
|
||||||
|
if ((this as any).os.isSignedIn) {
|
||||||
|
this.connection.send({
|
||||||
|
type: 'decapture',
|
||||||
|
id: this.p.id
|
||||||
|
});
|
||||||
|
if (withHandler) this.connection.off('note-updated', this.onStreamNoteUpdated);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onStreamConnected() {
|
||||||
|
this.capture();
|
||||||
|
},
|
||||||
|
onStreamNoteUpdated(data) {
|
||||||
|
const note = data.note;
|
||||||
|
if (note.id == this.note.id) {
|
||||||
|
this.$emit('update:note', note);
|
||||||
|
} else if (note.id == this.note.renoteId) {
|
||||||
|
this.note.renote = note;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
reply() {
|
||||||
|
(this as any).os.new(MkPostFormWindow, {
|
||||||
|
reply: this.p
|
||||||
|
});
|
||||||
|
},
|
||||||
|
renote() {
|
||||||
|
(this as any).os.new(MkRenoteFormWindow, {
|
||||||
|
note: this.p
|
||||||
|
});
|
||||||
|
},
|
||||||
|
react() {
|
||||||
|
(this as any).os.new(MkReactionPicker, {
|
||||||
|
source: this.$refs.reactButton,
|
||||||
|
note: this.p
|
||||||
|
});
|
||||||
|
},
|
||||||
|
menu() {
|
||||||
|
(this as any).os.new(MkNoteMenu, {
|
||||||
|
source: this.$refs.menuButton,
|
||||||
|
note: this.p
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onKeydown(e) {
|
||||||
|
let shouldBeCancel = true;
|
||||||
|
|
||||||
|
switch (true) {
|
||||||
|
case e.which == 38: // [↑]
|
||||||
|
case e.which == 74: // [j]
|
||||||
|
case e.which == 9 && e.shiftKey: // [Shift] + [Tab]
|
||||||
|
focus(this.$el, e => e.previousElementSibling);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case e.which == 40: // [↓]
|
||||||
|
case e.which == 75: // [k]
|
||||||
|
case e.which == 9: // [Tab]
|
||||||
|
focus(this.$el, e => e.nextElementSibling);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case e.which == 81: // [q]
|
||||||
|
case e.which == 69: // [e]
|
||||||
|
this.renote();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case e.which == 70: // [f]
|
||||||
|
case e.which == 76: // [l]
|
||||||
|
//this.like();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case e.which == 82: // [r]
|
||||||
|
this.reply();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
shouldBeCancel = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldBeCancel) e.preventDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="stylus" scoped>
|
||||||
|
@import '~const.styl'
|
||||||
|
|
||||||
|
.note
|
||||||
|
margin 0
|
||||||
|
padding 0
|
||||||
|
background #fff
|
||||||
|
border-bottom solid 1px #eaeaea
|
||||||
|
|
||||||
|
&:first-child
|
||||||
|
border-top-left-radius 6px
|
||||||
|
border-top-right-radius 6px
|
||||||
|
|
||||||
|
> .renote
|
||||||
|
border-top-left-radius 6px
|
||||||
|
border-top-right-radius 6px
|
||||||
|
|
||||||
|
&:last-of-type
|
||||||
|
border-bottom none
|
||||||
|
|
||||||
|
&:focus
|
||||||
|
z-index 1
|
||||||
|
|
||||||
|
&:after
|
||||||
|
content ""
|
||||||
|
pointer-events none
|
||||||
|
position absolute
|
||||||
|
top 2px
|
||||||
|
right 2px
|
||||||
|
bottom 2px
|
||||||
|
left 2px
|
||||||
|
border 2px solid rgba($theme-color, 0.3)
|
||||||
|
border-radius 4px
|
||||||
|
|
||||||
|
> .renote
|
||||||
|
color #9dbb00
|
||||||
|
background linear-gradient(to bottom, #edfde2 0%, #fff 100%)
|
||||||
|
|
||||||
|
> p
|
||||||
|
margin 0
|
||||||
|
padding 16px 32px
|
||||||
|
line-height 28px
|
||||||
|
|
||||||
|
.avatar-anchor
|
||||||
|
display inline-block
|
||||||
|
|
||||||
|
.avatar
|
||||||
|
vertical-align bottom
|
||||||
|
width 28px
|
||||||
|
height 28px
|
||||||
|
margin 0 8px 0 0
|
||||||
|
border-radius 6px
|
||||||
|
|
||||||
|
[data-fa]
|
||||||
|
margin-right 4px
|
||||||
|
|
||||||
|
.name
|
||||||
|
font-weight bold
|
||||||
|
|
||||||
|
> .mk-time
|
||||||
|
position absolute
|
||||||
|
top 16px
|
||||||
|
right 32px
|
||||||
|
font-size 0.9em
|
||||||
|
line-height 28px
|
||||||
|
|
||||||
|
& + article
|
||||||
|
padding-top 8px
|
||||||
|
|
||||||
|
> .reply-to
|
||||||
|
padding 0 16px
|
||||||
|
background rgba(0, 0, 0, 0.0125)
|
||||||
|
|
||||||
|
> .mk-note-preview
|
||||||
|
background transparent
|
||||||
|
|
||||||
|
> article
|
||||||
|
padding 28px 32px 18px 32px
|
||||||
|
|
||||||
|
&:after
|
||||||
|
content ""
|
||||||
|
display block
|
||||||
|
clear both
|
||||||
|
|
||||||
|
&:hover
|
||||||
|
> .main > footer > button
|
||||||
|
color #888
|
||||||
|
|
||||||
|
> .avatar-anchor
|
||||||
|
display block
|
||||||
|
float left
|
||||||
|
margin 0 16px 10px 0
|
||||||
|
//position -webkit-sticky
|
||||||
|
//position sticky
|
||||||
|
//top 74px
|
||||||
|
|
||||||
|
> .avatar
|
||||||
|
display block
|
||||||
|
width 58px
|
||||||
|
height 58px
|
||||||
|
margin 0
|
||||||
|
border-radius 8px
|
||||||
|
vertical-align bottom
|
||||||
|
|
||||||
|
> .main
|
||||||
|
float left
|
||||||
|
width calc(100% - 74px)
|
||||||
|
|
||||||
|
> header
|
||||||
|
display flex
|
||||||
|
align-items center
|
||||||
|
margin-bottom 4px
|
||||||
|
white-space nowrap
|
||||||
|
|
||||||
|
> .name
|
||||||
|
display block
|
||||||
|
margin 0 .5em 0 0
|
||||||
|
padding 0
|
||||||
|
overflow hidden
|
||||||
|
color #627079
|
||||||
|
font-size 1em
|
||||||
|
font-weight bold
|
||||||
|
text-decoration none
|
||||||
|
text-overflow ellipsis
|
||||||
|
|
||||||
|
&:hover
|
||||||
|
text-decoration underline
|
||||||
|
|
||||||
|
> .is-bot
|
||||||
|
margin 0 .5em 0 0
|
||||||
|
padding 1px 6px
|
||||||
|
font-size 12px
|
||||||
|
color #aaa
|
||||||
|
border solid 1px #ddd
|
||||||
|
border-radius 3px
|
||||||
|
|
||||||
|
> .username
|
||||||
|
margin 0 .5em 0 0
|
||||||
|
color #ccc
|
||||||
|
|
||||||
|
> .info
|
||||||
|
margin-left auto
|
||||||
|
font-size 0.9em
|
||||||
|
|
||||||
|
> .mobile
|
||||||
|
margin-right 8px
|
||||||
|
color #ccc
|
||||||
|
|
||||||
|
> .app
|
||||||
|
margin-right 8px
|
||||||
|
padding-right 8px
|
||||||
|
color #ccc
|
||||||
|
border-right solid 1px #eaeaea
|
||||||
|
|
||||||
|
> .created-at
|
||||||
|
color #c0c0c0
|
||||||
|
|
||||||
|
> .body
|
||||||
|
|
||||||
|
> .text
|
||||||
|
cursor default
|
||||||
|
display block
|
||||||
|
margin 0
|
||||||
|
padding 0
|
||||||
|
overflow-wrap break-word
|
||||||
|
font-size 1.1em
|
||||||
|
color #717171
|
||||||
|
|
||||||
|
>>> .quote
|
||||||
|
margin 8px
|
||||||
|
padding 6px 12px
|
||||||
|
color #aaa
|
||||||
|
border-left solid 3px #eee
|
||||||
|
|
||||||
|
> .reply
|
||||||
|
margin-right 8px
|
||||||
|
color #717171
|
||||||
|
|
||||||
|
> .rp
|
||||||
|
margin-left 4px
|
||||||
|
font-style oblique
|
||||||
|
color #a0bf46
|
||||||
|
|
||||||
|
> .location
|
||||||
|
margin 4px 0
|
||||||
|
font-size 12px
|
||||||
|
color #ccc
|
||||||
|
|
||||||
|
> .map
|
||||||
|
width 100%
|
||||||
|
height 300px
|
||||||
|
|
||||||
|
&:empty
|
||||||
|
display none
|
||||||
|
|
||||||
|
> .tags
|
||||||
|
margin 4px 0 0 0
|
||||||
|
|
||||||
|
> *
|
||||||
|
display inline-block
|
||||||
|
margin 0 8px 0 0
|
||||||
|
padding 2px 8px 2px 16px
|
||||||
|
font-size 90%
|
||||||
|
color #8d969e
|
||||||
|
background #edf0f3
|
||||||
|
border-radius 4px
|
||||||
|
|
||||||
|
&:before
|
||||||
|
content ""
|
||||||
|
display block
|
||||||
|
position absolute
|
||||||
|
top 0
|
||||||
|
bottom 0
|
||||||
|
left 4px
|
||||||
|
width 8px
|
||||||
|
height 8px
|
||||||
|
margin auto 0
|
||||||
|
background #fff
|
||||||
|
border-radius 100%
|
||||||
|
|
||||||
|
&:hover
|
||||||
|
text-decoration none
|
||||||
|
background #e2e7ec
|
||||||
|
|
||||||
|
.mk-url-preview
|
||||||
|
margin-top 8px
|
||||||
|
|
||||||
|
> .channel
|
||||||
|
margin 0
|
||||||
|
|
||||||
|
> .mk-poll
|
||||||
|
font-size 80%
|
||||||
|
|
||||||
|
> .renote
|
||||||
|
margin 8px 0
|
||||||
|
|
||||||
|
> .mk-note-preview
|
||||||
|
padding 16px
|
||||||
|
border dashed 1px #c0dac6
|
||||||
|
border-radius 8px
|
||||||
|
|
||||||
|
> footer
|
||||||
|
> button
|
||||||
|
margin 0 28px 0 0
|
||||||
|
padding 0 8px
|
||||||
|
line-height 32px
|
||||||
|
font-size 1em
|
||||||
|
color #ddd
|
||||||
|
background transparent
|
||||||
|
border none
|
||||||
|
cursor pointer
|
||||||
|
|
||||||
|
&:hover
|
||||||
|
color #666
|
||||||
|
|
||||||
|
> .count
|
||||||
|
display inline
|
||||||
|
margin 0 0 0 8px
|
||||||
|
color #999
|
||||||
|
|
||||||
|
&.reacted
|
||||||
|
color $theme-color
|
||||||
|
|
||||||
|
&:last-child
|
||||||
|
position absolute
|
||||||
|
right 0
|
||||||
|
margin 0
|
||||||
|
|
||||||
|
> .detail
|
||||||
|
padding-top 4px
|
||||||
|
background rgba(0, 0, 0, 0.0125)
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style lang="stylus" module>
|
||||||
|
.text
|
||||||
|
|
||||||
|
code
|
||||||
|
padding 4px 8px
|
||||||
|
margin 0 0.5em
|
||||||
|
font-size 80%
|
||||||
|
color #525252
|
||||||
|
background #f8f8f8
|
||||||
|
border-radius 2px
|
||||||
|
|
||||||
|
pre > code
|
||||||
|
padding 16px
|
||||||
|
margin 0
|
||||||
|
|
||||||
|
[data-is-me]:after
|
||||||
|
content "you"
|
||||||
|
padding 0 4px
|
||||||
|
margin-left 4px
|
||||||
|
font-size 80%
|
||||||
|
color $theme-color-foreground
|
||||||
|
background $theme-color
|
||||||
|
border-radius 4px
|
||||||
|
</style>
|
|
@ -1,10 +1,10 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="mk-posts">
|
<div class="mk-notes">
|
||||||
<template v-for="(post, i) in _posts">
|
<template v-for="(note, i) in _notes">
|
||||||
<x-post :post="post" :key="post.id" @update:post="onPostUpdated(i, $event)"/>
|
<x-note :note="note" :key="note.id" @update:note="onNoteUpdated(i, $event)"/>
|
||||||
<p class="date" v-if="i != posts.length - 1 && post._date != _posts[i + 1]._date">
|
<p class="date" v-if="i != notes.length - 1 && note._date != _notes[i + 1]._date">
|
||||||
<span>%fa:angle-up%{{ post._datetext }}</span>
|
<span>%fa:angle-up%{{ note._datetext }}</span>
|
||||||
<span>%fa:angle-down%{{ _posts[i + 1]._datetext }}</span>
|
<span>%fa:angle-down%{{ _notes[i + 1]._datetext }}</span>
|
||||||
</p>
|
</p>
|
||||||
</template>
|
</template>
|
||||||
<footer>
|
<footer>
|
||||||
|
@ -15,26 +15,26 @@
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import XPost from './posts.post.vue';
|
import XNote from './notes.note.vue';
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
components: {
|
components: {
|
||||||
XPost
|
XNote
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
posts: {
|
notes: {
|
||||||
type: Array,
|
type: Array,
|
||||||
default: () => []
|
default: () => []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
_posts(): any[] {
|
_notes(): any[] {
|
||||||
return (this.posts as any).map(post => {
|
return (this.notes as any).map(note => {
|
||||||
const date = new Date(post.createdAt).getDate();
|
const date = new Date(note.createdAt).getDate();
|
||||||
const month = new Date(post.createdAt).getMonth() + 1;
|
const month = new Date(note.createdAt).getMonth() + 1;
|
||||||
post._date = date;
|
note._date = date;
|
||||||
post._datetext = `${month}月 ${date}日`;
|
note._datetext = `${month}月 ${date}日`;
|
||||||
return post;
|
return note;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -42,15 +42,15 @@ export default Vue.extend({
|
||||||
focus() {
|
focus() {
|
||||||
(this.$el as any).children[0].focus();
|
(this.$el as any).children[0].focus();
|
||||||
},
|
},
|
||||||
onPostUpdated(i, post) {
|
onNoteUpdated(i, note) {
|
||||||
Vue.set((this as any).posts, i, post);
|
Vue.set((this as any).notes, i, note);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="stylus" scoped>
|
<style lang="stylus" scoped>
|
||||||
.mk-posts
|
.mk-notes
|
||||||
|
|
||||||
> .date
|
> .date
|
||||||
display block
|
display block
|
|
@ -13,33 +13,33 @@
|
||||||
<mk-reaction-icon :reaction="notification.reaction"/>
|
<mk-reaction-icon :reaction="notification.reaction"/>
|
||||||
<router-link :to="`/@${getAcct(notification.user)}`" v-user-preview="notification.user.id">{{ getUserName(notification.user) }}</router-link>
|
<router-link :to="`/@${getAcct(notification.user)}`" v-user-preview="notification.user.id">{{ getUserName(notification.user) }}</router-link>
|
||||||
</p>
|
</p>
|
||||||
<router-link class="post-ref" :to="`/@${getAcct(notification.post.user)}/${notification.post.id}`">
|
<router-link class="note-ref" :to="`/@${getAcct(notification.note.user)}/${notification.note.id}`">
|
||||||
%fa:quote-left%{{ getPostSummary(notification.post) }}%fa:quote-right%
|
%fa:quote-left%{{ getNoteSummary(notification.note) }}%fa:quote-right%
|
||||||
</router-link>
|
</router-link>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-if="notification.type == 'repost'">
|
<template v-if="notification.type == 'renote'">
|
||||||
<router-link class="avatar-anchor" :to="`/@${getAcct(notification.post.user)}`" v-user-preview="notification.post.userId">
|
<router-link class="avatar-anchor" :to="`/@${getAcct(notification.note.user)}`" v-user-preview="notification.note.userId">
|
||||||
<img class="avatar" :src="`${notification.post.user.avatarUrl}?thumbnail&size=48`" alt="avatar"/>
|
<img class="avatar" :src="`${notification.note.user.avatarUrl}?thumbnail&size=48`" alt="avatar"/>
|
||||||
</router-link>
|
</router-link>
|
||||||
<div class="text">
|
<div class="text">
|
||||||
<p>%fa:retweet%
|
<p>%fa:retweet%
|
||||||
<router-link :to="`/@${getAcct(notification.post.user)}`" v-user-preview="notification.post.userId">{{ getUserName(notification.post.user) }}</router-link>
|
<router-link :to="`/@${getAcct(notification.note.user)}`" v-user-preview="notification.note.userId">{{ getUserName(notification.note.user) }}</router-link>
|
||||||
</p>
|
</p>
|
||||||
<router-link class="post-ref" :to="`/@${getAcct(notification.post.user)}/${notification.post.id}`">
|
<router-link class="note-ref" :to="`/@${getAcct(notification.note.user)}/${notification.note.id}`">
|
||||||
%fa:quote-left%{{ getPostSummary(notification.post.repost) }}%fa:quote-right%
|
%fa:quote-left%{{ getNoteSummary(notification.note.renote) }}%fa:quote-right%
|
||||||
</router-link>
|
</router-link>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-if="notification.type == 'quote'">
|
<template v-if="notification.type == 'quote'">
|
||||||
<router-link class="avatar-anchor" :to="`/@${getAcct(notification.post.user)}`" v-user-preview="notification.post.userId">
|
<router-link class="avatar-anchor" :to="`/@${getAcct(notification.note.user)}`" v-user-preview="notification.note.userId">
|
||||||
<img class="avatar" :src="`${notification.post.user.avatarUrl}?thumbnail&size=48`" alt="avatar"/>
|
<img class="avatar" :src="`${notification.note.user.avatarUrl}?thumbnail&size=48`" alt="avatar"/>
|
||||||
</router-link>
|
</router-link>
|
||||||
<div class="text">
|
<div class="text">
|
||||||
<p>%fa:quote-left%
|
<p>%fa:quote-left%
|
||||||
<router-link :to="`/@${getAcct(notification.post.user)}`" v-user-preview="notification.post.userId">{{ getUserName(notification.post.user) }}</router-link>
|
<router-link :to="`/@${getAcct(notification.note.user)}`" v-user-preview="notification.note.userId">{{ getUserName(notification.note.user) }}</router-link>
|
||||||
</p>
|
</p>
|
||||||
<router-link class="post-preview" :to="`/@${getAcct(notification.post.user)}/${notification.post.id}`">{{ getPostSummary(notification.post) }}</router-link>
|
<router-link class="note-preview" :to="`/@${getAcct(notification.note.user)}/${notification.note.id}`">{{ getNoteSummary(notification.note) }}</router-link>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-if="notification.type == 'follow'">
|
<template v-if="notification.type == 'follow'">
|
||||||
|
@ -53,25 +53,25 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-if="notification.type == 'reply'">
|
<template v-if="notification.type == 'reply'">
|
||||||
<router-link class="avatar-anchor" :to="`/@${getAcct(notification.post.user)}`" v-user-preview="notification.post.userId">
|
<router-link class="avatar-anchor" :to="`/@${getAcct(notification.note.user)}`" v-user-preview="notification.note.userId">
|
||||||
<img class="avatar" :src="`${notification.post.user.avatarUrl}?thumbnail&size=48`" alt="avatar"/>
|
<img class="avatar" :src="`${notification.note.user.avatarUrl}?thumbnail&size=48`" alt="avatar"/>
|
||||||
</router-link>
|
</router-link>
|
||||||
<div class="text">
|
<div class="text">
|
||||||
<p>%fa:reply%
|
<p>%fa:reply%
|
||||||
<router-link :to="`/@${getAcct(notification.post.user)}`" v-user-preview="notification.post.userId">{{ getUserName(notification.post.user) }}</router-link>
|
<router-link :to="`/@${getAcct(notification.note.user)}`" v-user-preview="notification.note.userId">{{ getUserName(notification.note.user) }}</router-link>
|
||||||
</p>
|
</p>
|
||||||
<router-link class="post-preview" :to="`/@${getAcct(notification.post.user)}/${notification.post.id}`">{{ getPostSummary(notification.post) }}</router-link>
|
<router-link class="note-preview" :to="`/@${getAcct(notification.note.user)}/${notification.note.id}`">{{ getNoteSummary(notification.note) }}</router-link>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-if="notification.type == 'mention'">
|
<template v-if="notification.type == 'mention'">
|
||||||
<router-link class="avatar-anchor" :to="`/@${getAcct(notification.post.user)}`" v-user-preview="notification.post.userId">
|
<router-link class="avatar-anchor" :to="`/@${getAcct(notification.note.user)}`" v-user-preview="notification.note.userId">
|
||||||
<img class="avatar" :src="`${notification.post.user.avatarUrl}?thumbnail&size=48`" alt="avatar"/>
|
<img class="avatar" :src="`${notification.note.user.avatarUrl}?thumbnail&size=48`" alt="avatar"/>
|
||||||
</router-link>
|
</router-link>
|
||||||
<div class="text">
|
<div class="text">
|
||||||
<p>%fa:at%
|
<p>%fa:at%
|
||||||
<router-link :to="`/@${getAcct(notification.post.user)}`" v-user-preview="notification.post.userId">{{ getUserName(notification.post.user) }}</router-link>
|
<router-link :to="`/@${getAcct(notification.note.user)}`" v-user-preview="notification.note.userId">{{ getUserName(notification.note.user) }}</router-link>
|
||||||
</p>
|
</p>
|
||||||
<a class="post-preview" :href="`/@${getAcct(notification.post.user)}/${notification.post.id}`">{{ getPostSummary(notification.post) }}</a>
|
<a class="note-preview" :href="`/@${getAcct(notification.note.user)}/${notification.note.id}`">{{ getNoteSummary(notification.note) }}</a>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-if="notification.type == 'poll_vote'">
|
<template v-if="notification.type == 'poll_vote'">
|
||||||
|
@ -80,8 +80,8 @@
|
||||||
</router-link>
|
</router-link>
|
||||||
<div class="text">
|
<div class="text">
|
||||||
<p>%fa:chart-pie%<a :href="`/@${getAcct(notification.user)}`" v-user-preview="notification.user.id">{{ getUserName(notification.user) }}</a></p>
|
<p>%fa:chart-pie%<a :href="`/@${getAcct(notification.user)}`" v-user-preview="notification.user.id">{{ getUserName(notification.user) }}</a></p>
|
||||||
<router-link class="post-ref" :to="`/@${getAcct(notification.post.user)}/${notification.post.id}`">
|
<router-link class="note-ref" :to="`/@${getAcct(notification.note.user)}/${notification.note.id}`">
|
||||||
%fa:quote-left%{{ getPostSummary(notification.post) }}%fa:quote-right%
|
%fa:quote-left%{{ getNoteSummary(notification.note) }}%fa:quote-right%
|
||||||
</router-link>
|
</router-link>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -103,7 +103,7 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import getAcct from '../../../../../acct/render';
|
import getAcct from '../../../../../acct/render';
|
||||||
import getPostSummary from '../../../../../renderers/get-post-summary';
|
import getNoteSummary from '../../../../../renderers/get-note-summary';
|
||||||
import getUserName from '../../../../../renderers/get-user-name';
|
import getUserName from '../../../../../renderers/get-user-name';
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
|
@ -115,7 +115,7 @@ export default Vue.extend({
|
||||||
moreNotifications: false,
|
moreNotifications: false,
|
||||||
connection: null,
|
connection: null,
|
||||||
connectionId: null,
|
connectionId: null,
|
||||||
getPostSummary
|
getNoteSummary
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -241,10 +241,10 @@ export default Vue.extend({
|
||||||
i, .mk-reaction-icon
|
i, .mk-reaction-icon
|
||||||
margin-right 4px
|
margin-right 4px
|
||||||
|
|
||||||
.post-preview
|
.note-preview
|
||||||
color rgba(0, 0, 0, 0.7)
|
color rgba(0, 0, 0, 0.7)
|
||||||
|
|
||||||
.post-ref
|
.note-ref
|
||||||
color rgba(0, 0, 0, 0.7)
|
color rgba(0, 0, 0, 0.7)
|
||||||
|
|
||||||
[data-fa]
|
[data-fa]
|
||||||
|
@ -254,7 +254,7 @@ export default Vue.extend({
|
||||||
display inline-block
|
display inline-block
|
||||||
margin-right 3px
|
margin-right 3px
|
||||||
|
|
||||||
&.repost, &.quote
|
&.renote, &.quote
|
||||||
.text p i
|
.text p i
|
||||||
color #77B255
|
color #77B255
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="mk-post-detail" :title="title">
|
<div class="mk-note-detail" :title="title">
|
||||||
<button
|
<button
|
||||||
class="read-more"
|
class="read-more"
|
||||||
v-if="p.reply && p.reply.replyId && context == null"
|
v-if="p.reply && p.reply.replyId && context == null"
|
||||||
|
@ -11,19 +11,19 @@
|
||||||
<template v-if="contextFetching">%fa:spinner .pulse%</template>
|
<template v-if="contextFetching">%fa:spinner .pulse%</template>
|
||||||
</button>
|
</button>
|
||||||
<div class="context">
|
<div class="context">
|
||||||
<x-sub v-for="post in context" :key="post.id" :post="post"/>
|
<x-sub v-for="note in context" :key="note.id" :note="note"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="reply-to" v-if="p.reply">
|
<div class="reply-to" v-if="p.reply">
|
||||||
<x-sub :post="p.reply"/>
|
<x-sub :note="p.reply"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="repost" v-if="isRepost">
|
<div class="renote" v-if="isRenote">
|
||||||
<p>
|
<p>
|
||||||
<router-link class="avatar-anchor" :to="`/@${acct}`" v-user-preview="post.userId">
|
<router-link class="avatar-anchor" :to="`/@${acct}`" v-user-preview="note.userId">
|
||||||
<img class="avatar" :src="`${post.user.avatarUrl}?thumbnail&size=32`" alt="avatar"/>
|
<img class="avatar" :src="`${note.user.avatarUrl}?thumbnail&size=32`" alt="avatar"/>
|
||||||
</router-link>
|
</router-link>
|
||||||
%fa:retweet%
|
%fa:retweet%
|
||||||
<router-link class="name" :href="`/@${acct}`">{{ getUserName(post.user) }}</router-link>
|
<router-link class="name" :href="`/@${acct}`">{{ getUserName(note.user) }}</router-link>
|
||||||
がRepost
|
がRenote
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<article>
|
<article>
|
||||||
|
@ -38,28 +38,28 @@
|
||||||
</router-link>
|
</router-link>
|
||||||
</header>
|
</header>
|
||||||
<div class="body">
|
<div class="body">
|
||||||
<mk-post-html :class="$style.text" v-if="p.text" :text="p.text" :i="os.i"/>
|
<mk-note-html :class="$style.text" v-if="p.text" :text="p.text" :i="os.i"/>
|
||||||
<div class="media" v-if="p.media.length > 0">
|
<div class="media" v-if="p.media.length > 0">
|
||||||
<mk-media-list :media-list="p.media"/>
|
<mk-media-list :media-list="p.media"/>
|
||||||
</div>
|
</div>
|
||||||
<mk-poll v-if="p.poll" :post="p"/>
|
<mk-poll v-if="p.poll" :note="p"/>
|
||||||
<mk-url-preview v-for="url in urls" :url="url" :key="url"/>
|
<mk-url-preview v-for="url in urls" :url="url" :key="url"/>
|
||||||
<div class="tags" v-if="p.tags && p.tags.length > 0">
|
<div class="tags" v-if="p.tags && p.tags.length > 0">
|
||||||
<router-link v-for="tag in p.tags" :key="tag" :to="`/search?q=#${tag}`">{{ tag }}</router-link>
|
<router-link v-for="tag in p.tags" :key="tag" :to="`/search?q=#${tag}`">{{ tag }}</router-link>
|
||||||
</div>
|
</div>
|
||||||
<a class="location" v-if="p.geo" :href="`http://maps.google.com/maps?q=${p.geo.coordinates[1]},${p.geo.coordinates[0]}`" target="_blank">%fa:map-marker-alt% 位置情報</a>
|
<a class="location" v-if="p.geo" :href="`http://maps.google.com/maps?q=${p.geo.coordinates[1]},${p.geo.coordinates[0]}`" target="_blank">%fa:map-marker-alt% 位置情報</a>
|
||||||
<div class="map" v-if="p.geo" ref="map"></div>
|
<div class="map" v-if="p.geo" ref="map"></div>
|
||||||
<div class="repost" v-if="p.repost">
|
<div class="renote" v-if="p.renote">
|
||||||
<mk-post-preview :post="p.repost"/>
|
<mk-note-preview :note="p.renote"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<footer>
|
<footer>
|
||||||
<mk-reactions-viewer :post="p"/>
|
<mk-reactions-viewer :note="p"/>
|
||||||
<button @click="reply" title="返信">
|
<button @click="reply" title="返信">
|
||||||
%fa:reply%<p class="count" v-if="p.repliesCount > 0">{{ p.repliesCount }}</p>
|
%fa:reply%<p class="count" v-if="p.repliesCount > 0">{{ p.repliesCount }}</p>
|
||||||
</button>
|
</button>
|
||||||
<button @click="repost" title="Repost">
|
<button @click="renote" title="Renote">
|
||||||
%fa:retweet%<p class="count" v-if="p.repostCount > 0">{{ p.repostCount }}</p>
|
%fa:retweet%<p class="count" v-if="p.renoteCount > 0">{{ p.renoteCount }}</p>
|
||||||
</button>
|
</button>
|
||||||
<button :class="{ reacted: p.myReaction != null }" @click="react" ref="reactButton" title="リアクション">
|
<button :class="{ reacted: p.myReaction != null }" @click="react" ref="reactButton" title="リアクション">
|
||||||
%fa:plus%<p class="count" v-if="p.reactions_count > 0">{{ p.reactions_count }}</p>
|
%fa:plus%<p class="count" v-if="p.reactions_count > 0">{{ p.reactions_count }}</p>
|
||||||
|
@ -70,7 +70,7 @@
|
||||||
</footer>
|
</footer>
|
||||||
</article>
|
</article>
|
||||||
<div class="replies" v-if="!compact">
|
<div class="replies" v-if="!compact">
|
||||||
<x-sub v-for="post in replies" :key="post.id" :post="post"/>
|
<x-sub v-for="note in replies" :key="note.id" :note="note"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -83,10 +83,10 @@ import getUserName from '../../../../../renderers/get-user-name';
|
||||||
import parse from '../../../../../text/parse';
|
import parse from '../../../../../text/parse';
|
||||||
|
|
||||||
import MkPostFormWindow from './post-form-window.vue';
|
import MkPostFormWindow from './post-form-window.vue';
|
||||||
import MkRepostFormWindow from './repost-form-window.vue';
|
import MkRenoteFormWindow from './renote-form-window.vue';
|
||||||
import MkPostMenu from '../../../common/views/components/post-menu.vue';
|
import MkNoteMenu from '../../../common/views/components/note-menu.vue';
|
||||||
import MkReactionPicker from '../../../common/views/components/reaction-picker.vue';
|
import MkReactionPicker from '../../../common/views/components/reaction-picker.vue';
|
||||||
import XSub from './post-detail.sub.vue';
|
import XSub from './note-detail.sub.vue';
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
components: {
|
components: {
|
||||||
|
@ -94,7 +94,7 @@ export default Vue.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
post: {
|
note: {
|
||||||
type: Object,
|
type: Object,
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
|
@ -112,14 +112,14 @@ export default Vue.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
isRepost(): boolean {
|
isRenote(): boolean {
|
||||||
return (this.post.repost &&
|
return (this.note.renote &&
|
||||||
this.post.text == null &&
|
this.note.text == null &&
|
||||||
this.post.mediaIds == null &&
|
this.note.mediaIds == null &&
|
||||||
this.post.poll == null);
|
this.note.poll == null);
|
||||||
},
|
},
|
||||||
p(): any {
|
p(): any {
|
||||||
return this.isRepost ? this.post.repost : this.post;
|
return this.isRenote ? this.note.renote : this.note;
|
||||||
},
|
},
|
||||||
reactionsCount(): number {
|
reactionsCount(): number {
|
||||||
return this.p.reactionCounts
|
return this.p.reactionCounts
|
||||||
|
@ -132,10 +132,10 @@ export default Vue.extend({
|
||||||
return dateStringify(this.p.createdAt);
|
return dateStringify(this.p.createdAt);
|
||||||
},
|
},
|
||||||
acct(): string {
|
acct(): string {
|
||||||
return getAcct(this.post.user);
|
return getAcct(this.note.user);
|
||||||
},
|
},
|
||||||
name(): string {
|
name(): string {
|
||||||
return getUserName(this.post.user);
|
return getUserName(this.note.user);
|
||||||
},
|
},
|
||||||
pAcct(): string {
|
pAcct(): string {
|
||||||
return getAcct(this.p.user);
|
return getAcct(this.p.user);
|
||||||
|
@ -158,8 +158,8 @@ export default Vue.extend({
|
||||||
mounted() {
|
mounted() {
|
||||||
// Get replies
|
// Get replies
|
||||||
if (!this.compact) {
|
if (!this.compact) {
|
||||||
(this as any).api('posts/replies', {
|
(this as any).api('notes/replies', {
|
||||||
postId: this.p.id,
|
noteId: this.p.id,
|
||||||
limit: 8
|
limit: 8
|
||||||
}).then(replies => {
|
}).then(replies => {
|
||||||
this.replies = replies;
|
this.replies = replies;
|
||||||
|
@ -190,8 +190,8 @@ export default Vue.extend({
|
||||||
this.contextFetching = true;
|
this.contextFetching = true;
|
||||||
|
|
||||||
// Fetch context
|
// Fetch context
|
||||||
(this as any).api('posts/context', {
|
(this as any).api('notes/context', {
|
||||||
postId: this.p.replyId
|
noteId: this.p.replyId
|
||||||
}).then(context => {
|
}).then(context => {
|
||||||
this.contextFetching = false;
|
this.contextFetching = false;
|
||||||
this.context = context.reverse();
|
this.context = context.reverse();
|
||||||
|
@ -202,21 +202,21 @@ export default Vue.extend({
|
||||||
reply: this.p
|
reply: this.p
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
repost() {
|
renote() {
|
||||||
(this as any).os.new(MkRepostFormWindow, {
|
(this as any).os.new(MkRenoteFormWindow, {
|
||||||
post: this.p
|
note: this.p
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
react() {
|
react() {
|
||||||
(this as any).os.new(MkReactionPicker, {
|
(this as any).os.new(MkReactionPicker, {
|
||||||
source: this.$refs.reactButton,
|
source: this.$refs.reactButton,
|
||||||
post: this.p
|
note: this.p
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
menu() {
|
menu() {
|
||||||
(this as any).os.new(MkPostMenu, {
|
(this as any).os.new(MkNoteMenu, {
|
||||||
source: this.$refs.menuButton,
|
source: this.$refs.menuButton,
|
||||||
post: this.p
|
note: this.p
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -226,7 +226,7 @@ export default Vue.extend({
|
||||||
<style lang="stylus" scoped>
|
<style lang="stylus" scoped>
|
||||||
@import '~const.styl'
|
@import '~const.styl'
|
||||||
|
|
||||||
.mk-post-detail
|
.mk-note-detail
|
||||||
margin 0
|
margin 0
|
||||||
padding 0
|
padding 0
|
||||||
overflow hidden
|
overflow hidden
|
||||||
|
@ -263,7 +263,7 @@ export default Vue.extend({
|
||||||
> *
|
> *
|
||||||
border-bottom 1px solid #eef0f2
|
border-bottom 1px solid #eef0f2
|
||||||
|
|
||||||
> .repost
|
> .renote
|
||||||
color #9dbb00
|
color #9dbb00
|
||||||
background linear-gradient(to bottom, #edfde2 0%, #fff 100%)
|
background linear-gradient(to bottom, #edfde2 0%, #fff 100%)
|
||||||
|
|
||||||
|
@ -355,10 +355,10 @@ export default Vue.extend({
|
||||||
> .body
|
> .body
|
||||||
padding 8px 0
|
padding 8px 0
|
||||||
|
|
||||||
> .repost
|
> .renote
|
||||||
margin 8px 0
|
margin 8px 0
|
||||||
|
|
||||||
> .mk-post-preview
|
> .mk-note-preview
|
||||||
padding 16px
|
padding 16px
|
||||||
border dashed 1px #c0dac6
|
border dashed 1px #c0dac6
|
||||||
border-radius 8px
|
border-radius 8px
|
||||||
|
|
|
@ -2,13 +2,13 @@
|
||||||
<mk-window ref="window" is-modal @closed="$destroy">
|
<mk-window ref="window" is-modal @closed="$destroy">
|
||||||
<span slot="header">
|
<span slot="header">
|
||||||
<span :class="$style.icon" v-if="geo">%fa:map-marker-alt%</span>
|
<span :class="$style.icon" v-if="geo">%fa:map-marker-alt%</span>
|
||||||
<span v-if="!reply">%i18n:desktop.tags.mk-post-form-window.post%</span>
|
<span v-if="!reply">%i18n:desktop.tags.mk-post-form-window.note%</span>
|
||||||
<span v-if="reply">%i18n:desktop.tags.mk-post-form-window.reply%</span>
|
<span v-if="reply">%i18n:desktop.tags.mk-post-form-window.reply%</span>
|
||||||
<span :class="$style.count" v-if="media.length != 0">{{ '%i18n:desktop.tags.mk-post-form-window.attaches%'.replace('{}', media.length) }}</span>
|
<span :class="$style.count" v-if="media.length != 0">{{ '%i18n:desktop.tags.mk-post-form-window.attaches%'.replace('{}', media.length) }}</span>
|
||||||
<span :class="$style.count" v-if="uploadings.length != 0">{{ '%i18n:desktop.tags.mk-post-form-window.uploading-media%'.replace('{}', uploadings.length) }}<mk-ellipsis/></span>
|
<span :class="$style.count" v-if="uploadings.length != 0">{{ '%i18n:desktop.tags.mk-post-form-window.uploading-media%'.replace('{}', uploadings.length) }}<mk-ellipsis/></span>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<mk-post-preview v-if="reply" :class="$style.postPreview" :post="reply"/>
|
<mk-note-preview v-if="reply" :class="$style.notePreview" :note="reply"/>
|
||||||
<mk-post-form ref="form"
|
<mk-post-form ref="form"
|
||||||
:reply="reply"
|
:reply="reply"
|
||||||
@posted="onPosted"
|
@posted="onPosted"
|
||||||
|
@ -70,7 +70,7 @@ export default Vue.extend({
|
||||||
&:after
|
&:after
|
||||||
content ')'
|
content ')'
|
||||||
|
|
||||||
.postPreview
|
.notePreview
|
||||||
margin 16px 22px
|
margin 16px 22px
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -46,7 +46,7 @@ export default Vue.extend({
|
||||||
components: {
|
components: {
|
||||||
XDraggable
|
XDraggable
|
||||||
},
|
},
|
||||||
props: ['reply', 'repost'],
|
props: ['reply', 'renote'],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
posting: false,
|
posting: false,
|
||||||
|
@ -61,28 +61,28 @@ export default Vue.extend({
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
draftId(): string {
|
draftId(): string {
|
||||||
return this.repost
|
return this.renote
|
||||||
? 'repost:' + this.repost.id
|
? 'renote:' + this.renote.id
|
||||||
: this.reply
|
: this.reply
|
||||||
? 'reply:' + this.reply.id
|
? 'reply:' + this.reply.id
|
||||||
: 'post';
|
: 'note';
|
||||||
},
|
},
|
||||||
placeholder(): string {
|
placeholder(): string {
|
||||||
return this.repost
|
return this.renote
|
||||||
? '%i18n:desktop.tags.mk-post-form.quote-placeholder%'
|
? '%i18n:desktop.tags.mk-post-form.quote-placeholder%'
|
||||||
: this.reply
|
: this.reply
|
||||||
? '%i18n:desktop.tags.mk-post-form.reply-placeholder%'
|
? '%i18n:desktop.tags.mk-post-form.reply-placeholder%'
|
||||||
: '%i18n:desktop.tags.mk-post-form.post-placeholder%';
|
: '%i18n:desktop.tags.mk-post-form.note-placeholder%';
|
||||||
},
|
},
|
||||||
submitText(): string {
|
submitText(): string {
|
||||||
return this.repost
|
return this.renote
|
||||||
? '%i18n:desktop.tags.mk-post-form.repost%'
|
? '%i18n:desktop.tags.mk-post-form.renote%'
|
||||||
: this.reply
|
: this.reply
|
||||||
? '%i18n:desktop.tags.mk-post-form.reply%'
|
? '%i18n:desktop.tags.mk-post-form.reply%'
|
||||||
: '%i18n:desktop.tags.mk-post-form.post%';
|
: '%i18n:desktop.tags.mk-post-form.note%';
|
||||||
},
|
},
|
||||||
canPost(): boolean {
|
canPost(): boolean {
|
||||||
return !this.posting && (this.text.length != 0 || this.files.length != 0 || this.poll || this.repost);
|
return !this.posting && (this.text.length != 0 || this.files.length != 0 || this.poll || this.renote);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
|
@ -217,11 +217,11 @@ export default Vue.extend({
|
||||||
post() {
|
post() {
|
||||||
this.posting = true;
|
this.posting = true;
|
||||||
|
|
||||||
(this as any).api('posts/create', {
|
(this as any).api('notes/create', {
|
||||||
text: this.text == '' ? undefined : this.text,
|
text: this.text == '' ? undefined : this.text,
|
||||||
mediaIds: this.files.length > 0 ? this.files.map(f => f.id) : undefined,
|
mediaIds: this.files.length > 0 ? this.files.map(f => f.id) : undefined,
|
||||||
replyId: this.reply ? this.reply.id : undefined,
|
replyId: this.reply ? this.reply.id : undefined,
|
||||||
repostId: this.repost ? this.repost.id : undefined,
|
renoteId: this.renote ? this.renote.id : undefined,
|
||||||
poll: this.poll ? (this.$refs.poll as any).get() : undefined,
|
poll: this.poll ? (this.$refs.poll as any).get() : undefined,
|
||||||
geo: this.geo ? {
|
geo: this.geo ? {
|
||||||
coordinates: [this.geo.longitude, this.geo.latitude],
|
coordinates: [this.geo.longitude, this.geo.latitude],
|
||||||
|
@ -235,17 +235,17 @@ export default Vue.extend({
|
||||||
this.clear();
|
this.clear();
|
||||||
this.deleteDraft();
|
this.deleteDraft();
|
||||||
this.$emit('posted');
|
this.$emit('posted');
|
||||||
(this as any).apis.notify(this.repost
|
(this as any).apis.notify(this.renote
|
||||||
? '%i18n:desktop.tags.mk-post-form.reposted%'
|
? '%i18n:desktop.tags.mk-post-form.reposted%'
|
||||||
: this.reply
|
: this.reply
|
||||||
? '%i18n:desktop.tags.mk-post-form.replied%'
|
? '%i18n:desktop.tags.mk-post-form.replied%'
|
||||||
: '%i18n:desktop.tags.mk-post-form.posted%');
|
: '%i18n:desktop.tags.mk-post-form.posted%');
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
(this as any).apis.notify(this.repost
|
(this as any).apis.notify(this.renote
|
||||||
? '%i18n:desktop.tags.mk-post-form.repost-failed%'
|
? '%i18n:desktop.tags.mk-post-form.renote-failed%'
|
||||||
: this.reply
|
: this.reply
|
||||||
? '%i18n:desktop.tags.mk-post-form.reply-failed%'
|
? '%i18n:desktop.tags.mk-post-form.reply-failed%'
|
||||||
: '%i18n:desktop.tags.mk-post-form.post-failed%');
|
: '%i18n:desktop.tags.mk-post-form.note-failed%');
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
this.posting = false;
|
this.posting = false;
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,19 +1,19 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="post" tabindex="-1" :title="title" @keydown="onKeydown">
|
<div class="note" tabindex="-1" :title="title" @keydown="onKeydown">
|
||||||
<div class="reply-to" v-if="p.reply">
|
<div class="reply-to" v-if="p.reply">
|
||||||
<x-sub :post="p.reply"/>
|
<x-sub :note="p.reply"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="repost" v-if="isRepost">
|
<div class="renote" v-if="isRenote">
|
||||||
<p>
|
<p>
|
||||||
<router-link class="avatar-anchor" :to="`/@${acct}`" v-user-preview="post.userId">
|
<router-link class="avatar-anchor" :to="`/@${acct}`" v-user-preview="note.userId">
|
||||||
<img class="avatar" :src="`${post.user.avatarUrl}?thumbnail&size=32`" alt="avatar"/>
|
<img class="avatar" :src="`${note.user.avatarUrl}?thumbnail&size=32`" alt="avatar"/>
|
||||||
</router-link>
|
</router-link>
|
||||||
%fa:retweet%
|
%fa:retweet%
|
||||||
<span>{{ '%i18n:desktop.tags.mk-timeline-post.reposted-by%'.substr(0, '%i18n:desktop.tags.mk-timeline-post.reposted-by%'.indexOf('{')) }}</span>
|
<span>{{ '%i18n:desktop.tags.mk-timeline-note.reposted-by%'.substr(0, '%i18n:desktop.tags.mk-timeline-note.reposted-by%'.indexOf('{')) }}</span>
|
||||||
<a class="name" :href="`/@${acct}`" v-user-preview="post.userId">{{ getUserName(post.user) }}</a>
|
<a class="name" :href="`/@${acct}`" v-user-preview="note.userId">{{ getUserName(note.user) }}</a>
|
||||||
<span>{{ '%i18n:desktop.tags.mk-timeline-post.reposted-by%'.substr('%i18n:desktop.tags.mk-timeline-post.reposted-by%'.indexOf('}') + 1) }}</span>
|
<span>{{ '%i18n:desktop.tags.mk-timeline-note.reposted-by%'.substr('%i18n:desktop.tags.mk-timeline-note.reposted-by%'.indexOf('}') + 1) }}</span>
|
||||||
</p>
|
</p>
|
||||||
<mk-time :time="post.createdAt"/>
|
<mk-time :time="note.createdAt"/>
|
||||||
</div>
|
</div>
|
||||||
<article>
|
<article>
|
||||||
<router-link class="avatar-anchor" :to="`/@${acct}`">
|
<router-link class="avatar-anchor" :to="`/@${acct}`">
|
||||||
|
@ -38,38 +38,38 @@
|
||||||
</p>
|
</p>
|
||||||
<div class="text">
|
<div class="text">
|
||||||
<a class="reply" v-if="p.reply">%fa:reply%</a>
|
<a class="reply" v-if="p.reply">%fa:reply%</a>
|
||||||
<mk-post-html v-if="p.textHtml" :text="p.text" :i="os.i" :class="$style.text"/>
|
<mk-note-html v-if="p.textHtml" :text="p.text" :i="os.i" :class="$style.text"/>
|
||||||
<a class="rp" v-if="p.repost">RP:</a>
|
<a class="rp" v-if="p.renote">RP:</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="media" v-if="p.media.length > 0">
|
<div class="media" v-if="p.media.length > 0">
|
||||||
<mk-media-list :media-list="p.media"/>
|
<mk-media-list :media-list="p.media"/>
|
||||||
</div>
|
</div>
|
||||||
<mk-poll v-if="p.poll" :post="p" ref="pollViewer"/>
|
<mk-poll v-if="p.poll" :note="p" ref="pollViewer"/>
|
||||||
<div class="tags" v-if="p.tags && p.tags.length > 0">
|
<div class="tags" v-if="p.tags && p.tags.length > 0">
|
||||||
<router-link v-for="tag in p.tags" :key="tag" :to="`/search?q=#${tag}`">{{ tag }}</router-link>
|
<router-link v-for="tag in p.tags" :key="tag" :to="`/search?q=#${tag}`">{{ tag }}</router-link>
|
||||||
</div>
|
</div>
|
||||||
<a class="location" v-if="p.geo" :href="`http://maps.google.com/maps?q=${p.geo.coordinates[1]},${p.geo.coordinates[0]}`" target="_blank">%fa:map-marker-alt% 位置情報</a>
|
<a class="location" v-if="p.geo" :href="`http://maps.google.com/maps?q=${p.geo.coordinates[1]},${p.geo.coordinates[0]}`" target="_blank">%fa:map-marker-alt% 位置情報</a>
|
||||||
<div class="map" v-if="p.geo" ref="map"></div>
|
<div class="map" v-if="p.geo" ref="map"></div>
|
||||||
<div class="repost" v-if="p.repost">
|
<div class="renote" v-if="p.renote">
|
||||||
<mk-post-preview :post="p.repost"/>
|
<mk-note-preview :note="p.renote"/>
|
||||||
</div>
|
</div>
|
||||||
<mk-url-preview v-for="url in urls" :url="url" :key="url"/>
|
<mk-url-preview v-for="url in urls" :url="url" :key="url"/>
|
||||||
</div>
|
</div>
|
||||||
<footer>
|
<footer>
|
||||||
<mk-reactions-viewer :post="p" ref="reactionsViewer"/>
|
<mk-reactions-viewer :note="p" ref="reactionsViewer"/>
|
||||||
<button @click="reply" title="%i18n:desktop.tags.mk-timeline-post.reply%">
|
<button @click="reply" title="%i18n:desktop.tags.mk-timeline-note.reply%">
|
||||||
%fa:reply%<p class="count" v-if="p.repliesCount > 0">{{ p.repliesCount }}</p>
|
%fa:reply%<p class="count" v-if="p.repliesCount > 0">{{ p.repliesCount }}</p>
|
||||||
</button>
|
</button>
|
||||||
<button @click="repost" title="%i18n:desktop.tags.mk-timeline-post.repost%">
|
<button @click="renote" title="%i18n:desktop.tags.mk-timeline-note.renote%">
|
||||||
%fa:retweet%<p class="count" v-if="p.repostCount > 0">{{ p.repostCount }}</p>
|
%fa:retweet%<p class="count" v-if="p.renoteCount > 0">{{ p.renoteCount }}</p>
|
||||||
</button>
|
</button>
|
||||||
<button :class="{ reacted: p.myReaction != null }" @click="react" ref="reactButton" title="%i18n:desktop.tags.mk-timeline-post.add-reaction%">
|
<button :class="{ reacted: p.myReaction != null }" @click="react" ref="reactButton" title="%i18n:desktop.tags.mk-timeline-note.add-reaction%">
|
||||||
%fa:plus%<p class="count" v-if="p.reactions_count > 0">{{ p.reactions_count }}</p>
|
%fa:plus%<p class="count" v-if="p.reactions_count > 0">{{ p.reactions_count }}</p>
|
||||||
</button>
|
</button>
|
||||||
<button @click="menu" ref="menuButton">
|
<button @click="menu" ref="menuButton">
|
||||||
%fa:ellipsis-h%
|
%fa:ellipsis-h%
|
||||||
</button>
|
</button>
|
||||||
<button title="%i18n:desktop.tags.mk-timeline-post.detail">
|
<button title="%i18n:desktop.tags.mk-timeline-note.detail">
|
||||||
<template v-if="!isDetailOpened">%fa:caret-down%</template>
|
<template v-if="!isDetailOpened">%fa:caret-down%</template>
|
||||||
<template v-if="isDetailOpened">%fa:caret-up%</template>
|
<template v-if="isDetailOpened">%fa:caret-up%</template>
|
||||||
</button>
|
</button>
|
||||||
|
@ -77,7 +77,7 @@
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
<div class="detail" v-if="isDetailOpened">
|
<div class="detail" v-if="isDetailOpened">
|
||||||
<mk-post-status-graph width="462" height="130" :post="p"/>
|
<mk-note-status-graph width="462" height="130" :note="p"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -90,10 +90,10 @@ import getUserName from '../../../../../renderers/get-user-name';
|
||||||
import parse from '../../../../../text/parse';
|
import parse from '../../../../../text/parse';
|
||||||
|
|
||||||
import MkPostFormWindow from './post-form-window.vue';
|
import MkPostFormWindow from './post-form-window.vue';
|
||||||
import MkRepostFormWindow from './repost-form-window.vue';
|
import MkRenoteFormWindow from './renote-form-window.vue';
|
||||||
import MkPostMenu from '../../../common/views/components/post-menu.vue';
|
import MkNoteMenu from '../../../common/views/components/note-menu.vue';
|
||||||
import MkReactionPicker from '../../../common/views/components/reaction-picker.vue';
|
import MkReactionPicker from '../../../common/views/components/reaction-picker.vue';
|
||||||
import XSub from './posts.post.sub.vue';
|
import XSub from './notes.note.sub.vue';
|
||||||
|
|
||||||
function focus(el, fn) {
|
function focus(el, fn) {
|
||||||
const target = fn(el);
|
const target = fn(el);
|
||||||
|
@ -111,7 +111,7 @@ export default Vue.extend({
|
||||||
XSub
|
XSub
|
||||||
},
|
},
|
||||||
|
|
||||||
props: ['post'],
|
props: ['note'],
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
@ -128,14 +128,14 @@ export default Vue.extend({
|
||||||
name(): string {
|
name(): string {
|
||||||
return getUserName(this.p.user);
|
return getUserName(this.p.user);
|
||||||
},
|
},
|
||||||
isRepost(): boolean {
|
isRenote(): boolean {
|
||||||
return (this.post.repost &&
|
return (this.note.renote &&
|
||||||
this.post.text == null &&
|
this.note.text == null &&
|
||||||
this.post.mediaIds == null &&
|
this.note.mediaIds == null &&
|
||||||
this.post.poll == null);
|
this.note.poll == null);
|
||||||
},
|
},
|
||||||
p(): any {
|
p(): any {
|
||||||
return this.isRepost ? this.post.repost : this.post;
|
return this.isRenote ? this.note.renote : this.note;
|
||||||
},
|
},
|
||||||
reactionsCount(): number {
|
reactionsCount(): number {
|
||||||
return this.p.reactionCounts
|
return this.p.reactionCounts
|
||||||
|
@ -211,7 +211,7 @@ export default Vue.extend({
|
||||||
type: 'capture',
|
type: 'capture',
|
||||||
id: this.p.id
|
id: this.p.id
|
||||||
});
|
});
|
||||||
if (withHandler) this.connection.on('post-updated', this.onStreamPostUpdated);
|
if (withHandler) this.connection.on('note-updated', this.onStreamNoteUpdated);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
decapture(withHandler = false) {
|
decapture(withHandler = false) {
|
||||||
|
@ -220,18 +220,18 @@ export default Vue.extend({
|
||||||
type: 'decapture',
|
type: 'decapture',
|
||||||
id: this.p.id
|
id: this.p.id
|
||||||
});
|
});
|
||||||
if (withHandler) this.connection.off('post-updated', this.onStreamPostUpdated);
|
if (withHandler) this.connection.off('note-updated', this.onStreamNoteUpdated);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onStreamConnected() {
|
onStreamConnected() {
|
||||||
this.capture();
|
this.capture();
|
||||||
},
|
},
|
||||||
onStreamPostUpdated(data) {
|
onStreamNoteUpdated(data) {
|
||||||
const post = data.post;
|
const note = data.note;
|
||||||
if (post.id == this.post.id) {
|
if (note.id == this.note.id) {
|
||||||
this.$emit('update:post', post);
|
this.$emit('update:note', note);
|
||||||
} else if (post.id == this.post.repostId) {
|
} else if (note.id == this.note.renoteId) {
|
||||||
this.post.repost = post;
|
this.note.renote = note;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
reply() {
|
reply() {
|
||||||
|
@ -239,21 +239,21 @@ export default Vue.extend({
|
||||||
reply: this.p
|
reply: this.p
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
repost() {
|
renote() {
|
||||||
(this as any).os.new(MkRepostFormWindow, {
|
(this as any).os.new(MkRenoteFormWindow, {
|
||||||
post: this.p
|
note: this.p
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
react() {
|
react() {
|
||||||
(this as any).os.new(MkReactionPicker, {
|
(this as any).os.new(MkReactionPicker, {
|
||||||
source: this.$refs.reactButton,
|
source: this.$refs.reactButton,
|
||||||
post: this.p
|
note: this.p
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
menu() {
|
menu() {
|
||||||
(this as any).os.new(MkPostMenu, {
|
(this as any).os.new(MkNoteMenu, {
|
||||||
source: this.$refs.menuButton,
|
source: this.$refs.menuButton,
|
||||||
post: this.p
|
note: this.p
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
onKeydown(e) {
|
onKeydown(e) {
|
||||||
|
@ -274,7 +274,7 @@ export default Vue.extend({
|
||||||
|
|
||||||
case e.which == 81: // [q]
|
case e.which == 81: // [q]
|
||||||
case e.which == 69: // [e]
|
case e.which == 69: // [e]
|
||||||
this.repost();
|
this.renote();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case e.which == 70: // [f]
|
case e.which == 70: // [f]
|
||||||
|
@ -299,7 +299,7 @@ export default Vue.extend({
|
||||||
<style lang="stylus" scoped>
|
<style lang="stylus" scoped>
|
||||||
@import '~const.styl'
|
@import '~const.styl'
|
||||||
|
|
||||||
.post
|
.note
|
||||||
margin 0
|
margin 0
|
||||||
padding 0
|
padding 0
|
||||||
background #fff
|
background #fff
|
||||||
|
@ -309,7 +309,7 @@ export default Vue.extend({
|
||||||
border-top-left-radius 6px
|
border-top-left-radius 6px
|
||||||
border-top-right-radius 6px
|
border-top-right-radius 6px
|
||||||
|
|
||||||
> .repost
|
> .renote
|
||||||
border-top-left-radius 6px
|
border-top-left-radius 6px
|
||||||
border-top-right-radius 6px
|
border-top-right-radius 6px
|
||||||
|
|
||||||
|
@ -330,7 +330,7 @@ export default Vue.extend({
|
||||||
border 2px solid rgba($theme-color, 0.3)
|
border 2px solid rgba($theme-color, 0.3)
|
||||||
border-radius 4px
|
border-radius 4px
|
||||||
|
|
||||||
> .repost
|
> .renote
|
||||||
color #9dbb00
|
color #9dbb00
|
||||||
background linear-gradient(to bottom, #edfde2 0%, #fff 100%)
|
background linear-gradient(to bottom, #edfde2 0%, #fff 100%)
|
||||||
|
|
||||||
|
@ -369,7 +369,7 @@ export default Vue.extend({
|
||||||
padding 0 16px
|
padding 0 16px
|
||||||
background rgba(0, 0, 0, 0.0125)
|
background rgba(0, 0, 0, 0.0125)
|
||||||
|
|
||||||
> .mk-post-preview
|
> .mk-note-preview
|
||||||
background transparent
|
background transparent
|
||||||
|
|
||||||
> article
|
> article
|
||||||
|
@ -529,10 +529,10 @@ export default Vue.extend({
|
||||||
> .mk-poll
|
> .mk-poll
|
||||||
font-size 80%
|
font-size 80%
|
||||||
|
|
||||||
> .repost
|
> .renote
|
||||||
margin 8px 0
|
margin 8px 0
|
||||||
|
|
||||||
> .mk-post-preview
|
> .mk-note-preview
|
||||||
padding 16px
|
padding 16px
|
||||||
border dashed 1px #c0dac6
|
border dashed 1px #c0dac6
|
||||||
border-radius 8px
|
border-radius 8px
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
<template>
|
||||||
|
<mk-window ref="window" is-modal @closed="$destroy">
|
||||||
|
<span slot="header" :class="$style.header">%fa:retweet%%i18n:desktop.tags.mk-renote-form-window.title%</span>
|
||||||
|
<mk-renote-form ref="form" :note="note" @posted="onPosted" @canceled="onCanceled"/>
|
||||||
|
</mk-window>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import Vue from 'vue';
|
||||||
|
|
||||||
|
export default Vue.extend({
|
||||||
|
props: ['note'],
|
||||||
|
mounted() {
|
||||||
|
document.addEventListener('keydown', this.onDocumentKeydown);
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
document.removeEventListener('keydown', this.onDocumentKeydown);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onDocumentKeydown(e) {
|
||||||
|
if (e.target.tagName != 'INPUT' && e.target.tagName != 'TEXTAREA') {
|
||||||
|
if (e.which == 27) { // Esc
|
||||||
|
(this.$refs.window as any).close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onPosted() {
|
||||||
|
(this.$refs.window as any).close();
|
||||||
|
},
|
||||||
|
onCanceled() {
|
||||||
|
(this.$refs.window as any).close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="stylus" module>
|
||||||
|
.header
|
||||||
|
> [data-fa]
|
||||||
|
margin-right 4px
|
||||||
|
|
||||||
|
</style>
|
131
src/client/app/desktop/views/components/renote-form.vue
Normal file
131
src/client/app/desktop/views/components/renote-form.vue
Normal file
|
@ -0,0 +1,131 @@
|
||||||
|
<template>
|
||||||
|
<div class="mk-renote-form">
|
||||||
|
<mk-note-preview :note="note"/>
|
||||||
|
<template v-if="!quote">
|
||||||
|
<footer>
|
||||||
|
<a class="quote" v-if="!quote" @click="onQuote">%i18n:desktop.tags.mk-renote-form.quote%</a>
|
||||||
|
<button class="cancel" @click="cancel">%i18n:desktop.tags.mk-renote-form.cancel%</button>
|
||||||
|
<button class="ok" @click="ok" :disabled="wait">{{ wait ? '%i18n:desktop.tags.mk-renote-form.reposting%' : '%i18n:desktop.tags.mk-renote-form.renote%' }}</button>
|
||||||
|
</footer>
|
||||||
|
</template>
|
||||||
|
<template v-if="quote">
|
||||||
|
<mk-post-form ref="form" :renote="note" @posted="onChildFormPosted"/>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import Vue from 'vue';
|
||||||
|
|
||||||
|
export default Vue.extend({
|
||||||
|
props: ['note'],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
wait: false,
|
||||||
|
quote: false
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
ok() {
|
||||||
|
this.wait = true;
|
||||||
|
(this as any).api('notes/create', {
|
||||||
|
renoteId: this.note.id
|
||||||
|
}).then(data => {
|
||||||
|
this.$emit('posted');
|
||||||
|
(this as any).apis.notify('%i18n:desktop.tags.mk-renote-form.success%');
|
||||||
|
}).catch(err => {
|
||||||
|
(this as any).apis.notify('%i18n:desktop.tags.mk-renote-form.failure%');
|
||||||
|
}).then(() => {
|
||||||
|
this.wait = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
cancel() {
|
||||||
|
this.$emit('canceled');
|
||||||
|
},
|
||||||
|
onQuote() {
|
||||||
|
this.quote = true;
|
||||||
|
|
||||||
|
this.$nextTick(() => {
|
||||||
|
(this.$refs.form as any).focus();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onChildFormPosted() {
|
||||||
|
this.$emit('posted');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="stylus" scoped>
|
||||||
|
@import '~const.styl'
|
||||||
|
|
||||||
|
.mk-renote-form
|
||||||
|
|
||||||
|
> .mk-note-preview
|
||||||
|
margin 16px 22px
|
||||||
|
|
||||||
|
> footer
|
||||||
|
height 72px
|
||||||
|
background lighten($theme-color, 95%)
|
||||||
|
|
||||||
|
> .quote
|
||||||
|
position absolute
|
||||||
|
bottom 16px
|
||||||
|
left 28px
|
||||||
|
line-height 40px
|
||||||
|
|
||||||
|
button
|
||||||
|
display block
|
||||||
|
position absolute
|
||||||
|
bottom 16px
|
||||||
|
cursor pointer
|
||||||
|
padding 0
|
||||||
|
margin 0
|
||||||
|
width 120px
|
||||||
|
height 40px
|
||||||
|
font-size 1em
|
||||||
|
outline none
|
||||||
|
border-radius 4px
|
||||||
|
|
||||||
|
&:focus
|
||||||
|
&:after
|
||||||
|
content ""
|
||||||
|
pointer-events none
|
||||||
|
position absolute
|
||||||
|
top -5px
|
||||||
|
right -5px
|
||||||
|
bottom -5px
|
||||||
|
left -5px
|
||||||
|
border 2px solid rgba($theme-color, 0.3)
|
||||||
|
border-radius 8px
|
||||||
|
|
||||||
|
> .cancel
|
||||||
|
right 148px
|
||||||
|
color #888
|
||||||
|
background linear-gradient(to bottom, #ffffff 0%, #f5f5f5 100%)
|
||||||
|
border solid 1px #e2e2e2
|
||||||
|
|
||||||
|
&:hover
|
||||||
|
background linear-gradient(to bottom, #f9f9f9 0%, #ececec 100%)
|
||||||
|
border-color #dcdcdc
|
||||||
|
|
||||||
|
&:active
|
||||||
|
background #ececec
|
||||||
|
border-color #dcdcdc
|
||||||
|
|
||||||
|
> .ok
|
||||||
|
right 16px
|
||||||
|
font-weight bold
|
||||||
|
color $theme-color-foreground
|
||||||
|
background linear-gradient(to bottom, lighten($theme-color, 25%) 0%, lighten($theme-color, 10%) 100%)
|
||||||
|
border solid 1px lighten($theme-color, 15%)
|
||||||
|
|
||||||
|
&:hover
|
||||||
|
background linear-gradient(to bottom, lighten($theme-color, 8%) 0%, darken($theme-color, 8%) 100%)
|
||||||
|
border-color $theme-color
|
||||||
|
|
||||||
|
&:active
|
||||||
|
background $theme-color
|
||||||
|
border-color $theme-color
|
||||||
|
|
||||||
|
</style>
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<mk-window ref="window" is-modal @closed="$destroy">
|
<mk-window ref="window" is-modal @closed="$destroy">
|
||||||
<span slot="header" :class="$style.header">%fa:retweet%%i18n:desktop.tags.mk-repost-form-window.title%</span>
|
<span slot="header" :class="$style.header">%fa:retweet%%i18n:desktop.tags.mk-renote-form-window.title%</span>
|
||||||
<mk-repost-form ref="form" :post="post" @posted="onPosted" @canceled="onCanceled"/>
|
<mk-renote-form ref="form" :note="note" @posted="onPosted" @canceled="onCanceled"/>
|
||||||
</mk-window>
|
</mk-window>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
props: ['post'],
|
props: ['note'],
|
||||||
mounted() {
|
mounted() {
|
||||||
document.addEventListener('keydown', this.onDocumentKeydown);
|
document.addEventListener('keydown', this.onDocumentKeydown);
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="mk-repost-form">
|
<div class="mk-renote-form">
|
||||||
<mk-post-preview :post="post"/>
|
<mk-note-preview :note="note"/>
|
||||||
<template v-if="!quote">
|
<template v-if="!quote">
|
||||||
<footer>
|
<footer>
|
||||||
<a class="quote" v-if="!quote" @click="onQuote">%i18n:desktop.tags.mk-repost-form.quote%</a>
|
<a class="quote" v-if="!quote" @click="onQuote">%i18n:desktop.tags.mk-renote-form.quote%</a>
|
||||||
<button class="cancel" @click="cancel">%i18n:desktop.tags.mk-repost-form.cancel%</button>
|
<button class="cancel" @click="cancel">%i18n:desktop.tags.mk-renote-form.cancel%</button>
|
||||||
<button class="ok" @click="ok" :disabled="wait">{{ wait ? '%i18n:desktop.tags.mk-repost-form.reposting%' : '%i18n:desktop.tags.mk-repost-form.repost%' }}</button>
|
<button class="ok" @click="ok" :disabled="wait">{{ wait ? '%i18n:desktop.tags.mk-renote-form.reposting%' : '%i18n:desktop.tags.mk-renote-form.renote%' }}</button>
|
||||||
</footer>
|
</footer>
|
||||||
</template>
|
</template>
|
||||||
<template v-if="quote">
|
<template v-if="quote">
|
||||||
<mk-post-form ref="form" :repost="post" @posted="onChildFormPosted"/>
|
<mk-post-form ref="form" :renote="note" @posted="onChildFormPosted"/>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -18,7 +18,7 @@
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
props: ['post'],
|
props: ['note'],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
wait: false,
|
wait: false,
|
||||||
|
@ -28,13 +28,13 @@ export default Vue.extend({
|
||||||
methods: {
|
methods: {
|
||||||
ok() {
|
ok() {
|
||||||
this.wait = true;
|
this.wait = true;
|
||||||
(this as any).api('posts/create', {
|
(this as any).api('notes/create', {
|
||||||
repostId: this.post.id
|
renoteId: this.note.id
|
||||||
}).then(data => {
|
}).then(data => {
|
||||||
this.$emit('posted');
|
this.$emit('posted');
|
||||||
(this as any).apis.notify('%i18n:desktop.tags.mk-repost-form.success%');
|
(this as any).apis.notify('%i18n:desktop.tags.mk-renote-form.success%');
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
(this as any).apis.notify('%i18n:desktop.tags.mk-repost-form.failure%');
|
(this as any).apis.notify('%i18n:desktop.tags.mk-renote-form.failure%');
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
this.wait = false;
|
this.wait = false;
|
||||||
});
|
});
|
||||||
|
@ -59,9 +59,9 @@ export default Vue.extend({
|
||||||
<style lang="stylus" scoped>
|
<style lang="stylus" scoped>
|
||||||
@import '~const.styl'
|
@import '~const.styl'
|
||||||
|
|
||||||
.mk-repost-form
|
.mk-renote-form
|
||||||
|
|
||||||
> .mk-post-preview
|
> .mk-note-preview
|
||||||
margin 16px 22px
|
margin 16px 22px
|
||||||
|
|
||||||
> footer
|
> footer
|
||||||
|
|
44
src/client/app/desktop/views/components/sub-note-content.vue
Normal file
44
src/client/app/desktop/views/components/sub-note-content.vue
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
<template>
|
||||||
|
<div class="mk-sub-note-content">
|
||||||
|
<div class="body">
|
||||||
|
<a class="reply" v-if="note.replyId">%fa:reply%</a>
|
||||||
|
<mk-note-html :text="note.text" :i="os.i"/>
|
||||||
|
<a class="rp" v-if="note.renoteId" :href="`/note:${note.renoteId}`">RP: ...</a>
|
||||||
|
</div>
|
||||||
|
<details v-if="note.media.length > 0">
|
||||||
|
<summary>({{ note.media.length }}つのメディア)</summary>
|
||||||
|
<mk-media-list :media-list="note.media"/>
|
||||||
|
</details>
|
||||||
|
<details v-if="note.poll">
|
||||||
|
<summary>投票</summary>
|
||||||
|
<mk-poll :note="note"/>
|
||||||
|
</details>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import Vue from 'vue';
|
||||||
|
|
||||||
|
export default Vue.extend({
|
||||||
|
props: ['note']
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="stylus" scoped>
|
||||||
|
.mk-sub-note-content
|
||||||
|
overflow-wrap break-word
|
||||||
|
|
||||||
|
> .body
|
||||||
|
> .reply
|
||||||
|
margin-right 6px
|
||||||
|
color #717171
|
||||||
|
|
||||||
|
> .rp
|
||||||
|
margin-left 4px
|
||||||
|
font-style oblique
|
||||||
|
color #a0bf46
|
||||||
|
|
||||||
|
mk-poll
|
||||||
|
font-size 80%
|
||||||
|
|
||||||
|
</style>
|
|
@ -1,44 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="mk-sub-post-content">
|
|
||||||
<div class="body">
|
|
||||||
<a class="reply" v-if="post.replyId">%fa:reply%</a>
|
|
||||||
<mk-post-html :text="post.text" :i="os.i"/>
|
|
||||||
<a class="rp" v-if="post.repostId" :href="`/post:${post.repostId}`">RP: ...</a>
|
|
||||||
</div>
|
|
||||||
<details v-if="post.media.length > 0">
|
|
||||||
<summary>({{ post.media.length }}つのメディア)</summary>
|
|
||||||
<mk-media-list :media-list="post.media"/>
|
|
||||||
</details>
|
|
||||||
<details v-if="post.poll">
|
|
||||||
<summary>投票</summary>
|
|
||||||
<mk-poll :post="post"/>
|
|
||||||
</details>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import Vue from 'vue';
|
|
||||||
|
|
||||||
export default Vue.extend({
|
|
||||||
props: ['post']
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="stylus" scoped>
|
|
||||||
.mk-sub-post-content
|
|
||||||
overflow-wrap break-word
|
|
||||||
|
|
||||||
> .body
|
|
||||||
> .reply
|
|
||||||
margin-right 6px
|
|
||||||
color #717171
|
|
||||||
|
|
||||||
> .rp
|
|
||||||
margin-left 4px
|
|
||||||
font-style oblique
|
|
||||||
color #a0bf46
|
|
||||||
|
|
||||||
mk-poll
|
|
||||||
font-size 80%
|
|
||||||
|
|
||||||
</style>
|
|
|
@ -4,15 +4,15 @@
|
||||||
<div class="fetching" v-if="fetching">
|
<div class="fetching" v-if="fetching">
|
||||||
<mk-ellipsis-icon/>
|
<mk-ellipsis-icon/>
|
||||||
</div>
|
</div>
|
||||||
<p class="empty" v-if="posts.length == 0 && !fetching">
|
<p class="empty" v-if="notes.length == 0 && !fetching">
|
||||||
%fa:R comments%自分の投稿や、自分がフォローしているユーザーの投稿が表示されます。
|
%fa:R comments%自分の投稿や、自分がフォローしているユーザーの投稿が表示されます。
|
||||||
</p>
|
</p>
|
||||||
<mk-posts :posts="posts" ref="timeline">
|
<mk-notes :notes="notes" ref="timeline">
|
||||||
<button slot="footer" @click="more" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }">
|
<button slot="footer" @click="more" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }">
|
||||||
<template v-if="!moreFetching">もっと見る</template>
|
<template v-if="!moreFetching">もっと見る</template>
|
||||||
<template v-if="moreFetching">%fa:spinner .pulse .fw%</template>
|
<template v-if="moreFetching">%fa:spinner .pulse .fw%</template>
|
||||||
</button>
|
</button>
|
||||||
</mk-posts>
|
</mk-notes>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ export default Vue.extend({
|
||||||
fetching: true,
|
fetching: true,
|
||||||
moreFetching: false,
|
moreFetching: false,
|
||||||
existMore: false,
|
existMore: false,
|
||||||
posts: [],
|
notes: [],
|
||||||
connection: null,
|
connection: null,
|
||||||
connectionId: null,
|
connectionId: null,
|
||||||
date: null
|
date: null
|
||||||
|
@ -41,7 +41,7 @@ export default Vue.extend({
|
||||||
this.connection = (this as any).os.stream.getConnection();
|
this.connection = (this as any).os.stream.getConnection();
|
||||||
this.connectionId = (this as any).os.stream.use();
|
this.connectionId = (this as any).os.stream.use();
|
||||||
|
|
||||||
this.connection.on('post', this.onPost);
|
this.connection.on('note', this.onNote);
|
||||||
this.connection.on('follow', this.onChangeFollowing);
|
this.connection.on('follow', this.onChangeFollowing);
|
||||||
this.connection.on('unfollow', this.onChangeFollowing);
|
this.connection.on('unfollow', this.onChangeFollowing);
|
||||||
|
|
||||||
|
@ -51,7 +51,7 @@ export default Vue.extend({
|
||||||
this.fetch();
|
this.fetch();
|
||||||
},
|
},
|
||||||
beforeDestroy() {
|
beforeDestroy() {
|
||||||
this.connection.off('post', this.onPost);
|
this.connection.off('note', this.onNote);
|
||||||
this.connection.off('follow', this.onChangeFollowing);
|
this.connection.off('follow', this.onChangeFollowing);
|
||||||
this.connection.off('unfollow', this.onChangeFollowing);
|
this.connection.off('unfollow', this.onChangeFollowing);
|
||||||
(this as any).os.stream.dispose(this.connectionId);
|
(this as any).os.stream.dispose(this.connectionId);
|
||||||
|
@ -63,45 +63,45 @@ export default Vue.extend({
|
||||||
fetch(cb?) {
|
fetch(cb?) {
|
||||||
this.fetching = true;
|
this.fetching = true;
|
||||||
|
|
||||||
(this as any).api('posts/timeline', {
|
(this as any).api('notes/timeline', {
|
||||||
limit: 11,
|
limit: 11,
|
||||||
untilDate: this.date ? this.date.getTime() : undefined
|
untilDate: this.date ? this.date.getTime() : undefined
|
||||||
}).then(posts => {
|
}).then(notes => {
|
||||||
if (posts.length == 11) {
|
if (notes.length == 11) {
|
||||||
posts.pop();
|
notes.pop();
|
||||||
this.existMore = true;
|
this.existMore = true;
|
||||||
}
|
}
|
||||||
this.posts = posts;
|
this.notes = notes;
|
||||||
this.fetching = false;
|
this.fetching = false;
|
||||||
this.$emit('loaded');
|
this.$emit('loaded');
|
||||||
if (cb) cb();
|
if (cb) cb();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
more() {
|
more() {
|
||||||
if (this.moreFetching || this.fetching || this.posts.length == 0 || !this.existMore) return;
|
if (this.moreFetching || this.fetching || this.notes.length == 0 || !this.existMore) return;
|
||||||
this.moreFetching = true;
|
this.moreFetching = true;
|
||||||
(this as any).api('posts/timeline', {
|
(this as any).api('notes/timeline', {
|
||||||
limit: 11,
|
limit: 11,
|
||||||
untilId: this.posts[this.posts.length - 1].id
|
untilId: this.notes[this.notes.length - 1].id
|
||||||
}).then(posts => {
|
}).then(notes => {
|
||||||
if (posts.length == 11) {
|
if (notes.length == 11) {
|
||||||
posts.pop();
|
notes.pop();
|
||||||
} else {
|
} else {
|
||||||
this.existMore = false;
|
this.existMore = false;
|
||||||
}
|
}
|
||||||
this.posts = this.posts.concat(posts);
|
this.notes = this.notes.concat(notes);
|
||||||
this.moreFetching = false;
|
this.moreFetching = false;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
onPost(post) {
|
onNote(note) {
|
||||||
// サウンドを再生する
|
// サウンドを再生する
|
||||||
if ((this as any).os.isEnableSounds) {
|
if ((this as any).os.isEnableSounds) {
|
||||||
const sound = new Audio(`${url}/assets/post.mp3`);
|
const sound = new Audio(`${url}/assets/note.mp3`);
|
||||||
sound.volume = localStorage.getItem('soundVolume') ? parseInt(localStorage.getItem('soundVolume'), 10) / 100 : 1;
|
sound.volume = localStorage.getItem('soundVolume') ? parseInt(localStorage.getItem('soundVolume'), 10) / 100 : 1;
|
||||||
sound.play();
|
sound.play();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.posts.unshift(post);
|
this.notes.unshift(note);
|
||||||
},
|
},
|
||||||
onChangeFollowing() {
|
onChangeFollowing() {
|
||||||
this.fetch();
|
this.fetch();
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="post">
|
<div class="note">
|
||||||
<button @click="post" title="%i18n:desktop.tags.mk-ui-header-post-button.post%">%fa:pencil-alt%</button>
|
<button @click="post" title="%i18n:desktop.tags.mk-ui-header-note-button.note%">%fa:pencil-alt%</button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ export default Vue.extend({
|
||||||
<style lang="stylus" scoped>
|
<style lang="stylus" scoped>
|
||||||
@import '~const.styl'
|
@import '~const.styl'
|
||||||
|
|
||||||
.post
|
.note
|
||||||
display inline-block
|
display inline-block
|
||||||
padding 8px
|
padding 8px
|
||||||
height 100%
|
height 100%
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
<div class="description">{{ u.description }}</div>
|
<div class="description">{{ u.description }}</div>
|
||||||
<div class="status">
|
<div class="status">
|
||||||
<div>
|
<div>
|
||||||
<p>投稿</p><a>{{ u.postsCount }}</a>
|
<p>投稿</p><a>{{ u.notesCount }}</a>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p>フォロー</p><a>{{ u.followingCount }}</a>
|
<p>フォロー</p><a>{{ u.followingCount }}</a>
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import Progress from '../../../common/scripts/loading';
|
import Progress from '../../../common/scripts/loading';
|
||||||
import getPostSummary from '../../../../../renderers/get-post-summary';
|
import getNoteSummary from '../../../../../renderers/get-note-summary';
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
props: {
|
props: {
|
||||||
|
@ -29,13 +29,13 @@ export default Vue.extend({
|
||||||
this.connection = (this as any).os.stream.getConnection();
|
this.connection = (this as any).os.stream.getConnection();
|
||||||
this.connectionId = (this as any).os.stream.use();
|
this.connectionId = (this as any).os.stream.use();
|
||||||
|
|
||||||
this.connection.on('post', this.onStreamPost);
|
this.connection.on('note', this.onStreamNote);
|
||||||
document.addEventListener('visibilitychange', this.onVisibilitychange, false);
|
document.addEventListener('visibilitychange', this.onVisibilitychange, false);
|
||||||
|
|
||||||
Progress.start();
|
Progress.start();
|
||||||
},
|
},
|
||||||
beforeDestroy() {
|
beforeDestroy() {
|
||||||
this.connection.off('post', this.onStreamPost);
|
this.connection.off('note', this.onStreamNote);
|
||||||
(this as any).os.stream.dispose(this.connectionId);
|
(this as any).os.stream.dispose(this.connectionId);
|
||||||
document.removeEventListener('visibilitychange', this.onVisibilitychange);
|
document.removeEventListener('visibilitychange', this.onVisibilitychange);
|
||||||
},
|
},
|
||||||
|
@ -44,10 +44,10 @@ export default Vue.extend({
|
||||||
Progress.done();
|
Progress.done();
|
||||||
},
|
},
|
||||||
|
|
||||||
onStreamPost(post) {
|
onStreamNote(note) {
|
||||||
if (document.hidden && post.userId != (this as any).os.i.id) {
|
if (document.hidden && note.userId != (this as any).os.i.id) {
|
||||||
this.unreadCount++;
|
this.unreadCount++;
|
||||||
document.title = `(${this.unreadCount}) ${getPostSummary(post)}`;
|
document.title = `(${this.unreadCount}) ${getNoteSummary(note)}`;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
<template>
|
<template>
|
||||||
<mk-ui>
|
<mk-ui>
|
||||||
<main v-if="!fetching">
|
<main v-if="!fetching">
|
||||||
<a v-if="post.next" :href="post.next">%fa:angle-up%%i18n:desktop.tags.mk-post-page.next%</a>
|
<a v-if="note.next" :href="note.next">%fa:angle-up%%i18n:desktop.tags.mk-note-page.next%</a>
|
||||||
<mk-post-detail :post="post"/>
|
<mk-note-detail :note="note"/>
|
||||||
<a v-if="post.prev" :href="post.prev">%fa:angle-down%%i18n:desktop.tags.mk-post-page.prev%</a>
|
<a v-if="note.prev" :href="note.prev">%fa:angle-down%%i18n:desktop.tags.mk-note-page.prev%</a>
|
||||||
</main>
|
</main>
|
||||||
</mk-ui>
|
</mk-ui>
|
||||||
</template>
|
</template>
|
||||||
|
@ -16,7 +16,7 @@ export default Vue.extend({
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
fetching: true,
|
fetching: true,
|
||||||
post: null
|
note: null
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
|
@ -30,10 +30,10 @@ export default Vue.extend({
|
||||||
Progress.start();
|
Progress.start();
|
||||||
this.fetching = true;
|
this.fetching = true;
|
||||||
|
|
||||||
(this as any).api('posts/show', {
|
(this as any).api('notes/show', {
|
||||||
postId: this.$route.params.post
|
noteId: this.$route.params.note
|
||||||
}).then(post => {
|
}).then(note => {
|
||||||
this.post = post;
|
this.note = note;
|
||||||
this.fetching = false;
|
this.fetching = false;
|
||||||
|
|
||||||
Progress.done();
|
Progress.done();
|
||||||
|
@ -60,7 +60,7 @@ main
|
||||||
> [data-fa]
|
> [data-fa]
|
||||||
margin-right 4px
|
margin-right 4px
|
||||||
|
|
||||||
> .mk-post-detail
|
> .mk-note-detail
|
||||||
margin 0 auto
|
margin 0 auto
|
||||||
width 640px
|
width 640px
|
||||||
|
|
|
@ -7,12 +7,12 @@
|
||||||
<mk-ellipsis-icon/>
|
<mk-ellipsis-icon/>
|
||||||
</div>
|
</div>
|
||||||
<p :class="$style.empty" v-if="!fetching && empty">%fa:search%「{{ q }}」に関する投稿は見つかりませんでした。</p>
|
<p :class="$style.empty" v-if="!fetching && empty">%fa:search%「{{ q }}」に関する投稿は見つかりませんでした。</p>
|
||||||
<mk-posts ref="timeline" :class="$style.posts" :posts="posts">
|
<mk-notes ref="timeline" :class="$style.notes" :notes="notes">
|
||||||
<div slot="footer">
|
<div slot="footer">
|
||||||
<template v-if="!moreFetching">%fa:search%</template>
|
<template v-if="!moreFetching">%fa:search%</template>
|
||||||
<template v-if="moreFetching">%fa:spinner .pulse .fw%</template>
|
<template v-if="moreFetching">%fa:spinner .pulse .fw%</template>
|
||||||
</div>
|
</div>
|
||||||
</mk-posts>
|
</mk-notes>
|
||||||
</mk-ui>
|
</mk-ui>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ export default Vue.extend({
|
||||||
moreFetching: false,
|
moreFetching: false,
|
||||||
existMore: false,
|
existMore: false,
|
||||||
offset: 0,
|
offset: 0,
|
||||||
posts: []
|
notes: []
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
|
@ -38,7 +38,7 @@ export default Vue.extend({
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
empty(): boolean {
|
empty(): boolean {
|
||||||
return this.posts.length == 0;
|
return this.notes.length == 0;
|
||||||
},
|
},
|
||||||
q(): string {
|
q(): string {
|
||||||
return this.$route.query.q;
|
return this.$route.query.q;
|
||||||
|
@ -66,33 +66,33 @@ export default Vue.extend({
|
||||||
this.fetching = true;
|
this.fetching = true;
|
||||||
Progress.start();
|
Progress.start();
|
||||||
|
|
||||||
(this as any).api('posts/search', Object.assign({
|
(this as any).api('notes/search', Object.assign({
|
||||||
limit: limit + 1,
|
limit: limit + 1,
|
||||||
offset: this.offset
|
offset: this.offset
|
||||||
}, parse(this.q))).then(posts => {
|
}, parse(this.q))).then(notes => {
|
||||||
if (posts.length == limit + 1) {
|
if (notes.length == limit + 1) {
|
||||||
posts.pop();
|
notes.pop();
|
||||||
this.existMore = true;
|
this.existMore = true;
|
||||||
}
|
}
|
||||||
this.posts = posts;
|
this.notes = notes;
|
||||||
this.fetching = false;
|
this.fetching = false;
|
||||||
Progress.done();
|
Progress.done();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
more() {
|
more() {
|
||||||
if (this.moreFetching || this.fetching || this.posts.length == 0 || !this.existMore) return;
|
if (this.moreFetching || this.fetching || this.notes.length == 0 || !this.existMore) return;
|
||||||
this.offset += limit;
|
this.offset += limit;
|
||||||
this.moreFetching = true;
|
this.moreFetching = true;
|
||||||
return (this as any).api('posts/search', Object.assign({
|
return (this as any).api('notes/search', Object.assign({
|
||||||
limit: limit + 1,
|
limit: limit + 1,
|
||||||
offset: this.offset
|
offset: this.offset
|
||||||
}, parse(this.q))).then(posts => {
|
}, parse(this.q))).then(notes => {
|
||||||
if (posts.length == limit + 1) {
|
if (notes.length == limit + 1) {
|
||||||
posts.pop();
|
notes.pop();
|
||||||
} else {
|
} else {
|
||||||
this.existMore = false;
|
this.existMore = false;
|
||||||
}
|
}
|
||||||
this.posts = this.posts.concat(posts);
|
this.notes = this.notes.concat(notes);
|
||||||
this.moreFetching = false;
|
this.moreFetching = false;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -111,7 +111,7 @@ export default Vue.extend({
|
||||||
margin 0 auto
|
margin 0 auto
|
||||||
color #555
|
color #555
|
||||||
|
|
||||||
.posts
|
.notes
|
||||||
max-width 600px
|
max-width 600px
|
||||||
margin 0 auto
|
margin 0 auto
|
||||||
border solid 1px rgba(0, 0, 0, 0.075)
|
border solid 1px rgba(0, 0, 0, 0.075)
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<main>
|
<main>
|
||||||
<mk-post-detail v-if="user.pinnedPost" :post="user.pinnedPost" :compact="true"/>
|
<mk-note-detail v-if="user.pinnedNote" :note="user.pinnedNote" :compact="true"/>
|
||||||
<x-timeline class="timeline" ref="tl" :user="user"/>
|
<x-timeline class="timeline" ref="tl" :user="user"/>
|
||||||
</main>
|
</main>
|
||||||
<div>
|
<div>
|
||||||
|
|
|
@ -22,13 +22,13 @@ export default Vue.extend({
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
(this as any).api('users/posts', {
|
(this as any).api('users/notes', {
|
||||||
userId: this.user.id,
|
userId: this.user.id,
|
||||||
withMedia: true,
|
withMedia: true,
|
||||||
limit: 9
|
limit: 9
|
||||||
}).then(posts => {
|
}).then(notes => {
|
||||||
posts.forEach(post => {
|
notes.forEach(note => {
|
||||||
post.media.forEach(media => {
|
note.media.forEach(media => {
|
||||||
if (this.images.length < 9) this.images.push(media);
|
if (this.images.length < 9) this.images.push(media);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
<p>%fa:B twitter%<a :href="`https://twitter.com/${user.account.twitter.screenName}`" target="_blank">@{{ user.account.twitter.screenName }}</a></p>
|
<p>%fa:B twitter%<a :href="`https://twitter.com/${user.account.twitter.screenName}`" target="_blank">@{{ user.account.twitter.screenName }}</a></p>
|
||||||
</div>
|
</div>
|
||||||
<div class="status">
|
<div class="status">
|
||||||
<p class="posts-count">%fa:angle-right%<a>{{ user.postsCount }}</a><b>投稿</b></p>
|
<p class="notes-count">%fa:angle-right%<a>{{ user.notesCount }}</a><b>投稿</b></p>
|
||||||
<p class="following">%fa:angle-right%<a @click="showFollowing">{{ user.followingCount }}</a>人を<b>フォロー</b></p>
|
<p class="following">%fa:angle-right%<a @click="showFollowing">{{ user.followingCount }}</a>人を<b>フォロー</b></p>
|
||||||
<p class="followers">%fa:angle-right%<a @click="showFollowers">{{ user.followersCount }}</a>人の<b>フォロワー</b></p>
|
<p class="followers">%fa:angle-right%<a @click="showFollowers">{{ user.followersCount }}</a>人の<b>フォロワー</b></p>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -8,12 +8,12 @@
|
||||||
<mk-ellipsis-icon/>
|
<mk-ellipsis-icon/>
|
||||||
</div>
|
</div>
|
||||||
<p class="empty" v-if="empty">%fa:R comments%このユーザーはまだ何も投稿していないようです。</p>
|
<p class="empty" v-if="empty">%fa:R comments%このユーザーはまだ何も投稿していないようです。</p>
|
||||||
<mk-posts ref="timeline" :posts="posts">
|
<mk-notes ref="timeline" :notes="notes">
|
||||||
<div slot="footer">
|
<div slot="footer">
|
||||||
<template v-if="!moreFetching">%fa:moon%</template>
|
<template v-if="!moreFetching">%fa:moon%</template>
|
||||||
<template v-if="moreFetching">%fa:spinner .pulse .fw%</template>
|
<template v-if="moreFetching">%fa:spinner .pulse .fw%</template>
|
||||||
</div>
|
</div>
|
||||||
</mk-posts>
|
</mk-notes>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ export default Vue.extend({
|
||||||
moreFetching: false,
|
moreFetching: false,
|
||||||
mode: 'default',
|
mode: 'default',
|
||||||
unreadCount: 0,
|
unreadCount: 0,
|
||||||
posts: [],
|
notes: [],
|
||||||
date: null
|
date: null
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
@ -38,7 +38,7 @@ export default Vue.extend({
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
empty(): boolean {
|
empty(): boolean {
|
||||||
return this.posts.length == 0;
|
return this.notes.length == 0;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
@ -60,26 +60,26 @@ export default Vue.extend({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
fetch(cb?) {
|
fetch(cb?) {
|
||||||
(this as any).api('users/posts', {
|
(this as any).api('users/notes', {
|
||||||
userId: this.user.id,
|
userId: this.user.id,
|
||||||
untilDate: this.date ? this.date.getTime() : undefined,
|
untilDate: this.date ? this.date.getTime() : undefined,
|
||||||
with_replies: this.mode == 'with-replies'
|
with_replies: this.mode == 'with-replies'
|
||||||
}).then(posts => {
|
}).then(notes => {
|
||||||
this.posts = posts;
|
this.notes = notes;
|
||||||
this.fetching = false;
|
this.fetching = false;
|
||||||
if (cb) cb();
|
if (cb) cb();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
more() {
|
more() {
|
||||||
if (this.moreFetching || this.fetching || this.posts.length == 0) return;
|
if (this.moreFetching || this.fetching || this.notes.length == 0) return;
|
||||||
this.moreFetching = true;
|
this.moreFetching = true;
|
||||||
(this as any).api('users/posts', {
|
(this as any).api('users/notes', {
|
||||||
userId: this.user.id,
|
userId: this.user.id,
|
||||||
with_replies: this.mode == 'with-replies',
|
with_replies: this.mode == 'with-replies',
|
||||||
untilId: this.posts[this.posts.length - 1].id
|
untilId: this.notes[this.notes.length - 1].id
|
||||||
}).then(posts => {
|
}).then(notes => {
|
||||||
this.moreFetching = false;
|
this.moreFetching = false;
|
||||||
this.posts = this.posts.concat(posts);
|
this.notes = this.notes.concat(notes);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
onScroll() {
|
onScroll() {
|
||||||
|
|
|
@ -24,11 +24,11 @@ export default Vue.extend({
|
||||||
|
|
||||||
if (/^>>([0-9]+) /.test(this.text)) {
|
if (/^>>([0-9]+) /.test(this.text)) {
|
||||||
const index = this.text.match(/^>>([0-9]+) /)[1];
|
const index = this.text.match(/^>>([0-9]+) /)[1];
|
||||||
reply = (this.$parent as any).posts.find(p => p.index.toString() == index);
|
reply = (this.$parent as any).notes.find(p => p.index.toString() == index);
|
||||||
this.text = this.text.replace(/^>>([0-9]+) /, '');
|
this.text = this.text.replace(/^>>([0-9]+) /, '');
|
||||||
}
|
}
|
||||||
|
|
||||||
(this as any).api('posts/create', {
|
(this as any).api('notes/create', {
|
||||||
text: this.text,
|
text: this.text,
|
||||||
replyId: reply ? reply.id : undefined,
|
replyId: reply ? reply.id : undefined,
|
||||||
channelId: (this.$parent as any).channel.id
|
channelId: (this.$parent as any).channel.id
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="post">
|
<div class="note">
|
||||||
<header>
|
<header>
|
||||||
<a class="index" @click="reply">{{ post.index }}:</a>
|
<a class="index" @click="reply">{{ note.index }}:</a>
|
||||||
<router-link class="name" :to="`/@${acct}`" v-user-preview="post.user.id"><b>{{ name }}</b></router-link>
|
<router-link class="name" :to="`/@${acct}`" v-user-preview="note.user.id"><b>{{ name }}</b></router-link>
|
||||||
<span>ID:<i>{{ acct }}</i></span>
|
<span>ID:<i>{{ acct }}</i></span>
|
||||||
</header>
|
</header>
|
||||||
<div>
|
<div>
|
||||||
<a v-if="post.reply">>>{{ post.reply.index }}</a>
|
<a v-if="note.reply">>>{{ note.reply.index }}</a>
|
||||||
{{ post.text }}
|
{{ note.text }}
|
||||||
<div class="media" v-if="post.media">
|
<div class="media" v-if="note.media">
|
||||||
<a v-for="file in post.media" :href="file.url" target="_blank">
|
<a v-for="file in note.media" :href="file.url" target="_blank">
|
||||||
<img :src="`${file.url}?thumbnail&size=512`" :alt="file.name" :title="file.name"/>
|
<img :src="`${file.url}?thumbnail&size=512`" :alt="file.name" :title="file.name"/>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
@ -23,25 +23,25 @@ import getAcct from '../../../../../acct/render';
|
||||||
import getUserName from '../../../../../renderers/get-user-name';
|
import getUserName from '../../../../../renderers/get-user-name';
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
props: ['post'],
|
props: ['note'],
|
||||||
computed: {
|
computed: {
|
||||||
acct() {
|
acct() {
|
||||||
return getAcct(this.post.user);
|
return getAcct(this.note.user);
|
||||||
},
|
},
|
||||||
name() {
|
name() {
|
||||||
return getUserName(this.post.user);
|
return getUserName(this.note.user);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
reply() {
|
reply() {
|
||||||
this.$emit('reply', this.post);
|
this.$emit('reply', this.note);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="stylus" scoped>
|
<style lang="stylus" scoped>
|
||||||
.post
|
.note
|
||||||
margin 0
|
margin 0
|
||||||
padding 0
|
padding 0
|
||||||
color #444
|
color #444
|
|
@ -1,9 +1,9 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="channel">
|
<div class="channel">
|
||||||
<p v-if="fetching">読み込み中<mk-ellipsis/></p>
|
<p v-if="fetching">読み込み中<mk-ellipsis/></p>
|
||||||
<div v-if="!fetching" ref="posts" class="posts">
|
<div v-if="!fetching" ref="notes" class="notes">
|
||||||
<p v-if="posts.length == 0">まだ投稿がありません</p>
|
<p v-if="notes.length == 0">まだ投稿がありません</p>
|
||||||
<x-post class="post" v-for="post in posts.slice().reverse()" :post="post" :key="post.id" @reply="reply"/>
|
<x-note class="note" v-for="note in notes.slice().reverse()" :note="note" :key="note.id" @reply="reply"/>
|
||||||
</div>
|
</div>
|
||||||
<x-form class="form" ref="form"/>
|
<x-form class="form" ref="form"/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -13,18 +13,18 @@
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import ChannelStream from '../../../common/scripts/streaming/channel';
|
import ChannelStream from '../../../common/scripts/streaming/channel';
|
||||||
import XForm from './channel.channel.form.vue';
|
import XForm from './channel.channel.form.vue';
|
||||||
import XPost from './channel.channel.post.vue';
|
import XNote from './channel.channel.note.vue';
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
components: {
|
components: {
|
||||||
XForm,
|
XForm,
|
||||||
XPost
|
XNote
|
||||||
},
|
},
|
||||||
props: ['channel'],
|
props: ['channel'],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
fetching: true,
|
fetching: true,
|
||||||
posts: [],
|
notes: [],
|
||||||
connection: null
|
connection: null
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
@ -43,10 +43,10 @@ export default Vue.extend({
|
||||||
zap() {
|
zap() {
|
||||||
this.fetching = true;
|
this.fetching = true;
|
||||||
|
|
||||||
(this as any).api('channels/posts', {
|
(this as any).api('channels/notes', {
|
||||||
channelId: this.channel.id
|
channelId: this.channel.id
|
||||||
}).then(posts => {
|
}).then(notes => {
|
||||||
this.posts = posts;
|
this.notes = notes;
|
||||||
this.fetching = false;
|
this.fetching = false;
|
||||||
|
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
|
@ -55,24 +55,24 @@ export default Vue.extend({
|
||||||
|
|
||||||
this.disconnect();
|
this.disconnect();
|
||||||
this.connection = new ChannelStream((this as any).os, this.channel.id);
|
this.connection = new ChannelStream((this as any).os, this.channel.id);
|
||||||
this.connection.on('post', this.onPost);
|
this.connection.on('note', this.onNote);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
disconnect() {
|
disconnect() {
|
||||||
if (this.connection) {
|
if (this.connection) {
|
||||||
this.connection.off('post', this.onPost);
|
this.connection.off('note', this.onNote);
|
||||||
this.connection.close();
|
this.connection.close();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onPost(post) {
|
onNote(note) {
|
||||||
this.posts.unshift(post);
|
this.notes.unshift(note);
|
||||||
this.scrollToBottom();
|
this.scrollToBottom();
|
||||||
},
|
},
|
||||||
scrollToBottom() {
|
scrollToBottom() {
|
||||||
(this.$refs.posts as any).scrollTop = (this.$refs.posts as any).scrollHeight;
|
(this.$refs.notes as any).scrollTop = (this.$refs.notes as any).scrollHeight;
|
||||||
},
|
},
|
||||||
reply(post) {
|
reply(note) {
|
||||||
(this.$refs.form as any).text = `>>${ post.index } `;
|
(this.$refs.form as any).text = `>>${ note.index } `;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -87,12 +87,12 @@ export default Vue.extend({
|
||||||
text-align center
|
text-align center
|
||||||
color #aaa
|
color #aaa
|
||||||
|
|
||||||
> .posts
|
> .notes
|
||||||
height calc(100% - 38px)
|
height calc(100% - 38px)
|
||||||
overflow auto
|
overflow auto
|
||||||
font-size 0.9em
|
font-size 0.9em
|
||||||
|
|
||||||
> .post
|
> .note
|
||||||
border-bottom solid 1px #eee
|
border-bottom solid 1px #eee
|
||||||
|
|
||||||
&:last-child
|
&:last-child
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
<div class="poll" v-if="!fetching && poll != null">
|
<div class="poll" v-if="!fetching && poll != null">
|
||||||
<p v-if="poll.text"><router-link to="`/@${ acct }/${ poll.id }`">{{ poll.text }}</router-link></p>
|
<p v-if="poll.text"><router-link to="`/@${ acct }/${ poll.id }`">{{ poll.text }}</router-link></p>
|
||||||
<p v-if="!poll.text"><router-link to="`/@${ acct }/${ poll.id }`">%fa:link%</router-link></p>
|
<p v-if="!poll.text"><router-link to="`/@${ acct }/${ poll.id }`">%fa:link%</router-link></p>
|
||||||
<mk-poll :post="poll"/>
|
<mk-poll :note="poll"/>
|
||||||
</div>
|
</div>
|
||||||
<p class="empty" v-if="!fetching && poll == null">%i18n:desktop.tags.mk-recommended-polls-home-widget.nothing%</p>
|
<p class="empty" v-if="!fetching && poll == null">%i18n:desktop.tags.mk-recommended-polls-home-widget.nothing%</p>
|
||||||
<p class="fetching" v-if="fetching">%fa:spinner .pulse .fw%%i18n:common.loading%<mk-ellipsis/></p>
|
<p class="fetching" v-if="fetching">%fa:spinner .pulse .fw%%i18n:common.loading%<mk-ellipsis/></p>
|
||||||
|
@ -47,11 +47,11 @@ export default define({
|
||||||
this.fetching = true;
|
this.fetching = true;
|
||||||
this.poll = null;
|
this.poll = null;
|
||||||
|
|
||||||
(this as any).api('posts/polls/recommendation', {
|
(this as any).api('notes/polls/recommendation', {
|
||||||
limit: 1,
|
limit: 1,
|
||||||
offset: this.offset
|
offset: this.offset
|
||||||
}).then(posts => {
|
}).then(notes => {
|
||||||
const poll = posts ? posts[0] : null;
|
const poll = notes ? notes[0] : null;
|
||||||
if (poll == null) {
|
if (poll == null) {
|
||||||
this.offset = 0;
|
this.offset = 0;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
<p class="title">%fa:pencil-alt%%i18n:desktop.tags.mk-post-form-home-widget.title%</p>
|
<p class="title">%fa:pencil-alt%%i18n:desktop.tags.mk-post-form-home-widget.title%</p>
|
||||||
</template>
|
</template>
|
||||||
<textarea :disabled="posting" v-model="text" @keydown="onKeydown" placeholder="%i18n:desktop.tags.mk-post-form-home-widget.placeholder%"></textarea>
|
<textarea :disabled="posting" v-model="text" @keydown="onKeydown" placeholder="%i18n:desktop.tags.mk-post-form-home-widget.placeholder%"></textarea>
|
||||||
<button @click="post" :disabled="posting">%i18n:desktop.tags.mk-post-form-home-widget.post%</button>
|
<button @click="post" :disabled="posting">%i18n:desktop.tags.mk-post-form-home-widget.note%</button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@ export default define({
|
||||||
post() {
|
post() {
|
||||||
this.posting = true;
|
this.posting = true;
|
||||||
|
|
||||||
(this as any).api('posts/create', {
|
(this as any).api('notes/create', {
|
||||||
text: this.text
|
text: this.text
|
||||||
}).then(data => {
|
}).then(data => {
|
||||||
this.clear();
|
this.clear();
|
||||||
|
|
|
@ -5,8 +5,8 @@
|
||||||
<button @click="fetch" title="%i18n:desktop.tags.mk-trends-home-widget.refresh%">%fa:sync%</button>
|
<button @click="fetch" title="%i18n:desktop.tags.mk-trends-home-widget.refresh%">%fa:sync%</button>
|
||||||
</template>
|
</template>
|
||||||
<p class="fetching" v-if="fetching">%fa:spinner .pulse .fw%%i18n:common.loading%<mk-ellipsis/></p>
|
<p class="fetching" v-if="fetching">%fa:spinner .pulse .fw%%i18n:common.loading%<mk-ellipsis/></p>
|
||||||
<div class="post" v-else-if="post != null">
|
<div class="note" v-else-if="note != null">
|
||||||
<p class="text"><router-link :to="`/@${ acct }/${ post.id }`">{{ post.text }}</router-link></p>
|
<p class="text"><router-link :to="`/@${ acct }/${ note.id }`">{{ note.text }}</router-link></p>
|
||||||
<p class="author">―<router-link :to="`/@${ acct }`">@{{ acct }}</router-link></p>
|
<p class="author">―<router-link :to="`/@${ acct }`">@{{ acct }}</router-link></p>
|
||||||
</div>
|
</div>
|
||||||
<p class="empty" v-else>%i18n:desktop.tags.mk-trends-home-widget.nothing%</p>
|
<p class="empty" v-else>%i18n:desktop.tags.mk-trends-home-widget.nothing%</p>
|
||||||
|
@ -25,12 +25,12 @@ export default define({
|
||||||
}).extend({
|
}).extend({
|
||||||
computed: {
|
computed: {
|
||||||
acct() {
|
acct() {
|
||||||
return getAcct(this.post.user);
|
return getAcct(this.note.user);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
post: null,
|
note: null,
|
||||||
fetching: true,
|
fetching: true,
|
||||||
offset: 0
|
offset: 0
|
||||||
};
|
};
|
||||||
|
@ -44,23 +44,23 @@ export default define({
|
||||||
},
|
},
|
||||||
fetch() {
|
fetch() {
|
||||||
this.fetching = true;
|
this.fetching = true;
|
||||||
this.post = null;
|
this.note = null;
|
||||||
|
|
||||||
(this as any).api('posts/trend', {
|
(this as any).api('notes/trend', {
|
||||||
limit: 1,
|
limit: 1,
|
||||||
offset: this.offset,
|
offset: this.offset,
|
||||||
repost: false,
|
renote: false,
|
||||||
reply: false,
|
reply: false,
|
||||||
media: false,
|
media: false,
|
||||||
poll: false
|
poll: false
|
||||||
}).then(posts => {
|
}).then(notes => {
|
||||||
const post = posts ? posts[0] : null;
|
const note = notes ? notes[0] : null;
|
||||||
if (post == null) {
|
if (note == null) {
|
||||||
this.offset = 0;
|
this.offset = 0;
|
||||||
} else {
|
} else {
|
||||||
this.offset++;
|
this.offset++;
|
||||||
}
|
}
|
||||||
this.post = post;
|
this.note = note;
|
||||||
this.fetching = false;
|
this.fetching = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -103,7 +103,7 @@ export default define({
|
||||||
&:active
|
&:active
|
||||||
color #999
|
color #999
|
||||||
|
|
||||||
> .post
|
> .note
|
||||||
padding 16px
|
padding 16px
|
||||||
font-size 12px
|
font-size 12px
|
||||||
font-style oblique
|
font-style oblique
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
<b-form-checkbox-group v-model="permission" stacked>
|
<b-form-checkbox-group v-model="permission" stacked>
|
||||||
<b-form-checkbox value="account-read">アカウントの情報を見る。</b-form-checkbox>
|
<b-form-checkbox value="account-read">アカウントの情報を見る。</b-form-checkbox>
|
||||||
<b-form-checkbox value="account-write">アカウントの情報を操作する。</b-form-checkbox>
|
<b-form-checkbox value="account-write">アカウントの情報を操作する。</b-form-checkbox>
|
||||||
<b-form-checkbox value="post-write">投稿する。</b-form-checkbox>
|
<b-form-checkbox value="note-write">投稿する。</b-form-checkbox>
|
||||||
<b-form-checkbox value="reaction-write">リアクションしたりリアクションをキャンセルする。</b-form-checkbox>
|
<b-form-checkbox value="reaction-write">リアクションしたりリアクションをキャンセルする。</b-form-checkbox>
|
||||||
<b-form-checkbox value="following-write">フォローしたりフォロー解除する。</b-form-checkbox>
|
<b-form-checkbox value="following-write">フォローしたりフォロー解除する。</b-form-checkbox>
|
||||||
<b-form-checkbox value="drive-read">ドライブを見る。</b-form-checkbox>
|
<b-form-checkbox value="drive-read">ドライブを見る。</b-form-checkbox>
|
||||||
|
|
|
@ -1,24 +1,24 @@
|
||||||
import PostForm from '../views/components/post-form.vue';
|
import PostForm from '../views/components/post-form.vue';
|
||||||
//import RepostForm from '../views/components/repost-form.vue';
|
//import RenoteForm from '../views/components/renote-form.vue';
|
||||||
import getPostSummary from '../../../../renderers/get-post-summary';
|
import getNoteSummary from '../../../../renderers/get-note-summary';
|
||||||
|
|
||||||
export default (os) => (opts) => {
|
export default (os) => (opts) => {
|
||||||
const o = opts || {};
|
const o = opts || {};
|
||||||
|
|
||||||
if (o.repost) {
|
if (o.renote) {
|
||||||
/*const vm = new RepostForm({
|
/*const vm = new RenoteForm({
|
||||||
propsData: {
|
propsData: {
|
||||||
repost: o.repost
|
renote: o.renote
|
||||||
}
|
}
|
||||||
}).$mount();
|
}).$mount();
|
||||||
vm.$once('cancel', recover);
|
vm.$once('cancel', recover);
|
||||||
vm.$once('post', recover);
|
vm.$once('note', recover);
|
||||||
document.body.appendChild(vm.$el);*/
|
document.body.appendChild(vm.$el);*/
|
||||||
|
|
||||||
const text = window.prompt(`「${getPostSummary(o.repost)}」をRepost`);
|
const text = window.prompt(`「${getNoteSummary(o.renote)}」をRenote`);
|
||||||
if (text == null) return;
|
if (text == null) return;
|
||||||
os.api('posts/create', {
|
os.api('notes/create', {
|
||||||
repostId: o.repost.id,
|
renoteId: o.renote.id,
|
||||||
text: text == '' ? undefined : text
|
text: text == '' ? undefined : text
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
@ -36,7 +36,7 @@ export default (os) => (opts) => {
|
||||||
}
|
}
|
||||||
}).$mount();
|
}).$mount();
|
||||||
vm.$once('cancel', recover);
|
vm.$once('cancel', recover);
|
||||||
vm.$once('post', recover);
|
vm.$once('note', recover);
|
||||||
document.body.appendChild(vm.$el);
|
document.body.appendChild(vm.$el);
|
||||||
(vm as any).focus();
|
(vm as any).focus();
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@ import MkDrive from './views/pages/drive.vue';
|
||||||
import MkNotifications from './views/pages/notifications.vue';
|
import MkNotifications from './views/pages/notifications.vue';
|
||||||
import MkMessaging from './views/pages/messaging.vue';
|
import MkMessaging from './views/pages/messaging.vue';
|
||||||
import MkMessagingRoom from './views/pages/messaging-room.vue';
|
import MkMessagingRoom from './views/pages/messaging-room.vue';
|
||||||
import MkPost from './views/pages/post.vue';
|
import MkNote from './views/pages/note.vue';
|
||||||
import MkSearch from './views/pages/search.vue';
|
import MkSearch from './views/pages/search.vue';
|
||||||
import MkFollowers from './views/pages/followers.vue';
|
import MkFollowers from './views/pages/followers.vue';
|
||||||
import MkFollowing from './views/pages/following.vue';
|
import MkFollowing from './views/pages/following.vue';
|
||||||
|
@ -68,7 +68,7 @@ init((launch) => {
|
||||||
{ path: '/@:user', component: MkUser },
|
{ path: '/@:user', component: MkUser },
|
||||||
{ path: '/@:user/followers', component: MkFollowers },
|
{ path: '/@:user/followers', component: MkFollowers },
|
||||||
{ path: '/@:user/following', component: MkFollowing },
|
{ path: '/@:user/following', component: MkFollowing },
|
||||||
{ path: '/@:user/:post', component: MkPost }
|
{ path: '/@:user/:note', component: MkNote }
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -2,14 +2,14 @@
|
||||||
<div class="mk-activity">
|
<div class="mk-activity">
|
||||||
<svg v-if="data" ref="canvas" viewBox="0 0 30 1" preserveAspectRatio="none">
|
<svg v-if="data" ref="canvas" viewBox="0 0 30 1" preserveAspectRatio="none">
|
||||||
<g v-for="(d, i) in data">
|
<g v-for="(d, i) in data">
|
||||||
<rect width="0.8" :height="d.postsH"
|
<rect width="0.8" :height="d.notesH"
|
||||||
:x="i + 0.1" :y="1 - d.postsH - d.repliesH - d.repostsH"
|
:x="i + 0.1" :y="1 - d.notesH - d.repliesH - d.renotesH"
|
||||||
fill="#41ddde"/>
|
fill="#41ddde"/>
|
||||||
<rect width="0.8" :height="d.repliesH"
|
<rect width="0.8" :height="d.repliesH"
|
||||||
:x="i + 0.1" :y="1 - d.repliesH - d.repostsH"
|
:x="i + 0.1" :y="1 - d.repliesH - d.renotesH"
|
||||||
fill="#f7796c"/>
|
fill="#f7796c"/>
|
||||||
<rect width="0.8" :height="d.repostsH"
|
<rect width="0.8" :height="d.renotesH"
|
||||||
:x="i + 0.1" :y="1 - d.repostsH"
|
:x="i + 0.1" :y="1 - d.renotesH"
|
||||||
fill="#a1de41"/>
|
fill="#a1de41"/>
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
|
@ -32,12 +32,12 @@ export default Vue.extend({
|
||||||
userId: this.user.id,
|
userId: this.user.id,
|
||||||
limit: 30
|
limit: 30
|
||||||
}).then(data => {
|
}).then(data => {
|
||||||
data.forEach(d => d.total = d.posts + d.replies + d.reposts);
|
data.forEach(d => d.total = d.notes + d.replies + d.renotes);
|
||||||
this.peak = Math.max.apply(null, data.map(d => d.total));
|
this.peak = Math.max.apply(null, data.map(d => d.total));
|
||||||
data.forEach(d => {
|
data.forEach(d => {
|
||||||
d.postsH = d.posts / this.peak;
|
d.notesH = d.notes / this.peak;
|
||||||
d.repliesH = d.replies / this.peak;
|
d.repliesH = d.replies / this.peak;
|
||||||
d.repostsH = d.reposts / this.peak;
|
d.renotesH = d.renotes / this.peak;
|
||||||
});
|
});
|
||||||
data.reverse();
|
data.reverse();
|
||||||
this.data = data;
|
this.data = data;
|
||||||
|
|
|
@ -2,16 +2,16 @@ import Vue from 'vue';
|
||||||
|
|
||||||
import ui from './ui.vue';
|
import ui from './ui.vue';
|
||||||
import timeline from './timeline.vue';
|
import timeline from './timeline.vue';
|
||||||
import post from './post.vue';
|
import note from './note.vue';
|
||||||
import posts from './posts.vue';
|
import notes from './notes.vue';
|
||||||
import mediaImage from './media-image.vue';
|
import mediaImage from './media-image.vue';
|
||||||
import mediaVideo from './media-video.vue';
|
import mediaVideo from './media-video.vue';
|
||||||
import drive from './drive.vue';
|
import drive from './drive.vue';
|
||||||
import postPreview from './post-preview.vue';
|
import notePreview from './note-preview.vue';
|
||||||
import subPostContent from './sub-post-content.vue';
|
import subNoteContent from './sub-note-content.vue';
|
||||||
import postCard from './post-card.vue';
|
import noteCard from './note-card.vue';
|
||||||
import userCard from './user-card.vue';
|
import userCard from './user-card.vue';
|
||||||
import postDetail from './post-detail.vue';
|
import noteDetail from './note-detail.vue';
|
||||||
import followButton from './follow-button.vue';
|
import followButton from './follow-button.vue';
|
||||||
import friendsMaker from './friends-maker.vue';
|
import friendsMaker from './friends-maker.vue';
|
||||||
import notification from './notification.vue';
|
import notification from './notification.vue';
|
||||||
|
@ -25,16 +25,16 @@ import widgetContainer from './widget-container.vue';
|
||||||
|
|
||||||
Vue.component('mk-ui', ui);
|
Vue.component('mk-ui', ui);
|
||||||
Vue.component('mk-timeline', timeline);
|
Vue.component('mk-timeline', timeline);
|
||||||
Vue.component('mk-post', post);
|
Vue.component('mk-note', note);
|
||||||
Vue.component('mk-posts', posts);
|
Vue.component('mk-notes', notes);
|
||||||
Vue.component('mk-media-image', mediaImage);
|
Vue.component('mk-media-image', mediaImage);
|
||||||
Vue.component('mk-media-video', mediaVideo);
|
Vue.component('mk-media-video', mediaVideo);
|
||||||
Vue.component('mk-drive', drive);
|
Vue.component('mk-drive', drive);
|
||||||
Vue.component('mk-post-preview', postPreview);
|
Vue.component('mk-note-preview', notePreview);
|
||||||
Vue.component('mk-sub-post-content', subPostContent);
|
Vue.component('mk-sub-note-content', subNoteContent);
|
||||||
Vue.component('mk-post-card', postCard);
|
Vue.component('mk-note-card', noteCard);
|
||||||
Vue.component('mk-user-card', userCard);
|
Vue.component('mk-user-card', userCard);
|
||||||
Vue.component('mk-post-detail', postDetail);
|
Vue.component('mk-note-detail', noteDetail);
|
||||||
Vue.component('mk-follow-button', followButton);
|
Vue.component('mk-follow-button', followButton);
|
||||||
Vue.component('mk-friends-maker', friendsMaker);
|
Vue.component('mk-friends-maker', friendsMaker);
|
||||||
Vue.component('mk-notification', notification);
|
Vue.component('mk-notification', notification);
|
||||||
|
|
|
@ -1,41 +1,41 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="mk-post-card">
|
<div class="mk-note-card">
|
||||||
<a :href="`/@${acct}/${post.id}`">
|
<a :href="`/@${acct}/${note.id}`">
|
||||||
<header>
|
<header>
|
||||||
<img :src="`${acct}?thumbnail&size=64`" alt="avatar"/><h3>{{ name }}</h3>
|
<img :src="`${acct}?thumbnail&size=64`" alt="avatar"/><h3>{{ name }}</h3>
|
||||||
</header>
|
</header>
|
||||||
<div>
|
<div>
|
||||||
{{ text }}
|
{{ text }}
|
||||||
</div>
|
</div>
|
||||||
<mk-time :time="post.createdAt"/>
|
<mk-time :time="note.createdAt"/>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import summary from '../../../../../renderers/get-post-summary';
|
import summary from '../../../../../renderers/get-note-summary';
|
||||||
import getAcct from '../../../../../acct/render';
|
import getAcct from '../../../../../acct/render';
|
||||||
import getUserName from '../../../../../renderers/get-user-name';
|
import getUserName from '../../../../../renderers/get-user-name';
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
props: ['post'],
|
props: ['note'],
|
||||||
computed: {
|
computed: {
|
||||||
acct() {
|
acct() {
|
||||||
return getAcct(this.post.user);
|
return getAcct(this.note.user);
|
||||||
},
|
},
|
||||||
name() {
|
name() {
|
||||||
return getUserName(this.post.user);
|
return getUserName(this.note.user);
|
||||||
},
|
},
|
||||||
text(): string {
|
text(): string {
|
||||||
return summary(this.post);
|
return summary(this.note);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="stylus" scoped>
|
<style lang="stylus" scoped>
|
||||||
.mk-post-card
|
.mk-note-card
|
||||||
display inline-block
|
display inline-block
|
||||||
width 150px
|
width 150px
|
||||||
//height 120px
|
//height 120px
|
|
@ -1,18 +1,18 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="root sub">
|
<div class="root sub">
|
||||||
<router-link class="avatar-anchor" :to="`/@${acct}`">
|
<router-link class="avatar-anchor" :to="`/@${acct}`">
|
||||||
<img class="avatar" :src="`${post.user.avatarUrl}?thumbnail&size=64`" alt="avatar"/>
|
<img class="avatar" :src="`${note.user.avatarUrl}?thumbnail&size=64`" alt="avatar"/>
|
||||||
</router-link>
|
</router-link>
|
||||||
<div class="main">
|
<div class="main">
|
||||||
<header>
|
<header>
|
||||||
<router-link class="name" :to="`/@${acct}`">{{ getUserName(post.user) }}</router-link>
|
<router-link class="name" :to="`/@${acct}`">{{ getUserName(note.user) }}</router-link>
|
||||||
<span class="username">@{{ acct }}</span>
|
<span class="username">@{{ acct }}</span>
|
||||||
<router-link class="time" :to="`/@${acct}/${post.id}`">
|
<router-link class="time" :to="`/@${acct}/${note.id}`">
|
||||||
<mk-time :time="post.createdAt"/>
|
<mk-time :time="note.createdAt"/>
|
||||||
</router-link>
|
</router-link>
|
||||||
</header>
|
</header>
|
||||||
<div class="body">
|
<div class="body">
|
||||||
<mk-sub-post-content class="text" :post="post"/>
|
<mk-sub-note-content class="text" :note="note"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -24,13 +24,13 @@ import getAcct from '../../../../../acct/render';
|
||||||
import getUserName from '../../../../../renderers/get-user-name';
|
import getUserName from '../../../../../renderers/get-user-name';
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
props: ['post'],
|
props: ['note'],
|
||||||
computed: {
|
computed: {
|
||||||
acct() {
|
acct() {
|
||||||
return getAcct(this.post.user);
|
return getAcct(this.note.user);
|
||||||
},
|
},
|
||||||
name() {
|
name() {
|
||||||
return getUserName(this.post.user);
|
return getUserName(this.note.user);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
462
src/client/app/mobile/views/components/note-detail.vue
Normal file
462
src/client/app/mobile/views/components/note-detail.vue
Normal file
|
@ -0,0 +1,462 @@
|
||||||
|
<template>
|
||||||
|
<div class="mk-note-detail">
|
||||||
|
<button
|
||||||
|
class="more"
|
||||||
|
v-if="p.reply && p.reply.replyId && context == null"
|
||||||
|
@click="fetchContext"
|
||||||
|
:disabled="fetchingContext"
|
||||||
|
>
|
||||||
|
<template v-if="!contextFetching">%fa:ellipsis-v%</template>
|
||||||
|
<template v-if="contextFetching">%fa:spinner .pulse%</template>
|
||||||
|
</button>
|
||||||
|
<div class="context">
|
||||||
|
<x-sub v-for="note in context" :key="note.id" :note="note"/>
|
||||||
|
</div>
|
||||||
|
<div class="reply-to" v-if="p.reply">
|
||||||
|
<x-sub :note="p.reply"/>
|
||||||
|
</div>
|
||||||
|
<div class="renote" v-if="isRenote">
|
||||||
|
<p>
|
||||||
|
<router-link class="avatar-anchor" :to="`/@${acct}`">
|
||||||
|
<img class="avatar" :src="`${note.user.avatarUrl}?thumbnail&size=32`" alt="avatar"/>
|
||||||
|
</router-link>
|
||||||
|
%fa:retweet%
|
||||||
|
<router-link class="name" :to="`/@${acct}`">
|
||||||
|
{{ name }}
|
||||||
|
</router-link>
|
||||||
|
がRenote
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<article>
|
||||||
|
<header>
|
||||||
|
<router-link class="avatar-anchor" :to="`/@${pAcct}`">
|
||||||
|
<img class="avatar" :src="`${p.user.avatarUrl}?thumbnail&size=64`" alt="avatar"/>
|
||||||
|
</router-link>
|
||||||
|
<div>
|
||||||
|
<router-link class="name" :to="`/@${pAcct}`">{{ pName }}</router-link>
|
||||||
|
<span class="username">@{{ pAcct }}</span>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
<div class="body">
|
||||||
|
<mk-note-html v-if="p.text" :ast="p.text" :i="os.i" :class="$style.text"/>
|
||||||
|
<div class="tags" v-if="p.tags && p.tags.length > 0">
|
||||||
|
<router-link v-for="tag in p.tags" :key="tag" :to="`/search?q=#${tag}`">{{ tag }}</router-link>
|
||||||
|
</div>
|
||||||
|
<div class="media" v-if="p.media.length > 0">
|
||||||
|
<mk-media-list :media-list="p.media"/>
|
||||||
|
</div>
|
||||||
|
<mk-poll v-if="p.poll" :note="p"/>
|
||||||
|
<mk-url-preview v-for="url in urls" :url="url" :key="url"/>
|
||||||
|
<a class="location" v-if="p.geo" :href="`http://maps.google.com/maps?q=${p.geo.coordinates[1]},${p.geo.coordinates[0]}`" target="_blank">%fa:map-marker-alt% 位置情報</a>
|
||||||
|
<div class="map" v-if="p.geo" ref="map"></div>
|
||||||
|
<div class="renote" v-if="p.renote">
|
||||||
|
<mk-note-preview :note="p.renote"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<router-link class="time" :to="`/@${pAcct}/${p.id}`">
|
||||||
|
<mk-time :time="p.createdAt" mode="detail"/>
|
||||||
|
</router-link>
|
||||||
|
<footer>
|
||||||
|
<mk-reactions-viewer :note="p"/>
|
||||||
|
<button @click="reply" title="%i18n:mobile.tags.mk-note-detail.reply%">
|
||||||
|
%fa:reply%<p class="count" v-if="p.repliesCount > 0">{{ p.repliesCount }}</p>
|
||||||
|
</button>
|
||||||
|
<button @click="renote" title="Renote">
|
||||||
|
%fa:retweet%<p class="count" v-if="p.renoteCount > 0">{{ p.renoteCount }}</p>
|
||||||
|
</button>
|
||||||
|
<button :class="{ reacted: p.myReaction != null }" @click="react" ref="reactButton" title="%i18n:mobile.tags.mk-note-detail.reaction%">
|
||||||
|
%fa:plus%<p class="count" v-if="p.reactions_count > 0">{{ p.reactions_count }}</p>
|
||||||
|
</button>
|
||||||
|
<button @click="menu" ref="menuButton">
|
||||||
|
%fa:ellipsis-h%
|
||||||
|
</button>
|
||||||
|
</footer>
|
||||||
|
</article>
|
||||||
|
<div class="replies" v-if="!compact">
|
||||||
|
<x-sub v-for="note in replies" :key="note.id" :note="note"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import Vue from 'vue';
|
||||||
|
import getAcct from '../../../../../acct/render';
|
||||||
|
import getUserName from '../../../../../renderers/get-user-name';
|
||||||
|
import parse from '../../../../../text/parse';
|
||||||
|
|
||||||
|
import MkNoteMenu from '../../../common/views/components/note-menu.vue';
|
||||||
|
import MkReactionPicker from '../../../common/views/components/reaction-picker.vue';
|
||||||
|
import XSub from './note-detail.sub.vue';
|
||||||
|
|
||||||
|
export default Vue.extend({
|
||||||
|
components: {
|
||||||
|
XSub
|
||||||
|
},
|
||||||
|
|
||||||
|
props: {
|
||||||
|
note: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
compact: {
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
context: [],
|
||||||
|
contextFetching: false,
|
||||||
|
replies: []
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
acct(): string {
|
||||||
|
return getAcct(this.note.user);
|
||||||
|
},
|
||||||
|
name(): string {
|
||||||
|
return getUserName(this.note.user);
|
||||||
|
},
|
||||||
|
pAcct(): string {
|
||||||
|
return getAcct(this.p.user);
|
||||||
|
},
|
||||||
|
pName(): string {
|
||||||
|
return getUserName(this.p.user);
|
||||||
|
},
|
||||||
|
isRenote(): boolean {
|
||||||
|
return (this.note.renote &&
|
||||||
|
this.note.text == null &&
|
||||||
|
this.note.mediaIds == null &&
|
||||||
|
this.note.poll == null);
|
||||||
|
},
|
||||||
|
p(): any {
|
||||||
|
return this.isRenote ? this.note.renote : this.note;
|
||||||
|
},
|
||||||
|
reactionsCount(): number {
|
||||||
|
return this.p.reactionCounts
|
||||||
|
? Object.keys(this.p.reactionCounts)
|
||||||
|
.map(key => this.p.reactionCounts[key])
|
||||||
|
.reduce((a, b) => a + b)
|
||||||
|
: 0;
|
||||||
|
},
|
||||||
|
urls(): string[] {
|
||||||
|
if (this.p.text) {
|
||||||
|
const ast = parse(this.p.text);
|
||||||
|
return ast
|
||||||
|
.filter(t => (t.type == 'url' || t.type == 'link') && !t.silent)
|
||||||
|
.map(t => t.url);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
// Get replies
|
||||||
|
if (!this.compact) {
|
||||||
|
(this as any).api('notes/replies', {
|
||||||
|
noteId: this.p.id,
|
||||||
|
limit: 8
|
||||||
|
}).then(replies => {
|
||||||
|
this.replies = replies;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw map
|
||||||
|
if (this.p.geo) {
|
||||||
|
const shouldShowMap = (this as any).os.isSignedIn ? (this as any).os.i.account.clientSettings.showMaps : true;
|
||||||
|
if (shouldShowMap) {
|
||||||
|
(this as any).os.getGoogleMaps().then(maps => {
|
||||||
|
const uluru = new maps.LatLng(this.p.geo.coordinates[1], this.p.geo.coordinates[0]);
|
||||||
|
const map = new maps.Map(this.$refs.map, {
|
||||||
|
center: uluru,
|
||||||
|
zoom: 15
|
||||||
|
});
|
||||||
|
new maps.Marker({
|
||||||
|
position: uluru,
|
||||||
|
map: map
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
fetchContext() {
|
||||||
|
this.contextFetching = true;
|
||||||
|
|
||||||
|
// Fetch context
|
||||||
|
(this as any).api('notes/context', {
|
||||||
|
noteId: this.p.replyId
|
||||||
|
}).then(context => {
|
||||||
|
this.contextFetching = false;
|
||||||
|
this.context = context.reverse();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
reply() {
|
||||||
|
(this as any).apis.post({
|
||||||
|
reply: this.p
|
||||||
|
});
|
||||||
|
},
|
||||||
|
renote() {
|
||||||
|
(this as any).apis.post({
|
||||||
|
renote: this.p
|
||||||
|
});
|
||||||
|
},
|
||||||
|
react() {
|
||||||
|
(this as any).os.new(MkReactionPicker, {
|
||||||
|
source: this.$refs.reactButton,
|
||||||
|
note: this.p,
|
||||||
|
compact: true
|
||||||
|
});
|
||||||
|
},
|
||||||
|
menu() {
|
||||||
|
(this as any).os.new(MkNoteMenu, {
|
||||||
|
source: this.$refs.menuButton,
|
||||||
|
note: this.p,
|
||||||
|
compact: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="stylus" scoped>
|
||||||
|
@import '~const.styl'
|
||||||
|
|
||||||
|
.mk-note-detail
|
||||||
|
overflow hidden
|
||||||
|
margin 0 auto
|
||||||
|
padding 0
|
||||||
|
width 100%
|
||||||
|
text-align left
|
||||||
|
background #fff
|
||||||
|
border-radius 8px
|
||||||
|
box-shadow 0 0 0 1px rgba(0, 0, 0, 0.2)
|
||||||
|
|
||||||
|
> .fetching
|
||||||
|
padding 64px 0
|
||||||
|
|
||||||
|
> .more
|
||||||
|
display block
|
||||||
|
margin 0
|
||||||
|
padding 10px 0
|
||||||
|
width 100%
|
||||||
|
font-size 1em
|
||||||
|
text-align center
|
||||||
|
color #999
|
||||||
|
cursor pointer
|
||||||
|
background #fafafa
|
||||||
|
outline none
|
||||||
|
border none
|
||||||
|
border-bottom solid 1px #eef0f2
|
||||||
|
border-radius 6px 6px 0 0
|
||||||
|
box-shadow none
|
||||||
|
|
||||||
|
&:hover
|
||||||
|
background #f6f6f6
|
||||||
|
|
||||||
|
&:active
|
||||||
|
background #f0f0f0
|
||||||
|
|
||||||
|
&:disabled
|
||||||
|
color #ccc
|
||||||
|
|
||||||
|
> .context
|
||||||
|
> *
|
||||||
|
border-bottom 1px solid #eef0f2
|
||||||
|
|
||||||
|
> .renote
|
||||||
|
color #9dbb00
|
||||||
|
background linear-gradient(to bottom, #edfde2 0%, #fff 100%)
|
||||||
|
|
||||||
|
> p
|
||||||
|
margin 0
|
||||||
|
padding 16px 32px
|
||||||
|
|
||||||
|
.avatar-anchor
|
||||||
|
display inline-block
|
||||||
|
|
||||||
|
.avatar
|
||||||
|
vertical-align bottom
|
||||||
|
min-width 28px
|
||||||
|
min-height 28px
|
||||||
|
max-width 28px
|
||||||
|
max-height 28px
|
||||||
|
margin 0 8px 0 0
|
||||||
|
border-radius 6px
|
||||||
|
|
||||||
|
[data-fa]
|
||||||
|
margin-right 4px
|
||||||
|
|
||||||
|
.name
|
||||||
|
font-weight bold
|
||||||
|
|
||||||
|
& + article
|
||||||
|
padding-top 8px
|
||||||
|
|
||||||
|
> .reply-to
|
||||||
|
border-bottom 1px solid #eef0f2
|
||||||
|
|
||||||
|
> article
|
||||||
|
padding 14px 16px 9px 16px
|
||||||
|
|
||||||
|
@media (min-width 500px)
|
||||||
|
padding 28px 32px 18px 32px
|
||||||
|
|
||||||
|
&:after
|
||||||
|
content ""
|
||||||
|
display block
|
||||||
|
clear both
|
||||||
|
|
||||||
|
&:hover
|
||||||
|
> .main > footer > button
|
||||||
|
color #888
|
||||||
|
|
||||||
|
> header
|
||||||
|
display flex
|
||||||
|
line-height 1.1
|
||||||
|
|
||||||
|
> .avatar-anchor
|
||||||
|
display block
|
||||||
|
padding 0 .5em 0 0
|
||||||
|
|
||||||
|
> .avatar
|
||||||
|
display block
|
||||||
|
width 54px
|
||||||
|
height 54px
|
||||||
|
margin 0
|
||||||
|
border-radius 8px
|
||||||
|
vertical-align bottom
|
||||||
|
|
||||||
|
@media (min-width 500px)
|
||||||
|
width 60px
|
||||||
|
height 60px
|
||||||
|
|
||||||
|
> div
|
||||||
|
|
||||||
|
> .name
|
||||||
|
display inline-block
|
||||||
|
margin .4em 0
|
||||||
|
color #777
|
||||||
|
font-size 16px
|
||||||
|
font-weight bold
|
||||||
|
text-align left
|
||||||
|
text-decoration none
|
||||||
|
|
||||||
|
&:hover
|
||||||
|
text-decoration underline
|
||||||
|
|
||||||
|
> .username
|
||||||
|
display block
|
||||||
|
text-align left
|
||||||
|
margin 0
|
||||||
|
color #ccc
|
||||||
|
|
||||||
|
> .body
|
||||||
|
padding 8px 0
|
||||||
|
|
||||||
|
> .renote
|
||||||
|
margin 8px 0
|
||||||
|
|
||||||
|
> .mk-note-preview
|
||||||
|
padding 16px
|
||||||
|
border dashed 1px #c0dac6
|
||||||
|
border-radius 8px
|
||||||
|
|
||||||
|
> .location
|
||||||
|
margin 4px 0
|
||||||
|
font-size 12px
|
||||||
|
color #ccc
|
||||||
|
|
||||||
|
> .map
|
||||||
|
width 100%
|
||||||
|
height 200px
|
||||||
|
|
||||||
|
&:empty
|
||||||
|
display none
|
||||||
|
|
||||||
|
> .mk-url-preview
|
||||||
|
margin-top 8px
|
||||||
|
|
||||||
|
> .media
|
||||||
|
> img
|
||||||
|
display block
|
||||||
|
max-width 100%
|
||||||
|
|
||||||
|
> .tags
|
||||||
|
margin 4px 0 0 0
|
||||||
|
|
||||||
|
> *
|
||||||
|
display inline-block
|
||||||
|
margin 0 8px 0 0
|
||||||
|
padding 2px 8px 2px 16px
|
||||||
|
font-size 90%
|
||||||
|
color #8d969e
|
||||||
|
background #edf0f3
|
||||||
|
border-radius 4px
|
||||||
|
|
||||||
|
&:before
|
||||||
|
content ""
|
||||||
|
display block
|
||||||
|
position absolute
|
||||||
|
top 0
|
||||||
|
bottom 0
|
||||||
|
left 4px
|
||||||
|
width 8px
|
||||||
|
height 8px
|
||||||
|
margin auto 0
|
||||||
|
background #fff
|
||||||
|
border-radius 100%
|
||||||
|
|
||||||
|
> .time
|
||||||
|
font-size 16px
|
||||||
|
color #c0c0c0
|
||||||
|
|
||||||
|
> footer
|
||||||
|
font-size 1.2em
|
||||||
|
|
||||||
|
> button
|
||||||
|
margin 0
|
||||||
|
padding 8px
|
||||||
|
background transparent
|
||||||
|
border none
|
||||||
|
box-shadow none
|
||||||
|
font-size 1em
|
||||||
|
color #ddd
|
||||||
|
cursor pointer
|
||||||
|
|
||||||
|
&:not(:last-child)
|
||||||
|
margin-right 28px
|
||||||
|
|
||||||
|
&:hover
|
||||||
|
color #666
|
||||||
|
|
||||||
|
> .count
|
||||||
|
display inline
|
||||||
|
margin 0 0 0 8px
|
||||||
|
color #999
|
||||||
|
|
||||||
|
&.reacted
|
||||||
|
color $theme-color
|
||||||
|
|
||||||
|
> .replies
|
||||||
|
> *
|
||||||
|
border-top 1px solid #eef0f2
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style lang="stylus" module>
|
||||||
|
.text
|
||||||
|
display block
|
||||||
|
margin 0
|
||||||
|
padding 0
|
||||||
|
overflow-wrap break-word
|
||||||
|
font-size 16px
|
||||||
|
color #717171
|
||||||
|
|
||||||
|
@media (min-width 500px)
|
||||||
|
font-size 24px
|
||||||
|
|
||||||
|
</style>
|
|
@ -1,18 +1,18 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="mk-post-preview">
|
<div class="mk-note-preview">
|
||||||
<router-link class="avatar-anchor" :to="`/@${acct}`">
|
<router-link class="avatar-anchor" :to="`/@${acct}`">
|
||||||
<img class="avatar" :src="`${post.user.avatarUrl}?thumbnail&size=64`" alt="avatar"/>
|
<img class="avatar" :src="`${note.user.avatarUrl}?thumbnail&size=64`" alt="avatar"/>
|
||||||
</router-link>
|
</router-link>
|
||||||
<div class="main">
|
<div class="main">
|
||||||
<header>
|
<header>
|
||||||
<router-link class="name" :to="`/@${acct}`">{{ name }}</router-link>
|
<router-link class="name" :to="`/@${acct}`">{{ name }}</router-link>
|
||||||
<span class="username">@{{ acct }}</span>
|
<span class="username">@{{ acct }}</span>
|
||||||
<router-link class="time" :to="`/@${acct}/${post.id}`">
|
<router-link class="time" :to="`/@${acct}/${note.id}`">
|
||||||
<mk-time :time="post.createdAt"/>
|
<mk-time :time="note.createdAt"/>
|
||||||
</router-link>
|
</router-link>
|
||||||
</header>
|
</header>
|
||||||
<div class="body">
|
<div class="body">
|
||||||
<mk-sub-post-content class="text" :post="post"/>
|
<mk-sub-note-content class="text" :note="note"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -24,20 +24,20 @@ import getAcct from '../../../../../acct/render';
|
||||||
import getUserName from '../../../../../renderers/get-user-name';
|
import getUserName from '../../../../../renderers/get-user-name';
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
props: ['post'],
|
props: ['note'],
|
||||||
computed: {
|
computed: {
|
||||||
acct() {
|
acct() {
|
||||||
return getAcct(this.post.user);
|
return getAcct(this.note.user);
|
||||||
},
|
},
|
||||||
name() {
|
name() {
|
||||||
return getUserName(this.post.user);
|
return getUserName(this.note.user);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="stylus" scoped>
|
<style lang="stylus" scoped>
|
||||||
.mk-post-preview
|
.mk-note-preview
|
||||||
margin 0
|
margin 0
|
||||||
padding 0
|
padding 0
|
||||||
font-size 0.9em
|
font-size 0.9em
|
|
@ -1,18 +1,18 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="sub">
|
<div class="sub">
|
||||||
<router-link class="avatar-anchor" :to="`/@${acct}`">
|
<router-link class="avatar-anchor" :to="`/@${acct}`">
|
||||||
<img class="avatar" :src="`${post.user.avatarUrl}?thumbnail&size=96`" alt="avatar"/>
|
<img class="avatar" :src="`${note.user.avatarUrl}?thumbnail&size=96`" alt="avatar"/>
|
||||||
</router-link>
|
</router-link>
|
||||||
<div class="main">
|
<div class="main">
|
||||||
<header>
|
<header>
|
||||||
<router-link class="name" :to="`/@${acct}`">{{ getUserName(post.user) }}</router-link>
|
<router-link class="name" :to="`/@${acct}`">{{ getUserName(note.user) }}</router-link>
|
||||||
<span class="username">@{{ acct }}</span>
|
<span class="username">@{{ acct }}</span>
|
||||||
<router-link class="created-at" :to="`/@${acct}/${post.id}`">
|
<router-link class="created-at" :to="`/@${acct}/${note.id}`">
|
||||||
<mk-time :time="post.createdAt"/>
|
<mk-time :time="note.createdAt"/>
|
||||||
</router-link>
|
</router-link>
|
||||||
</header>
|
</header>
|
||||||
<div class="body">
|
<div class="body">
|
||||||
<mk-sub-post-content class="text" :post="post"/>
|
<mk-sub-note-content class="text" :note="note"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -24,13 +24,13 @@ import getAcct from '../../../../../acct/render';
|
||||||
import getUserName from '../../../../../renderers/get-user-name';
|
import getUserName from '../../../../../renderers/get-user-name';
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
props: ['post'],
|
props: ['note'],
|
||||||
computed: {
|
computed: {
|
||||||
acct() {
|
acct() {
|
||||||
return getAcct(this.post.user);
|
return getAcct(this.note.user);
|
||||||
},
|
},
|
||||||
name() {
|
name() {
|
||||||
return getUserName(this.post.user);
|
return getUserName(this.note.user);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
540
src/client/app/mobile/views/components/note.vue
Normal file
540
src/client/app/mobile/views/components/note.vue
Normal file
|
@ -0,0 +1,540 @@
|
||||||
|
<template>
|
||||||
|
<div class="note" :class="{ renote: isRenote }">
|
||||||
|
<div class="reply-to" v-if="p.reply">
|
||||||
|
<x-sub :note="p.reply"/>
|
||||||
|
</div>
|
||||||
|
<div class="renote" v-if="isRenote">
|
||||||
|
<p>
|
||||||
|
<router-link class="avatar-anchor" :to="`/@${acct}`">
|
||||||
|
<img class="avatar" :src="`${note.user.avatarUrl}?thumbnail&size=64`" alt="avatar"/>
|
||||||
|
</router-link>
|
||||||
|
%fa:retweet%
|
||||||
|
<span>{{ '%i18n:mobile.tags.mk-timeline-note.reposted-by%'.substr(0, '%i18n:mobile.tags.mk-timeline-note.reposted-by%'.indexOf('{')) }}</span>
|
||||||
|
<router-link class="name" :to="`/@${acct}`">{{ name }}</router-link>
|
||||||
|
<span>{{ '%i18n:mobile.tags.mk-timeline-note.reposted-by%'.substr('%i18n:mobile.tags.mk-timeline-note.reposted-by%'.indexOf('}') + 1) }}</span>
|
||||||
|
</p>
|
||||||
|
<mk-time :time="note.createdAt"/>
|
||||||
|
</div>
|
||||||
|
<article>
|
||||||
|
<router-link class="avatar-anchor" :to="`/@${pAcct}`">
|
||||||
|
<img class="avatar" :src="`${p.user.avatarUrl}?thumbnail&size=96`" alt="avatar"/>
|
||||||
|
</router-link>
|
||||||
|
<div class="main">
|
||||||
|
<header>
|
||||||
|
<router-link class="name" :to="`/@${pAcct}`">{{ pName }}</router-link>
|
||||||
|
<span class="is-bot" v-if="p.user.host === null && p.user.account.isBot">bot</span>
|
||||||
|
<span class="username">@{{ pAcct }}</span>
|
||||||
|
<div class="info">
|
||||||
|
<span class="mobile" v-if="p.viaMobile">%fa:mobile-alt%</span>
|
||||||
|
<router-link class="created-at" :to="url">
|
||||||
|
<mk-time :time="p.createdAt"/>
|
||||||
|
</router-link>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
<div class="body">
|
||||||
|
<p class="channel" v-if="p.channel != null"><a target="_blank">{{ p.channel.title }}</a>:</p>
|
||||||
|
<div class="text">
|
||||||
|
<a class="reply" v-if="p.reply">
|
||||||
|
%fa:reply%
|
||||||
|
</a>
|
||||||
|
<mk-note-html v-if="p.text" :text="p.text" :i="os.i" :class="$style.text"/>
|
||||||
|
<a class="rp" v-if="p.renote != null">RP:</a>
|
||||||
|
</div>
|
||||||
|
<div class="media" v-if="p.media.length > 0">
|
||||||
|
<mk-media-list :media-list="p.media"/>
|
||||||
|
</div>
|
||||||
|
<mk-poll v-if="p.poll" :note="p" ref="pollViewer"/>
|
||||||
|
<div class="tags" v-if="p.tags && p.tags.length > 0">
|
||||||
|
<router-link v-for="tag in p.tags" :key="tag" :to="`/search?q=#${tag}`">{{ tag }}</router-link>
|
||||||
|
</div>
|
||||||
|
<mk-url-preview v-for="url in urls" :url="url" :key="url"/>
|
||||||
|
<a class="location" v-if="p.geo" :href="`http://maps.google.com/maps?q=${p.geo.coordinates[1]},${p.geo.coordinates[0]}`" target="_blank">%fa:map-marker-alt% 位置情報</a>
|
||||||
|
<div class="map" v-if="p.geo" ref="map"></div>
|
||||||
|
<span class="app" v-if="p.app">via <b>{{ p.app.name }}</b></span>
|
||||||
|
<div class="renote" v-if="p.renote">
|
||||||
|
<mk-note-preview :note="p.renote"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<footer>
|
||||||
|
<mk-reactions-viewer :note="p" ref="reactionsViewer"/>
|
||||||
|
<button @click="reply">
|
||||||
|
%fa:reply%<p class="count" v-if="p.repliesCount > 0">{{ p.repliesCount }}</p>
|
||||||
|
</button>
|
||||||
|
<button @click="renote" title="Renote">
|
||||||
|
%fa:retweet%<p class="count" v-if="p.renoteCount > 0">{{ p.renoteCount }}</p>
|
||||||
|
</button>
|
||||||
|
<button :class="{ reacted: p.myReaction != null }" @click="react" ref="reactButton">
|
||||||
|
%fa:plus%<p class="count" v-if="p.reactions_count > 0">{{ p.reactions_count }}</p>
|
||||||
|
</button>
|
||||||
|
<button class="menu" @click="menu" ref="menuButton">
|
||||||
|
%fa:ellipsis-h%
|
||||||
|
</button>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import Vue from 'vue';
|
||||||
|
import getAcct from '../../../../../acct/render';
|
||||||
|
import getUserName from '../../../../../renderers/get-user-name';
|
||||||
|
import parse from '../../../../../text/parse';
|
||||||
|
|
||||||
|
import MkNoteMenu from '../../../common/views/components/note-menu.vue';
|
||||||
|
import MkReactionPicker from '../../../common/views/components/reaction-picker.vue';
|
||||||
|
import XSub from './note.sub.vue';
|
||||||
|
|
||||||
|
export default Vue.extend({
|
||||||
|
components: {
|
||||||
|
XSub
|
||||||
|
},
|
||||||
|
|
||||||
|
props: ['note'],
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
connection: null,
|
||||||
|
connectionId: null
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
acct(): string {
|
||||||
|
return getAcct(this.note.user);
|
||||||
|
},
|
||||||
|
name(): string {
|
||||||
|
return getUserName(this.note.user);
|
||||||
|
},
|
||||||
|
pAcct(): string {
|
||||||
|
return getAcct(this.p.user);
|
||||||
|
},
|
||||||
|
pName(): string {
|
||||||
|
return getUserName(this.p.user);
|
||||||
|
},
|
||||||
|
isRenote(): boolean {
|
||||||
|
return (this.note.renote &&
|
||||||
|
this.note.text == null &&
|
||||||
|
this.note.mediaIds == null &&
|
||||||
|
this.note.poll == null);
|
||||||
|
},
|
||||||
|
p(): any {
|
||||||
|
return this.isRenote ? this.note.renote : this.note;
|
||||||
|
},
|
||||||
|
reactionsCount(): number {
|
||||||
|
return this.p.reactionCounts
|
||||||
|
? Object.keys(this.p.reactionCounts)
|
||||||
|
.map(key => this.p.reactionCounts[key])
|
||||||
|
.reduce((a, b) => a + b)
|
||||||
|
: 0;
|
||||||
|
},
|
||||||
|
url(): string {
|
||||||
|
return `/@${this.pAcct}/${this.p.id}`;
|
||||||
|
},
|
||||||
|
urls(): string[] {
|
||||||
|
if (this.p.text) {
|
||||||
|
const ast = parse(this.p.text);
|
||||||
|
return ast
|
||||||
|
.filter(t => (t.type == 'url' || t.type == 'link') && !t.silent)
|
||||||
|
.map(t => t.url);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
created() {
|
||||||
|
if ((this as any).os.isSignedIn) {
|
||||||
|
this.connection = (this as any).os.stream.getConnection();
|
||||||
|
this.connectionId = (this as any).os.stream.use();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
this.capture(true);
|
||||||
|
|
||||||
|
if ((this as any).os.isSignedIn) {
|
||||||
|
this.connection.on('_connected_', this.onStreamConnected);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw map
|
||||||
|
if (this.p.geo) {
|
||||||
|
const shouldShowMap = (this as any).os.isSignedIn ? (this as any).os.i.account.clientSettings.showMaps : true;
|
||||||
|
if (shouldShowMap) {
|
||||||
|
(this as any).os.getGoogleMaps().then(maps => {
|
||||||
|
const uluru = new maps.LatLng(this.p.geo.coordinates[1], this.p.geo.coordinates[0]);
|
||||||
|
const map = new maps.Map(this.$refs.map, {
|
||||||
|
center: uluru,
|
||||||
|
zoom: 15
|
||||||
|
});
|
||||||
|
new maps.Marker({
|
||||||
|
position: uluru,
|
||||||
|
map: map
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
beforeDestroy() {
|
||||||
|
this.decapture(true);
|
||||||
|
|
||||||
|
if ((this as any).os.isSignedIn) {
|
||||||
|
this.connection.off('_connected_', this.onStreamConnected);
|
||||||
|
(this as any).os.stream.dispose(this.connectionId);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
capture(withHandler = false) {
|
||||||
|
if ((this as any).os.isSignedIn) {
|
||||||
|
this.connection.send({
|
||||||
|
type: 'capture',
|
||||||
|
id: this.p.id
|
||||||
|
});
|
||||||
|
if (withHandler) this.connection.on('note-updated', this.onStreamNoteUpdated);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
decapture(withHandler = false) {
|
||||||
|
if ((this as any).os.isSignedIn) {
|
||||||
|
this.connection.send({
|
||||||
|
type: 'decapture',
|
||||||
|
id: this.p.id
|
||||||
|
});
|
||||||
|
if (withHandler) this.connection.off('note-updated', this.onStreamNoteUpdated);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onStreamConnected() {
|
||||||
|
this.capture();
|
||||||
|
},
|
||||||
|
onStreamNoteUpdated(data) {
|
||||||
|
const note = data.note;
|
||||||
|
if (note.id == this.note.id) {
|
||||||
|
this.$emit('update:note', note);
|
||||||
|
} else if (note.id == this.note.renoteId) {
|
||||||
|
this.note.renote = note;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
reply() {
|
||||||
|
(this as any).apis.post({
|
||||||
|
reply: this.p
|
||||||
|
});
|
||||||
|
},
|
||||||
|
renote() {
|
||||||
|
(this as any).apis.post({
|
||||||
|
renote: this.p
|
||||||
|
});
|
||||||
|
},
|
||||||
|
react() {
|
||||||
|
(this as any).os.new(MkReactionPicker, {
|
||||||
|
source: this.$refs.reactButton,
|
||||||
|
note: this.p,
|
||||||
|
compact: true
|
||||||
|
});
|
||||||
|
},
|
||||||
|
menu() {
|
||||||
|
(this as any).os.new(MkNoteMenu, {
|
||||||
|
source: this.$refs.menuButton,
|
||||||
|
note: this.p,
|
||||||
|
compact: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="stylus" scoped>
|
||||||
|
@import '~const.styl'
|
||||||
|
|
||||||
|
.note
|
||||||
|
font-size 12px
|
||||||
|
border-bottom solid 1px #eaeaea
|
||||||
|
|
||||||
|
&:first-child
|
||||||
|
border-radius 8px 8px 0 0
|
||||||
|
|
||||||
|
> .renote
|
||||||
|
border-radius 8px 8px 0 0
|
||||||
|
|
||||||
|
&:last-of-type
|
||||||
|
border-bottom none
|
||||||
|
|
||||||
|
@media (min-width 350px)
|
||||||
|
font-size 14px
|
||||||
|
|
||||||
|
@media (min-width 500px)
|
||||||
|
font-size 16px
|
||||||
|
|
||||||
|
> .renote
|
||||||
|
color #9dbb00
|
||||||
|
background linear-gradient(to bottom, #edfde2 0%, #fff 100%)
|
||||||
|
|
||||||
|
> p
|
||||||
|
margin 0
|
||||||
|
padding 8px 16px
|
||||||
|
line-height 28px
|
||||||
|
|
||||||
|
@media (min-width 500px)
|
||||||
|
padding 16px
|
||||||
|
|
||||||
|
.avatar-anchor
|
||||||
|
display inline-block
|
||||||
|
|
||||||
|
.avatar
|
||||||
|
vertical-align bottom
|
||||||
|
width 28px
|
||||||
|
height 28px
|
||||||
|
margin 0 8px 0 0
|
||||||
|
border-radius 6px
|
||||||
|
|
||||||
|
[data-fa]
|
||||||
|
margin-right 4px
|
||||||
|
|
||||||
|
.name
|
||||||
|
font-weight bold
|
||||||
|
|
||||||
|
> .mk-time
|
||||||
|
position absolute
|
||||||
|
top 8px
|
||||||
|
right 16px
|
||||||
|
font-size 0.9em
|
||||||
|
line-height 28px
|
||||||
|
|
||||||
|
@media (min-width 500px)
|
||||||
|
top 16px
|
||||||
|
|
||||||
|
& + article
|
||||||
|
padding-top 8px
|
||||||
|
|
||||||
|
> .reply-to
|
||||||
|
background rgba(0, 0, 0, 0.0125)
|
||||||
|
|
||||||
|
> .mk-note-preview
|
||||||
|
background transparent
|
||||||
|
|
||||||
|
> article
|
||||||
|
padding 14px 16px 9px 16px
|
||||||
|
|
||||||
|
&:after
|
||||||
|
content ""
|
||||||
|
display block
|
||||||
|
clear both
|
||||||
|
|
||||||
|
> .avatar-anchor
|
||||||
|
display block
|
||||||
|
float left
|
||||||
|
margin 0 10px 8px 0
|
||||||
|
position -webkit-sticky
|
||||||
|
position sticky
|
||||||
|
top 62px
|
||||||
|
|
||||||
|
@media (min-width 500px)
|
||||||
|
margin-right 16px
|
||||||
|
|
||||||
|
> .avatar
|
||||||
|
display block
|
||||||
|
width 48px
|
||||||
|
height 48px
|
||||||
|
margin 0
|
||||||
|
border-radius 6px
|
||||||
|
vertical-align bottom
|
||||||
|
|
||||||
|
@media (min-width 500px)
|
||||||
|
width 58px
|
||||||
|
height 58px
|
||||||
|
border-radius 8px
|
||||||
|
|
||||||
|
> .main
|
||||||
|
float left
|
||||||
|
width calc(100% - 58px)
|
||||||
|
|
||||||
|
@media (min-width 500px)
|
||||||
|
width calc(100% - 74px)
|
||||||
|
|
||||||
|
> header
|
||||||
|
display flex
|
||||||
|
align-items center
|
||||||
|
white-space nowrap
|
||||||
|
|
||||||
|
@media (min-width 500px)
|
||||||
|
margin-bottom 2px
|
||||||
|
|
||||||
|
> .name
|
||||||
|
display block
|
||||||
|
margin 0 0.5em 0 0
|
||||||
|
padding 0
|
||||||
|
overflow hidden
|
||||||
|
color #627079
|
||||||
|
font-size 1em
|
||||||
|
font-weight bold
|
||||||
|
text-decoration none
|
||||||
|
text-overflow ellipsis
|
||||||
|
|
||||||
|
&:hover
|
||||||
|
text-decoration underline
|
||||||
|
|
||||||
|
> .is-bot
|
||||||
|
margin 0 0.5em 0 0
|
||||||
|
padding 1px 6px
|
||||||
|
font-size 12px
|
||||||
|
color #aaa
|
||||||
|
border solid 1px #ddd
|
||||||
|
border-radius 3px
|
||||||
|
|
||||||
|
> .username
|
||||||
|
margin 0 0.5em 0 0
|
||||||
|
color #ccc
|
||||||
|
|
||||||
|
> .info
|
||||||
|
margin-left auto
|
||||||
|
font-size 0.9em
|
||||||
|
|
||||||
|
> .mobile
|
||||||
|
margin-right 6px
|
||||||
|
color #c0c0c0
|
||||||
|
|
||||||
|
> .created-at
|
||||||
|
color #c0c0c0
|
||||||
|
|
||||||
|
> .body
|
||||||
|
|
||||||
|
> .text
|
||||||
|
display block
|
||||||
|
margin 0
|
||||||
|
padding 0
|
||||||
|
overflow-wrap break-word
|
||||||
|
font-size 1.1em
|
||||||
|
color #717171
|
||||||
|
|
||||||
|
>>> .quote
|
||||||
|
margin 8px
|
||||||
|
padding 6px 12px
|
||||||
|
color #aaa
|
||||||
|
border-left solid 3px #eee
|
||||||
|
|
||||||
|
> .reply
|
||||||
|
margin-right 8px
|
||||||
|
color #717171
|
||||||
|
|
||||||
|
> .rp
|
||||||
|
margin-left 4px
|
||||||
|
font-style oblique
|
||||||
|
color #a0bf46
|
||||||
|
|
||||||
|
[data-is-me]:after
|
||||||
|
content "you"
|
||||||
|
padding 0 4px
|
||||||
|
margin-left 4px
|
||||||
|
font-size 80%
|
||||||
|
color $theme-color-foreground
|
||||||
|
background $theme-color
|
||||||
|
border-radius 4px
|
||||||
|
|
||||||
|
.mk-url-preview
|
||||||
|
margin-top 8px
|
||||||
|
|
||||||
|
> .channel
|
||||||
|
margin 0
|
||||||
|
|
||||||
|
> .tags
|
||||||
|
margin 4px 0 0 0
|
||||||
|
|
||||||
|
> *
|
||||||
|
display inline-block
|
||||||
|
margin 0 8px 0 0
|
||||||
|
padding 2px 8px 2px 16px
|
||||||
|
font-size 90%
|
||||||
|
color #8d969e
|
||||||
|
background #edf0f3
|
||||||
|
border-radius 4px
|
||||||
|
|
||||||
|
&:before
|
||||||
|
content ""
|
||||||
|
display block
|
||||||
|
position absolute
|
||||||
|
top 0
|
||||||
|
bottom 0
|
||||||
|
left 4px
|
||||||
|
width 8px
|
||||||
|
height 8px
|
||||||
|
margin auto 0
|
||||||
|
background #fff
|
||||||
|
border-radius 100%
|
||||||
|
|
||||||
|
> .media
|
||||||
|
> img
|
||||||
|
display block
|
||||||
|
max-width 100%
|
||||||
|
|
||||||
|
> .location
|
||||||
|
margin 4px 0
|
||||||
|
font-size 12px
|
||||||
|
color #ccc
|
||||||
|
|
||||||
|
> .map
|
||||||
|
width 100%
|
||||||
|
height 200px
|
||||||
|
|
||||||
|
&:empty
|
||||||
|
display none
|
||||||
|
|
||||||
|
> .app
|
||||||
|
font-size 12px
|
||||||
|
color #ccc
|
||||||
|
|
||||||
|
> .mk-poll
|
||||||
|
font-size 80%
|
||||||
|
|
||||||
|
> .renote
|
||||||
|
margin 8px 0
|
||||||
|
|
||||||
|
> .mk-note-preview
|
||||||
|
padding 16px
|
||||||
|
border dashed 1px #c0dac6
|
||||||
|
border-radius 8px
|
||||||
|
|
||||||
|
> footer
|
||||||
|
> button
|
||||||
|
margin 0
|
||||||
|
padding 8px
|
||||||
|
background transparent
|
||||||
|
border none
|
||||||
|
box-shadow none
|
||||||
|
font-size 1em
|
||||||
|
color #ddd
|
||||||
|
cursor pointer
|
||||||
|
|
||||||
|
&:not(:last-child)
|
||||||
|
margin-right 28px
|
||||||
|
|
||||||
|
&:hover
|
||||||
|
color #666
|
||||||
|
|
||||||
|
> .count
|
||||||
|
display inline
|
||||||
|
margin 0 0 0 8px
|
||||||
|
color #999
|
||||||
|
|
||||||
|
&.reacted
|
||||||
|
color $theme-color
|
||||||
|
|
||||||
|
&.menu
|
||||||
|
@media (max-width 350px)
|
||||||
|
display none
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style lang="stylus" module>
|
||||||
|
.text
|
||||||
|
code
|
||||||
|
padding 4px 8px
|
||||||
|
margin 0 0.5em
|
||||||
|
font-size 80%
|
||||||
|
color #525252
|
||||||
|
background #f8f8f8
|
||||||
|
border-radius 2px
|
||||||
|
|
||||||
|
pre > code
|
||||||
|
padding 16px
|
||||||
|
margin 0
|
||||||
|
</style>
|
|
@ -1,12 +1,12 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="mk-posts">
|
<div class="mk-notes">
|
||||||
<slot name="head"></slot>
|
<slot name="head"></slot>
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
<template v-for="(post, i) in _posts">
|
<template v-for="(note, i) in _notes">
|
||||||
<mk-post :post="post" :key="post.id" @update:post="onPostUpdated(i, $event)"/>
|
<mk-note :note="note" :key="note.id" @update:note="onNoteUpdated(i, $event)"/>
|
||||||
<p class="date" v-if="i != posts.length - 1 && post._date != _posts[i + 1]._date">
|
<p class="date" v-if="i != notes.length - 1 && note._date != _notes[i + 1]._date">
|
||||||
<span>%fa:angle-up%{{ post._datetext }}</span>
|
<span>%fa:angle-up%{{ note._datetext }}</span>
|
||||||
<span>%fa:angle-down%{{ _posts[i + 1]._datetext }}</span>
|
<span>%fa:angle-down%{{ _notes[i + 1]._datetext }}</span>
|
||||||
</p>
|
</p>
|
||||||
</template>
|
</template>
|
||||||
<footer>
|
<footer>
|
||||||
|
@ -20,25 +20,25 @@ import Vue from 'vue';
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
props: {
|
props: {
|
||||||
posts: {
|
notes: {
|
||||||
type: Array,
|
type: Array,
|
||||||
default: () => []
|
default: () => []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
_posts(): any[] {
|
_notes(): any[] {
|
||||||
return (this.posts as any).map(post => {
|
return (this.notes as any).map(note => {
|
||||||
const date = new Date(post.createdAt).getDate();
|
const date = new Date(note.createdAt).getDate();
|
||||||
const month = new Date(post.createdAt).getMonth() + 1;
|
const month = new Date(note.createdAt).getMonth() + 1;
|
||||||
post._date = date;
|
note._date = date;
|
||||||
post._datetext = `${month}月 ${date}日`;
|
note._datetext = `${month}月 ${date}日`;
|
||||||
return post;
|
return note;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
onPostUpdated(i, post) {
|
onNoteUpdated(i, note) {
|
||||||
Vue.set((this as any).posts, i, post);
|
Vue.set((this as any).notes, i, note);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -47,7 +47,7 @@ export default Vue.extend({
|
||||||
<style lang="stylus" scoped>
|
<style lang="stylus" scoped>
|
||||||
@import '~const.styl'
|
@import '~const.styl'
|
||||||
|
|
||||||
.mk-posts
|
.mk-notes
|
||||||
background #fff
|
background #fff
|
||||||
border-radius 8px
|
border-radius 8px
|
||||||
box-shadow 0 0 0 1px rgba(0, 0, 0, 0.2)
|
box-shadow 0 0 0 1px rgba(0, 0, 0, 0.2)
|
|
@ -4,23 +4,23 @@
|
||||||
<img class="avatar" :src="`${notification.user.avatarUrl}?thumbnail&size=64`" alt="avatar"/>
|
<img class="avatar" :src="`${notification.user.avatarUrl}?thumbnail&size=64`" alt="avatar"/>
|
||||||
<div class="text">
|
<div class="text">
|
||||||
<p><mk-reaction-icon :reaction="notification.reaction"/>{{ name }}</p>
|
<p><mk-reaction-icon :reaction="notification.reaction"/>{{ name }}</p>
|
||||||
<p class="post-ref">%fa:quote-left%{{ getPostSummary(notification.post) }}%fa:quote-right%</p>
|
<p class="note-ref">%fa:quote-left%{{ getNoteSummary(notification.note) }}%fa:quote-right%</p>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-if="notification.type == 'repost'">
|
<template v-if="notification.type == 'renote'">
|
||||||
<img class="avatar" :src="`${notification.post.user.avatarUrl}?thumbnail&size=64`" alt="avatar"/>
|
<img class="avatar" :src="`${notification.note.user.avatarUrl}?thumbnail&size=64`" alt="avatar"/>
|
||||||
<div class="text">
|
<div class="text">
|
||||||
<p>%fa:retweet%{{ posterName }}</p>
|
<p>%fa:retweet%{{ noteerName }}</p>
|
||||||
<p class="post-ref">%fa:quote-left%{{ getPostSummary(notification.post.repost) }}%fa:quote-right%</p>
|
<p class="note-ref">%fa:quote-left%{{ getNoteSummary(notification.note.renote) }}%fa:quote-right%</p>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-if="notification.type == 'quote'">
|
<template v-if="notification.type == 'quote'">
|
||||||
<img class="avatar" :src="`${notification.post.user.avatarUrl}?thumbnail&size=64`" alt="avatar"/>
|
<img class="avatar" :src="`${notification.note.user.avatarUrl}?thumbnail&size=64`" alt="avatar"/>
|
||||||
<div class="text">
|
<div class="text">
|
||||||
<p>%fa:quote-left%{{ posterName }}</p>
|
<p>%fa:quote-left%{{ noteerName }}</p>
|
||||||
<p class="post-preview">{{ getPostSummary(notification.post) }}</p>
|
<p class="note-preview">{{ getNoteSummary(notification.note) }}</p>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -32,18 +32,18 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-if="notification.type == 'reply'">
|
<template v-if="notification.type == 'reply'">
|
||||||
<img class="avatar" :src="`${notification.post.user.avatarUrl}?thumbnail&size=64`" alt="avatar"/>
|
<img class="avatar" :src="`${notification.note.user.avatarUrl}?thumbnail&size=64`" alt="avatar"/>
|
||||||
<div class="text">
|
<div class="text">
|
||||||
<p>%fa:reply%{{ posterName }}</p>
|
<p>%fa:reply%{{ noteerName }}</p>
|
||||||
<p class="post-preview">{{ getPostSummary(notification.post) }}</p>
|
<p class="note-preview">{{ getNoteSummary(notification.note) }}</p>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-if="notification.type == 'mention'">
|
<template v-if="notification.type == 'mention'">
|
||||||
<img class="avatar" :src="`${notification.post.user.avatarUrl}?thumbnail&size=64`" alt="avatar"/>
|
<img class="avatar" :src="`${notification.note.user.avatarUrl}?thumbnail&size=64`" alt="avatar"/>
|
||||||
<div class="text">
|
<div class="text">
|
||||||
<p>%fa:at%{{ posterName }}</p>
|
<p>%fa:at%{{ noteerName }}</p>
|
||||||
<p class="post-preview">{{ getPostSummary(notification.post) }}</p>
|
<p class="note-preview">{{ getNoteSummary(notification.note) }}</p>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -51,7 +51,7 @@
|
||||||
<img class="avatar" :src="`${notification.user.avatarUrl}?thumbnail&size=64`" alt="avatar"/>
|
<img class="avatar" :src="`${notification.user.avatarUrl}?thumbnail&size=64`" alt="avatar"/>
|
||||||
<div class="text">
|
<div class="text">
|
||||||
<p>%fa:chart-pie%{{ name }}</p>
|
<p>%fa:chart-pie%{{ name }}</p>
|
||||||
<p class="post-ref">%fa:quote-left%{{ getPostSummary(notification.post) }}%fa:quote-right%</p>
|
<p class="note-ref">%fa:quote-left%{{ getNoteSummary(notification.note) }}%fa:quote-right%</p>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
|
@ -59,7 +59,7 @@
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import getPostSummary from '../../../../../renderers/get-post-summary';
|
import getNoteSummary from '../../../../../renderers/get-note-summary';
|
||||||
import getUserName from '../../../../../renderers/get-user-name';
|
import getUserName from '../../../../../renderers/get-user-name';
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
|
@ -68,13 +68,13 @@ export default Vue.extend({
|
||||||
name() {
|
name() {
|
||||||
return getUserName(this.notification.user);
|
return getUserName(this.notification.user);
|
||||||
},
|
},
|
||||||
posterName() {
|
noteerName() {
|
||||||
return getUserName(this.notification.post.user);
|
return getUserName(this.notification.note.user);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
getPostSummary
|
getNoteSummary
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -112,7 +112,7 @@ export default Vue.extend({
|
||||||
i, mk-reaction-icon
|
i, mk-reaction-icon
|
||||||
margin-right 4px
|
margin-right 4px
|
||||||
|
|
||||||
.post-ref
|
.note-ref
|
||||||
|
|
||||||
[data-fa]
|
[data-fa]
|
||||||
font-size 1em
|
font-size 1em
|
||||||
|
@ -121,7 +121,7 @@ export default Vue.extend({
|
||||||
display inline-block
|
display inline-block
|
||||||
margin-right 3px
|
margin-right 3px
|
||||||
|
|
||||||
&.repost, &.quote
|
&.renote, &.quote
|
||||||
.text p i
|
.text p i
|
||||||
color #77B255
|
color #77B255
|
||||||
|
|
||||||
|
|
|
@ -10,31 +10,31 @@
|
||||||
<mk-reaction-icon :reaction="notification.reaction"/>
|
<mk-reaction-icon :reaction="notification.reaction"/>
|
||||||
<router-link :to="`/@${acct}`">{{ getUserName(notification.user) }}</router-link>
|
<router-link :to="`/@${acct}`">{{ getUserName(notification.user) }}</router-link>
|
||||||
</p>
|
</p>
|
||||||
<router-link class="post-ref" :to="`/@${acct}/${notification.post.id}`">
|
<router-link class="note-ref" :to="`/@${acct}/${notification.note.id}`">
|
||||||
%fa:quote-left%{{ getPostSummary(notification.post) }}
|
%fa:quote-left%{{ getNoteSummary(notification.note) }}
|
||||||
%fa:quote-right%
|
%fa:quote-right%
|
||||||
</router-link>
|
</router-link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="notification repost" v-if="notification.type == 'repost'">
|
<div class="notification renote" v-if="notification.type == 'renote'">
|
||||||
<mk-time :time="notification.createdAt"/>
|
<mk-time :time="notification.createdAt"/>
|
||||||
<router-link class="avatar-anchor" :to="`/@${acct}`">
|
<router-link class="avatar-anchor" :to="`/@${acct}`">
|
||||||
<img class="avatar" :src="`${notification.post.user.avatarUrl}?thumbnail&size=64`" alt="avatar"/>
|
<img class="avatar" :src="`${notification.note.user.avatarUrl}?thumbnail&size=64`" alt="avatar"/>
|
||||||
</router-link>
|
</router-link>
|
||||||
<div class="text">
|
<div class="text">
|
||||||
<p>
|
<p>
|
||||||
%fa:retweet%
|
%fa:retweet%
|
||||||
<router-link :to="`/@${acct}`">{{ getUserName(notification.post.user) }}</router-link>
|
<router-link :to="`/@${acct}`">{{ getUserName(notification.note.user) }}</router-link>
|
||||||
</p>
|
</p>
|
||||||
<router-link class="post-ref" :to="`/@${acct}/${notification.post.id}`">
|
<router-link class="note-ref" :to="`/@${acct}/${notification.note.id}`">
|
||||||
%fa:quote-left%{{ getPostSummary(notification.post.repost) }}%fa:quote-right%
|
%fa:quote-left%{{ getNoteSummary(notification.note.renote) }}%fa:quote-right%
|
||||||
</router-link>
|
</router-link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<template v-if="notification.type == 'quote'">
|
<template v-if="notification.type == 'quote'">
|
||||||
<mk-post :post="notification.post"/>
|
<mk-note :note="notification.note"/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<div class="notification follow" v-if="notification.type == 'follow'">
|
<div class="notification follow" v-if="notification.type == 'follow'">
|
||||||
|
@ -51,11 +51,11 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<template v-if="notification.type == 'reply'">
|
<template v-if="notification.type == 'reply'">
|
||||||
<mk-post :post="notification.post"/>
|
<mk-note :note="notification.note"/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-if="notification.type == 'mention'">
|
<template v-if="notification.type == 'mention'">
|
||||||
<mk-post :post="notification.post"/>
|
<mk-note :note="notification.note"/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<div class="notification poll_vote" v-if="notification.type == 'poll_vote'">
|
<div class="notification poll_vote" v-if="notification.type == 'poll_vote'">
|
||||||
|
@ -68,8 +68,8 @@
|
||||||
%fa:chart-pie%
|
%fa:chart-pie%
|
||||||
<router-link :to="`/@${acct}`">{{ getUserName(notification.user) }}</router-link>
|
<router-link :to="`/@${acct}`">{{ getUserName(notification.user) }}</router-link>
|
||||||
</p>
|
</p>
|
||||||
<router-link class="post-ref" :to="`/@${acct}/${notification.post.id}`">
|
<router-link class="note-ref" :to="`/@${acct}/${notification.note.id}`">
|
||||||
%fa:quote-left%{{ getPostSummary(notification.post) }}%fa:quote-right%
|
%fa:quote-left%{{ getNoteSummary(notification.note) }}%fa:quote-right%
|
||||||
</router-link>
|
</router-link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -78,7 +78,7 @@
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import getPostSummary from '../../../../../renderers/get-post-summary';
|
import getNoteSummary from '../../../../../renderers/get-note-summary';
|
||||||
import getAcct from '../../../../../acct/render';
|
import getAcct from '../../../../../acct/render';
|
||||||
import getUserName from '../../../../../renderers/get-user-name';
|
import getUserName from '../../../../../renderers/get-user-name';
|
||||||
|
|
||||||
|
@ -91,13 +91,13 @@ export default Vue.extend({
|
||||||
name() {
|
name() {
|
||||||
return getUserName(this.notification.user);
|
return getUserName(this.notification.user);
|
||||||
},
|
},
|
||||||
posterName() {
|
noteerName() {
|
||||||
return getUserName(this.notification.post.user);
|
return getUserName(this.notification.note.user);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
getPostSummary
|
getNoteSummary
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -146,10 +146,10 @@ export default Vue.extend({
|
||||||
i, .mk-reaction-icon
|
i, .mk-reaction-icon
|
||||||
margin-right 4px
|
margin-right 4px
|
||||||
|
|
||||||
> .post-preview
|
> .note-preview
|
||||||
color rgba(0, 0, 0, 0.7)
|
color rgba(0, 0, 0, 0.7)
|
||||||
|
|
||||||
> .post-ref
|
> .note-ref
|
||||||
color rgba(0, 0, 0, 0.7)
|
color rgba(0, 0, 0, 0.7)
|
||||||
|
|
||||||
[data-fa]
|
[data-fa]
|
||||||
|
@ -159,7 +159,7 @@ export default Vue.extend({
|
||||||
display inline-block
|
display inline-block
|
||||||
margin-right 3px
|
margin-right 3px
|
||||||
|
|
||||||
&.repost
|
&.renote
|
||||||
.text p i
|
.text p i
|
||||||
color #77B255
|
color #77B255
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="mk-post-detail">
|
<div class="mk-note-detail">
|
||||||
<button
|
<button
|
||||||
class="more"
|
class="more"
|
||||||
v-if="p.reply && p.reply.replyId && context == null"
|
v-if="p.reply && p.reply.replyId && context == null"
|
||||||
|
@ -10,21 +10,21 @@
|
||||||
<template v-if="contextFetching">%fa:spinner .pulse%</template>
|
<template v-if="contextFetching">%fa:spinner .pulse%</template>
|
||||||
</button>
|
</button>
|
||||||
<div class="context">
|
<div class="context">
|
||||||
<x-sub v-for="post in context" :key="post.id" :post="post"/>
|
<x-sub v-for="note in context" :key="note.id" :note="note"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="reply-to" v-if="p.reply">
|
<div class="reply-to" v-if="p.reply">
|
||||||
<x-sub :post="p.reply"/>
|
<x-sub :note="p.reply"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="repost" v-if="isRepost">
|
<div class="renote" v-if="isRenote">
|
||||||
<p>
|
<p>
|
||||||
<router-link class="avatar-anchor" :to="`/@${acct}`">
|
<router-link class="avatar-anchor" :to="`/@${acct}`">
|
||||||
<img class="avatar" :src="`${post.user.avatarUrl}?thumbnail&size=32`" alt="avatar"/>
|
<img class="avatar" :src="`${note.user.avatarUrl}?thumbnail&size=32`" alt="avatar"/>
|
||||||
</router-link>
|
</router-link>
|
||||||
%fa:retweet%
|
%fa:retweet%
|
||||||
<router-link class="name" :to="`/@${acct}`">
|
<router-link class="name" :to="`/@${acct}`">
|
||||||
{{ name }}
|
{{ name }}
|
||||||
</router-link>
|
</router-link>
|
||||||
がRepost
|
がRenote
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<article>
|
<article>
|
||||||
|
@ -38,33 +38,33 @@
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
<div class="body">
|
<div class="body">
|
||||||
<mk-post-html v-if="p.text" :ast="p.text" :i="os.i" :class="$style.text"/>
|
<mk-note-html v-if="p.text" :ast="p.text" :i="os.i" :class="$style.text"/>
|
||||||
<div class="tags" v-if="p.tags && p.tags.length > 0">
|
<div class="tags" v-if="p.tags && p.tags.length > 0">
|
||||||
<router-link v-for="tag in p.tags" :key="tag" :to="`/search?q=#${tag}`">{{ tag }}</router-link>
|
<router-link v-for="tag in p.tags" :key="tag" :to="`/search?q=#${tag}`">{{ tag }}</router-link>
|
||||||
</div>
|
</div>
|
||||||
<div class="media" v-if="p.media.length > 0">
|
<div class="media" v-if="p.media.length > 0">
|
||||||
<mk-media-list :media-list="p.media"/>
|
<mk-media-list :media-list="p.media"/>
|
||||||
</div>
|
</div>
|
||||||
<mk-poll v-if="p.poll" :post="p"/>
|
<mk-poll v-if="p.poll" :note="p"/>
|
||||||
<mk-url-preview v-for="url in urls" :url="url" :key="url"/>
|
<mk-url-preview v-for="url in urls" :url="url" :key="url"/>
|
||||||
<a class="location" v-if="p.geo" :href="`http://maps.google.com/maps?q=${p.geo.coordinates[1]},${p.geo.coordinates[0]}`" target="_blank">%fa:map-marker-alt% 位置情報</a>
|
<a class="location" v-if="p.geo" :href="`http://maps.google.com/maps?q=${p.geo.coordinates[1]},${p.geo.coordinates[0]}`" target="_blank">%fa:map-marker-alt% 位置情報</a>
|
||||||
<div class="map" v-if="p.geo" ref="map"></div>
|
<div class="map" v-if="p.geo" ref="map"></div>
|
||||||
<div class="repost" v-if="p.repost">
|
<div class="renote" v-if="p.renote">
|
||||||
<mk-post-preview :post="p.repost"/>
|
<mk-note-preview :note="p.renote"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<router-link class="time" :to="`/@${pAcct}/${p.id}`">
|
<router-link class="time" :to="`/@${pAcct}/${p.id}`">
|
||||||
<mk-time :time="p.createdAt" mode="detail"/>
|
<mk-time :time="p.createdAt" mode="detail"/>
|
||||||
</router-link>
|
</router-link>
|
||||||
<footer>
|
<footer>
|
||||||
<mk-reactions-viewer :post="p"/>
|
<mk-reactions-viewer :note="p"/>
|
||||||
<button @click="reply" title="%i18n:mobile.tags.mk-post-detail.reply%">
|
<button @click="reply" title="%i18n:mobile.tags.mk-note-detail.reply%">
|
||||||
%fa:reply%<p class="count" v-if="p.repliesCount > 0">{{ p.repliesCount }}</p>
|
%fa:reply%<p class="count" v-if="p.repliesCount > 0">{{ p.repliesCount }}</p>
|
||||||
</button>
|
</button>
|
||||||
<button @click="repost" title="Repost">
|
<button @click="renote" title="Renote">
|
||||||
%fa:retweet%<p class="count" v-if="p.repostCount > 0">{{ p.repostCount }}</p>
|
%fa:retweet%<p class="count" v-if="p.renoteCount > 0">{{ p.renoteCount }}</p>
|
||||||
</button>
|
</button>
|
||||||
<button :class="{ reacted: p.myReaction != null }" @click="react" ref="reactButton" title="%i18n:mobile.tags.mk-post-detail.reaction%">
|
<button :class="{ reacted: p.myReaction != null }" @click="react" ref="reactButton" title="%i18n:mobile.tags.mk-note-detail.reaction%">
|
||||||
%fa:plus%<p class="count" v-if="p.reactions_count > 0">{{ p.reactions_count }}</p>
|
%fa:plus%<p class="count" v-if="p.reactions_count > 0">{{ p.reactions_count }}</p>
|
||||||
</button>
|
</button>
|
||||||
<button @click="menu" ref="menuButton">
|
<button @click="menu" ref="menuButton">
|
||||||
|
@ -73,7 +73,7 @@
|
||||||
</footer>
|
</footer>
|
||||||
</article>
|
</article>
|
||||||
<div class="replies" v-if="!compact">
|
<div class="replies" v-if="!compact">
|
||||||
<x-sub v-for="post in replies" :key="post.id" :post="post"/>
|
<x-sub v-for="note in replies" :key="note.id" :note="note"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -84,9 +84,9 @@ import getAcct from '../../../../../acct/render';
|
||||||
import getUserName from '../../../../../renderers/get-user-name';
|
import getUserName from '../../../../../renderers/get-user-name';
|
||||||
import parse from '../../../../../text/parse';
|
import parse from '../../../../../text/parse';
|
||||||
|
|
||||||
import MkPostMenu from '../../../common/views/components/post-menu.vue';
|
import MkNoteMenu from '../../../common/views/components/note-menu.vue';
|
||||||
import MkReactionPicker from '../../../common/views/components/reaction-picker.vue';
|
import MkReactionPicker from '../../../common/views/components/reaction-picker.vue';
|
||||||
import XSub from './post-detail.sub.vue';
|
import XSub from './note-detail.sub.vue';
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
components: {
|
components: {
|
||||||
|
@ -94,7 +94,7 @@ export default Vue.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
post: {
|
note: {
|
||||||
type: Object,
|
type: Object,
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
|
@ -113,10 +113,10 @@ export default Vue.extend({
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
acct(): string {
|
acct(): string {
|
||||||
return getAcct(this.post.user);
|
return getAcct(this.note.user);
|
||||||
},
|
},
|
||||||
name(): string {
|
name(): string {
|
||||||
return getUserName(this.post.user);
|
return getUserName(this.note.user);
|
||||||
},
|
},
|
||||||
pAcct(): string {
|
pAcct(): string {
|
||||||
return getAcct(this.p.user);
|
return getAcct(this.p.user);
|
||||||
|
@ -124,14 +124,14 @@ export default Vue.extend({
|
||||||
pName(): string {
|
pName(): string {
|
||||||
return getUserName(this.p.user);
|
return getUserName(this.p.user);
|
||||||
},
|
},
|
||||||
isRepost(): boolean {
|
isRenote(): boolean {
|
||||||
return (this.post.repost &&
|
return (this.note.renote &&
|
||||||
this.post.text == null &&
|
this.note.text == null &&
|
||||||
this.post.mediaIds == null &&
|
this.note.mediaIds == null &&
|
||||||
this.post.poll == null);
|
this.note.poll == null);
|
||||||
},
|
},
|
||||||
p(): any {
|
p(): any {
|
||||||
return this.isRepost ? this.post.repost : this.post;
|
return this.isRenote ? this.note.renote : this.note;
|
||||||
},
|
},
|
||||||
reactionsCount(): number {
|
reactionsCount(): number {
|
||||||
return this.p.reactionCounts
|
return this.p.reactionCounts
|
||||||
|
@ -155,8 +155,8 @@ export default Vue.extend({
|
||||||
mounted() {
|
mounted() {
|
||||||
// Get replies
|
// Get replies
|
||||||
if (!this.compact) {
|
if (!this.compact) {
|
||||||
(this as any).api('posts/replies', {
|
(this as any).api('notes/replies', {
|
||||||
postId: this.p.id,
|
noteId: this.p.id,
|
||||||
limit: 8
|
limit: 8
|
||||||
}).then(replies => {
|
}).then(replies => {
|
||||||
this.replies = replies;
|
this.replies = replies;
|
||||||
|
@ -187,8 +187,8 @@ export default Vue.extend({
|
||||||
this.contextFetching = true;
|
this.contextFetching = true;
|
||||||
|
|
||||||
// Fetch context
|
// Fetch context
|
||||||
(this as any).api('posts/context', {
|
(this as any).api('notes/context', {
|
||||||
postId: this.p.replyId
|
noteId: this.p.replyId
|
||||||
}).then(context => {
|
}).then(context => {
|
||||||
this.contextFetching = false;
|
this.contextFetching = false;
|
||||||
this.context = context.reverse();
|
this.context = context.reverse();
|
||||||
|
@ -199,22 +199,22 @@ export default Vue.extend({
|
||||||
reply: this.p
|
reply: this.p
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
repost() {
|
renote() {
|
||||||
(this as any).apis.post({
|
(this as any).apis.post({
|
||||||
repost: this.p
|
renote: this.p
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
react() {
|
react() {
|
||||||
(this as any).os.new(MkReactionPicker, {
|
(this as any).os.new(MkReactionPicker, {
|
||||||
source: this.$refs.reactButton,
|
source: this.$refs.reactButton,
|
||||||
post: this.p,
|
note: this.p,
|
||||||
compact: true
|
compact: true
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
menu() {
|
menu() {
|
||||||
(this as any).os.new(MkPostMenu, {
|
(this as any).os.new(MkNoteMenu, {
|
||||||
source: this.$refs.menuButton,
|
source: this.$refs.menuButton,
|
||||||
post: this.p,
|
note: this.p,
|
||||||
compact: true
|
compact: true
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -225,7 +225,7 @@ export default Vue.extend({
|
||||||
<style lang="stylus" scoped>
|
<style lang="stylus" scoped>
|
||||||
@import '~const.styl'
|
@import '~const.styl'
|
||||||
|
|
||||||
.mk-post-detail
|
.mk-note-detail
|
||||||
overflow hidden
|
overflow hidden
|
||||||
margin 0 auto
|
margin 0 auto
|
||||||
padding 0
|
padding 0
|
||||||
|
@ -267,7 +267,7 @@ export default Vue.extend({
|
||||||
> *
|
> *
|
||||||
border-bottom 1px solid #eef0f2
|
border-bottom 1px solid #eef0f2
|
||||||
|
|
||||||
> .repost
|
> .renote
|
||||||
color #9dbb00
|
color #9dbb00
|
||||||
background linear-gradient(to bottom, #edfde2 0%, #fff 100%)
|
background linear-gradient(to bottom, #edfde2 0%, #fff 100%)
|
||||||
|
|
||||||
|
@ -357,10 +357,10 @@ export default Vue.extend({
|
||||||
> .body
|
> .body
|
||||||
padding 8px 0
|
padding 8px 0
|
||||||
|
|
||||||
> .repost
|
> .renote
|
||||||
margin 8px 0
|
margin 8px 0
|
||||||
|
|
||||||
> .mk-post-preview
|
> .mk-note-preview
|
||||||
padding 16px
|
padding 16px
|
||||||
border dashed 1px #c0dac6
|
border dashed 1px #c0dac6
|
||||||
border-radius 8px
|
border-radius 8px
|
||||||
|
|
|
@ -9,8 +9,8 @@
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
<div class="form">
|
<div class="form">
|
||||||
<mk-post-preview v-if="reply" :post="reply"/>
|
<mk-note-preview v-if="reply" :note="reply"/>
|
||||||
<textarea v-model="text" ref="text" :disabled="posting" :placeholder="reply ? '%i18n:mobile.tags.mk-post-form.reply-placeholder%' : '%i18n:mobile.tags.mk-post-form.post-placeholder%'"></textarea>
|
<textarea v-model="text" ref="text" :disabled="posting" :placeholder="reply ? '%i18n:mobile.tags.mk-post-form.reply-placeholder%' : '%i18n:mobile.tags.mk-post-form.note-placeholder%'"></textarea>
|
||||||
<div class="attaches" v-show="files.length != 0">
|
<div class="attaches" v-show="files.length != 0">
|
||||||
<x-draggable class="files" :list="files" :options="{ animation: 150 }">
|
<x-draggable class="files" :list="files" :options="{ animation: 150 }">
|
||||||
<div class="file" v-for="file in files" :key="file.id">
|
<div class="file" v-for="file in files" :key="file.id">
|
||||||
|
@ -112,7 +112,7 @@ export default Vue.extend({
|
||||||
post() {
|
post() {
|
||||||
this.posting = true;
|
this.posting = true;
|
||||||
const viaMobile = (this as any).os.i.account.clientSettings.disableViaMobile !== true;
|
const viaMobile = (this as any).os.i.account.clientSettings.disableViaMobile !== true;
|
||||||
(this as any).api('posts/create', {
|
(this as any).api('notes/create', {
|
||||||
text: this.text == '' ? undefined : this.text,
|
text: this.text == '' ? undefined : this.text,
|
||||||
mediaIds: this.files.length > 0 ? this.files.map(f => f.id) : undefined,
|
mediaIds: this.files.length > 0 ? this.files.map(f => f.id) : undefined,
|
||||||
replyId: this.reply ? this.reply.id : undefined,
|
replyId: this.reply ? this.reply.id : undefined,
|
||||||
|
@ -127,7 +127,7 @@ export default Vue.extend({
|
||||||
} : null,
|
} : null,
|
||||||
viaMobile: viaMobile
|
viaMobile: viaMobile
|
||||||
}).then(data => {
|
}).then(data => {
|
||||||
this.$emit('post');
|
this.$emit('note');
|
||||||
this.$destroy();
|
this.$destroy();
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
this.posting = false;
|
this.posting = false;
|
||||||
|
@ -200,7 +200,7 @@ export default Vue.extend({
|
||||||
max-width 500px
|
max-width 500px
|
||||||
margin 0 auto
|
margin 0 auto
|
||||||
|
|
||||||
> .mk-post-preview
|
> .mk-note-preview
|
||||||
padding 16px
|
padding 16px
|
||||||
|
|
||||||
> .attaches
|
> .attaches
|
||||||
|
|
|
@ -1,19 +1,19 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="post" :class="{ repost: isRepost }">
|
<div class="note" :class="{ renote: isRenote }">
|
||||||
<div class="reply-to" v-if="p.reply">
|
<div class="reply-to" v-if="p.reply">
|
||||||
<x-sub :post="p.reply"/>
|
<x-sub :note="p.reply"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="repost" v-if="isRepost">
|
<div class="renote" v-if="isRenote">
|
||||||
<p>
|
<p>
|
||||||
<router-link class="avatar-anchor" :to="`/@${acct}`">
|
<router-link class="avatar-anchor" :to="`/@${acct}`">
|
||||||
<img class="avatar" :src="`${post.user.avatarUrl}?thumbnail&size=64`" alt="avatar"/>
|
<img class="avatar" :src="`${note.user.avatarUrl}?thumbnail&size=64`" alt="avatar"/>
|
||||||
</router-link>
|
</router-link>
|
||||||
%fa:retweet%
|
%fa:retweet%
|
||||||
<span>{{ '%i18n:mobile.tags.mk-timeline-post.reposted-by%'.substr(0, '%i18n:mobile.tags.mk-timeline-post.reposted-by%'.indexOf('{')) }}</span>
|
<span>{{ '%i18n:mobile.tags.mk-timeline-note.reposted-by%'.substr(0, '%i18n:mobile.tags.mk-timeline-note.reposted-by%'.indexOf('{')) }}</span>
|
||||||
<router-link class="name" :to="`/@${acct}`">{{ name }}</router-link>
|
<router-link class="name" :to="`/@${acct}`">{{ name }}</router-link>
|
||||||
<span>{{ '%i18n:mobile.tags.mk-timeline-post.reposted-by%'.substr('%i18n:mobile.tags.mk-timeline-post.reposted-by%'.indexOf('}') + 1) }}</span>
|
<span>{{ '%i18n:mobile.tags.mk-timeline-note.reposted-by%'.substr('%i18n:mobile.tags.mk-timeline-note.reposted-by%'.indexOf('}') + 1) }}</span>
|
||||||
</p>
|
</p>
|
||||||
<mk-time :time="post.createdAt"/>
|
<mk-time :time="note.createdAt"/>
|
||||||
</div>
|
</div>
|
||||||
<article>
|
<article>
|
||||||
<router-link class="avatar-anchor" :to="`/@${pAcct}`">
|
<router-link class="avatar-anchor" :to="`/@${pAcct}`">
|
||||||
|
@ -37,13 +37,13 @@
|
||||||
<a class="reply" v-if="p.reply">
|
<a class="reply" v-if="p.reply">
|
||||||
%fa:reply%
|
%fa:reply%
|
||||||
</a>
|
</a>
|
||||||
<mk-post-html v-if="p.text" :text="p.text" :i="os.i" :class="$style.text"/>
|
<mk-note-html v-if="p.text" :text="p.text" :i="os.i" :class="$style.text"/>
|
||||||
<a class="rp" v-if="p.repost != null">RP:</a>
|
<a class="rp" v-if="p.renote != null">RP:</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="media" v-if="p.media.length > 0">
|
<div class="media" v-if="p.media.length > 0">
|
||||||
<mk-media-list :media-list="p.media"/>
|
<mk-media-list :media-list="p.media"/>
|
||||||
</div>
|
</div>
|
||||||
<mk-poll v-if="p.poll" :post="p" ref="pollViewer"/>
|
<mk-poll v-if="p.poll" :note="p" ref="pollViewer"/>
|
||||||
<div class="tags" v-if="p.tags && p.tags.length > 0">
|
<div class="tags" v-if="p.tags && p.tags.length > 0">
|
||||||
<router-link v-for="tag in p.tags" :key="tag" :to="`/search?q=#${tag}`">{{ tag }}</router-link>
|
<router-link v-for="tag in p.tags" :key="tag" :to="`/search?q=#${tag}`">{{ tag }}</router-link>
|
||||||
</div>
|
</div>
|
||||||
|
@ -51,17 +51,17 @@
|
||||||
<a class="location" v-if="p.geo" :href="`http://maps.google.com/maps?q=${p.geo.coordinates[1]},${p.geo.coordinates[0]}`" target="_blank">%fa:map-marker-alt% 位置情報</a>
|
<a class="location" v-if="p.geo" :href="`http://maps.google.com/maps?q=${p.geo.coordinates[1]},${p.geo.coordinates[0]}`" target="_blank">%fa:map-marker-alt% 位置情報</a>
|
||||||
<div class="map" v-if="p.geo" ref="map"></div>
|
<div class="map" v-if="p.geo" ref="map"></div>
|
||||||
<span class="app" v-if="p.app">via <b>{{ p.app.name }}</b></span>
|
<span class="app" v-if="p.app">via <b>{{ p.app.name }}</b></span>
|
||||||
<div class="repost" v-if="p.repost">
|
<div class="renote" v-if="p.renote">
|
||||||
<mk-post-preview :post="p.repost"/>
|
<mk-note-preview :note="p.renote"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<footer>
|
<footer>
|
||||||
<mk-reactions-viewer :post="p" ref="reactionsViewer"/>
|
<mk-reactions-viewer :note="p" ref="reactionsViewer"/>
|
||||||
<button @click="reply">
|
<button @click="reply">
|
||||||
%fa:reply%<p class="count" v-if="p.repliesCount > 0">{{ p.repliesCount }}</p>
|
%fa:reply%<p class="count" v-if="p.repliesCount > 0">{{ p.repliesCount }}</p>
|
||||||
</button>
|
</button>
|
||||||
<button @click="repost" title="Repost">
|
<button @click="renote" title="Renote">
|
||||||
%fa:retweet%<p class="count" v-if="p.repostCount > 0">{{ p.repostCount }}</p>
|
%fa:retweet%<p class="count" v-if="p.renoteCount > 0">{{ p.renoteCount }}</p>
|
||||||
</button>
|
</button>
|
||||||
<button :class="{ reacted: p.myReaction != null }" @click="react" ref="reactButton">
|
<button :class="{ reacted: p.myReaction != null }" @click="react" ref="reactButton">
|
||||||
%fa:plus%<p class="count" v-if="p.reactions_count > 0">{{ p.reactions_count }}</p>
|
%fa:plus%<p class="count" v-if="p.reactions_count > 0">{{ p.reactions_count }}</p>
|
||||||
|
@ -81,16 +81,16 @@ import getAcct from '../../../../../acct/render';
|
||||||
import getUserName from '../../../../../renderers/get-user-name';
|
import getUserName from '../../../../../renderers/get-user-name';
|
||||||
import parse from '../../../../../text/parse';
|
import parse from '../../../../../text/parse';
|
||||||
|
|
||||||
import MkPostMenu from '../../../common/views/components/post-menu.vue';
|
import MkNoteMenu from '../../../common/views/components/note-menu.vue';
|
||||||
import MkReactionPicker from '../../../common/views/components/reaction-picker.vue';
|
import MkReactionPicker from '../../../common/views/components/reaction-picker.vue';
|
||||||
import XSub from './post.sub.vue';
|
import XSub from './note.sub.vue';
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
components: {
|
components: {
|
||||||
XSub
|
XSub
|
||||||
},
|
},
|
||||||
|
|
||||||
props: ['post'],
|
props: ['note'],
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
@ -101,10 +101,10 @@ export default Vue.extend({
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
acct(): string {
|
acct(): string {
|
||||||
return getAcct(this.post.user);
|
return getAcct(this.note.user);
|
||||||
},
|
},
|
||||||
name(): string {
|
name(): string {
|
||||||
return getUserName(this.post.user);
|
return getUserName(this.note.user);
|
||||||
},
|
},
|
||||||
pAcct(): string {
|
pAcct(): string {
|
||||||
return getAcct(this.p.user);
|
return getAcct(this.p.user);
|
||||||
|
@ -112,14 +112,14 @@ export default Vue.extend({
|
||||||
pName(): string {
|
pName(): string {
|
||||||
return getUserName(this.p.user);
|
return getUserName(this.p.user);
|
||||||
},
|
},
|
||||||
isRepost(): boolean {
|
isRenote(): boolean {
|
||||||
return (this.post.repost &&
|
return (this.note.renote &&
|
||||||
this.post.text == null &&
|
this.note.text == null &&
|
||||||
this.post.mediaIds == null &&
|
this.note.mediaIds == null &&
|
||||||
this.post.poll == null);
|
this.note.poll == null);
|
||||||
},
|
},
|
||||||
p(): any {
|
p(): any {
|
||||||
return this.isRepost ? this.post.repost : this.post;
|
return this.isRenote ? this.note.renote : this.note;
|
||||||
},
|
},
|
||||||
reactionsCount(): number {
|
reactionsCount(): number {
|
||||||
return this.p.reactionCounts
|
return this.p.reactionCounts
|
||||||
|
@ -192,7 +192,7 @@ export default Vue.extend({
|
||||||
type: 'capture',
|
type: 'capture',
|
||||||
id: this.p.id
|
id: this.p.id
|
||||||
});
|
});
|
||||||
if (withHandler) this.connection.on('post-updated', this.onStreamPostUpdated);
|
if (withHandler) this.connection.on('note-updated', this.onStreamNoteUpdated);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
decapture(withHandler = false) {
|
decapture(withHandler = false) {
|
||||||
|
@ -201,18 +201,18 @@ export default Vue.extend({
|
||||||
type: 'decapture',
|
type: 'decapture',
|
||||||
id: this.p.id
|
id: this.p.id
|
||||||
});
|
});
|
||||||
if (withHandler) this.connection.off('post-updated', this.onStreamPostUpdated);
|
if (withHandler) this.connection.off('note-updated', this.onStreamNoteUpdated);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onStreamConnected() {
|
onStreamConnected() {
|
||||||
this.capture();
|
this.capture();
|
||||||
},
|
},
|
||||||
onStreamPostUpdated(data) {
|
onStreamNoteUpdated(data) {
|
||||||
const post = data.post;
|
const note = data.note;
|
||||||
if (post.id == this.post.id) {
|
if (note.id == this.note.id) {
|
||||||
this.$emit('update:post', post);
|
this.$emit('update:note', note);
|
||||||
} else if (post.id == this.post.repostId) {
|
} else if (note.id == this.note.renoteId) {
|
||||||
this.post.repost = post;
|
this.note.renote = note;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
reply() {
|
reply() {
|
||||||
|
@ -220,22 +220,22 @@ export default Vue.extend({
|
||||||
reply: this.p
|
reply: this.p
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
repost() {
|
renote() {
|
||||||
(this as any).apis.post({
|
(this as any).apis.post({
|
||||||
repost: this.p
|
renote: this.p
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
react() {
|
react() {
|
||||||
(this as any).os.new(MkReactionPicker, {
|
(this as any).os.new(MkReactionPicker, {
|
||||||
source: this.$refs.reactButton,
|
source: this.$refs.reactButton,
|
||||||
post: this.p,
|
note: this.p,
|
||||||
compact: true
|
compact: true
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
menu() {
|
menu() {
|
||||||
(this as any).os.new(MkPostMenu, {
|
(this as any).os.new(MkNoteMenu, {
|
||||||
source: this.$refs.menuButton,
|
source: this.$refs.menuButton,
|
||||||
post: this.p,
|
note: this.p,
|
||||||
compact: true
|
compact: true
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -246,14 +246,14 @@ export default Vue.extend({
|
||||||
<style lang="stylus" scoped>
|
<style lang="stylus" scoped>
|
||||||
@import '~const.styl'
|
@import '~const.styl'
|
||||||
|
|
||||||
.post
|
.note
|
||||||
font-size 12px
|
font-size 12px
|
||||||
border-bottom solid 1px #eaeaea
|
border-bottom solid 1px #eaeaea
|
||||||
|
|
||||||
&:first-child
|
&:first-child
|
||||||
border-radius 8px 8px 0 0
|
border-radius 8px 8px 0 0
|
||||||
|
|
||||||
> .repost
|
> .renote
|
||||||
border-radius 8px 8px 0 0
|
border-radius 8px 8px 0 0
|
||||||
|
|
||||||
&:last-of-type
|
&:last-of-type
|
||||||
|
@ -265,7 +265,7 @@ export default Vue.extend({
|
||||||
@media (min-width 500px)
|
@media (min-width 500px)
|
||||||
font-size 16px
|
font-size 16px
|
||||||
|
|
||||||
> .repost
|
> .renote
|
||||||
color #9dbb00
|
color #9dbb00
|
||||||
background linear-gradient(to bottom, #edfde2 0%, #fff 100%)
|
background linear-gradient(to bottom, #edfde2 0%, #fff 100%)
|
||||||
|
|
||||||
|
@ -309,7 +309,7 @@ export default Vue.extend({
|
||||||
> .reply-to
|
> .reply-to
|
||||||
background rgba(0, 0, 0, 0.0125)
|
background rgba(0, 0, 0, 0.0125)
|
||||||
|
|
||||||
> .mk-post-preview
|
> .mk-note-preview
|
||||||
background transparent
|
background transparent
|
||||||
|
|
||||||
> article
|
> article
|
||||||
|
@ -485,10 +485,10 @@ export default Vue.extend({
|
||||||
> .mk-poll
|
> .mk-poll
|
||||||
font-size 80%
|
font-size 80%
|
||||||
|
|
||||||
> .repost
|
> .renote
|
||||||
margin 8px 0
|
margin 8px 0
|
||||||
|
|
||||||
> .mk-post-preview
|
> .mk-note-preview
|
||||||
padding 16px
|
padding 16px
|
||||||
border dashed 1px #c0dac6
|
border dashed 1px #c0dac6
|
||||||
border-radius 8px
|
border-radius 8px
|
||||||
|
|
43
src/client/app/mobile/views/components/sub-note-content.vue
Normal file
43
src/client/app/mobile/views/components/sub-note-content.vue
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
<template>
|
||||||
|
<div class="mk-sub-note-content">
|
||||||
|
<div class="body">
|
||||||
|
<a class="reply" v-if="note.replyId">%fa:reply%</a>
|
||||||
|
<mk-note-html v-if="note.text" :text="note.text" :i="os.i"/>
|
||||||
|
<a class="rp" v-if="note.renoteId">RP: ...</a>
|
||||||
|
</div>
|
||||||
|
<details v-if="note.media.length > 0">
|
||||||
|
<summary>({{ note.media.length }}個のメディア)</summary>
|
||||||
|
<mk-media-list :media-list="note.media"/>
|
||||||
|
</details>
|
||||||
|
<details v-if="note.poll">
|
||||||
|
<summary>%i18n:mobile.tags.mk-sub-note-content.poll%</summary>
|
||||||
|
<mk-poll :note="note"/>
|
||||||
|
</details>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import Vue from 'vue';
|
||||||
|
export default Vue.extend({
|
||||||
|
props: ['note']
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="stylus" scoped>
|
||||||
|
.mk-sub-note-content
|
||||||
|
overflow-wrap break-word
|
||||||
|
|
||||||
|
> .body
|
||||||
|
> .reply
|
||||||
|
margin-right 6px
|
||||||
|
color #717171
|
||||||
|
|
||||||
|
> .rp
|
||||||
|
margin-left 4px
|
||||||
|
font-style oblique
|
||||||
|
color #a0bf46
|
||||||
|
|
||||||
|
mk-poll
|
||||||
|
font-size 80%
|
||||||
|
|
||||||
|
</style>
|
|
@ -1,43 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="mk-sub-post-content">
|
|
||||||
<div class="body">
|
|
||||||
<a class="reply" v-if="post.replyId">%fa:reply%</a>
|
|
||||||
<mk-post-html v-if="post.text" :text="post.text" :i="os.i"/>
|
|
||||||
<a class="rp" v-if="post.repostId">RP: ...</a>
|
|
||||||
</div>
|
|
||||||
<details v-if="post.media.length > 0">
|
|
||||||
<summary>({{ post.media.length }}個のメディア)</summary>
|
|
||||||
<mk-media-list :media-list="post.media"/>
|
|
||||||
</details>
|
|
||||||
<details v-if="post.poll">
|
|
||||||
<summary>%i18n:mobile.tags.mk-sub-post-content.poll%</summary>
|
|
||||||
<mk-poll :post="post"/>
|
|
||||||
</details>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import Vue from 'vue';
|
|
||||||
export default Vue.extend({
|
|
||||||
props: ['post']
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="stylus" scoped>
|
|
||||||
.mk-sub-post-content
|
|
||||||
overflow-wrap break-word
|
|
||||||
|
|
||||||
> .body
|
|
||||||
> .reply
|
|
||||||
margin-right 6px
|
|
||||||
color #717171
|
|
||||||
|
|
||||||
> .rp
|
|
||||||
margin-left 4px
|
|
||||||
font-style oblique
|
|
||||||
color #a0bf46
|
|
||||||
|
|
||||||
mk-poll
|
|
||||||
font-size 80%
|
|
||||||
|
|
||||||
</style>
|
|
|
@ -1,11 +1,11 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="mk-timeline">
|
<div class="mk-timeline">
|
||||||
<mk-friends-maker v-if="alone"/>
|
<mk-friends-maker v-if="alone"/>
|
||||||
<mk-posts :posts="posts">
|
<mk-notes :notes="notes">
|
||||||
<div class="init" v-if="fetching">
|
<div class="init" v-if="fetching">
|
||||||
%fa:spinner .pulse%%i18n:common.loading%
|
%fa:spinner .pulse%%i18n:common.loading%
|
||||||
</div>
|
</div>
|
||||||
<div class="empty" v-if="!fetching && posts.length == 0">
|
<div class="empty" v-if="!fetching && notes.length == 0">
|
||||||
%fa:R comments%
|
%fa:R comments%
|
||||||
%i18n:mobile.tags.mk-home-timeline.empty-timeline%
|
%i18n:mobile.tags.mk-home-timeline.empty-timeline%
|
||||||
</div>
|
</div>
|
||||||
|
@ -13,7 +13,7 @@
|
||||||
<span v-if="!moreFetching">%i18n:mobile.tags.mk-timeline.load-more%</span>
|
<span v-if="!moreFetching">%i18n:mobile.tags.mk-timeline.load-more%</span>
|
||||||
<span v-if="moreFetching">%i18n:common.loading%<mk-ellipsis/></span>
|
<span v-if="moreFetching">%i18n:common.loading%<mk-ellipsis/></span>
|
||||||
</button>
|
</button>
|
||||||
</mk-posts>
|
</mk-notes>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@ export default Vue.extend({
|
||||||
return {
|
return {
|
||||||
fetching: true,
|
fetching: true,
|
||||||
moreFetching: false,
|
moreFetching: false,
|
||||||
posts: [],
|
notes: [],
|
||||||
existMore: false,
|
existMore: false,
|
||||||
connection: null,
|
connection: null,
|
||||||
connectionId: null
|
connectionId: null
|
||||||
|
@ -48,14 +48,14 @@ export default Vue.extend({
|
||||||
this.connection = (this as any).os.stream.getConnection();
|
this.connection = (this as any).os.stream.getConnection();
|
||||||
this.connectionId = (this as any).os.stream.use();
|
this.connectionId = (this as any).os.stream.use();
|
||||||
|
|
||||||
this.connection.on('post', this.onPost);
|
this.connection.on('note', this.onNote);
|
||||||
this.connection.on('follow', this.onChangeFollowing);
|
this.connection.on('follow', this.onChangeFollowing);
|
||||||
this.connection.on('unfollow', this.onChangeFollowing);
|
this.connection.on('unfollow', this.onChangeFollowing);
|
||||||
|
|
||||||
this.fetch();
|
this.fetch();
|
||||||
},
|
},
|
||||||
beforeDestroy() {
|
beforeDestroy() {
|
||||||
this.connection.off('post', this.onPost);
|
this.connection.off('note', this.onNote);
|
||||||
this.connection.off('follow', this.onChangeFollowing);
|
this.connection.off('follow', this.onChangeFollowing);
|
||||||
this.connection.off('unfollow', this.onChangeFollowing);
|
this.connection.off('unfollow', this.onChangeFollowing);
|
||||||
(this as any).os.stream.dispose(this.connectionId);
|
(this as any).os.stream.dispose(this.connectionId);
|
||||||
|
@ -63,15 +63,15 @@ export default Vue.extend({
|
||||||
methods: {
|
methods: {
|
||||||
fetch(cb?) {
|
fetch(cb?) {
|
||||||
this.fetching = true;
|
this.fetching = true;
|
||||||
(this as any).api('posts/timeline', {
|
(this as any).api('notes/timeline', {
|
||||||
limit: limit + 1,
|
limit: limit + 1,
|
||||||
untilDate: this.date ? (this.date as any).getTime() : undefined
|
untilDate: this.date ? (this.date as any).getTime() : undefined
|
||||||
}).then(posts => {
|
}).then(notes => {
|
||||||
if (posts.length == limit + 1) {
|
if (notes.length == limit + 1) {
|
||||||
posts.pop();
|
notes.pop();
|
||||||
this.existMore = true;
|
this.existMore = true;
|
||||||
}
|
}
|
||||||
this.posts = posts;
|
this.notes = notes;
|
||||||
this.fetching = false;
|
this.fetching = false;
|
||||||
this.$emit('loaded');
|
this.$emit('loaded');
|
||||||
if (cb) cb();
|
if (cb) cb();
|
||||||
|
@ -79,22 +79,22 @@ export default Vue.extend({
|
||||||
},
|
},
|
||||||
more() {
|
more() {
|
||||||
this.moreFetching = true;
|
this.moreFetching = true;
|
||||||
(this as any).api('posts/timeline', {
|
(this as any).api('notes/timeline', {
|
||||||
limit: limit + 1,
|
limit: limit + 1,
|
||||||
untilId: this.posts[this.posts.length - 1].id
|
untilId: this.notes[this.notes.length - 1].id
|
||||||
}).then(posts => {
|
}).then(notes => {
|
||||||
if (posts.length == limit + 1) {
|
if (notes.length == limit + 1) {
|
||||||
posts.pop();
|
notes.pop();
|
||||||
this.existMore = true;
|
this.existMore = true;
|
||||||
} else {
|
} else {
|
||||||
this.existMore = false;
|
this.existMore = false;
|
||||||
}
|
}
|
||||||
this.posts = this.posts.concat(posts);
|
this.notes = this.notes.concat(notes);
|
||||||
this.moreFetching = false;
|
this.moreFetching = false;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
onPost(post) {
|
onNote(note) {
|
||||||
this.posts.unshift(post);
|
this.notes.unshift(note);
|
||||||
},
|
},
|
||||||
onChangeFollowing() {
|
onChangeFollowing() {
|
||||||
this.fetch();
|
this.fetch();
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="mk-user-timeline">
|
<div class="mk-user-timeline">
|
||||||
<mk-posts :posts="posts">
|
<mk-notes :notes="notes">
|
||||||
<div class="init" v-if="fetching">
|
<div class="init" v-if="fetching">
|
||||||
%fa:spinner .pulse%%i18n:common.loading%
|
%fa:spinner .pulse%%i18n:common.loading%
|
||||||
</div>
|
</div>
|
||||||
<div class="empty" v-if="!fetching && posts.length == 0">
|
<div class="empty" v-if="!fetching && notes.length == 0">
|
||||||
%fa:R comments%
|
%fa:R comments%
|
||||||
{{ withMedia ? '%i18n:mobile.tags.mk-user-timeline.no-posts-with-media%' : '%i18n:mobile.tags.mk-user-timeline.no-posts%' }}
|
{{ withMedia ? '%i18n:mobile.tags.mk-user-timeline.no-notes-with-media%' : '%i18n:mobile.tags.mk-user-timeline.no-notes%' }}
|
||||||
</div>
|
</div>
|
||||||
<button v-if="!fetching && existMore" @click="more" :disabled="moreFetching" slot="tail">
|
<button v-if="!fetching && existMore" @click="more" :disabled="moreFetching" slot="tail">
|
||||||
<span v-if="!moreFetching">%i18n:mobile.tags.mk-user-timeline.load-more%</span>
|
<span v-if="!moreFetching">%i18n:mobile.tags.mk-user-timeline.load-more%</span>
|
||||||
<span v-if="moreFetching">%i18n:common.loading%<mk-ellipsis/></span>
|
<span v-if="moreFetching">%i18n:common.loading%<mk-ellipsis/></span>
|
||||||
</button>
|
</button>
|
||||||
</mk-posts>
|
</mk-notes>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -26,22 +26,22 @@ export default Vue.extend({
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
fetching: true,
|
fetching: true,
|
||||||
posts: [],
|
notes: [],
|
||||||
existMore: false,
|
existMore: false,
|
||||||
moreFetching: false
|
moreFetching: false
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
(this as any).api('users/posts', {
|
(this as any).api('users/notes', {
|
||||||
userId: this.user.id,
|
userId: this.user.id,
|
||||||
withMedia: this.withMedia,
|
withMedia: this.withMedia,
|
||||||
limit: limit + 1
|
limit: limit + 1
|
||||||
}).then(posts => {
|
}).then(notes => {
|
||||||
if (posts.length == limit + 1) {
|
if (notes.length == limit + 1) {
|
||||||
posts.pop();
|
notes.pop();
|
||||||
this.existMore = true;
|
this.existMore = true;
|
||||||
}
|
}
|
||||||
this.posts = posts;
|
this.notes = notes;
|
||||||
this.fetching = false;
|
this.fetching = false;
|
||||||
this.$emit('loaded');
|
this.$emit('loaded');
|
||||||
});
|
});
|
||||||
|
@ -49,19 +49,19 @@ export default Vue.extend({
|
||||||
methods: {
|
methods: {
|
||||||
more() {
|
more() {
|
||||||
this.moreFetching = true;
|
this.moreFetching = true;
|
||||||
(this as any).api('users/posts', {
|
(this as any).api('users/notes', {
|
||||||
userId: this.user.id,
|
userId: this.user.id,
|
||||||
withMedia: this.withMedia,
|
withMedia: this.withMedia,
|
||||||
limit: limit + 1,
|
limit: limit + 1,
|
||||||
untilId: this.posts[this.posts.length - 1].id
|
untilId: this.notes[this.notes.length - 1].id
|
||||||
}).then(posts => {
|
}).then(notes => {
|
||||||
if (posts.length == limit + 1) {
|
if (notes.length == limit + 1) {
|
||||||
posts.pop();
|
notes.pop();
|
||||||
this.existMore = true;
|
this.existMore = true;
|
||||||
} else {
|
} else {
|
||||||
this.existMore = false;
|
this.existMore = false;
|
||||||
}
|
}
|
||||||
this.posts = this.posts.concat(posts);
|
this.notes = this.notes.concat(notes);
|
||||||
this.moreFetching = false;
|
this.moreFetching = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,7 +64,7 @@ import Vue from 'vue';
|
||||||
import * as XDraggable from 'vuedraggable';
|
import * as XDraggable from 'vuedraggable';
|
||||||
import * as uuid from 'uuid';
|
import * as uuid from 'uuid';
|
||||||
import Progress from '../../../common/scripts/loading';
|
import Progress from '../../../common/scripts/loading';
|
||||||
import getPostSummary from '../../../../../renderers/get-post-summary';
|
import getNoteSummary from '../../../../../renderers/get-note-summary';
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
components: {
|
components: {
|
||||||
|
@ -124,14 +124,14 @@ export default Vue.extend({
|
||||||
this.connection = (this as any).os.stream.getConnection();
|
this.connection = (this as any).os.stream.getConnection();
|
||||||
this.connectionId = (this as any).os.stream.use();
|
this.connectionId = (this as any).os.stream.use();
|
||||||
|
|
||||||
this.connection.on('post', this.onStreamPost);
|
this.connection.on('note', this.onStreamNote);
|
||||||
this.connection.on('mobile_home_updated', this.onHomeUpdated);
|
this.connection.on('mobile_home_updated', this.onHomeUpdated);
|
||||||
document.addEventListener('visibilitychange', this.onVisibilitychange, false);
|
document.addEventListener('visibilitychange', this.onVisibilitychange, false);
|
||||||
|
|
||||||
Progress.start();
|
Progress.start();
|
||||||
},
|
},
|
||||||
beforeDestroy() {
|
beforeDestroy() {
|
||||||
this.connection.off('post', this.onStreamPost);
|
this.connection.off('note', this.onStreamNote);
|
||||||
this.connection.off('mobile_home_updated', this.onHomeUpdated);
|
this.connection.off('mobile_home_updated', this.onHomeUpdated);
|
||||||
(this as any).os.stream.dispose(this.connectionId);
|
(this as any).os.stream.dispose(this.connectionId);
|
||||||
document.removeEventListener('visibilitychange', this.onVisibilitychange);
|
document.removeEventListener('visibilitychange', this.onVisibilitychange);
|
||||||
|
@ -143,10 +143,10 @@ export default Vue.extend({
|
||||||
onLoaded() {
|
onLoaded() {
|
||||||
Progress.done();
|
Progress.done();
|
||||||
},
|
},
|
||||||
onStreamPost(post) {
|
onStreamNote(note) {
|
||||||
if (document.hidden && post.userId !== (this as any).os.i.id) {
|
if (document.hidden && note.userId !== (this as any).os.i.id) {
|
||||||
this.unreadCount++;
|
this.unreadCount++;
|
||||||
document.title = `(${this.unreadCount}) ${getPostSummary(post)}`;
|
document.title = `(${this.unreadCount}) ${getNoteSummary(note)}`;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onVisibilitychange() {
|
onVisibilitychange() {
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
<template>
|
<template>
|
||||||
<mk-ui>
|
<mk-ui>
|
||||||
<span slot="header">%fa:R sticky-note%%i18n:mobile.tags.mk-post-page.title%</span>
|
<span slot="header">%fa:R sticky-note%%i18n:mobile.tags.mk-note-page.title%</span>
|
||||||
<main v-if="!fetching">
|
<main v-if="!fetching">
|
||||||
<a v-if="post.next" :href="post.next">%fa:angle-up%%i18n:mobile.tags.mk-post-page.next%</a>
|
<a v-if="note.next" :href="note.next">%fa:angle-up%%i18n:mobile.tags.mk-note-page.next%</a>
|
||||||
<div>
|
<div>
|
||||||
<mk-post-detail :post="post"/>
|
<mk-note-detail :note="note"/>
|
||||||
</div>
|
</div>
|
||||||
<a v-if="post.prev" :href="post.prev">%fa:angle-down%%i18n:mobile.tags.mk-post-page.prev%</a>
|
<a v-if="note.prev" :href="note.prev">%fa:angle-down%%i18n:mobile.tags.mk-note-page.prev%</a>
|
||||||
</main>
|
</main>
|
||||||
</mk-ui>
|
</mk-ui>
|
||||||
</template>
|
</template>
|
||||||
|
@ -19,7 +19,7 @@ export default Vue.extend({
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
fetching: true,
|
fetching: true,
|
||||||
post: null
|
note: null
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
|
@ -37,10 +37,10 @@ export default Vue.extend({
|
||||||
Progress.start();
|
Progress.start();
|
||||||
this.fetching = true;
|
this.fetching = true;
|
||||||
|
|
||||||
(this as any).api('posts/show', {
|
(this as any).api('notes/show', {
|
||||||
postId: this.$route.params.post
|
noteId: this.$route.params.note
|
||||||
}).then(post => {
|
}).then(note => {
|
||||||
this.post = post;
|
this.note = note;
|
||||||
this.fetching = false;
|
this.fetching = false;
|
||||||
|
|
||||||
Progress.done();
|
Progress.done();
|
|
@ -2,13 +2,13 @@
|
||||||
<mk-ui>
|
<mk-ui>
|
||||||
<span slot="header">%fa:search% {{ q }}</span>
|
<span slot="header">%fa:search% {{ q }}</span>
|
||||||
<main v-if="!fetching">
|
<main v-if="!fetching">
|
||||||
<mk-posts :class="$style.posts" :posts="posts">
|
<mk-notes :class="$style.notes" :notes="notes">
|
||||||
<span v-if="posts.length == 0">{{ '%i18n:mobile.tags.mk-search-posts.empty%'.replace('{}', q) }}</span>
|
<span v-if="notes.length == 0">{{ '%i18n:mobile.tags.mk-search-notes.empty%'.replace('{}', q) }}</span>
|
||||||
<button v-if="existMore" @click="more" :disabled="fetching" slot="tail">
|
<button v-if="existMore" @click="more" :disabled="fetching" slot="tail">
|
||||||
<span v-if="!fetching">%i18n:mobile.tags.mk-timeline.load-more%</span>
|
<span v-if="!fetching">%i18n:mobile.tags.mk-timeline.load-more%</span>
|
||||||
<span v-if="fetching">%i18n:common.loading%<mk-ellipsis/></span>
|
<span v-if="fetching">%i18n:common.loading%<mk-ellipsis/></span>
|
||||||
</button>
|
</button>
|
||||||
</mk-posts>
|
</mk-notes>
|
||||||
</main>
|
</main>
|
||||||
</mk-ui>
|
</mk-ui>
|
||||||
</template>
|
</template>
|
||||||
|
@ -25,7 +25,7 @@ export default Vue.extend({
|
||||||
return {
|
return {
|
||||||
fetching: true,
|
fetching: true,
|
||||||
existMore: false,
|
existMore: false,
|
||||||
posts: [],
|
notes: [],
|
||||||
offset: 0
|
offset: 0
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
@ -48,30 +48,30 @@ export default Vue.extend({
|
||||||
this.fetching = true;
|
this.fetching = true;
|
||||||
Progress.start();
|
Progress.start();
|
||||||
|
|
||||||
(this as any).api('posts/search', Object.assign({
|
(this as any).api('notes/search', Object.assign({
|
||||||
limit: limit + 1
|
limit: limit + 1
|
||||||
}, parse(this.q))).then(posts => {
|
}, parse(this.q))).then(notes => {
|
||||||
if (posts.length == limit + 1) {
|
if (notes.length == limit + 1) {
|
||||||
posts.pop();
|
notes.pop();
|
||||||
this.existMore = true;
|
this.existMore = true;
|
||||||
}
|
}
|
||||||
this.posts = posts;
|
this.notes = notes;
|
||||||
this.fetching = false;
|
this.fetching = false;
|
||||||
Progress.done();
|
Progress.done();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
more() {
|
more() {
|
||||||
this.offset += limit;
|
this.offset += limit;
|
||||||
(this as any).api('posts/search', Object.assign({
|
(this as any).api('notes/search', Object.assign({
|
||||||
limit: limit + 1,
|
limit: limit + 1,
|
||||||
offset: this.offset
|
offset: this.offset
|
||||||
}, parse(this.q))).then(posts => {
|
}, parse(this.q))).then(notes => {
|
||||||
if (posts.length == limit + 1) {
|
if (notes.length == limit + 1) {
|
||||||
posts.pop();
|
notes.pop();
|
||||||
} else {
|
} else {
|
||||||
this.existMore = false;
|
this.existMore = false;
|
||||||
}
|
}
|
||||||
this.posts = this.posts.concat(posts);
|
this.notes = this.notes.concat(notes);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -79,7 +79,7 @@ export default Vue.extend({
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="stylus" module>
|
<style lang="stylus" module>
|
||||||
.posts
|
.notes
|
||||||
margin 8px auto
|
margin 8px auto
|
||||||
max-width 500px
|
max-width 500px
|
||||||
width calc(100% - 16px)
|
width calc(100% - 16px)
|
||||||
|
|
|
@ -27,8 +27,8 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="status">
|
<div class="status">
|
||||||
<a>
|
<a>
|
||||||
<b>{{ user.postsCount | number }}</b>
|
<b>{{ user.notesCount | number }}</b>
|
||||||
<i>%i18n:mobile.tags.mk-user.posts%</i>
|
<i>%i18n:mobile.tags.mk-user.notes%</i>
|
||||||
</a>
|
</a>
|
||||||
<a :href="`@${acct}/following`">
|
<a :href="`@${acct}/following`">
|
||||||
<b>{{ user.followingCount | number }}</b>
|
<b>{{ user.followingCount | number }}</b>
|
||||||
|
@ -44,13 +44,13 @@
|
||||||
<nav>
|
<nav>
|
||||||
<div class="nav-container">
|
<div class="nav-container">
|
||||||
<a :data-is-active=" page == 'home' " @click="page = 'home'">%i18n:mobile.tags.mk-user.overview%</a>
|
<a :data-is-active=" page == 'home' " @click="page = 'home'">%i18n:mobile.tags.mk-user.overview%</a>
|
||||||
<a :data-is-active=" page == 'posts' " @click="page = 'posts'">%i18n:mobile.tags.mk-user.timeline%</a>
|
<a :data-is-active=" page == 'notes' " @click="page = 'notes'">%i18n:mobile.tags.mk-user.timeline%</a>
|
||||||
<a :data-is-active=" page == 'media' " @click="page = 'media'">%i18n:mobile.tags.mk-user.media%</a>
|
<a :data-is-active=" page == 'media' " @click="page = 'media'">%i18n:mobile.tags.mk-user.media%</a>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
<div class="body">
|
<div class="body">
|
||||||
<x-home v-if="page == 'home'" :user="user"/>
|
<x-home v-if="page == 'home'" :user="user"/>
|
||||||
<mk-user-timeline v-if="page == 'posts'" :user="user"/>
|
<mk-user-timeline v-if="page == 'notes'" :user="user"/>
|
||||||
<mk-user-timeline v-if="page == 'media'" :user="user" with-media/>
|
<mk-user-timeline v-if="page == 'media'" :user="user" with-media/>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="root posts">
|
<div class="root notes">
|
||||||
<p class="fetching" v-if="fetching">%fa:spinner .pulse .fw%%i18n:mobile.tags.mk-user-overview-posts.loading%<mk-ellipsis/></p>
|
<p class="fetching" v-if="fetching">%fa:spinner .pulse .fw%%i18n:mobile.tags.mk-user-overview-notes.loading%<mk-ellipsis/></p>
|
||||||
<div v-if="!fetching && posts.length > 0">
|
<div v-if="!fetching && notes.length > 0">
|
||||||
<mk-post-card v-for="post in posts" :key="post.id" :post="post"/>
|
<mk-note-card v-for="note in notes" :key="note.id" :note="note"/>
|
||||||
</div>
|
</div>
|
||||||
<p class="empty" v-if="!fetching && posts.length == 0">%i18n:mobile.tags.mk-user-overview-posts.no-posts%</p>
|
<p class="empty" v-if="!fetching && notes.length == 0">%i18n:mobile.tags.mk-user-overview-notes.no-notes%</p>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -15,14 +15,14 @@ export default Vue.extend({
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
fetching: true,
|
fetching: true,
|
||||||
posts: []
|
notes: []
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
(this as any).api('users/posts', {
|
(this as any).api('users/notes', {
|
||||||
userId: this.user.id
|
userId: this.user.id
|
||||||
}).then(posts => {
|
}).then(notes => {
|
||||||
this.posts = posts;
|
this.notes = notes;
|
||||||
this.fetching = false;
|
this.fetching = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@ export default Vue.extend({
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="stylus" scoped>
|
<style lang="stylus" scoped>
|
||||||
.root.posts
|
.root.notes
|
||||||
|
|
||||||
> div
|
> div
|
||||||
overflow-x scroll
|
overflow-x scroll
|
|
@ -5,7 +5,7 @@
|
||||||
<a v-for="image in images"
|
<a v-for="image in images"
|
||||||
class="img"
|
class="img"
|
||||||
:style="`background-image: url(${image.media.url}?thumbnail&size=256)`"
|
:style="`background-image: url(${image.media.url}?thumbnail&size=256)`"
|
||||||
:href="`/@${getAcct(image.post.user)}/${image.post.id}`"
|
:href="`/@${getAcct(image.note.user)}/${image.note.id}`"
|
||||||
></a>
|
></a>
|
||||||
</div>
|
</div>
|
||||||
<p class="empty" v-if="!fetching && images.length == 0">%i18n:mobile.tags.mk-user-overview-photos.no-photos%</p>
|
<p class="empty" v-if="!fetching && images.length == 0">%i18n:mobile.tags.mk-user-overview-photos.no-photos%</p>
|
||||||
|
@ -28,15 +28,15 @@ export default Vue.extend({
|
||||||
getAcct
|
getAcct
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
(this as any).api('users/posts', {
|
(this as any).api('users/notes', {
|
||||||
userId: this.user.id,
|
userId: this.user.id,
|
||||||
withMedia: true,
|
withMedia: true,
|
||||||
limit: 6
|
limit: 6
|
||||||
}).then(posts => {
|
}).then(notes => {
|
||||||
posts.forEach(post => {
|
notes.forEach(note => {
|
||||||
post.media.forEach(media => {
|
note.media.forEach(media => {
|
||||||
if (this.images.length < 9) this.images.push({
|
if (this.images.length < 9) this.images.push({
|
||||||
post,
|
note,
|
||||||
media
|
media
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="root home">
|
<div class="root home">
|
||||||
<mk-post-detail v-if="user.pinnedPost" :post="user.pinnedPost" :compact="true"/>
|
<mk-note-detail v-if="user.pinnedNote" :note="user.pinnedNote" :compact="true"/>
|
||||||
<section class="recent-posts">
|
<section class="recent-notes">
|
||||||
<h2>%fa:R comments%%i18n:mobile.tags.mk-user-overview.recent-posts%</h2>
|
<h2>%fa:R comments%%i18n:mobile.tags.mk-user-overview.recent-notes%</h2>
|
||||||
<div>
|
<div>
|
||||||
<x-posts :user="user"/>
|
<x-notes :user="user"/>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<section class="images">
|
<section class="images">
|
||||||
|
@ -37,14 +37,14 @@
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import XPosts from './home.posts.vue';
|
import XNotes from './home.notes.vue';
|
||||||
import XPhotos from './home.photos.vue';
|
import XPhotos from './home.photos.vue';
|
||||||
import XFriends from './home.friends.vue';
|
import XFriends from './home.friends.vue';
|
||||||
import XFollowersYouKnow from './home.followers-you-know.vue';
|
import XFollowersYouKnow from './home.followers-you-know.vue';
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
components: {
|
components: {
|
||||||
XPosts,
|
XNotes,
|
||||||
XPhotos,
|
XPhotos,
|
||||||
XFriends,
|
XFriends,
|
||||||
XFollowersYouKnow
|
XFollowersYouKnow
|
||||||
|
@ -58,7 +58,7 @@ export default Vue.extend({
|
||||||
max-width 600px
|
max-width 600px
|
||||||
margin 0 auto
|
margin 0 auto
|
||||||
|
|
||||||
> .mk-post-detail
|
> .mk-note-detail
|
||||||
margin 0 0 8px 0
|
margin 0 0 8px 0
|
||||||
|
|
||||||
> section
|
> section
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<h1>Misskey<i>Statistics</i></h1>
|
<h1>Misskey<i>Statistics</i></h1>
|
||||||
<main v-if="!initializing">
|
<main v-if="!initializing">
|
||||||
<mk-users stats={ stats }/>
|
<mk-users stats={ stats }/>
|
||||||
<mk-posts stats={ stats }/>
|
<mk-notes stats={ stats }/>
|
||||||
</main>
|
</main>
|
||||||
<footer><a href={ _URL_ }>{ _HOST_ }</a></footer>
|
<footer><a href={ _URL_ }>{ _HOST_ }</a></footer>
|
||||||
<style lang="stylus" scoped>
|
<style lang="stylus" scoped>
|
||||||
|
@ -56,9 +56,9 @@
|
||||||
</script>
|
</script>
|
||||||
</mk-index>
|
</mk-index>
|
||||||
|
|
||||||
<mk-posts>
|
<mk-notes>
|
||||||
<h2>%i18n:stats.posts-count% <b>{ stats.postsCount }</b></h2>
|
<h2>%i18n:stats.notes-count% <b>{ stats.notesCount }</b></h2>
|
||||||
<mk-posts-chart v-if="!initializing" data={ data }/>
|
<mk-notes-chart v-if="!initializing" data={ data }/>
|
||||||
<style lang="stylus" scoped>
|
<style lang="stylus" scoped>
|
||||||
:scope
|
:scope
|
||||||
display block
|
display block
|
||||||
|
@ -70,7 +70,7 @@
|
||||||
this.stats = this.opts.stats;
|
this.stats = this.opts.stats;
|
||||||
|
|
||||||
this.on('mount', () => {
|
this.on('mount', () => {
|
||||||
this.$root.$data.os.api('aggregation/posts', {
|
this.$root.$data.os.api('aggregation/notes', {
|
||||||
limit: 365
|
limit: 365
|
||||||
}).then(data => {
|
}).then(data => {
|
||||||
this.update({
|
this.update({
|
||||||
|
@ -80,7 +80,7 @@
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
</mk-posts>
|
</mk-notes>
|
||||||
|
|
||||||
<mk-users>
|
<mk-users>
|
||||||
<h2>%i18n:stats.users-count% <b>{ stats.usersCount }</b></h2>
|
<h2>%i18n:stats.users-count% <b>{ stats.usersCount }</b></h2>
|
||||||
|
@ -108,11 +108,11 @@
|
||||||
</script>
|
</script>
|
||||||
</mk-users>
|
</mk-users>
|
||||||
|
|
||||||
<mk-posts-chart>
|
<mk-notes-chart>
|
||||||
<svg riot-viewBox="0 0 { viewBoxX } { viewBoxY }" preserveAspectRatio="none">
|
<svg riot-viewBox="0 0 { viewBoxX } { viewBoxY }" preserveAspectRatio="none">
|
||||||
<title>Black ... Total<br/>Blue ... Posts<br/>Red ... Replies<br/>Green ... Reposts</title>
|
<title>Black ... Total<br/>Blue ... Notes<br/>Red ... Replies<br/>Green ... Renotes</title>
|
||||||
<polyline
|
<polyline
|
||||||
riot-points={ pointsPost }
|
riot-points={ pointsNote }
|
||||||
fill="none"
|
fill="none"
|
||||||
stroke-width="1"
|
stroke-width="1"
|
||||||
stroke="#41ddde"/>
|
stroke="#41ddde"/>
|
||||||
|
@ -122,7 +122,7 @@
|
||||||
stroke-width="1"
|
stroke-width="1"
|
||||||
stroke="#f7796c"/>
|
stroke="#f7796c"/>
|
||||||
<polyline
|
<polyline
|
||||||
riot-points={ pointsRepost }
|
riot-points={ pointsRenote }
|
||||||
fill="none"
|
fill="none"
|
||||||
stroke-width="1"
|
stroke-width="1"
|
||||||
stroke="#a1de41"/>
|
stroke="#a1de41"/>
|
||||||
|
@ -147,7 +147,7 @@
|
||||||
this.viewBoxY = 80;
|
this.viewBoxY = 80;
|
||||||
|
|
||||||
this.data = this.opts.data.reverse();
|
this.data = this.opts.data.reverse();
|
||||||
this.data.forEach(d => d.total = d.posts + d.replies + d.reposts);
|
this.data.forEach(d => d.total = d.notes + d.replies + d.renotes);
|
||||||
const peak = Math.max.apply(null, this.data.map(d => d.total));
|
const peak = Math.max.apply(null, this.data.map(d => d.total));
|
||||||
|
|
||||||
this.on('mount', () => {
|
this.on('mount', () => {
|
||||||
|
@ -156,14 +156,14 @@
|
||||||
|
|
||||||
this.render = () => {
|
this.render = () => {
|
||||||
this.update({
|
this.update({
|
||||||
pointsPost: this.data.map((d, i) => `${i},${(1 - (d.posts / peak)) * this.viewBoxY}`).join(' '),
|
pointsNote: this.data.map((d, i) => `${i},${(1 - (d.notes / peak)) * this.viewBoxY}`).join(' '),
|
||||||
pointsReply: this.data.map((d, i) => `${i},${(1 - (d.replies / peak)) * this.viewBoxY}`).join(' '),
|
pointsReply: this.data.map((d, i) => `${i},${(1 - (d.replies / peak)) * this.viewBoxY}`).join(' '),
|
||||||
pointsRepost: this.data.map((d, i) => `${i},${(1 - (d.reposts / peak)) * this.viewBoxY}`).join(' '),
|
pointsRenote: this.data.map((d, i) => `${i},${(1 - (d.renotes / peak)) * this.viewBoxY}`).join(' '),
|
||||||
pointsTotal: this.data.map((d, i) => `${i},${(1 - (d.total / peak)) * this.viewBoxY}`).join(' ')
|
pointsTotal: this.data.map((d, i) => `${i},${(1 - (d.total / peak)) * this.viewBoxY}`).join(' ')
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
</mk-posts-chart>
|
</mk-notes-chart>
|
||||||
|
|
||||||
<mk-users-chart>
|
<mk-users-chart>
|
||||||
<svg riot-viewBox="0 0 { viewBoxX } { viewBoxY }" preserveAspectRatio="none">
|
<svg riot-viewBox="0 0 { viewBoxX } { viewBoxY }" preserveAspectRatio="none">
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
endpoint: "posts/create"
|
endpoint: "notes/create"
|
||||||
|
|
||||||
desc:
|
desc:
|
||||||
ja: "投稿します。"
|
ja: "投稿します。"
|
||||||
en: "Compose new post."
|
en: "Compose new note."
|
||||||
|
|
||||||
params:
|
params:
|
||||||
- name: "text"
|
- name: "text"
|
||||||
|
@ -10,7 +10,7 @@ params:
|
||||||
optional: true
|
optional: true
|
||||||
desc:
|
desc:
|
||||||
ja: "投稿の本文"
|
ja: "投稿の本文"
|
||||||
en: "The text of your post"
|
en: "The text of your note"
|
||||||
- name: "cw"
|
- name: "cw"
|
||||||
type: "string"
|
type: "string"
|
||||||
optional: true
|
optional: true
|
||||||
|
@ -24,17 +24,17 @@ params:
|
||||||
ja: "添付するメディア(1~4つ)"
|
ja: "添付するメディア(1~4つ)"
|
||||||
en: "Media you want to attach (1~4)"
|
en: "Media you want to attach (1~4)"
|
||||||
- name: "replyId"
|
- name: "replyId"
|
||||||
type: "id(Post)"
|
type: "id(Note)"
|
||||||
optional: true
|
optional: true
|
||||||
desc:
|
desc:
|
||||||
ja: "返信する投稿"
|
ja: "返信する投稿"
|
||||||
en: "The post you want to reply"
|
en: "The note you want to reply"
|
||||||
- name: "repostId"
|
- name: "renoteId"
|
||||||
type: "id(Post)"
|
type: "id(Note)"
|
||||||
optional: true
|
optional: true
|
||||||
desc:
|
desc:
|
||||||
ja: "引用する投稿"
|
ja: "引用する投稿"
|
||||||
en: "The post you want to quote"
|
en: "The note you want to quote"
|
||||||
- name: "poll"
|
- name: "poll"
|
||||||
type: "object"
|
type: "object"
|
||||||
optional: true
|
optional: true
|
||||||
|
@ -51,9 +51,9 @@ params:
|
||||||
en: "Choices of a poll"
|
en: "Choices of a poll"
|
||||||
|
|
||||||
res:
|
res:
|
||||||
- name: "createdPost"
|
- name: "createdNote"
|
||||||
type: "entity(Post)"
|
type: "entity(Note)"
|
||||||
optional: false
|
optional: false
|
||||||
desc:
|
desc:
|
||||||
ja: "作成した投稿"
|
ja: "作成した投稿"
|
||||||
en: "A post that created"
|
en: "A note that created"
|
|
@ -1,4 +1,4 @@
|
||||||
endpoint: "posts/timeline"
|
endpoint: "notes/timeline"
|
||||||
|
|
||||||
desc:
|
desc:
|
||||||
ja: "タイムラインを取得します。"
|
ja: "タイムラインを取得します。"
|
||||||
|
@ -11,12 +11,12 @@ params:
|
||||||
desc:
|
desc:
|
||||||
ja: "取得する最大の数"
|
ja: "取得する最大の数"
|
||||||
- name: "sinceId"
|
- name: "sinceId"
|
||||||
type: "id(Post)"
|
type: "id(Note)"
|
||||||
optional: true
|
optional: true
|
||||||
desc:
|
desc:
|
||||||
ja: "指定すると、この投稿を基点としてより新しい投稿を取得します"
|
ja: "指定すると、この投稿を基点としてより新しい投稿を取得します"
|
||||||
- name: "untilId"
|
- name: "untilId"
|
||||||
type: "id(Post)"
|
type: "id(Note)"
|
||||||
optional: true
|
optional: true
|
||||||
desc:
|
desc:
|
||||||
ja: "指定すると、この投稿を基点としてより古い投稿を取得します"
|
ja: "指定すると、この投稿を基点としてより古い投稿を取得します"
|
174
src/client/docs/api/entities/note.yaml
Normal file
174
src/client/docs/api/entities/note.yaml
Normal file
|
@ -0,0 +1,174 @@
|
||||||
|
name: "Note"
|
||||||
|
|
||||||
|
desc:
|
||||||
|
ja: "投稿。"
|
||||||
|
en: "A note."
|
||||||
|
|
||||||
|
props:
|
||||||
|
- name: "id"
|
||||||
|
type: "id"
|
||||||
|
optional: false
|
||||||
|
desc:
|
||||||
|
ja: "投稿ID"
|
||||||
|
en: "The ID of this note"
|
||||||
|
- name: "createdAt"
|
||||||
|
type: "date"
|
||||||
|
optional: false
|
||||||
|
desc:
|
||||||
|
ja: "投稿日時"
|
||||||
|
en: "The posted date of this note"
|
||||||
|
- name: "viaMobile"
|
||||||
|
type: "boolean"
|
||||||
|
optional: true
|
||||||
|
desc:
|
||||||
|
ja: "モバイル端末から投稿したか否か(自己申告であることに留意)"
|
||||||
|
en: "Whether this note sent via a mobile device"
|
||||||
|
- name: "text"
|
||||||
|
type: "string"
|
||||||
|
optional: true
|
||||||
|
desc:
|
||||||
|
ja: "投稿の本文 (ローカルの場合Markdown風のフォーマット)"
|
||||||
|
en: "The text of this note (in Markdown like format if local)"
|
||||||
|
- name: "textHtml"
|
||||||
|
type: "string"
|
||||||
|
optional: true
|
||||||
|
desc:
|
||||||
|
ja: "投稿の本文 (HTML) (投稿時は無視)"
|
||||||
|
en: "The text of this note (in HTML. Ignored when posting.)"
|
||||||
|
- name: "mediaIds"
|
||||||
|
type: "id(DriveFile)[]"
|
||||||
|
optional: true
|
||||||
|
desc:
|
||||||
|
ja: "添付されているメディアのID (なければレスポンスでは空配列)"
|
||||||
|
en: "The IDs of the attached media (empty array for response if no media is attached)"
|
||||||
|
- name: "media"
|
||||||
|
type: "entity(DriveFile)[]"
|
||||||
|
optional: true
|
||||||
|
desc:
|
||||||
|
ja: "添付されているメディア"
|
||||||
|
en: "The attached media"
|
||||||
|
- name: "userId"
|
||||||
|
type: "id(User)"
|
||||||
|
optional: false
|
||||||
|
desc:
|
||||||
|
ja: "投稿者ID"
|
||||||
|
en: "The ID of author of this note"
|
||||||
|
- name: "user"
|
||||||
|
type: "entity(User)"
|
||||||
|
optional: true
|
||||||
|
desc:
|
||||||
|
ja: "投稿者"
|
||||||
|
en: "The author of this note"
|
||||||
|
- name: "myReaction"
|
||||||
|
type: "string"
|
||||||
|
optional: true
|
||||||
|
desc:
|
||||||
|
ja: "この投稿に対する自分の<a href='/docs/api/reactions'>リアクション</a>"
|
||||||
|
en: "The your <a href='/docs/api/reactions'>reaction</a> of this note"
|
||||||
|
- name: "reactionCounts"
|
||||||
|
type: "object"
|
||||||
|
optional: false
|
||||||
|
desc:
|
||||||
|
ja: "<a href='/docs/api/reactions'>リアクション</a>をキーとし、この投稿に対するそのリアクションの数を値としたオブジェクト"
|
||||||
|
- name: "replyId"
|
||||||
|
type: "id(Note)"
|
||||||
|
optional: true
|
||||||
|
desc:
|
||||||
|
ja: "返信した投稿のID"
|
||||||
|
en: "The ID of the replyed note"
|
||||||
|
- name: "reply"
|
||||||
|
type: "entity(Note)"
|
||||||
|
optional: true
|
||||||
|
desc:
|
||||||
|
ja: "返信した投稿"
|
||||||
|
en: "The replyed note"
|
||||||
|
- name: "renoteId"
|
||||||
|
type: "id(Note)"
|
||||||
|
optional: true
|
||||||
|
desc:
|
||||||
|
ja: "引用した投稿のID"
|
||||||
|
en: "The ID of the quoted note"
|
||||||
|
- name: "renote"
|
||||||
|
type: "entity(Note)"
|
||||||
|
optional: true
|
||||||
|
desc:
|
||||||
|
ja: "引用した投稿"
|
||||||
|
en: "The quoted note"
|
||||||
|
- name: "poll"
|
||||||
|
type: "object"
|
||||||
|
optional: true
|
||||||
|
desc:
|
||||||
|
ja: "投票"
|
||||||
|
en: "The poll"
|
||||||
|
defName: "poll"
|
||||||
|
def:
|
||||||
|
- name: "choices"
|
||||||
|
type: "object[]"
|
||||||
|
optional: false
|
||||||
|
desc:
|
||||||
|
ja: "投票の選択肢"
|
||||||
|
en: "The choices of this poll"
|
||||||
|
defName: "choice"
|
||||||
|
def:
|
||||||
|
- name: "id"
|
||||||
|
type: "number"
|
||||||
|
optional: false
|
||||||
|
desc:
|
||||||
|
ja: "選択肢ID"
|
||||||
|
en: "The ID of this choice"
|
||||||
|
- name: "isVoted"
|
||||||
|
type: "boolean"
|
||||||
|
optional: true
|
||||||
|
desc:
|
||||||
|
ja: "自分がこの選択肢に投票したかどうか"
|
||||||
|
en: "Whether you voted to this choice"
|
||||||
|
- name: "text"
|
||||||
|
type: "string"
|
||||||
|
optional: false
|
||||||
|
desc:
|
||||||
|
ja: "選択肢本文"
|
||||||
|
en: "The text of this choice"
|
||||||
|
- name: "votes"
|
||||||
|
type: "number"
|
||||||
|
optional: false
|
||||||
|
desc:
|
||||||
|
ja: "この選択肢に投票された数"
|
||||||
|
en: "The number voted for this choice"
|
||||||
|
- name: "geo"
|
||||||
|
type: "object"
|
||||||
|
optional: true
|
||||||
|
desc:
|
||||||
|
ja: "位置情報"
|
||||||
|
en: "Geo location"
|
||||||
|
defName: "geo"
|
||||||
|
def:
|
||||||
|
- name: "coordinates"
|
||||||
|
type: "number[]"
|
||||||
|
optional: false
|
||||||
|
desc:
|
||||||
|
ja: "座標。最初に経度:-180〜180で表す。最後に緯度:-90〜90で表す。"
|
||||||
|
- name: "altitude"
|
||||||
|
type: "number"
|
||||||
|
optional: false
|
||||||
|
desc:
|
||||||
|
ja: "高度。メートル単位で表す。"
|
||||||
|
- name: "accuracy"
|
||||||
|
type: "number"
|
||||||
|
optional: false
|
||||||
|
desc:
|
||||||
|
ja: "緯度、経度の精度。メートル単位で表す。"
|
||||||
|
- name: "altitudeAccuracy"
|
||||||
|
type: "number"
|
||||||
|
optional: false
|
||||||
|
desc:
|
||||||
|
ja: "高度の精度。メートル単位で表す。"
|
||||||
|
- name: "heading"
|
||||||
|
type: "number"
|
||||||
|
optional: false
|
||||||
|
desc:
|
||||||
|
ja: "方角。0〜360の角度で表す。0が北、90が東、180が南、270が西。"
|
||||||
|
- name: "speed"
|
||||||
|
type: "number"
|
||||||
|
optional: false
|
||||||
|
desc:
|
||||||
|
ja: "速度。メートル / 秒数で表す。"
|
|
@ -1,8 +1,8 @@
|
||||||
name: "Post"
|
name: "Note"
|
||||||
|
|
||||||
desc:
|
desc:
|
||||||
ja: "投稿。"
|
ja: "投稿。"
|
||||||
en: "A post."
|
en: "A note."
|
||||||
|
|
||||||
props:
|
props:
|
||||||
- name: "id"
|
- name: "id"
|
||||||
|
@ -10,31 +10,31 @@ props:
|
||||||
optional: false
|
optional: false
|
||||||
desc:
|
desc:
|
||||||
ja: "投稿ID"
|
ja: "投稿ID"
|
||||||
en: "The ID of this post"
|
en: "The ID of this note"
|
||||||
- name: "createdAt"
|
- name: "createdAt"
|
||||||
type: "date"
|
type: "date"
|
||||||
optional: false
|
optional: false
|
||||||
desc:
|
desc:
|
||||||
ja: "投稿日時"
|
ja: "投稿日時"
|
||||||
en: "The posted date of this post"
|
en: "The posted date of this note"
|
||||||
- name: "viaMobile"
|
- name: "viaMobile"
|
||||||
type: "boolean"
|
type: "boolean"
|
||||||
optional: true
|
optional: true
|
||||||
desc:
|
desc:
|
||||||
ja: "モバイル端末から投稿したか否か(自己申告であることに留意)"
|
ja: "モバイル端末から投稿したか否か(自己申告であることに留意)"
|
||||||
en: "Whether this post sent via a mobile device"
|
en: "Whether this note sent via a mobile device"
|
||||||
- name: "text"
|
- name: "text"
|
||||||
type: "string"
|
type: "string"
|
||||||
optional: true
|
optional: true
|
||||||
desc:
|
desc:
|
||||||
ja: "投稿の本文 (ローカルの場合Markdown風のフォーマット)"
|
ja: "投稿の本文 (ローカルの場合Markdown風のフォーマット)"
|
||||||
en: "The text of this post (in Markdown like format if local)"
|
en: "The text of this note (in Markdown like format if local)"
|
||||||
- name: "textHtml"
|
- name: "textHtml"
|
||||||
type: "string"
|
type: "string"
|
||||||
optional: true
|
optional: true
|
||||||
desc:
|
desc:
|
||||||
ja: "投稿の本文 (HTML) (投稿時は無視)"
|
ja: "投稿の本文 (HTML) (投稿時は無視)"
|
||||||
en: "The text of this post (in HTML. Ignored when posting.)"
|
en: "The text of this note (in HTML. Ignored when posting.)"
|
||||||
- name: "mediaIds"
|
- name: "mediaIds"
|
||||||
type: "id(DriveFile)[]"
|
type: "id(DriveFile)[]"
|
||||||
optional: true
|
optional: true
|
||||||
|
@ -52,48 +52,48 @@ props:
|
||||||
optional: false
|
optional: false
|
||||||
desc:
|
desc:
|
||||||
ja: "投稿者ID"
|
ja: "投稿者ID"
|
||||||
en: "The ID of author of this post"
|
en: "The ID of author of this note"
|
||||||
- name: "user"
|
- name: "user"
|
||||||
type: "entity(User)"
|
type: "entity(User)"
|
||||||
optional: true
|
optional: true
|
||||||
desc:
|
desc:
|
||||||
ja: "投稿者"
|
ja: "投稿者"
|
||||||
en: "The author of this post"
|
en: "The author of this note"
|
||||||
- name: "myReaction"
|
- name: "myReaction"
|
||||||
type: "string"
|
type: "string"
|
||||||
optional: true
|
optional: true
|
||||||
desc:
|
desc:
|
||||||
ja: "この投稿に対する自分の<a href='/docs/api/reactions'>リアクション</a>"
|
ja: "この投稿に対する自分の<a href='/docs/api/reactions'>リアクション</a>"
|
||||||
en: "The your <a href='/docs/api/reactions'>reaction</a> of this post"
|
en: "The your <a href='/docs/api/reactions'>reaction</a> of this note"
|
||||||
- name: "reactionCounts"
|
- name: "reactionCounts"
|
||||||
type: "object"
|
type: "object"
|
||||||
optional: false
|
optional: false
|
||||||
desc:
|
desc:
|
||||||
ja: "<a href='/docs/api/reactions'>リアクション</a>をキーとし、この投稿に対するそのリアクションの数を値としたオブジェクト"
|
ja: "<a href='/docs/api/reactions'>リアクション</a>をキーとし、この投稿に対するそのリアクションの数を値としたオブジェクト"
|
||||||
- name: "replyId"
|
- name: "replyId"
|
||||||
type: "id(Post)"
|
type: "id(Note)"
|
||||||
optional: true
|
optional: true
|
||||||
desc:
|
desc:
|
||||||
ja: "返信した投稿のID"
|
ja: "返信した投稿のID"
|
||||||
en: "The ID of the replyed post"
|
en: "The ID of the replyed note"
|
||||||
- name: "reply"
|
- name: "reply"
|
||||||
type: "entity(Post)"
|
type: "entity(Note)"
|
||||||
optional: true
|
optional: true
|
||||||
desc:
|
desc:
|
||||||
ja: "返信した投稿"
|
ja: "返信した投稿"
|
||||||
en: "The replyed post"
|
en: "The replyed note"
|
||||||
- name: "repostId"
|
- name: "renoteId"
|
||||||
type: "id(Post)"
|
type: "id(Note)"
|
||||||
optional: true
|
optional: true
|
||||||
desc:
|
desc:
|
||||||
ja: "引用した投稿のID"
|
ja: "引用した投稿のID"
|
||||||
en: "The ID of the quoted post"
|
en: "The ID of the quoted note"
|
||||||
- name: "repost"
|
- name: "renote"
|
||||||
type: "entity(Post)"
|
type: "entity(Note)"
|
||||||
optional: true
|
optional: true
|
||||||
desc:
|
desc:
|
||||||
ja: "引用した投稿"
|
ja: "引用した投稿"
|
||||||
en: "The quoted post"
|
en: "The quoted note"
|
||||||
- name: "poll"
|
- name: "poll"
|
||||||
type: "object"
|
type: "object"
|
||||||
optional: true
|
optional: true
|
||||||
|
|
|
@ -81,24 +81,24 @@ props:
|
||||||
desc:
|
desc:
|
||||||
ja: "自分がこのユーザーをミュートしているか"
|
ja: "自分がこのユーザーをミュートしているか"
|
||||||
en: "Whether you muted this user"
|
en: "Whether you muted this user"
|
||||||
- name: "postsCount"
|
- name: "notesCount"
|
||||||
type: "number"
|
type: "number"
|
||||||
optional: false
|
optional: false
|
||||||
desc:
|
desc:
|
||||||
ja: "投稿の数"
|
ja: "投稿の数"
|
||||||
en: "The number of the posts of this user"
|
en: "The number of the notes of this user"
|
||||||
- name: "pinnedPost"
|
- name: "pinnedNote"
|
||||||
type: "entity(Post)"
|
type: "entity(Note)"
|
||||||
optional: true
|
optional: true
|
||||||
desc:
|
desc:
|
||||||
ja: "ピン留めされた投稿"
|
ja: "ピン留めされた投稿"
|
||||||
en: "The pinned post of this user"
|
en: "The pinned note of this user"
|
||||||
- name: "pinnedPostId"
|
- name: "pinnedNoteId"
|
||||||
type: "id(Post)"
|
type: "id(Note)"
|
||||||
optional: true
|
optional: true
|
||||||
desc:
|
desc:
|
||||||
ja: "ピン留めされた投稿のID"
|
ja: "ピン留めされた投稿のID"
|
||||||
en: "The ID of the pinned post of this user"
|
en: "The ID of the pinned note of this user"
|
||||||
- name: "driveCapacity"
|
- name: "driveCapacity"
|
||||||
type: "number"
|
type: "number"
|
||||||
optional: false
|
optional: false
|
||||||
|
|
|
@ -4,7 +4,7 @@ p ユーザーページから、そのユーザーをミュートすることが
|
||||||
|
|
||||||
p ユーザーをミュートすると、そのユーザーに関する次のコンテンツがMisskeyに表示されなくなります:
|
p ユーザーをミュートすると、そのユーザーに関する次のコンテンツがMisskeyに表示されなくなります:
|
||||||
ul
|
ul
|
||||||
li タイムラインや投稿の検索結果内の、そのユーザーの投稿(およびそれらの投稿に対する返信やRepost)
|
li タイムラインや投稿の検索結果内の、そのユーザーの投稿(およびそれらの投稿に対する返信やRenote)
|
||||||
li そのユーザーからの通知
|
li そのユーザーからの通知
|
||||||
li メッセージ履歴一覧内の、そのユーザーとのメッセージ履歴
|
li メッセージ履歴一覧内の、そのユーザーとのメッセージ履歴
|
||||||
|
|
||||||
|
|
|
@ -64,19 +64,19 @@ section
|
||||||
tr
|
tr
|
||||||
td mute
|
td mute
|
||||||
td
|
td
|
||||||
| mute_all ... ミュートしているユーザーの投稿とその投稿に対する返信やRepostを除外する(デフォルト)
|
| mute_all ... ミュートしているユーザーの投稿とその投稿に対する返信やRenoteを除外する(デフォルト)
|
||||||
br
|
br
|
||||||
| mute_related ... ミュートしているユーザーの投稿に対する返信やRepostだけ除外する
|
| mute_related ... ミュートしているユーザーの投稿に対する返信やRenoteだけ除外する
|
||||||
br
|
br
|
||||||
| mute_direct ... ミュートしているユーザーの投稿だけ除外する
|
| mute_direct ... ミュートしているユーザーの投稿だけ除外する
|
||||||
br
|
br
|
||||||
| disabled ... ミュートしているユーザーの投稿とその投稿に対する返信やRepostも含める
|
| disabled ... ミュートしているユーザーの投稿とその投稿に対する返信やRenoteも含める
|
||||||
br
|
br
|
||||||
| direct_only ... ミュートしているユーザーの投稿だけに限定
|
| direct_only ... ミュートしているユーザーの投稿だけに限定
|
||||||
br
|
br
|
||||||
| related_only ... ミュートしているユーザーの投稿に対する返信やRepostだけに限定
|
| related_only ... ミュートしているユーザーの投稿に対する返信やRenoteだけに限定
|
||||||
br
|
br
|
||||||
| all_only ... ミュートしているユーザーの投稿とその投稿に対する返信やRepostに限定
|
| all_only ... ミュートしているユーザーの投稿とその投稿に対する返信やRenoteに限定
|
||||||
tr
|
tr
|
||||||
td reply
|
td reply
|
||||||
td
|
td
|
||||||
|
@ -86,11 +86,11 @@ section
|
||||||
br
|
br
|
||||||
| null ... 特に限定しない(デフォルト)
|
| null ... 特に限定しない(デフォルト)
|
||||||
tr
|
tr
|
||||||
td repost
|
td renote
|
||||||
td
|
td
|
||||||
| true ... Repostに限定。
|
| true ... Renoteに限定。
|
||||||
br
|
br
|
||||||
| false ... Repostでない投稿に限定。
|
| false ... Renoteでない投稿に限定。
|
||||||
br
|
br
|
||||||
| null ... 特に限定しない(デフォルト)
|
| null ... 特に限定しない(デフォルト)
|
||||||
tr
|
tr
|
||||||
|
|
|
@ -8,5 +8,5 @@ export type IFavorite = {
|
||||||
_id: mongo.ObjectID;
|
_id: mongo.ObjectID;
|
||||||
createdAt: Date;
|
createdAt: Date;
|
||||||
userId: mongo.ObjectID;
|
userId: mongo.ObjectID;
|
||||||
postId: mongo.ObjectID;
|
noteId: mongo.ObjectID;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
import * as mongo from 'mongodb';
|
import * as mongo from 'mongodb';
|
||||||
import deepcopy = require('deepcopy');
|
import deepcopy = require('deepcopy');
|
||||||
import db from '../db/mongodb';
|
import db from '../db/mongodb';
|
||||||
import Reaction from './post-reaction';
|
import Reaction from './note-reaction';
|
||||||
import { pack as packUser } from './user';
|
import { pack as packUser } from './user';
|
||||||
|
|
||||||
const PostReaction = db.get<IPostReaction>('postReactions');
|
const NoteReaction = db.get<INoteReaction>('noteReactions');
|
||||||
PostReaction.createIndex(['userId', 'postId'], { unique: true });
|
NoteReaction.createIndex(['userId', 'noteId'], { unique: true });
|
||||||
export default PostReaction;
|
export default NoteReaction;
|
||||||
|
|
||||||
export interface IPostReaction {
|
export interface INoteReaction {
|
||||||
_id: mongo.ObjectID;
|
_id: mongo.ObjectID;
|
||||||
createdAt: Date;
|
createdAt: Date;
|
||||||
postId: mongo.ObjectID;
|
noteId: mongo.ObjectID;
|
||||||
userId: mongo.ObjectID;
|
userId: mongo.ObjectID;
|
||||||
reaction: string;
|
reaction: string;
|
||||||
}
|
}
|
13
src/models/note-watching.ts
Normal file
13
src/models/note-watching.ts
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
import * as mongo from 'mongodb';
|
||||||
|
import db from '../db/mongodb';
|
||||||
|
|
||||||
|
const NoteWatching = db.get<INoteWatching>('noteWatching');
|
||||||
|
NoteWatching.createIndex(['userId', 'noteId'], { unique: true });
|
||||||
|
export default NoteWatching;
|
||||||
|
|
||||||
|
export interface INoteWatching {
|
||||||
|
_id: mongo.ObjectID;
|
||||||
|
createdAt: Date;
|
||||||
|
userId: mongo.ObjectID;
|
||||||
|
noteId: mongo.ObjectID;
|
||||||
|
}
|
|
@ -6,14 +6,14 @@ import { IUser, pack as packUser } from './user';
|
||||||
import { pack as packApp } from './app';
|
import { pack as packApp } from './app';
|
||||||
import { pack as packChannel } from './channel';
|
import { pack as packChannel } from './channel';
|
||||||
import Vote from './poll-vote';
|
import Vote from './poll-vote';
|
||||||
import Reaction from './post-reaction';
|
import Reaction from './note-reaction';
|
||||||
import { pack as packFile } from './drive-file';
|
import { pack as packFile } from './drive-file';
|
||||||
|
|
||||||
const Post = db.get<IPost>('posts');
|
const Note = db.get<INote>('notes');
|
||||||
|
|
||||||
Post.createIndex('uri', { sparse: true, unique: true });
|
Note.createIndex('uri', { sparse: true, unique: true });
|
||||||
|
|
||||||
export default Post;
|
export default Note;
|
||||||
|
|
||||||
export function isValidText(text: string): boolean {
|
export function isValidText(text: string): boolean {
|
||||||
return text.length <= 1000 && text.trim() != '';
|
return text.length <= 1000 && text.trim() != '';
|
||||||
|
@ -23,14 +23,14 @@ export function isValidCw(text: string): boolean {
|
||||||
return text.length <= 100 && text.trim() != '';
|
return text.length <= 100 && text.trim() != '';
|
||||||
}
|
}
|
||||||
|
|
||||||
export type IPost = {
|
export type INote = {
|
||||||
_id: mongo.ObjectID;
|
_id: mongo.ObjectID;
|
||||||
channelId: mongo.ObjectID;
|
channelId: mongo.ObjectID;
|
||||||
createdAt: Date;
|
createdAt: Date;
|
||||||
deletedAt: Date;
|
deletedAt: Date;
|
||||||
mediaIds: mongo.ObjectID[];
|
mediaIds: mongo.ObjectID[];
|
||||||
replyId: mongo.ObjectID;
|
replyId: mongo.ObjectID;
|
||||||
repostId: mongo.ObjectID;
|
renoteId: mongo.ObjectID;
|
||||||
poll: any; // todo
|
poll: any; // todo
|
||||||
text: string;
|
text: string;
|
||||||
tags: string[];
|
tags: string[];
|
||||||
|
@ -39,7 +39,7 @@ export type IPost = {
|
||||||
userId: mongo.ObjectID;
|
userId: mongo.ObjectID;
|
||||||
appId: mongo.ObjectID;
|
appId: mongo.ObjectID;
|
||||||
viaMobile: boolean;
|
viaMobile: boolean;
|
||||||
repostCount: number;
|
renoteCount: number;
|
||||||
repliesCount: number;
|
repliesCount: number;
|
||||||
reactionCounts: any;
|
reactionCounts: any;
|
||||||
mentions: mongo.ObjectID[];
|
mentions: mongo.ObjectID[];
|
||||||
|
@ -57,7 +57,7 @@ export type IPost = {
|
||||||
_reply?: {
|
_reply?: {
|
||||||
userId: mongo.ObjectID;
|
userId: mongo.ObjectID;
|
||||||
};
|
};
|
||||||
_repost?: {
|
_renote?: {
|
||||||
userId: mongo.ObjectID;
|
userId: mongo.ObjectID;
|
||||||
};
|
};
|
||||||
_user: {
|
_user: {
|
||||||
|
@ -70,15 +70,15 @@ export type IPost = {
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pack a post for API response
|
* Pack a note for API response
|
||||||
*
|
*
|
||||||
* @param post target
|
* @param note target
|
||||||
* @param me? serializee
|
* @param me? serializee
|
||||||
* @param options? serialize options
|
* @param options? serialize options
|
||||||
* @return response
|
* @return response
|
||||||
*/
|
*/
|
||||||
export const pack = async (
|
export const pack = async (
|
||||||
post: string | mongo.ObjectID | IPost,
|
note: string | mongo.ObjectID | INote,
|
||||||
me?: string | mongo.ObjectID | IUser,
|
me?: string | mongo.ObjectID | IUser,
|
||||||
options?: {
|
options?: {
|
||||||
detail: boolean
|
detail: boolean
|
||||||
|
@ -97,58 +97,58 @@ export const pack = async (
|
||||||
: (me as IUser)._id
|
: (me as IUser)._id
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
let _post: any;
|
let _note: any;
|
||||||
|
|
||||||
// Populate the post if 'post' is ID
|
// Populate the note if 'note' is ID
|
||||||
if (mongo.ObjectID.prototype.isPrototypeOf(post)) {
|
if (mongo.ObjectID.prototype.isPrototypeOf(note)) {
|
||||||
_post = await Post.findOne({
|
_note = await Note.findOne({
|
||||||
_id: post
|
_id: note
|
||||||
});
|
});
|
||||||
} else if (typeof post === 'string') {
|
} else if (typeof note === 'string') {
|
||||||
_post = await Post.findOne({
|
_note = await Note.findOne({
|
||||||
_id: new mongo.ObjectID(post)
|
_id: new mongo.ObjectID(note)
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
_post = deepcopy(post);
|
_note = deepcopy(note);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_post) throw 'invalid post arg.';
|
if (!_note) throw 'invalid note arg.';
|
||||||
|
|
||||||
const id = _post._id;
|
const id = _note._id;
|
||||||
|
|
||||||
// Rename _id to id
|
// Rename _id to id
|
||||||
_post.id = _post._id;
|
_note.id = _note._id;
|
||||||
delete _post._id;
|
delete _note._id;
|
||||||
|
|
||||||
delete _post.mentions;
|
delete _note.mentions;
|
||||||
if (_post.geo) delete _post.geo.type;
|
if (_note.geo) delete _note.geo.type;
|
||||||
|
|
||||||
// Populate user
|
// Populate user
|
||||||
_post.user = packUser(_post.userId, meId);
|
_note.user = packUser(_note.userId, meId);
|
||||||
|
|
||||||
// Populate app
|
// Populate app
|
||||||
if (_post.appId) {
|
if (_note.appId) {
|
||||||
_post.app = packApp(_post.appId);
|
_note.app = packApp(_note.appId);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Populate channel
|
// Populate channel
|
||||||
if (_post.channelId) {
|
if (_note.channelId) {
|
||||||
_post.channel = packChannel(_post.channelId);
|
_note.channel = packChannel(_note.channelId);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Populate media
|
// Populate media
|
||||||
if (_post.mediaIds) {
|
if (_note.mediaIds) {
|
||||||
_post.media = Promise.all(_post.mediaIds.map(fileId =>
|
_note.media = Promise.all(_note.mediaIds.map(fileId =>
|
||||||
packFile(fileId)
|
packFile(fileId)
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
// When requested a detailed post data
|
// When requested a detailed note data
|
||||||
if (opts.detail) {
|
if (opts.detail) {
|
||||||
// Get previous post info
|
// Get previous note info
|
||||||
_post.prev = (async () => {
|
_note.prev = (async () => {
|
||||||
const prev = await Post.findOne({
|
const prev = await Note.findOne({
|
||||||
userId: _post.userId,
|
userId: _note.userId,
|
||||||
_id: {
|
_id: {
|
||||||
$lt: id
|
$lt: id
|
||||||
}
|
}
|
||||||
|
@ -163,10 +163,10 @@ export const pack = async (
|
||||||
return prev ? prev._id : null;
|
return prev ? prev._id : null;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
// Get next post info
|
// Get next note info
|
||||||
_post.next = (async () => {
|
_note.next = (async () => {
|
||||||
const next = await Post.findOne({
|
const next = await Note.findOne({
|
||||||
userId: _post.userId,
|
userId: _note.userId,
|
||||||
_id: {
|
_id: {
|
||||||
$gt: id
|
$gt: id
|
||||||
}
|
}
|
||||||
|
@ -181,27 +181,27 @@ export const pack = async (
|
||||||
return next ? next._id : null;
|
return next ? next._id : null;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
if (_post.replyId) {
|
if (_note.replyId) {
|
||||||
// Populate reply to post
|
// Populate reply to note
|
||||||
_post.reply = pack(_post.replyId, meId, {
|
_note.reply = pack(_note.replyId, meId, {
|
||||||
detail: false
|
detail: false
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_post.repostId) {
|
if (_note.renoteId) {
|
||||||
// Populate repost
|
// Populate renote
|
||||||
_post.repost = pack(_post.repostId, meId, {
|
_note.renote = pack(_note.renoteId, meId, {
|
||||||
detail: _post.text == null
|
detail: _note.text == null
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Poll
|
// Poll
|
||||||
if (meId && _post.poll) {
|
if (meId && _note.poll) {
|
||||||
_post.poll = (async (poll) => {
|
_note.poll = (async (poll) => {
|
||||||
const vote = await Vote
|
const vote = await Vote
|
||||||
.findOne({
|
.findOne({
|
||||||
userId: meId,
|
userId: meId,
|
||||||
postId: id
|
noteId: id
|
||||||
});
|
});
|
||||||
|
|
||||||
if (vote != null) {
|
if (vote != null) {
|
||||||
|
@ -212,16 +212,16 @@ export const pack = async (
|
||||||
}
|
}
|
||||||
|
|
||||||
return poll;
|
return poll;
|
||||||
})(_post.poll);
|
})(_note.poll);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch my reaction
|
// Fetch my reaction
|
||||||
if (meId) {
|
if (meId) {
|
||||||
_post.myReaction = (async () => {
|
_note.myReaction = (async () => {
|
||||||
const reaction = await Reaction
|
const reaction = await Reaction
|
||||||
.findOne({
|
.findOne({
|
||||||
userId: meId,
|
userId: meId,
|
||||||
postId: id,
|
noteId: id,
|
||||||
deletedAt: { $exists: false }
|
deletedAt: { $exists: false }
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -234,8 +234,8 @@ export const pack = async (
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// resolve promises in _post object
|
// resolve promises in _note object
|
||||||
_post = await rap(_post);
|
_note = await rap(_note);
|
||||||
|
|
||||||
return _post;
|
return _note;
|
||||||
};
|
};
|
|
@ -2,7 +2,7 @@ import * as mongo from 'mongodb';
|
||||||
import deepcopy = require('deepcopy');
|
import deepcopy = require('deepcopy');
|
||||||
import db from '../db/mongodb';
|
import db from '../db/mongodb';
|
||||||
import { IUser, pack as packUser } from './user';
|
import { IUser, pack as packUser } from './user';
|
||||||
import { pack as packPost } from './post';
|
import { pack as packNote } from './note';
|
||||||
|
|
||||||
const Notification = db.get<INotification>('notifications');
|
const Notification = db.get<INotification>('notifications');
|
||||||
export default Notification;
|
export default Notification;
|
||||||
|
@ -36,12 +36,12 @@ export interface INotification {
|
||||||
* follow - フォローされた
|
* follow - フォローされた
|
||||||
* mention - 投稿で自分が言及された
|
* mention - 投稿で自分が言及された
|
||||||
* reply - (自分または自分がWatchしている)投稿が返信された
|
* reply - (自分または自分がWatchしている)投稿が返信された
|
||||||
* repost - (自分または自分がWatchしている)投稿がRepostされた
|
* renote - (自分または自分がWatchしている)投稿がRenoteされた
|
||||||
* quote - (自分または自分がWatchしている)投稿が引用Repostされた
|
* quote - (自分または自分がWatchしている)投稿が引用Renoteされた
|
||||||
* reaction - (自分または自分がWatchしている)投稿にリアクションされた
|
* reaction - (自分または自分がWatchしている)投稿にリアクションされた
|
||||||
* poll_vote - (自分または自分がWatchしている)投稿の投票に投票された
|
* poll_vote - (自分または自分がWatchしている)投稿の投票に投票された
|
||||||
*/
|
*/
|
||||||
type: 'follow' | 'mention' | 'reply' | 'repost' | 'quote' | 'reaction' | 'poll_vote';
|
type: 'follow' | 'mention' | 'reply' | 'renote' | 'quote' | 'reaction' | 'poll_vote';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 通知が読まれたかどうか
|
* 通知が読まれたかどうか
|
||||||
|
@ -91,12 +91,12 @@ export const pack = (notification: any) => new Promise<any>(async (resolve, reje
|
||||||
break;
|
break;
|
||||||
case 'mention':
|
case 'mention':
|
||||||
case 'reply':
|
case 'reply':
|
||||||
case 'repost':
|
case 'renote':
|
||||||
case 'quote':
|
case 'quote':
|
||||||
case 'reaction':
|
case 'reaction':
|
||||||
case 'poll_vote':
|
case 'poll_vote':
|
||||||
// Populate post
|
// Populate note
|
||||||
_notification.post = await packPost(_notification.postId, me);
|
_notification.note = await packNote(_notification.noteId, me);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
console.error(`Unknown type: ${_notification.type}`);
|
console.error(`Unknown type: ${_notification.type}`);
|
||||||
|
|
|
@ -8,6 +8,6 @@ export interface IPollVote {
|
||||||
_id: mongo.ObjectID;
|
_id: mongo.ObjectID;
|
||||||
createdAt: Date;
|
createdAt: Date;
|
||||||
userId: mongo.ObjectID;
|
userId: mongo.ObjectID;
|
||||||
postId: mongo.ObjectID;
|
noteId: mongo.ObjectID;
|
||||||
choice: number;
|
choice: number;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +0,0 @@
|
||||||
import * as mongo from 'mongodb';
|
|
||||||
import db from '../db/mongodb';
|
|
||||||
|
|
||||||
const PostWatching = db.get<IPostWatching>('postWatching');
|
|
||||||
PostWatching.createIndex(['userId', 'postId'], { unique: true });
|
|
||||||
export default PostWatching;
|
|
||||||
|
|
||||||
export interface IPostWatching {
|
|
||||||
_id: mongo.ObjectID;
|
|
||||||
createdAt: Date;
|
|
||||||
userId: mongo.ObjectID;
|
|
||||||
postId: mongo.ObjectID;
|
|
||||||
}
|
|
|
@ -2,7 +2,7 @@ import * as mongo from 'mongodb';
|
||||||
import deepcopy = require('deepcopy');
|
import deepcopy = require('deepcopy');
|
||||||
import rap from '@prezzemolo/rap';
|
import rap from '@prezzemolo/rap';
|
||||||
import db from '../db/mongodb';
|
import db from '../db/mongodb';
|
||||||
import { IPost, pack as packPost } from './post';
|
import { INote, pack as packNote } from './note';
|
||||||
import Following from './following';
|
import Following from './following';
|
||||||
import Mute from './mute';
|
import Mute from './mute';
|
||||||
import getFriends from '../server/api/common/get-friends';
|
import getFriends from '../server/api/common/get-friends';
|
||||||
|
@ -22,7 +22,7 @@ type IUserBase = {
|
||||||
followersCount: number;
|
followersCount: number;
|
||||||
followingCount: number;
|
followingCount: number;
|
||||||
name?: string;
|
name?: string;
|
||||||
postsCount: number;
|
notesCount: number;
|
||||||
driveCapacity: number;
|
driveCapacity: number;
|
||||||
username: string;
|
username: string;
|
||||||
usernameLower: string;
|
usernameLower: string;
|
||||||
|
@ -30,8 +30,8 @@ type IUserBase = {
|
||||||
bannerId: mongo.ObjectID;
|
bannerId: mongo.ObjectID;
|
||||||
data: any;
|
data: any;
|
||||||
description: string;
|
description: string;
|
||||||
latestPost: IPost;
|
latestNote: INote;
|
||||||
pinnedPostId: mongo.ObjectID;
|
pinnedNoteId: mongo.ObjectID;
|
||||||
isSuspended: boolean;
|
isSuspended: boolean;
|
||||||
keywords: string[];
|
keywords: string[];
|
||||||
host: string;
|
host: string;
|
||||||
|
@ -120,7 +120,7 @@ export function init(user): IUser {
|
||||||
user._id = new mongo.ObjectID(user._id);
|
user._id = new mongo.ObjectID(user._id);
|
||||||
user.avatarId = new mongo.ObjectID(user.avatarId);
|
user.avatarId = new mongo.ObjectID(user.avatarId);
|
||||||
user.bannerId = new mongo.ObjectID(user.bannerId);
|
user.bannerId = new mongo.ObjectID(user.bannerId);
|
||||||
user.pinnedPostId = new mongo.ObjectID(user.pinnedPostId);
|
user.pinnedNoteId = new mongo.ObjectID(user.pinnedNoteId);
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -186,7 +186,7 @@ export const pack = (
|
||||||
delete _user._id;
|
delete _user._id;
|
||||||
|
|
||||||
// Remove needless properties
|
// Remove needless properties
|
||||||
delete _user.latestPost;
|
delete _user.latestNote;
|
||||||
|
|
||||||
if (!_user.host) {
|
if (!_user.host) {
|
||||||
// Remove private properties
|
// Remove private properties
|
||||||
|
@ -260,9 +260,9 @@ export const pack = (
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opts.detail) {
|
if (opts.detail) {
|
||||||
if (_user.pinnedPostId) {
|
if (_user.pinnedNoteId) {
|
||||||
// Populate pinned post
|
// Populate pinned note
|
||||||
_user.pinnedPost = packPost(_user.pinnedPostId, meId, {
|
_user.pinnedNote = packNote(_user.pinnedNoteId, meId, {
|
||||||
detail: true
|
detail: true
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,7 @@ const id = conf.othello_ai.id;
|
||||||
*/
|
*/
|
||||||
const i = conf.othello_ai.i;
|
const i = conf.othello_ai.i;
|
||||||
|
|
||||||
let post;
|
let note;
|
||||||
|
|
||||||
process.on('message', async msg => {
|
process.on('message', async msg => {
|
||||||
// 親プロセスからデータをもらう
|
// 親プロセスからデータをもらう
|
||||||
|
@ -51,13 +51,13 @@ process.on('message', async msg => {
|
||||||
? `?[${getUserName(user)}](${conf.url}/@${user.username})さんの接待を始めました!`
|
? `?[${getUserName(user)}](${conf.url}/@${user.username})さんの接待を始めました!`
|
||||||
: `対局を?[${getUserName(user)}](${conf.url}/@${user.username})さんと始めました! (強さ${form[0].value})`;
|
: `対局を?[${getUserName(user)}](${conf.url}/@${user.username})さんと始めました! (強さ${form[0].value})`;
|
||||||
|
|
||||||
const res = await request.post(`${conf.api_url}/posts/create`, {
|
const res = await request.post(`${conf.api_url}/notes/create`, {
|
||||||
json: { i,
|
json: { i,
|
||||||
text: `${text}\n→[観戦する](${url})`
|
text: `${text}\n→[観戦する](${url})`
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
post = res.createdPost;
|
note = res.createdNote;
|
||||||
//#endregion
|
//#endregion
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,9 +83,9 @@ process.on('message', async msg => {
|
||||||
? `?[${getUserName(user)}](${conf.url}/@${user.username})さんに勝ちました♪`
|
? `?[${getUserName(user)}](${conf.url}/@${user.username})さんに勝ちました♪`
|
||||||
: `?[${getUserName(user)}](${conf.url}/@${user.username})さんに負けました...`;
|
: `?[${getUserName(user)}](${conf.url}/@${user.username})さんに負けました...`;
|
||||||
|
|
||||||
await request.post(`${conf.api_url}/posts/create`, {
|
await request.post(`${conf.api_url}/notes/create`, {
|
||||||
json: { i,
|
json: { i,
|
||||||
repostId: post.id,
|
renoteId: note.id,
|
||||||
text: text
|
text: text
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -46,28 +46,28 @@ homeStream.on('message', message => {
|
||||||
|
|
||||||
// タイムライン上でなんか言われたまたは返信されたとき
|
// タイムライン上でなんか言われたまたは返信されたとき
|
||||||
if (msg.type == 'mention' || msg.type == 'reply') {
|
if (msg.type == 'mention' || msg.type == 'reply') {
|
||||||
const post = msg.body;
|
const note = msg.body;
|
||||||
|
|
||||||
if (post.userId == id) return;
|
if (note.userId == id) return;
|
||||||
|
|
||||||
// リアクションする
|
// リアクションする
|
||||||
request.post(`${conf.api_url}/posts/reactions/create`, {
|
request.post(`${conf.api_url}/notes/reactions/create`, {
|
||||||
json: { i,
|
json: { i,
|
||||||
postId: post.id,
|
noteId: note.id,
|
||||||
reaction: 'love'
|
reaction: 'love'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (post.text) {
|
if (note.text) {
|
||||||
if (post.text.indexOf('オセロ') > -1) {
|
if (note.text.indexOf('オセロ') > -1) {
|
||||||
request.post(`${conf.api_url}/posts/create`, {
|
request.post(`${conf.api_url}/notes/create`, {
|
||||||
json: { i,
|
json: { i,
|
||||||
replyId: post.id,
|
replyId: note.id,
|
||||||
text: '良いですよ~'
|
text: '良いですよ~'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
invite(post.userId);
|
invite(note.userId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue