From cf33e483f7e6f40e8cbbbc0118a7df70bdaf651f Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 29 Mar 2018 20:32:18 +0900 Subject: 整理した MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/client/docs/api/endpoints/posts/create.yaml | 53 ++++++ src/client/docs/api/endpoints/posts/timeline.yaml | 32 ++++ src/client/docs/api/endpoints/style.styl | 21 +++ src/client/docs/api/endpoints/view.pug | 32 ++++ src/client/docs/api/entities/drive-file.yaml | 73 +++++++++ src/client/docs/api/entities/post.yaml | 168 +++++++++++++++++++ src/client/docs/api/entities/style.styl | 1 + src/client/docs/api/entities/user.yaml | 173 ++++++++++++++++++++ src/client/docs/api/entities/view.pug | 20 +++ src/client/docs/api/gulpfile.ts | 188 ++++++++++++++++++++++ src/client/docs/api/mixins.pug | 37 +++++ src/client/docs/api/style.styl | 11 ++ 12 files changed, 809 insertions(+) create mode 100644 src/client/docs/api/endpoints/posts/create.yaml create mode 100644 src/client/docs/api/endpoints/posts/timeline.yaml create mode 100644 src/client/docs/api/endpoints/style.styl create mode 100644 src/client/docs/api/endpoints/view.pug create mode 100644 src/client/docs/api/entities/drive-file.yaml create mode 100644 src/client/docs/api/entities/post.yaml create mode 100644 src/client/docs/api/entities/style.styl create mode 100644 src/client/docs/api/entities/user.yaml create mode 100644 src/client/docs/api/entities/view.pug create mode 100644 src/client/docs/api/gulpfile.ts create mode 100644 src/client/docs/api/mixins.pug create mode 100644 src/client/docs/api/style.styl (limited to 'src/client/docs/api') diff --git a/src/client/docs/api/endpoints/posts/create.yaml b/src/client/docs/api/endpoints/posts/create.yaml new file mode 100644 index 0000000000..11d9f40c54 --- /dev/null +++ b/src/client/docs/api/endpoints/posts/create.yaml @@ -0,0 +1,53 @@ +endpoint: "posts/create" + +desc: + ja: "投稿します。" + en: "Compose new post." + +params: + - name: "text" + type: "string" + optional: true + desc: + ja: "投稿の本文" + en: "The text of your post" + - name: "mediaIds" + type: "id(DriveFile)[]" + optional: true + desc: + ja: "添付するメディア(1~4つ)" + en: "Media you want to attach (1~4)" + - name: "replyId" + type: "id(Post)" + optional: true + desc: + ja: "返信する投稿" + en: "The post you want to reply" + - name: "repostId" + type: "id(Post)" + optional: true + desc: + ja: "引用する投稿" + en: "The post you want to quote" + - name: "poll" + type: "object" + optional: true + desc: + ja: "投票" + en: "The poll" + defName: "poll" + def: + - name: "choices" + type: "string[]" + optional: false + desc: + ja: "投票の選択肢" + en: "Choices of a poll" + +res: + - name: "createdPost" + type: "entity(Post)" + optional: false + desc: + ja: "作成した投稿" + en: "A post that created" diff --git a/src/client/docs/api/endpoints/posts/timeline.yaml b/src/client/docs/api/endpoints/posts/timeline.yaml new file mode 100644 index 0000000000..9c44dd736a --- /dev/null +++ b/src/client/docs/api/endpoints/posts/timeline.yaml @@ -0,0 +1,32 @@ +endpoint: "posts/timeline" + +desc: + ja: "タイムラインを取得します。" + en: "Get your timeline." + +params: + - name: "limit" + type: "number" + optional: true + desc: + ja: "取得する最大の数" + - name: "sinceId" + type: "id(Post)" + optional: true + desc: + ja: "指定すると、この投稿を基点としてより新しい投稿を取得します" + - name: "untilId" + type: "id(Post)" + optional: true + desc: + ja: "指定すると、この投稿を基点としてより古い投稿を取得します" + - name: "sinceDate" + type: "number" + optional: true + desc: + ja: "指定した時間を基点としてより新しい投稿を取得します。数値は、1970 年 1 月 1 日 00:00:00 UTC から指定した日時までの経過時間をミリ秒単位で表します。" + - name: "untilDate" + type: "number" + optional: true + desc: + ja: "指定した時間を基点としてより古い投稿を取得します。数値は、1970 年 1 月 1 日 00:00:00 UTC から指定した日時までの経過時間をミリ秒単位で表します。" diff --git a/src/client/docs/api/endpoints/style.styl b/src/client/docs/api/endpoints/style.styl new file mode 100644 index 0000000000..2af9fe9a77 --- /dev/null +++ b/src/client/docs/api/endpoints/style.styl @@ -0,0 +1,21 @@ +@import "../style" + +#url + padding 8px 12px 8px 8px + font-family Consolas, 'Courier New', Courier, Monaco, monospace + color #fff + background #222e40 + border-radius 4px + + > .method + display inline-block + margin 0 8px 0 0 + padding 0 6px + color #f4fcff + background #17afc7 + border-radius 4px + user-select none + pointer-events none + + > .host + opacity 0.7 diff --git a/src/client/docs/api/endpoints/view.pug b/src/client/docs/api/endpoints/view.pug new file mode 100644 index 0000000000..d271a5517a --- /dev/null +++ b/src/client/docs/api/endpoints/view.pug @@ -0,0 +1,32 @@ +extends ../../layout.pug +include ../mixins + +block meta + link(rel="stylesheet" href="/assets/api/endpoints/style.css") + +block main + h1= endpoint + + p#url + span.method POST + span.host + = url.host + | / + span.path= url.path + + p#desc= desc[lang] || desc['ja'] + + section + h2 %i18n:docs.api.endpoints.params% + +propTable(params) + + if paramDefs + each paramDef in paramDefs + section(id= paramDef.name) + h3= paramDef.name + +propTable(paramDef.params) + + if res + section + h2 %i18n:docs.api.endpoints.res% + +propTable(res) diff --git a/src/client/docs/api/entities/drive-file.yaml b/src/client/docs/api/entities/drive-file.yaml new file mode 100644 index 0000000000..02ab0d608e --- /dev/null +++ b/src/client/docs/api/entities/drive-file.yaml @@ -0,0 +1,73 @@ +name: "DriveFile" + +desc: + ja: "ドライブのファイル。" + en: "A file of Drive." + +props: + - name: "id" + type: "id" + optional: false + desc: + ja: "ファイルID" + en: "The ID of this file" + - name: "createdAt" + type: "date" + optional: false + desc: + ja: "アップロード日時" + en: "The upload date of this file" + - name: "userId" + type: "id(User)" + optional: false + desc: + ja: "所有者ID" + en: "The ID of the owner of this file" + - name: "user" + type: "entity(User)" + optional: true + desc: + ja: "所有者" + en: "The owner of this file" + - name: "name" + type: "string" + optional: false + desc: + ja: "ファイル名" + en: "The name of this file" + - name: "md5" + type: "string" + optional: false + desc: + ja: "ファイルのMD5ハッシュ値" + en: "The md5 hash value of this file" + - name: "type" + type: "string" + optional: false + desc: + ja: "ファイルの種類" + en: "The type of this file" + - name: "datasize" + type: "number" + optional: false + desc: + ja: "ファイルサイズ(bytes)" + en: "The size of this file (bytes)" + - name: "url" + type: "string" + optional: false + desc: + ja: "ファイルのURL" + en: "The URL of this file" + - name: "folderId" + type: "id(DriveFolder)" + optional: true + desc: + ja: "フォルダID" + en: "The ID of the folder of this file" + - name: "folder" + type: "entity(DriveFolder)" + optional: true + desc: + ja: "フォルダ" + en: "The folder of this file" diff --git a/src/client/docs/api/entities/post.yaml b/src/client/docs/api/entities/post.yaml new file mode 100644 index 0000000000..74d7973e38 --- /dev/null +++ b/src/client/docs/api/entities/post.yaml @@ -0,0 +1,168 @@ +name: "Post" + +desc: + ja: "投稿。" + en: "A post." + +props: + - name: "id" + type: "id" + optional: false + desc: + ja: "投稿ID" + en: "The ID of this post" + - name: "createdAt" + type: "date" + optional: false + desc: + ja: "投稿日時" + en: "The posted date of this post" + - name: "viaMobile" + type: "boolean" + optional: true + desc: + ja: "モバイル端末から投稿したか否か(自己申告であることに留意)" + en: "Whether this post sent via a mobile device" + - name: "text" + type: "string" + optional: true + desc: + ja: "投稿の本文" + en: "The text of this post" + - name: "mediaIds" + type: "id(DriveFile)[]" + optional: true + desc: + ja: "添付されているメディアのID" + en: "The IDs of the attached media" + - 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 post" + - name: "user" + type: "entity(User)" + optional: true + desc: + ja: "投稿者" + en: "The author of this post" + - name: "myReaction" + type: "string" + optional: true + desc: + ja: "この投稿に対する自分のリアクション" + en: "The your reaction of this post" + - name: "reactionCounts" + type: "object" + optional: false + desc: + ja: "リアクションをキーとし、この投稿に対するそのリアクションの数を値としたオブジェクト" + - name: "replyId" + type: "id(Post)" + optional: true + desc: + ja: "返信した投稿のID" + en: "The ID of the replyed post" + - name: "reply" + type: "entity(Post)" + optional: true + desc: + ja: "返信した投稿" + en: "The replyed post" + - name: "repostId" + type: "id(Post)" + optional: true + desc: + ja: "引用した投稿のID" + en: "The ID of the quoted post" + - name: "repost" + type: "entity(Post)" + optional: true + desc: + ja: "引用した投稿" + en: "The quoted post" + - 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: "速度。メートル / 秒数で表す。" diff --git a/src/client/docs/api/entities/style.styl b/src/client/docs/api/entities/style.styl new file mode 100644 index 0000000000..bddf0f53ab --- /dev/null +++ b/src/client/docs/api/entities/style.styl @@ -0,0 +1 @@ +@import "../style" diff --git a/src/client/docs/api/entities/user.yaml b/src/client/docs/api/entities/user.yaml new file mode 100644 index 0000000000..a1fae1482b --- /dev/null +++ b/src/client/docs/api/entities/user.yaml @@ -0,0 +1,173 @@ +name: "User" + +desc: + ja: "ユーザー。" + en: "A user." + +props: + - name: "id" + type: "id" + optional: false + desc: + ja: "ユーザーID" + en: "The ID of this user" + - name: "createdAt" + type: "date" + optional: false + desc: + ja: "アカウント作成日時" + en: "The registered date of this user" + - name: "username" + type: "string" + optional: false + desc: + ja: "ユーザー名" + en: "The username of this user" + - name: "description" + type: "string" + optional: false + desc: + ja: "アカウントの説明(自己紹介)" + en: "The description of this user" + - name: "avatarId" + type: "id(DriveFile)" + optional: true + desc: + ja: "アバターのID" + en: "The ID of the avatar of this user" + - name: "avatarUrl" + type: "string" + optional: false + desc: + ja: "アバターのURL" + en: "The URL of the avatar of this user" + - name: "bannerId" + type: "id(DriveFile)" + optional: true + desc: + ja: "バナーのID" + en: "The ID of the banner of this user" + - name: "bannerUrl" + type: "string" + optional: false + desc: + ja: "バナーのURL" + en: "The URL of the banner of this user" + - name: "followersCount" + type: "number" + optional: false + desc: + ja: "フォロワーの数" + en: "The number of the followers for this user" + - name: "followingCount" + type: "number" + optional: false + desc: + ja: "フォローしているユーザーの数" + en: "The number of the following users for this user" + - name: "isFollowing" + type: "boolean" + optional: true + desc: + ja: "自分がこのユーザーをフォローしているか" + - name: "isFollowed" + type: "boolean" + optional: true + desc: + ja: "自分がこのユーザーにフォローされているか" + - name: "isMuted" + type: "boolean" + optional: true + desc: + ja: "自分がこのユーザーをミュートしているか" + en: "Whether you muted this user" + - name: "postsCount" + type: "number" + optional: false + desc: + ja: "投稿の数" + en: "The number of the posts of this user" + - name: "pinnedPost" + type: "entity(Post)" + optional: true + desc: + ja: "ピン留めされた投稿" + en: "The pinned post of this user" + - name: "pinnedPostId" + type: "id(Post)" + optional: true + desc: + ja: "ピン留めされた投稿のID" + en: "The ID of the pinned post of this user" + - name: "driveCapacity" + type: "number" + optional: false + desc: + ja: "ドライブの容量(bytes)" + en: "The capacity of drive of this user (bytes)" + - name: "host" + type: "string | null" + optional: false + desc: + ja: "ホスト (例: example.com:3000)" + en: "Host (e.g. example.com:3000)" + - name: "account" + type: "object" + optional: false + desc: + ja: "このサーバーにおけるアカウント" + en: "The account of this user on this server" + defName: "account" + def: + - name: "lastUsedAt" + type: "date" + optional: false + desc: + ja: "最終利用日時" + en: "The last used date of this user" + - name: "isBot" + type: "boolean" + optional: true + desc: + ja: "botか否か(自己申告であることに留意)" + en: "Whether is bot or not" + - name: "twitter" + type: "object" + optional: true + desc: + ja: "連携されているTwitterアカウント情報" + en: "The info of the connected twitter account of this user" + defName: "twitter" + def: + - name: "userId" + type: "string" + optional: false + desc: + ja: "ユーザーID" + en: "The user ID" + - name: "screenName" + type: "string" + optional: false + desc: + ja: "ユーザー名" + en: "The screen name of this user" + - name: "profile" + type: "object" + optional: false + desc: + ja: "プロフィール" + en: "The profile of this user" + defName: "profile" + def: + - name: "location" + type: "string" + optional: true + desc: + ja: "場所" + en: "The location of this user" + - name: "birthday" + type: "string" + optional: true + desc: + ja: "誕生日 (YYYY-MM-DD)" + en: "The birthday of this user (YYYY-MM-DD)" diff --git a/src/client/docs/api/entities/view.pug b/src/client/docs/api/entities/view.pug new file mode 100644 index 0000000000..2156463dc7 --- /dev/null +++ b/src/client/docs/api/entities/view.pug @@ -0,0 +1,20 @@ +extends ../../layout.pug +include ../mixins + +block meta + link(rel="stylesheet" href="/assets/api/entities/style.css") + +block main + h1= name + + p#desc= desc[lang] || desc['ja'] + + section + h2 %i18n:docs.api.entities.properties% + +propTable(props) + + if propDefs + each propDef in propDefs + section(id= propDef.name) + h3= propDef.name + +propTable(propDef.params) diff --git a/src/client/docs/api/gulpfile.ts b/src/client/docs/api/gulpfile.ts new file mode 100644 index 0000000000..16066b0d2e --- /dev/null +++ b/src/client/docs/api/gulpfile.ts @@ -0,0 +1,188 @@ +/** + * Gulp tasks + */ + +import * as fs from 'fs'; +import * as path from 'path'; +import * as glob from 'glob'; +import * as gulp from 'gulp'; +import * as pug from 'pug'; +import * as yaml from 'js-yaml'; +import * as mkdirp from 'mkdirp'; + +import locales from '../../../../locales'; +import I18nReplacer from '../../../build/i18n'; +import fa from '../../../build/fa'; +import config from './../../../conf'; + +import generateVars from '../vars'; + +const langs = Object.keys(locales); + +const kebab = string => string.replace(/([a-z])([A-Z])/g, '$1-$2').replace(/\s+/g, '-').toLowerCase(); + +const parseParam = param => { + const id = param.type.match(/^id\((.+?)\)|^id/); + const entity = param.type.match(/^entity\((.+?)\)/); + const isObject = /^object/.test(param.type); + const isDate = /^date/.test(param.type); + const isArray = /\[\]$/.test(param.type); + if (id) { + param.kind = 'id'; + param.type = 'string'; + param.entity = id[1]; + if (isArray) { + param.type += '[]'; + } + } + if (entity) { + param.kind = 'entity'; + param.type = 'object'; + param.entity = entity[1]; + if (isArray) { + param.type += '[]'; + } + } + if (isObject) { + param.kind = 'object'; + } + if (isDate) { + param.kind = 'date'; + param.type = 'string'; + if (isArray) { + param.type += '[]'; + } + } + + return param; +}; + +const sortParams = params => { + params.sort((a, b) => { + if (a.name < b.name) + return -1; + if (a.name > b.name) + return 1; + return 0; + }); + return params; +}; + +const extractDefs = params => { + let defs = []; + + params.forEach(param => { + if (param.def) { + defs.push({ + name: param.defName, + params: sortParams(param.def.map(p => parseParam(p))) + }); + + const childDefs = extractDefs(param.def); + + defs = defs.concat(childDefs); + } + }); + + return sortParams(defs); +}; + +gulp.task('doc:api', [ + 'doc:api:endpoints', + 'doc:api:entities' +]); + +gulp.task('doc:api:endpoints', async () => { + const commonVars = await generateVars(); + glob('./src/client/docs/api/endpoints/**/*.yaml', (globErr, files) => { + if (globErr) { + console.error(globErr); + return; + } + //console.log(files); + files.forEach(file => { + const ep = yaml.safeLoad(fs.readFileSync(file, 'utf-8')); + const vars = { + endpoint: ep.endpoint, + url: { + host: config.api_url, + path: ep.endpoint + }, + desc: ep.desc, + params: sortParams(ep.params.map(p => parseParam(p))), + paramDefs: extractDefs(ep.params), + res: ep.res ? sortParams(ep.res.map(p => parseParam(p))) : null, + resDefs: ep.res ? extractDefs(ep.res) : null, + }; + langs.forEach(lang => { + pug.renderFile('./src/client/docs/api/endpoints/view.pug', Object.assign({}, vars, { + lang, + title: ep.endpoint, + src: `https://github.com/syuilo/misskey/tree/master/src/client/docs/api/endpoints/${ep.endpoint}.yaml`, + kebab, + common: commonVars + }), (renderErr, html) => { + if (renderErr) { + console.error(renderErr); + return; + } + const i18n = new I18nReplacer(lang); + html = html.replace(i18n.pattern, i18n.replacement); + html = fa(html); + const htmlPath = `./built/client/docs/${lang}/api/endpoints/${ep.endpoint}.html`; + mkdirp(path.dirname(htmlPath), (mkdirErr) => { + if (mkdirErr) { + console.error(mkdirErr); + return; + } + fs.writeFileSync(htmlPath, html, 'utf-8'); + }); + }); + }); + }); + }); +}); + +gulp.task('doc:api:entities', async () => { + const commonVars = await generateVars(); + glob('./src/client/docs/api/entities/**/*.yaml', (globErr, files) => { + if (globErr) { + console.error(globErr); + return; + } + files.forEach(file => { + const entity = yaml.safeLoad(fs.readFileSync(file, 'utf-8')); + const vars = { + name: entity.name, + desc: entity.desc, + props: sortParams(entity.props.map(p => parseParam(p))), + propDefs: extractDefs(entity.props), + }; + langs.forEach(lang => { + pug.renderFile('./src/client/docs/api/entities/view.pug', Object.assign({}, vars, { + lang, + title: entity.name, + src: `https://github.com/syuilo/misskey/tree/master/src/client/docs/api/entities/${kebab(entity.name)}.yaml`, + kebab, + common: commonVars + }), (renderErr, html) => { + if (renderErr) { + console.error(renderErr); + return; + } + const i18n = new I18nReplacer(lang); + html = html.replace(i18n.pattern, i18n.replacement); + html = fa(html); + const htmlPath = `./built/client/docs/${lang}/api/entities/${kebab(entity.name)}.html`; + mkdirp(path.dirname(htmlPath), (mkdirErr) => { + if (mkdirErr) { + console.error(mkdirErr); + return; + } + fs.writeFileSync(htmlPath, html, 'utf-8'); + }); + }); + }); + }); + }); +}); diff --git a/src/client/docs/api/mixins.pug b/src/client/docs/api/mixins.pug new file mode 100644 index 0000000000..686bf6a2b6 --- /dev/null +++ b/src/client/docs/api/mixins.pug @@ -0,0 +1,37 @@ +mixin propTable(props) + table.props + thead: tr + th %i18n:docs.api.props.name% + th %i18n:docs.api.props.type% + th %i18n:docs.api.props.optional% + th %i18n:docs.api.props.description% + tbody + each prop in props + tr + td.name= prop.name + td.type + i= prop.type + if prop.kind == 'id' + if prop.entity + | ( + a(href=`/${lang}/api/entities/${kebab(prop.entity)}`)= prop.entity + | ID) + else + | (ID) + else if prop.kind == 'entity' + | ( + a(href=`/${lang}/api/entities/${kebab(prop.entity)}`)= prop.entity + | ) + else if prop.kind == 'object' + if prop.def + | ( + a(href=`#${prop.defName}`)= prop.defName + | ) + else if prop.kind == 'date' + | (Date) + td.optional + if prop.optional + | %i18n:docs.api.props.yes% + else + | %i18n:docs.api.props.no% + td.desc!= prop.desc[lang] || prop.desc['ja'] diff --git a/src/client/docs/api/style.styl b/src/client/docs/api/style.styl new file mode 100644 index 0000000000..3675a4da6f --- /dev/null +++ b/src/client/docs/api/style.styl @@ -0,0 +1,11 @@ +@import "../style" + +table.props + .name + font-weight bold + + .name + .type + .optional + font-family Consolas, 'Courier New', Courier, Monaco, monospace + -- cgit v1.2.3-freya From 75c4c844e6d679cd94da59525801fdd879e780c1 Mon Sep 17 00:00:00 2001 From: syuilo Date: Fri, 30 Mar 2018 11:24:07 +0900 Subject: cw --- src/client/docs/api/endpoints/posts/create.yaml | 6 ++++++ src/models/post.ts | 5 +++++ src/server/api/endpoints/posts/create.ts | 7 ++++++- 3 files changed, 17 insertions(+), 1 deletion(-) (limited to 'src/client/docs/api') diff --git a/src/client/docs/api/endpoints/posts/create.yaml b/src/client/docs/api/endpoints/posts/create.yaml index 11d9f40c54..d2d6e27fc7 100644 --- a/src/client/docs/api/endpoints/posts/create.yaml +++ b/src/client/docs/api/endpoints/posts/create.yaml @@ -11,6 +11,12 @@ params: desc: ja: "投稿の本文" en: "The text of your post" + - name: "cw" + type: "string" + optional: true + desc: + ja: "コンテンツの警告。このパラメータを指定すると設定したテキストで投稿のコンテンツを隠す事が出来ます。" + en: "Content Warning" - name: "mediaIds" type: "id(DriveFile)[]" optional: true diff --git a/src/models/post.ts b/src/models/post.ts index 833e599320..9bc0c1d3b9 100644 --- a/src/models/post.ts +++ b/src/models/post.ts @@ -18,6 +18,10 @@ export function isValidText(text: string): boolean { return text.length <= 1000 && text.trim() != ''; } +export function isValidCw(text: string): boolean { + return text.length <= 100 && text.trim() != ''; +} + export type IPost = { _id: mongo.ObjectID; channelId: mongo.ObjectID; @@ -27,6 +31,7 @@ export type IPost = { repostId: mongo.ObjectID; poll: any; // todo text: string; + cw: string; userId: mongo.ObjectID; appId: mongo.ObjectID; viaMobile: boolean; diff --git a/src/server/api/endpoints/posts/create.ts b/src/server/api/endpoints/posts/create.ts index 6b2957ae67..170b667191 100644 --- a/src/server/api/endpoints/posts/create.ts +++ b/src/server/api/endpoints/posts/create.ts @@ -4,7 +4,7 @@ import $ from 'cafy'; import deepEqual = require('deep-equal'); import parse from '../../../../common/text'; -import { default as Post, IPost, isValidText } from '../../../../models/post'; +import { default as Post, IPost, isValidText, isValidCw } from '../../../../models/post'; import { default as User, ILocalAccount, IUser } from '../../../../models/user'; import { default as Channel, IChannel } from '../../../../models/channel'; import Following from '../../../../models/following'; @@ -33,6 +33,10 @@ module.exports = (params, user: IUser, app) => new Promise(async (res, rej) => { const [text, textErr] = $(params.text).optional.string().pipe(isValidText).$; if (textErr) return rej('invalid text'); + // Get 'cw' parameter + const [cw, cwErr] = $(params.cw).optional.string().pipe(isValidCw).$; + if (cwErr) return rej('invalid cw'); + // Get 'viaMobile' parameter const [viaMobile = false, viaMobileErr] = $(params.viaMobile).optional.boolean().$; if (viaMobileErr) return rej('invalid viaMobile'); @@ -255,6 +259,7 @@ module.exports = (params, user: IUser, app) => new Promise(async (res, rej) => { repostId: repost ? repost._id : undefined, poll: poll, text: text, + cw: cw, tags: tags, userId: user._id, appId: app ? app._id : null, -- cgit v1.2.3-freya From 8c4143290735b39bc68bf98439b2735af1910518 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Sat, 31 Mar 2018 19:45:56 +0900 Subject: Set empty array instead of null to mediaIds property of posts --- src/client/app/desktop/views/components/post-detail.sub.vue | 2 +- src/client/app/desktop/views/components/post-detail.vue | 2 +- src/client/app/desktop/views/components/posts.post.vue | 2 +- src/client/app/desktop/views/components/sub-post-content.vue | 2 +- src/client/app/mobile/views/components/post-detail.vue | 2 +- src/client/app/mobile/views/components/post.vue | 2 +- src/client/app/mobile/views/components/sub-post-content.vue | 2 +- src/client/docs/api/entities/post.yaml | 4 ++-- src/server/api/endpoints/posts/create.ts | 2 +- tools/migration/nighthike/6.js | 1 + 10 files changed, 11 insertions(+), 10 deletions(-) create mode 100644 tools/migration/nighthike/6.js (limited to 'src/client/docs/api') diff --git a/src/client/app/desktop/views/components/post-detail.sub.vue b/src/client/app/desktop/views/components/post-detail.sub.vue index 35377e7c24..285b5dedee 100644 --- a/src/client/app/desktop/views/components/post-detail.sub.vue +++ b/src/client/app/desktop/views/components/post-detail.sub.vue @@ -17,7 +17,7 @@
-
+
diff --git a/src/client/app/desktop/views/components/post-detail.vue b/src/client/app/desktop/views/components/post-detail.vue index 5c7a7dfdbe..1811e22bad 100644 --- a/src/client/app/desktop/views/components/post-detail.vue +++ b/src/client/app/desktop/views/components/post-detail.vue @@ -39,7 +39,7 @@
-
+
diff --git a/src/client/app/desktop/views/components/posts.post.vue b/src/client/app/desktop/views/components/posts.post.vue index 37c6e63043..aa1f1db41c 100644 --- a/src/client/app/desktop/views/components/posts.post.vue +++ b/src/client/app/desktop/views/components/posts.post.vue @@ -41,7 +41,7 @@ RP:
-
+
diff --git a/src/client/app/desktop/views/components/sub-post-content.vue b/src/client/app/desktop/views/components/sub-post-content.vue index a79e5e0a4e..1f5ce38984 100644 --- a/src/client/app/desktop/views/components/sub-post-content.vue +++ b/src/client/app/desktop/views/components/sub-post-content.vue @@ -5,7 +5,7 @@ RP: ...
-
+
({{ post.media.length }}つのメディア)
diff --git a/src/client/app/mobile/views/components/post-detail.vue b/src/client/app/mobile/views/components/post-detail.vue index f0af1a61aa..6411011b89 100644 --- a/src/client/app/mobile/views/components/post-detail.vue +++ b/src/client/app/mobile/views/components/post-detail.vue @@ -42,7 +42,7 @@
{{ tag }}
-
+
diff --git a/src/client/app/mobile/views/components/post.vue b/src/client/app/mobile/views/components/post.vue index a01eb7669e..52fb095372 100644 --- a/src/client/app/mobile/views/components/post.vue +++ b/src/client/app/mobile/views/components/post.vue @@ -40,7 +40,7 @@ RP:
-
+
diff --git a/src/client/app/mobile/views/components/sub-post-content.vue b/src/client/app/mobile/views/components/sub-post-content.vue index b95883de77..5ff88089aa 100644 --- a/src/client/app/mobile/views/components/sub-post-content.vue +++ b/src/client/app/mobile/views/components/sub-post-content.vue @@ -5,7 +5,7 @@ RP: ...
-
+
({{ post.media.length }}個のメディア)
diff --git a/src/client/docs/api/entities/post.yaml b/src/client/docs/api/entities/post.yaml index 74d7973e38..da79866ba1 100644 --- a/src/client/docs/api/entities/post.yaml +++ b/src/client/docs/api/entities/post.yaml @@ -33,8 +33,8 @@ props: type: "id(DriveFile)[]" optional: true desc: - ja: "添付されているメディアのID" - en: "The IDs of the attached media" + 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 diff --git a/src/server/api/endpoints/posts/create.ts b/src/server/api/endpoints/posts/create.ts index 170b667191..aa7e93c28f 100644 --- a/src/server/api/endpoints/posts/create.ts +++ b/src/server/api/endpoints/posts/create.ts @@ -254,7 +254,7 @@ module.exports = (params, user: IUser, app) => new Promise(async (res, rej) => { createdAt: new Date(), channelId: channel ? channel._id : undefined, index: channel ? channel.index + 1 : undefined, - mediaIds: files ? files.map(file => file._id) : undefined, + mediaIds: files ? files.map(file => file._id) : [], replyId: reply ? reply._id : undefined, repostId: repost ? repost._id : undefined, poll: poll, diff --git a/tools/migration/nighthike/6.js b/tools/migration/nighthike/6.js new file mode 100644 index 0000000000..ff78df4e09 --- /dev/null +++ b/tools/migration/nighthike/6.js @@ -0,0 +1 @@ +db.posts.update({ mediaIds: null }, { $set: { mediaIds: [] } }, false, true); -- cgit v1.2.3-freya From 7da60a0147116130a94274e4a20ae54dd7d59dea Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Sat, 31 Mar 2018 19:53:30 +0900 Subject: Store texts as HTML --- src/client/app/common/views/components/index.ts | 2 +- .../views/components/messaging-room.message.vue | 33 +- .../app/common/views/components/post-html.ts | 141 --------- .../app/common/views/components/post-html.vue | 103 +++++++ src/client/app/common/views/components/url.vue | 66 ---- .../common/views/components/welcome-timeline.vue | 2 +- .../desktop/views/components/post-detail.sub.vue | 2 +- .../app/desktop/views/components/post-detail.vue | 27 +- .../app/desktop/views/components/posts.post.vue | 31 +- .../desktop/views/components/sub-post-content.vue | 2 +- .../app/mobile/views/components/post-detail.vue | 27 +- src/client/app/mobile/views/components/post.vue | 31 +- .../mobile/views/components/sub-post-content.vue | 2 +- src/client/docs/api/entities/post.yaml | 10 +- src/common/text/core/syntax-highlighter.ts | 334 --------------------- src/common/text/elements/bold.ts | 14 - src/common/text/elements/code.ts | 17 -- src/common/text/elements/emoji.ts | 14 - src/common/text/elements/hashtag.ts | 19 -- src/common/text/elements/inline-code.ts | 17 -- src/common/text/elements/link.ts | 19 -- src/common/text/elements/mention.ts | 17 -- src/common/text/elements/quote.ts | 14 - src/common/text/elements/url.ts | 14 - src/common/text/html.ts | 83 +++++ src/common/text/index.ts | 72 ----- src/common/text/parse/core/syntax-highlighter.ts | 334 +++++++++++++++++++++ src/common/text/parse/elements/bold.ts | 14 + src/common/text/parse/elements/code.ts | 17 ++ src/common/text/parse/elements/emoji.ts | 14 + src/common/text/parse/elements/hashtag.ts | 19 ++ src/common/text/parse/elements/inline-code.ts | 17 ++ src/common/text/parse/elements/link.ts | 19 ++ src/common/text/parse/elements/mention.ts | 17 ++ src/common/text/parse/elements/quote.ts | 14 + src/common/text/parse/elements/url.ts | 14 + src/common/text/parse/index.ts | 72 +++++ src/models/messaging-message.ts | 7 +- src/models/post.ts | 7 +- .../api/endpoints/messaging/messages/create.ts | 3 + src/server/api/endpoints/posts/create.ts | 4 +- tools/migration/nighthike/7.js | 16 + 42 files changed, 868 insertions(+), 833 deletions(-) delete mode 100644 src/client/app/common/views/components/post-html.ts create mode 100644 src/client/app/common/views/components/post-html.vue delete mode 100644 src/client/app/common/views/components/url.vue delete mode 100644 src/common/text/core/syntax-highlighter.ts delete mode 100644 src/common/text/elements/bold.ts delete mode 100644 src/common/text/elements/code.ts delete mode 100644 src/common/text/elements/emoji.ts delete mode 100644 src/common/text/elements/hashtag.ts delete mode 100644 src/common/text/elements/inline-code.ts delete mode 100644 src/common/text/elements/link.ts delete mode 100644 src/common/text/elements/mention.ts delete mode 100644 src/common/text/elements/quote.ts delete mode 100644 src/common/text/elements/url.ts create mode 100644 src/common/text/html.ts delete mode 100644 src/common/text/index.ts create mode 100644 src/common/text/parse/core/syntax-highlighter.ts create mode 100644 src/common/text/parse/elements/bold.ts create mode 100644 src/common/text/parse/elements/code.ts create mode 100644 src/common/text/parse/elements/emoji.ts create mode 100644 src/common/text/parse/elements/hashtag.ts create mode 100644 src/common/text/parse/elements/inline-code.ts create mode 100644 src/common/text/parse/elements/link.ts create mode 100644 src/common/text/parse/elements/mention.ts create mode 100644 src/common/text/parse/elements/quote.ts create mode 100644 src/common/text/parse/elements/url.ts create mode 100644 src/common/text/parse/index.ts create mode 100644 tools/migration/nighthike/7.js (limited to 'src/client/docs/api') diff --git a/src/client/app/common/views/components/index.ts b/src/client/app/common/views/components/index.ts index b58ba37ecb..8c10bdee28 100644 --- a/src/client/app/common/views/components/index.ts +++ b/src/client/app/common/views/components/index.ts @@ -4,7 +4,7 @@ import signin from './signin.vue'; import signup from './signup.vue'; import forkit from './forkit.vue'; import nav from './nav.vue'; -import postHtml from './post-html'; +import postHtml from './post-html.vue'; import poll from './poll.vue'; import pollEditor from './poll-editor.vue'; import reactionIcon from './reaction-icon.vue'; diff --git a/src/client/app/common/views/components/messaging-room.message.vue b/src/client/app/common/views/components/messaging-room.message.vue index 94f87fd709..25ceab85a1 100644 --- a/src/client/app/common/views/components/messaging-room.message.vue +++ b/src/client/app/common/views/components/messaging-room.message.vue @@ -4,13 +4,13 @@
-
+

%i18n:common.tags.mk-messaging-message.is-read%

- +
@@ -38,21 +38,32 @@ import getAcct from '../../../../../common/user/get-acct'; export default Vue.extend({ props: ['message'], + data() { + return { + urls: [] + }; + }, computed: { acct() { return getAcct(this.message.user); }, isMe(): boolean { return this.message.userId == (this as any).os.i.id; - }, - urls(): string[] { - if (this.message.ast) { - return this.message.ast - .filter(t => (t.type == 'url' || t.type == 'link') && !t.silent) - .map(t => t.url); - } else { - return null; - } + } + }, + watch: { + message: { + handler(newMessage, oldMessage) { + if (!oldMessage || newMessage.textHtml !== oldMessage.textHtml) { + this.$nextTick(() => { + const elements = this.$refs.text.$el.getElementsByTagName('a'); + + this.urls = [].filter.call(elements, ({ origin }) => origin !== location.origin) + .map(({ href }) => href); + }); + } + }, + immediate: true } } }); diff --git a/src/client/app/common/views/components/post-html.ts b/src/client/app/common/views/components/post-html.ts deleted file mode 100644 index 39d783aac5..0000000000 --- a/src/client/app/common/views/components/post-html.ts +++ /dev/null @@ -1,141 +0,0 @@ -import Vue from 'vue'; -import * as emojilib from 'emojilib'; -import getAcct from '../../../../../common/user/get-acct'; -import { url } from '../../../config'; -import MkUrl from './url.vue'; - -const flatten = list => list.reduce( - (a, b) => a.concat(Array.isArray(b) ? flatten(b) : b), [] -); - -export default Vue.component('mk-post-html', { - props: { - ast: { - type: Array, - required: true - }, - shouldBreak: { - type: Boolean, - default: true - }, - i: { - type: Object, - default: null - } - }, - render(createElement) { - const els = flatten((this as any).ast.map(token => { - switch (token.type) { - case 'text': - const text = token.content.replace(/(\r\n|\n|\r)/g, '\n'); - - if ((this as any).shouldBreak) { - const x = text.split('\n') - .map(t => t == '' ? [createElement('br')] : [createElement('span', t), createElement('br')]); - x[x.length - 1].pop(); - return x; - } else { - return createElement('span', text.replace(/\n/g, ' ')); - } - - case 'bold': - return createElement('strong', token.bold); - - case 'url': - return createElement(MkUrl, { - props: { - url: token.content, - target: '_blank' - } - }); - - case 'link': - return createElement('a', { - attrs: { - class: 'link', - href: token.url, - target: '_blank', - title: token.url - } - }, token.title); - - case 'mention': - return (createElement as any)('a', { - attrs: { - href: `${url}/@${getAcct(token)}`, - target: '_blank', - dataIsMe: (this as any).i && getAcct((this as any).i) == getAcct(token) - }, - directives: [{ - name: 'user-preview', - value: token.content - }] - }, token.content); - - case 'hashtag': - return createElement('a', { - attrs: { - href: `${url}/search?q=${token.content}`, - target: '_blank' - } - }, token.content); - - case 'code': - return createElement('pre', [ - createElement('code', { - domProps: { - innerHTML: token.html - } - }) - ]); - - case 'inline-code': - return createElement('code', { - domProps: { - innerHTML: token.html - } - }); - - case 'quote': - const text2 = token.quote.replace(/(\r\n|\n|\r)/g, '\n'); - - if ((this as any).shouldBreak) { - const x = text2.split('\n') - .map(t => [createElement('span', t), createElement('br')]); - x[x.length - 1].pop(); - return createElement('div', { - attrs: { - class: 'quote' - } - }, x); - } else { - return createElement('span', { - attrs: { - class: 'quote' - } - }, text2.replace(/\n/g, ' ')); - } - - case 'emoji': - const emoji = emojilib.lib[token.emoji]; - return createElement('span', emoji ? emoji.char : token.content); - - default: - console.log('unknown ast type:', token.type); - } - })); - - const _els = []; - els.forEach((el, i) => { - if (el.tag == 'br') { - if (els[i - 1].tag != 'div') { - _els.push(el); - } - } else { - _els.push(el); - } - }); - - return createElement('span', _els); - } -}); diff --git a/src/client/app/common/views/components/post-html.vue b/src/client/app/common/views/components/post-html.vue new file mode 100644 index 0000000000..1c949052b9 --- /dev/null +++ b/src/client/app/common/views/components/post-html.vue @@ -0,0 +1,103 @@ + + + + + diff --git a/src/client/app/common/views/components/url.vue b/src/client/app/common/views/components/url.vue deleted file mode 100644 index 14d4fc82f3..0000000000 --- a/src/client/app/common/views/components/url.vue +++ /dev/null @@ -1,66 +0,0 @@ - - - - - diff --git a/src/client/app/common/views/components/welcome-timeline.vue b/src/client/app/common/views/components/welcome-timeline.vue index 8f6199732a..f379029f9f 100644 --- a/src/client/app/common/views/components/welcome-timeline.vue +++ b/src/client/app/common/views/components/welcome-timeline.vue @@ -15,7 +15,7 @@
- +
diff --git a/src/client/app/desktop/views/components/post-detail.sub.vue b/src/client/app/desktop/views/components/post-detail.sub.vue index 285b5dedee..b6148d9b28 100644 --- a/src/client/app/desktop/views/components/post-detail.sub.vue +++ b/src/client/app/desktop/views/components/post-detail.sub.vue @@ -16,7 +16,7 @@
- +
diff --git a/src/client/app/desktop/views/components/post-detail.vue b/src/client/app/desktop/views/components/post-detail.vue index 1811e22bad..e75ebe34b4 100644 --- a/src/client/app/desktop/views/components/post-detail.vue +++ b/src/client/app/desktop/views/components/post-detail.vue @@ -38,7 +38,7 @@
- +
@@ -109,6 +109,7 @@ export default Vue.extend({ context: [], contextFetching: false, replies: [], + urls: [] }; }, computed: { @@ -130,15 +131,6 @@ export default Vue.extend({ }, title(): string { return dateStringify(this.p.createdAt); - }, - urls(): string[] { - if (this.p.ast) { - return this.p.ast - .filter(t => (t.type == 'url' || t.type == 'link') && !t.silent) - .map(t => t.url); - } else { - return null; - } } }, mounted() { @@ -170,6 +162,21 @@ export default Vue.extend({ } } }, + watch: { + post: { + handler(newPost, oldPost) { + if (!oldPost || newPost.text !== oldPost.text) { + this.$nextTick(() => { + const elements = this.$refs.text.$el.getElementsByTagName('a'); + + this.urls = [].filter.call(elements, ({ origin }) => origin !== location.origin) + .map(({ href }) => href); + }); + } + }, + immediate: true + } + }, methods: { fetchContext() { this.contextFetching = true; diff --git a/src/client/app/desktop/views/components/posts.post.vue b/src/client/app/desktop/views/components/posts.post.vue index aa1f1db41c..f3566c81bf 100644 --- a/src/client/app/desktop/views/components/posts.post.vue +++ b/src/client/app/desktop/views/components/posts.post.vue @@ -38,7 +38,7 @@

@@ -112,7 +112,8 @@ export default Vue.extend({ return { isDetailOpened: false, connection: null, - connectionId: null + connectionId: null, + urls: [] }; }, computed: { @@ -140,15 +141,6 @@ export default Vue.extend({ }, url(): string { return `/@${this.acct}/${this.p.id}`; - }, - urls(): string[] { - if (this.p.ast) { - return this.p.ast - .filter(t => (t.type == 'url' || t.type == 'link') && !t.silent) - .map(t => t.url); - } else { - return null; - } } }, created() { @@ -190,6 +182,21 @@ export default Vue.extend({ (this as any).os.stream.dispose(this.connectionId); } }, + watch: { + post: { + handler(newPost, oldPost) { + if (!oldPost || newPost.textHtml !== oldPost.textHtml) { + this.$nextTick(() => { + const elements = this.$refs.text.$el.getElementsByTagName('a'); + + this.urls = [].filter.call(elements, ({ origin }) => origin !== location.origin) + .map(({ href }) => href); + }); + } + }, + immediate: true + } + }, methods: { capture(withHandler = false) { if ((this as any).os.isSignedIn) { @@ -450,7 +457,7 @@ export default Vue.extend({ font-size 1.1em color #717171 - >>> .quote + >>> blockquote margin 8px padding 6px 12px color #aaa diff --git a/src/client/app/desktop/views/components/sub-post-content.vue b/src/client/app/desktop/views/components/sub-post-content.vue index 1f5ce38984..58c81e7552 100644 --- a/src/client/app/desktop/views/components/sub-post-content.vue +++ b/src/client/app/desktop/views/components/sub-post-content.vue @@ -2,7 +2,7 @@
diff --git a/src/client/app/mobile/views/components/post-detail.vue b/src/client/app/mobile/views/components/post-detail.vue index 6411011b89..77a73426f2 100644 --- a/src/client/app/mobile/views/components/post-detail.vue +++ b/src/client/app/mobile/views/components/post-detail.vue @@ -38,7 +38,7 @@
- +
{{ tag }}
@@ -103,6 +103,7 @@ export default Vue.extend({ context: [], contextFetching: false, replies: [], + urls: [] }; }, computed: { @@ -127,15 +128,6 @@ export default Vue.extend({ .map(key => this.p.reactionCounts[key]) .reduce((a, b) => a + b) : 0; - }, - urls(): string[] { - if (this.p.ast) { - return this.p.ast - .filter(t => (t.type == 'url' || t.type == 'link') && !t.silent) - .map(t => t.url); - } else { - return null; - } } }, mounted() { @@ -167,6 +159,21 @@ export default Vue.extend({ } } }, + watch: { + post: { + handler(newPost, oldPost) { + if (!oldPost || newPost.text !== oldPost.text) { + this.$nextTick(() => { + const elements = this.$refs.text.$el.getElementsByTagName('a'); + + this.urls = [].filter.call(elements, ({ origin }) => origin !== location.origin) + .map(({ href }) => href); + }); + } + }, + immediate: true + } + }, methods: { fetchContext() { this.contextFetching = true; diff --git a/src/client/app/mobile/views/components/post.vue b/src/client/app/mobile/views/components/post.vue index 52fb095372..96ec9632f1 100644 --- a/src/client/app/mobile/views/components/post.vue +++ b/src/client/app/mobile/views/components/post.vue @@ -37,7 +37,7 @@ %fa:reply% - + RP:
@@ -90,7 +90,8 @@ export default Vue.extend({ data() { return { connection: null, - connectionId: null + connectionId: null, + urls: [] }; }, computed: { @@ -118,15 +119,6 @@ export default Vue.extend({ }, url(): string { return `/@${this.pAcct}/${this.p.id}`; - }, - urls(): string[] { - if (this.p.ast) { - return this.p.ast - .filter(t => (t.type == 'url' || t.type == 'link') && !t.silent) - .map(t => t.url); - } else { - return null; - } } }, created() { @@ -168,6 +160,21 @@ export default Vue.extend({ (this as any).os.stream.dispose(this.connectionId); } }, + watch: { + post: { + handler(newPost, oldPost) { + if (!oldPost || newPost.text !== oldPost.text) { + this.$nextTick(() => { + const elements = this.$refs.text.$el.getElementsByTagName('a'); + + this.urls = [].filter.call(elements, ({ origin }) => origin !== location.origin) + .map(({ href }) => href); + }); + } + }, + immediate: true + } + }, methods: { capture(withHandler = false) { if ((this as any).os.isSignedIn) { @@ -389,7 +396,7 @@ export default Vue.extend({ font-size 1.1em color #717171 - >>> .quote + >>> blockquote margin 8px padding 6px 12px color #aaa diff --git a/src/client/app/mobile/views/components/sub-post-content.vue b/src/client/app/mobile/views/components/sub-post-content.vue index 5ff88089aa..955bb406b4 100644 --- a/src/client/app/mobile/views/components/sub-post-content.vue +++ b/src/client/app/mobile/views/components/sub-post-content.vue @@ -2,7 +2,7 @@
diff --git a/src/client/docs/api/entities/post.yaml b/src/client/docs/api/entities/post.yaml index da79866ba1..7077700129 100644 --- a/src/client/docs/api/entities/post.yaml +++ b/src/client/docs/api/entities/post.yaml @@ -27,8 +27,14 @@ props: type: "string" optional: true desc: - ja: "投稿の本文" - en: "The text of this post" + ja: "投稿の本文 (ローカルの場合Markdown風のフォーマット)" + en: "The text of this post (in Markdown like format if local)" + - name: "textHtml" + type: "string" + optional: true + desc: + ja: "投稿の本文 (HTML) (投稿時は無視)" + en: "The text of this post (in HTML. Ignored when posting.)" - name: "mediaIds" type: "id(DriveFile)[]" optional: true diff --git a/src/common/text/core/syntax-highlighter.ts b/src/common/text/core/syntax-highlighter.ts deleted file mode 100644 index c0396b1fc6..0000000000 --- a/src/common/text/core/syntax-highlighter.ts +++ /dev/null @@ -1,334 +0,0 @@ -function escape(text) { - return text - .replace(/>/g, '>') - .replace(/ k[0].toUpperCase() + k.substr(1))) - .concat(_keywords.map(k => k.toUpperCase())) - .sort((a, b) => b.length - a.length); - -const symbols = [ - '=', - '+', - '-', - '*', - '/', - '%', - '~', - '^', - '&', - '|', - '>', - '<', - '!', - '?' -]; - -const elements = [ - // comment - code => { - if (code.substr(0, 2) != '//') return null; - const match = code.match(/^\/\/(.+?)(\n|$)/); - if (!match) return null; - const comment = match[0]; - return { - html: `${escape(comment)}`, - next: comment.length - }; - }, - - // block comment - code => { - const match = code.match(/^\/\*([\s\S]+?)\*\//); - if (!match) return null; - return { - html: `${escape(match[0])}`, - next: match[0].length - }; - }, - - // string - code => { - if (!/^['"`]/.test(code)) return null; - const begin = code[0]; - let str = begin; - let thisIsNotAString = false; - for (let i = 1; i < code.length; i++) { - const char = code[i]; - if (char == '\\') { - str += char; - str += code[i + 1] || ''; - i++; - continue; - } else if (char == begin) { - str += char; - break; - } else if (char == '\n' || i == (code.length - 1)) { - thisIsNotAString = true; - break; - } else { - str += char; - } - } - if (thisIsNotAString) { - return null; - } else { - return { - html: `${escape(str)}`, - next: str.length - }; - } - }, - - // regexp - code => { - if (code[0] != '/') return null; - let regexp = ''; - let thisIsNotARegexp = false; - for (let i = 1; i < code.length; i++) { - const char = code[i]; - if (char == '\\') { - regexp += char; - regexp += code[i + 1] || ''; - i++; - continue; - } else if (char == '/') { - break; - } else if (char == '\n' || i == (code.length - 1)) { - thisIsNotARegexp = true; - break; - } else { - regexp += char; - } - } - - if (thisIsNotARegexp) return null; - if (regexp == '') return null; - if (regexp[0] == ' ' && regexp[regexp.length - 1] == ' ') return null; - - return { - html: `/${escape(regexp)}/`, - next: regexp.length + 2 - }; - }, - - // label - code => { - if (code[0] != '@') return null; - const match = code.match(/^@([a-zA-Z_-]+?)\n/); - if (!match) return null; - const label = match[0]; - return { - html: `${label}`, - next: label.length - }; - }, - - // number - (code, i, source) => { - const prev = source[i - 1]; - if (prev && /[a-zA-Z]/.test(prev)) return null; - if (!/^[\-\+]?[0-9\.]+/.test(code)) return null; - const match = code.match(/^[\-\+]?[0-9\.]+/)[0]; - if (match) { - return { - html: `${match}`, - next: match.length - }; - } else { - return null; - } - }, - - // nan - (code, i, source) => { - const prev = source[i - 1]; - if (prev && /[a-zA-Z]/.test(prev)) return null; - if (code.substr(0, 3) == 'NaN') { - return { - html: `NaN`, - next: 3 - }; - } else { - return null; - } - }, - - // method - code => { - const match = code.match(/^([a-zA-Z_-]+?)\(/); - if (!match) return null; - - if (match[1] == '-') return null; - - return { - html: `${match[1]}`, - next: match[1].length - }; - }, - - // property - (code, i, source) => { - const prev = source[i - 1]; - if (prev != '.') return null; - - const match = code.match(/^[a-zA-Z0-9_-]+/); - if (!match) return null; - - return { - html: `${match[0]}`, - next: match[0].length - }; - }, - - // keyword - (code, i, source) => { - const prev = source[i - 1]; - if (prev && /[a-zA-Z]/.test(prev)) return null; - - const match = keywords.filter(k => code.substr(0, k.length) == k)[0]; - if (match) { - if (/^[a-zA-Z]/.test(code.substr(match.length))) return null; - return { - html: `${match}`, - next: match.length - }; - } else { - return null; - } - }, - - // symbol - code => { - const match = symbols.filter(s => code[0] == s)[0]; - if (match) { - return { - html: `${match}`, - next: 1 - }; - } else { - return null; - } - } -]; - -// specify lang is todo -export default (source: string, lang?: string) => { - let code = source; - let html = ''; - - let i = 0; - - function push(token) { - html += token.html; - code = code.substr(token.next); - i += token.next; - } - - while (code != '') { - const parsed = elements.some(el => { - const e = el(code, i, source); - if (e) { - push(e); - return true; - } else { - return false; - } - }); - - if (!parsed) { - push({ - html: escape(code[0]), - next: 1 - }); - } - } - - return html; -}; diff --git a/src/common/text/elements/bold.ts b/src/common/text/elements/bold.ts deleted file mode 100644 index ce25764457..0000000000 --- a/src/common/text/elements/bold.ts +++ /dev/null @@ -1,14 +0,0 @@ -/** - * Bold - */ - -module.exports = text => { - const match = text.match(/^\*\*(.+?)\*\*/); - if (!match) return null; - const bold = match[0]; - return { - type: 'bold', - content: bold, - bold: bold.substr(2, bold.length - 4) - }; -}; diff --git a/src/common/text/elements/code.ts b/src/common/text/elements/code.ts deleted file mode 100644 index 4821e95fe2..0000000000 --- a/src/common/text/elements/code.ts +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Code (block) - */ - -import genHtml from '../core/syntax-highlighter'; - -module.exports = text => { - const match = text.match(/^```([\s\S]+?)```/); - if (!match) return null; - const code = match[0]; - return { - type: 'code', - content: code, - code: code.substr(3, code.length - 6).trim(), - html: genHtml(code.substr(3, code.length - 6).trim()) - }; -}; diff --git a/src/common/text/elements/emoji.ts b/src/common/text/elements/emoji.ts deleted file mode 100644 index e24231a223..0000000000 --- a/src/common/text/elements/emoji.ts +++ /dev/null @@ -1,14 +0,0 @@ -/** - * Emoji - */ - -module.exports = text => { - const match = text.match(/^:[a-zA-Z0-9+-_]+:/); - if (!match) return null; - const emoji = match[0]; - return { - type: 'emoji', - content: emoji, - emoji: emoji.substr(1, emoji.length - 2) - }; -}; diff --git a/src/common/text/elements/hashtag.ts b/src/common/text/elements/hashtag.ts deleted file mode 100644 index ee57b140b8..0000000000 --- a/src/common/text/elements/hashtag.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Hashtag - */ - -module.exports = (text, i) => { - if (!(/^\s#[^\s]+/.test(text) || (i == 0 && /^#[^\s]+/.test(text)))) return null; - const isHead = text[0] == '#'; - const hashtag = text.match(/^\s?#[^\s]+/)[0]; - const res: any[] = !isHead ? [{ - type: 'text', - content: text[0] - }] : []; - res.push({ - type: 'hashtag', - content: isHead ? hashtag : hashtag.substr(1), - hashtag: isHead ? hashtag.substr(1) : hashtag.substr(2) - }); - return res; -}; diff --git a/src/common/text/elements/inline-code.ts b/src/common/text/elements/inline-code.ts deleted file mode 100644 index 9f9ef51a2b..0000000000 --- a/src/common/text/elements/inline-code.ts +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Code (inline) - */ - -import genHtml from '../core/syntax-highlighter'; - -module.exports = text => { - const match = text.match(/^`(.+?)`/); - if (!match) return null; - const code = match[0]; - return { - type: 'inline-code', - content: code, - code: code.substr(1, code.length - 2).trim(), - html: genHtml(code.substr(1, code.length - 2).trim()) - }; -}; diff --git a/src/common/text/elements/link.ts b/src/common/text/elements/link.ts deleted file mode 100644 index 35563ddc3d..0000000000 --- a/src/common/text/elements/link.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Link - */ - -module.exports = text => { - const match = text.match(/^\??\[([^\[\]]+?)\]\((https?:\/\/[\w\/:%#@\$&\?!\(\)\[\]~\.=\+\-]+?)\)/); - if (!match) return null; - const silent = text[0] == '?'; - const link = match[0]; - const title = match[1]; - const url = match[2]; - return { - type: 'link', - content: link, - title: title, - url: url, - silent: silent - }; -}; diff --git a/src/common/text/elements/mention.ts b/src/common/text/elements/mention.ts deleted file mode 100644 index d05a76649d..0000000000 --- a/src/common/text/elements/mention.ts +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Mention - */ -import parseAcct from '../../../common/user/parse-acct'; - -module.exports = text => { - const match = text.match(/^(?:@[a-zA-Z0-9\-]+){1,2}/); - if (!match) return null; - const mention = match[0]; - const { username, host } = parseAcct(mention.substr(1)); - return { - type: 'mention', - content: mention, - username, - host - }; -}; diff --git a/src/common/text/elements/quote.ts b/src/common/text/elements/quote.ts deleted file mode 100644 index cc8cfffdc4..0000000000 --- a/src/common/text/elements/quote.ts +++ /dev/null @@ -1,14 +0,0 @@ -/** - * Quoted text - */ - -module.exports = text => { - const match = text.match(/^"([\s\S]+?)\n"/); - if (!match) return null; - const quote = match[0]; - return { - type: 'quote', - content: quote, - quote: quote.substr(1, quote.length - 2).trim(), - }; -}; diff --git a/src/common/text/elements/url.ts b/src/common/text/elements/url.ts deleted file mode 100644 index 1003aff9c3..0000000000 --- a/src/common/text/elements/url.ts +++ /dev/null @@ -1,14 +0,0 @@ -/** - * URL - */ - -module.exports = text => { - const match = text.match(/^https?:\/\/[\w\/:%#@\$&\?!\(\)\[\]~\.=\+\-]+/); - if (!match) return null; - const url = match[0]; - return { - type: 'url', - content: url, - url: url - }; -}; diff --git a/src/common/text/html.ts b/src/common/text/html.ts new file mode 100644 index 0000000000..797f3b3f33 --- /dev/null +++ b/src/common/text/html.ts @@ -0,0 +1,83 @@ +import { lib as emojilib } from 'emojilib'; +import { JSDOM } from 'jsdom'; + +const handlers = { + bold({ document }, { bold }) { + const b = document.createElement('b'); + b.textContent = bold; + document.body.appendChild(b); + }, + + code({ document }, { code }) { + const pre = document.createElement('pre'); + const inner = document.createElement('code'); + inner.innerHTML = code; + pre.appendChild(inner); + document.body.appendChild(pre); + }, + + emoji({ document }, { content, emoji }) { + const found = emojilib[emoji]; + const node = document.createTextNode(found ? found.char : content); + document.body.appendChild(node); + }, + + hashtag({ document }, { hashtag }) { + const a = document.createElement('a'); + a.href = '/search?q=#' + hashtag; + a.textContent = hashtag; + }, + + 'inline-code'({ document }, { code }) { + const element = document.createElement('code'); + element.textContent = code; + document.body.appendChild(element); + }, + + link({ document }, { url, title }) { + const a = document.createElement('a'); + a.href = url; + a.textContent = title; + document.body.appendChild(a); + }, + + mention({ document }, { content }) { + const a = document.createElement('a'); + a.href = '/' + content; + a.textContent = content; + document.body.appendChild(a); + }, + + quote({ document }, { quote }) { + const blockquote = document.createElement('blockquote'); + blockquote.textContent = quote; + document.body.appendChild(blockquote); + }, + + text({ document }, { content }) { + for (const text of content.split('\n')) { + const node = document.createTextNode(text); + document.body.appendChild(node); + + const br = document.createElement('br'); + document.body.appendChild(br); + } + }, + + url({ document }, { url }) { + const a = document.createElement('a'); + a.href = url; + a.textContent = url; + document.body.appendChild(a); + } +}; + +export default tokens => { + const { window } = new JSDOM(''); + + for (const token of tokens) { + handlers[token.type](window, token); + } + + return `

${window.document.body.innerHTML}

`; +}; diff --git a/src/common/text/index.ts b/src/common/text/index.ts deleted file mode 100644 index 1e2398dc38..0000000000 --- a/src/common/text/index.ts +++ /dev/null @@ -1,72 +0,0 @@ -/** - * Misskey Text Analyzer - */ - -const elements = [ - require('./elements/bold'), - require('./elements/url'), - require('./elements/link'), - require('./elements/mention'), - require('./elements/hashtag'), - require('./elements/code'), - require('./elements/inline-code'), - require('./elements/quote'), - require('./elements/emoji') -]; - -export default (source: string) => { - - if (source == '') { - return null; - } - - const tokens = []; - - function push(token) { - if (token != null) { - tokens.push(token); - source = source.substr(token.content.length); - } - } - - let i = 0; - - // パース - while (source != '') { - const parsed = elements.some(el => { - let _tokens = el(source, i); - if (_tokens) { - if (!Array.isArray(_tokens)) { - _tokens = [_tokens]; - } - _tokens.forEach(push); - return true; - } else { - return false; - } - }); - - if (!parsed) { - push({ - type: 'text', - content: source[0] - }); - } - - i++; - } - - // テキストを纏める - tokens[0] = [tokens[0]]; - return tokens.reduce((a, b) => { - if (a[a.length - 1].type == 'text' && b.type == 'text') { - const tail = a.pop(); - return a.concat({ - type: 'text', - content: tail.content + b.content - }); - } else { - return a.concat(b); - } - }); -}; diff --git a/src/common/text/parse/core/syntax-highlighter.ts b/src/common/text/parse/core/syntax-highlighter.ts new file mode 100644 index 0000000000..c0396b1fc6 --- /dev/null +++ b/src/common/text/parse/core/syntax-highlighter.ts @@ -0,0 +1,334 @@ +function escape(text) { + return text + .replace(/>/g, '>') + .replace(/ k[0].toUpperCase() + k.substr(1))) + .concat(_keywords.map(k => k.toUpperCase())) + .sort((a, b) => b.length - a.length); + +const symbols = [ + '=', + '+', + '-', + '*', + '/', + '%', + '~', + '^', + '&', + '|', + '>', + '<', + '!', + '?' +]; + +const elements = [ + // comment + code => { + if (code.substr(0, 2) != '//') return null; + const match = code.match(/^\/\/(.+?)(\n|$)/); + if (!match) return null; + const comment = match[0]; + return { + html: `${escape(comment)}`, + next: comment.length + }; + }, + + // block comment + code => { + const match = code.match(/^\/\*([\s\S]+?)\*\//); + if (!match) return null; + return { + html: `${escape(match[0])}`, + next: match[0].length + }; + }, + + // string + code => { + if (!/^['"`]/.test(code)) return null; + const begin = code[0]; + let str = begin; + let thisIsNotAString = false; + for (let i = 1; i < code.length; i++) { + const char = code[i]; + if (char == '\\') { + str += char; + str += code[i + 1] || ''; + i++; + continue; + } else if (char == begin) { + str += char; + break; + } else if (char == '\n' || i == (code.length - 1)) { + thisIsNotAString = true; + break; + } else { + str += char; + } + } + if (thisIsNotAString) { + return null; + } else { + return { + html: `${escape(str)}`, + next: str.length + }; + } + }, + + // regexp + code => { + if (code[0] != '/') return null; + let regexp = ''; + let thisIsNotARegexp = false; + for (let i = 1; i < code.length; i++) { + const char = code[i]; + if (char == '\\') { + regexp += char; + regexp += code[i + 1] || ''; + i++; + continue; + } else if (char == '/') { + break; + } else if (char == '\n' || i == (code.length - 1)) { + thisIsNotARegexp = true; + break; + } else { + regexp += char; + } + } + + if (thisIsNotARegexp) return null; + if (regexp == '') return null; + if (regexp[0] == ' ' && regexp[regexp.length - 1] == ' ') return null; + + return { + html: `/${escape(regexp)}/`, + next: regexp.length + 2 + }; + }, + + // label + code => { + if (code[0] != '@') return null; + const match = code.match(/^@([a-zA-Z_-]+?)\n/); + if (!match) return null; + const label = match[0]; + return { + html: `${label}`, + next: label.length + }; + }, + + // number + (code, i, source) => { + const prev = source[i - 1]; + if (prev && /[a-zA-Z]/.test(prev)) return null; + if (!/^[\-\+]?[0-9\.]+/.test(code)) return null; + const match = code.match(/^[\-\+]?[0-9\.]+/)[0]; + if (match) { + return { + html: `${match}`, + next: match.length + }; + } else { + return null; + } + }, + + // nan + (code, i, source) => { + const prev = source[i - 1]; + if (prev && /[a-zA-Z]/.test(prev)) return null; + if (code.substr(0, 3) == 'NaN') { + return { + html: `NaN`, + next: 3 + }; + } else { + return null; + } + }, + + // method + code => { + const match = code.match(/^([a-zA-Z_-]+?)\(/); + if (!match) return null; + + if (match[1] == '-') return null; + + return { + html: `${match[1]}`, + next: match[1].length + }; + }, + + // property + (code, i, source) => { + const prev = source[i - 1]; + if (prev != '.') return null; + + const match = code.match(/^[a-zA-Z0-9_-]+/); + if (!match) return null; + + return { + html: `${match[0]}`, + next: match[0].length + }; + }, + + // keyword + (code, i, source) => { + const prev = source[i - 1]; + if (prev && /[a-zA-Z]/.test(prev)) return null; + + const match = keywords.filter(k => code.substr(0, k.length) == k)[0]; + if (match) { + if (/^[a-zA-Z]/.test(code.substr(match.length))) return null; + return { + html: `${match}`, + next: match.length + }; + } else { + return null; + } + }, + + // symbol + code => { + const match = symbols.filter(s => code[0] == s)[0]; + if (match) { + return { + html: `${match}`, + next: 1 + }; + } else { + return null; + } + } +]; + +// specify lang is todo +export default (source: string, lang?: string) => { + let code = source; + let html = ''; + + let i = 0; + + function push(token) { + html += token.html; + code = code.substr(token.next); + i += token.next; + } + + while (code != '') { + const parsed = elements.some(el => { + const e = el(code, i, source); + if (e) { + push(e); + return true; + } else { + return false; + } + }); + + if (!parsed) { + push({ + html: escape(code[0]), + next: 1 + }); + } + } + + return html; +}; diff --git a/src/common/text/parse/elements/bold.ts b/src/common/text/parse/elements/bold.ts new file mode 100644 index 0000000000..ce25764457 --- /dev/null +++ b/src/common/text/parse/elements/bold.ts @@ -0,0 +1,14 @@ +/** + * Bold + */ + +module.exports = text => { + const match = text.match(/^\*\*(.+?)\*\*/); + if (!match) return null; + const bold = match[0]; + return { + type: 'bold', + content: bold, + bold: bold.substr(2, bold.length - 4) + }; +}; diff --git a/src/common/text/parse/elements/code.ts b/src/common/text/parse/elements/code.ts new file mode 100644 index 0000000000..4821e95fe2 --- /dev/null +++ b/src/common/text/parse/elements/code.ts @@ -0,0 +1,17 @@ +/** + * Code (block) + */ + +import genHtml from '../core/syntax-highlighter'; + +module.exports = text => { + const match = text.match(/^```([\s\S]+?)```/); + if (!match) return null; + const code = match[0]; + return { + type: 'code', + content: code, + code: code.substr(3, code.length - 6).trim(), + html: genHtml(code.substr(3, code.length - 6).trim()) + }; +}; diff --git a/src/common/text/parse/elements/emoji.ts b/src/common/text/parse/elements/emoji.ts new file mode 100644 index 0000000000..e24231a223 --- /dev/null +++ b/src/common/text/parse/elements/emoji.ts @@ -0,0 +1,14 @@ +/** + * Emoji + */ + +module.exports = text => { + const match = text.match(/^:[a-zA-Z0-9+-_]+:/); + if (!match) return null; + const emoji = match[0]; + return { + type: 'emoji', + content: emoji, + emoji: emoji.substr(1, emoji.length - 2) + }; +}; diff --git a/src/common/text/parse/elements/hashtag.ts b/src/common/text/parse/elements/hashtag.ts new file mode 100644 index 0000000000..ee57b140b8 --- /dev/null +++ b/src/common/text/parse/elements/hashtag.ts @@ -0,0 +1,19 @@ +/** + * Hashtag + */ + +module.exports = (text, i) => { + if (!(/^\s#[^\s]+/.test(text) || (i == 0 && /^#[^\s]+/.test(text)))) return null; + const isHead = text[0] == '#'; + const hashtag = text.match(/^\s?#[^\s]+/)[0]; + const res: any[] = !isHead ? [{ + type: 'text', + content: text[0] + }] : []; + res.push({ + type: 'hashtag', + content: isHead ? hashtag : hashtag.substr(1), + hashtag: isHead ? hashtag.substr(1) : hashtag.substr(2) + }); + return res; +}; diff --git a/src/common/text/parse/elements/inline-code.ts b/src/common/text/parse/elements/inline-code.ts new file mode 100644 index 0000000000..9f9ef51a2b --- /dev/null +++ b/src/common/text/parse/elements/inline-code.ts @@ -0,0 +1,17 @@ +/** + * Code (inline) + */ + +import genHtml from '../core/syntax-highlighter'; + +module.exports = text => { + const match = text.match(/^`(.+?)`/); + if (!match) return null; + const code = match[0]; + return { + type: 'inline-code', + content: code, + code: code.substr(1, code.length - 2).trim(), + html: genHtml(code.substr(1, code.length - 2).trim()) + }; +}; diff --git a/src/common/text/parse/elements/link.ts b/src/common/text/parse/elements/link.ts new file mode 100644 index 0000000000..35563ddc3d --- /dev/null +++ b/src/common/text/parse/elements/link.ts @@ -0,0 +1,19 @@ +/** + * Link + */ + +module.exports = text => { + const match = text.match(/^\??\[([^\[\]]+?)\]\((https?:\/\/[\w\/:%#@\$&\?!\(\)\[\]~\.=\+\-]+?)\)/); + if (!match) return null; + const silent = text[0] == '?'; + const link = match[0]; + const title = match[1]; + const url = match[2]; + return { + type: 'link', + content: link, + title: title, + url: url, + silent: silent + }; +}; diff --git a/src/common/text/parse/elements/mention.ts b/src/common/text/parse/elements/mention.ts new file mode 100644 index 0000000000..2025dfdaad --- /dev/null +++ b/src/common/text/parse/elements/mention.ts @@ -0,0 +1,17 @@ +/** + * Mention + */ +import parseAcct from '../../../../common/user/parse-acct'; + +module.exports = text => { + const match = text.match(/^(?:@[a-zA-Z0-9\-]+){1,2}/); + if (!match) return null; + const mention = match[0]; + const { username, host } = parseAcct(mention.substr(1)); + return { + type: 'mention', + content: mention, + username, + host + }; +}; diff --git a/src/common/text/parse/elements/quote.ts b/src/common/text/parse/elements/quote.ts new file mode 100644 index 0000000000..cc8cfffdc4 --- /dev/null +++ b/src/common/text/parse/elements/quote.ts @@ -0,0 +1,14 @@ +/** + * Quoted text + */ + +module.exports = text => { + const match = text.match(/^"([\s\S]+?)\n"/); + if (!match) return null; + const quote = match[0]; + return { + type: 'quote', + content: quote, + quote: quote.substr(1, quote.length - 2).trim(), + }; +}; diff --git a/src/common/text/parse/elements/url.ts b/src/common/text/parse/elements/url.ts new file mode 100644 index 0000000000..1003aff9c3 --- /dev/null +++ b/src/common/text/parse/elements/url.ts @@ -0,0 +1,14 @@ +/** + * URL + */ + +module.exports = text => { + const match = text.match(/^https?:\/\/[\w\/:%#@\$&\?!\(\)\[\]~\.=\+\-]+/); + if (!match) return null; + const url = match[0]; + return { + type: 'url', + content: url, + url: url + }; +}; diff --git a/src/common/text/parse/index.ts b/src/common/text/parse/index.ts new file mode 100644 index 0000000000..1e2398dc38 --- /dev/null +++ b/src/common/text/parse/index.ts @@ -0,0 +1,72 @@ +/** + * Misskey Text Analyzer + */ + +const elements = [ + require('./elements/bold'), + require('./elements/url'), + require('./elements/link'), + require('./elements/mention'), + require('./elements/hashtag'), + require('./elements/code'), + require('./elements/inline-code'), + require('./elements/quote'), + require('./elements/emoji') +]; + +export default (source: string) => { + + if (source == '') { + return null; + } + + const tokens = []; + + function push(token) { + if (token != null) { + tokens.push(token); + source = source.substr(token.content.length); + } + } + + let i = 0; + + // パース + while (source != '') { + const parsed = elements.some(el => { + let _tokens = el(source, i); + if (_tokens) { + if (!Array.isArray(_tokens)) { + _tokens = [_tokens]; + } + _tokens.forEach(push); + return true; + } else { + return false; + } + }); + + if (!parsed) { + push({ + type: 'text', + content: source[0] + }); + } + + i++; + } + + // テキストを纏める + tokens[0] = [tokens[0]]; + return tokens.reduce((a, b) => { + if (a[a.length - 1].type == 'text' && b.type == 'text') { + const tail = a.pop(); + return a.concat({ + type: 'text', + content: tail.content + b.content + }); + } else { + return a.concat(b); + } + }); +}; diff --git a/src/models/messaging-message.ts b/src/models/messaging-message.ts index 8bee657c34..974ee54ab8 100644 --- a/src/models/messaging-message.ts +++ b/src/models/messaging-message.ts @@ -3,7 +3,6 @@ import deepcopy = require('deepcopy'); import { pack as packUser } from './user'; import { pack as packFile } from './drive-file'; import db from '../db/mongodb'; -import parse from '../common/text'; const MessagingMessage = db.get('messagingMessages'); export default MessagingMessage; @@ -12,6 +11,7 @@ export interface IMessagingMessage { _id: mongo.ObjectID; createdAt: Date; text: string; + textHtml: string; userId: mongo.ObjectID; recipientId: mongo.ObjectID; isRead: boolean; @@ -60,11 +60,6 @@ export const pack = ( _message.id = _message._id; delete _message._id; - // Parse text - if (_message.text) { - _message.ast = parse(_message.text); - } - // Populate user _message.user = await packUser(_message.userId, me); diff --git a/src/models/post.ts b/src/models/post.ts index 9bc0c1d3b9..6c853e4f81 100644 --- a/src/models/post.ts +++ b/src/models/post.ts @@ -8,7 +8,6 @@ import { pack as packChannel } from './channel'; import Vote from './poll-vote'; import Reaction from './post-reaction'; import { pack as packFile } from './drive-file'; -import parse from '../common/text'; const Post = db.get('posts'); @@ -31,6 +30,7 @@ export type IPost = { repostId: mongo.ObjectID; poll: any; // todo text: string; + textHtml: string; cw: string; userId: mongo.ObjectID; appId: mongo.ObjectID; @@ -103,11 +103,6 @@ export const pack = async ( delete _post.mentions; if (_post.geo) delete _post.geo.type; - // Parse text - if (_post.text) { - _post.ast = parse(_post.text); - } - // Populate user _post.user = packUser(_post.userId, meId); diff --git a/src/server/api/endpoints/messaging/messages/create.ts b/src/server/api/endpoints/messaging/messages/create.ts index d8ffa9fdec..3d3b204da5 100644 --- a/src/server/api/endpoints/messaging/messages/create.ts +++ b/src/server/api/endpoints/messaging/messages/create.ts @@ -11,6 +11,8 @@ import DriveFile from '../../../../../models/drive-file'; import { pack } from '../../../../../models/messaging-message'; import publishUserStream from '../../../event'; import { publishMessagingStream, publishMessagingIndexStream, pushSw } from '../../../event'; +import html from '../../../../../common/text/html'; +import parse from '../../../../../common/text/parse'; import config from '../../../../../conf'; /** @@ -74,6 +76,7 @@ module.exports = (params, user) => new Promise(async (res, rej) => { fileId: file ? file._id : undefined, recipientId: recipient._id, text: text ? text : undefined, + textHtml: text ? html(parse(text)) : undefined, userId: user._id, isRead: false }); diff --git a/src/server/api/endpoints/posts/create.ts b/src/server/api/endpoints/posts/create.ts index aa7e93c28f..5342f77728 100644 --- a/src/server/api/endpoints/posts/create.ts +++ b/src/server/api/endpoints/posts/create.ts @@ -3,7 +3,8 @@ */ import $ from 'cafy'; import deepEqual = require('deep-equal'); -import parse from '../../../../common/text'; +import html from '../../../../common/text/html'; +import parse from '../../../../common/text/parse'; import { default as Post, IPost, isValidText, isValidCw } from '../../../../models/post'; import { default as User, ILocalAccount, IUser } from '../../../../models/user'; import { default as Channel, IChannel } from '../../../../models/channel'; @@ -259,6 +260,7 @@ module.exports = (params, user: IUser, app) => new Promise(async (res, rej) => { repostId: repost ? repost._id : undefined, poll: poll, text: text, + textHtml: tokens === null ? null : html(tokens), cw: cw, tags: tags, userId: user._id, diff --git a/tools/migration/nighthike/7.js b/tools/migration/nighthike/7.js new file mode 100644 index 0000000000..c5055da8ba --- /dev/null +++ b/tools/migration/nighthike/7.js @@ -0,0 +1,16 @@ +// for Node.js interpretation + +const Message = require('../../../built/models/messaging-message').default; +const Post = require('../../../built/models/post').default; +const html = require('../../../built/common/text/html').default; +const parse = require('../../../built/common/text/parse').default; + +Promise.all([Message, Post].map(async model => { + const documents = await model.find(); + + return Promise.all(documents.map(({ _id, text }) => model.update(_id, { + $set: { + textHtml: html(parse(text)) + } + }))); +})).catch(console.error).then(process.exit); -- cgit v1.2.3-freya From c0ad36193c0a06d37fa102353a9f3a59042a317c Mon Sep 17 00:00:00 2001 From: syuilo Date: Sun, 1 Apr 2018 12:37:53 +0900 Subject: :v: --- src/client/docs/api/gulpfile.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/client/docs/api') diff --git a/src/client/docs/api/gulpfile.ts b/src/client/docs/api/gulpfile.ts index 16066b0d2e..4b962fe0c6 100644 --- a/src/client/docs/api/gulpfile.ts +++ b/src/client/docs/api/gulpfile.ts @@ -101,7 +101,7 @@ gulp.task('doc:api:endpoints', async () => { } //console.log(files); files.forEach(file => { - const ep = yaml.safeLoad(fs.readFileSync(file, 'utf-8')); + const ep: any = yaml.safeLoad(fs.readFileSync(file, 'utf-8')); const vars = { endpoint: ep.endpoint, url: { -- cgit v1.2.3-freya From 3fb6834f7d06d52163102b69afe353f8c65ee003 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Mon, 2 Apr 2018 13:15:53 +0900 Subject: Introduce config module --- gulpfile.ts | 2 +- src/client/docs/api/gulpfile.ts | 2 +- src/client/docs/vars.ts | 2 +- src/conf.ts | 3 - src/config.ts | 154 --------------------- src/config/index.ts | 3 + src/config/load.ts | 57 ++++++++ src/config/types.ts | 97 +++++++++++++ src/db/elasticsearch.ts | 2 +- src/db/mongodb.ts | 2 +- src/db/redis.ts | 2 +- src/drive/add-file.ts | 2 +- src/event.ts | 2 +- src/index.ts | 19 ++- src/models/app.ts | 2 +- src/models/drive-file.ts | 2 +- src/models/user.ts | 2 +- src/othello/ai/back.ts | 2 +- src/othello/ai/front.ts | 2 +- src/processor/http/follow.ts | 2 +- src/push-sw.ts | 2 +- src/queue.ts | 2 +- src/remote/activitypub/create.ts | 2 +- src/remote/activitypub/renderer/document.ts | 2 +- src/remote/activitypub/renderer/follow.ts | 2 +- src/remote/activitypub/renderer/hashtag.ts | 2 +- src/remote/activitypub/renderer/image.ts | 2 +- src/remote/activitypub/renderer/key.ts | 2 +- src/remote/activitypub/renderer/note.ts | 2 +- src/remote/activitypub/renderer/person.ts | 2 +- src/server/activitypub/outbox.ts | 2 +- src/server/activitypub/publickey.ts | 2 +- src/server/activitypub/user.ts | 2 +- src/server/api/bot/interfaces/line.ts | 2 +- src/server/api/common/signin.ts | 2 +- src/server/api/endpoints/auth/session/generate.ts | 2 +- src/server/api/endpoints/i/2fa/register.ts | 2 +- src/server/api/endpoints/i/update.ts | 2 +- .../api/endpoints/messaging/messages/create.ts | 2 +- src/server/api/endpoints/meta.ts | 2 +- src/server/api/endpoints/posts/create.ts | 2 +- src/server/api/endpoints/users/search.ts | 2 +- src/server/api/private/signin.ts | 2 +- src/server/api/private/signup.ts | 2 +- src/server/api/service/github.ts | 2 +- src/server/api/service/twitter.ts | 2 +- src/server/api/streaming.ts | 2 +- src/server/index.ts | 2 +- src/server/webfinger.ts | 2 +- webpack.config.ts | 2 +- 50 files changed, 213 insertions(+), 208 deletions(-) delete mode 100644 src/conf.ts delete mode 100644 src/config.ts create mode 100644 src/config/index.ts create mode 100644 src/config/load.ts create mode 100644 src/config/types.ts (limited to 'src/client/docs/api') diff --git a/gulpfile.ts b/gulpfile.ts index a6e9e53df3..f372ed2993 100644 --- a/gulpfile.ts +++ b/gulpfile.ts @@ -24,7 +24,7 @@ const uglifyes = require('uglify-es'); import { fa } from './src/build/fa'; import version from './src/version'; -import config from './src/conf'; +import config from './src/config'; const uglify = uglifyComposer(uglifyes, console); diff --git a/src/client/docs/api/gulpfile.ts b/src/client/docs/api/gulpfile.ts index 4b962fe0c6..c986e0353a 100644 --- a/src/client/docs/api/gulpfile.ts +++ b/src/client/docs/api/gulpfile.ts @@ -13,7 +13,7 @@ import * as mkdirp from 'mkdirp'; import locales from '../../../../locales'; import I18nReplacer from '../../../build/i18n'; import fa from '../../../build/fa'; -import config from './../../../conf'; +import config from './../../../config'; import generateVars from '../vars'; diff --git a/src/client/docs/vars.ts b/src/client/docs/vars.ts index 1a3b48bd76..dbdc880611 100644 --- a/src/client/docs/vars.ts +++ b/src/client/docs/vars.ts @@ -6,7 +6,7 @@ import * as licenseChecker from 'license-checker'; import * as tmp from 'tmp'; import { fa } from '../../build/fa'; -import config from '../../conf'; +import config from '../../config'; import { licenseHtml } from '../../build/license'; const constants = require('../../const.json'); diff --git a/src/conf.ts b/src/conf.ts deleted file mode 100644 index b04a4c8594..0000000000 --- a/src/conf.ts +++ /dev/null @@ -1,3 +0,0 @@ -import load from './config'; - -export default load(); diff --git a/src/config.ts b/src/config.ts deleted file mode 100644 index 6d3e7740b1..0000000000 --- a/src/config.ts +++ /dev/null @@ -1,154 +0,0 @@ -/** - * Config loader - */ - -import * as fs from 'fs'; -import { URL } from 'url'; -import * as yaml from 'js-yaml'; -import isUrl = require('is-url'); - -/** - * Path of configuration directory - */ -const dir = `${__dirname}/../.config`; - -/** - * Path of configuration file - */ -export const path = process.env.NODE_ENV == 'test' - ? `${dir}/test.yml` - : `${dir}/default.yml`; - -/** - * ユーザーが設定する必要のある情報 - */ -type Source = { - /** - * メンテナ情報 - */ - maintainer: { - /** - * メンテナの名前 - */ - name: string; - /** - * メンテナの連絡先(URLかmailto形式のURL) - */ - url: string; - }; - url: string; - port: number; - https?: { [x: string]: string }; - mongodb: { - host: string; - port: number; - db: string; - user: string; - pass: string; - }; - redis: { - host: string; - port: number; - pass: string; - }; - elasticsearch: { - enable: boolean; - host: string; - port: number; - pass: string; - }; - recaptcha: { - site_key: string; - secret_key: string; - }; - accesslog?: string; - accesses?: { - enable: boolean; - port: number; - }; - twitter?: { - consumer_key: string; - consumer_secret: string; - }; - github_bot?: { - hook_secret: string; - username: string; - }; - othello_ai?: { - id: string; - i: string; - }; - line_bot?: { - channel_secret: string; - channel_access_token: string; - }; - analysis?: { - mecab_command?: string; - }; - - /** - * Service Worker - */ - sw?: { - public_key: string; - private_key: string; - }; - - google_maps_api_key: string; -}; - -/** - * Misskeyが自動的に(ユーザーが設定した情報から推論して)設定する情報 - */ -type Mixin = { - host: string; - hostname: string; - scheme: string; - ws_scheme: string; - api_url: string; - ws_url: string; - auth_url: string; - docs_url: string; - stats_url: string; - status_url: string; - dev_url: string; - drive_url: string; -}; - -export type Config = Source & Mixin; - -export default function load() { - const config = yaml.safeLoad(fs.readFileSync(path, 'utf-8')) as Source; - - const mixin = {} as Mixin; - - // Validate URLs - if (!isUrl(config.url)) urlError(config.url); - - const url = new URL(config.url); - config.url = normalizeUrl(config.url); - - mixin.host = url.host; - mixin.hostname = url.hostname; - mixin.scheme = url.protocol.replace(/:$/, ''); - mixin.ws_scheme = mixin.scheme.replace('http', 'ws'); - mixin.ws_url = `${mixin.ws_scheme}://${mixin.host}`; - mixin.api_url = `${mixin.scheme}://${mixin.host}/api`; - mixin.auth_url = `${mixin.scheme}://${mixin.host}/auth`; - mixin.dev_url = `${mixin.scheme}://${mixin.host}/dev`; - mixin.docs_url = `${mixin.scheme}://${mixin.host}/docs`; - mixin.stats_url = `${mixin.scheme}://${mixin.host}/stats`; - mixin.status_url = `${mixin.scheme}://${mixin.host}/status`; - mixin.drive_url = `${mixin.scheme}://${mixin.host}/files`; - - return Object.assign(config, mixin); -} - -function normalizeUrl(url: string) { - return url[url.length - 1] === '/' ? url.substr(0, url.length - 1) : url; -} - -function urlError(url: string) { - console.error(`「${url}」は、正しいURLではありません。先頭に http:// または https:// をつけ忘れてないかなど確認してください。`); - process.exit(); -} diff --git a/src/config/index.ts b/src/config/index.ts new file mode 100644 index 0000000000..7bfdca4612 --- /dev/null +++ b/src/config/index.ts @@ -0,0 +1,3 @@ +import load from './load'; + +export default load(); diff --git a/src/config/load.ts b/src/config/load.ts new file mode 100644 index 0000000000..9f4e2151f3 --- /dev/null +++ b/src/config/load.ts @@ -0,0 +1,57 @@ +/** + * Config loader + */ + +import * as fs from 'fs'; +import { URL } from 'url'; +import * as yaml from 'js-yaml'; +import { Source, Mixin } from './types'; +import isUrl = require('is-url'); + +/** + * Path of configuration directory + */ +const dir = `${__dirname}/../../.config`; + +/** + * Path of configuration file + */ +const path = process.env.NODE_ENV == 'test' + ? `${dir}/test.yml` + : `${dir}/default.yml`; + +export default function load() { + const config = yaml.safeLoad(fs.readFileSync(path, 'utf-8')) as Source; + + const mixin = {} as Mixin; + + // Validate URLs + if (!isUrl(config.url)) urlError(config.url); + + const url = new URL(config.url); + config.url = normalizeUrl(config.url); + + mixin.host = url.host; + mixin.hostname = url.hostname; + mixin.scheme = url.protocol.replace(/:$/, ''); + mixin.ws_scheme = mixin.scheme.replace('http', 'ws'); + mixin.ws_url = `${mixin.ws_scheme}://${mixin.host}`; + mixin.api_url = `${mixin.scheme}://${mixin.host}/api`; + mixin.auth_url = `${mixin.scheme}://${mixin.host}/auth`; + mixin.dev_url = `${mixin.scheme}://${mixin.host}/dev`; + mixin.docs_url = `${mixin.scheme}://${mixin.host}/docs`; + mixin.stats_url = `${mixin.scheme}://${mixin.host}/stats`; + mixin.status_url = `${mixin.scheme}://${mixin.host}/status`; + mixin.drive_url = `${mixin.scheme}://${mixin.host}/files`; + + return Object.assign(config, mixin); +} + +function normalizeUrl(url: string) { + return url[url.length - 1] === '/' ? url.substr(0, url.length - 1) : url; +} + +function urlError(url: string) { + console.error(`「${url}」は、正しいURLではありません。先頭に http:// または https:// をつけ忘れてないかなど確認してください。`); + process.exit(); +} diff --git a/src/config/types.ts b/src/config/types.ts new file mode 100644 index 0000000000..f802e70d1e --- /dev/null +++ b/src/config/types.ts @@ -0,0 +1,97 @@ +/** + * ユーザーが設定する必要のある情報 + */ +export type Source = { + /** + * メンテナ情報 + */ + maintainer: { + /** + * メンテナの名前 + */ + name: string; + /** + * メンテナの連絡先(URLかmailto形式のURL) + */ + url: string; + }; + url: string; + port: number; + https?: { [x: string]: string }; + mongodb: { + host: string; + port: number; + db: string; + user: string; + pass: string; + }; + redis: { + host: string; + port: number; + pass: string; + }; + elasticsearch: { + enable: boolean; + host: string; + port: number; + pass: string; + }; + recaptcha: { + site_key: string; + secret_key: string; + }; + accesslog?: string; + accesses?: { + enable: boolean; + port: number; + }; + twitter?: { + consumer_key: string; + consumer_secret: string; + }; + github_bot?: { + hook_secret: string; + username: string; + }; + othello_ai?: { + id: string; + i: string; + }; + line_bot?: { + channel_secret: string; + channel_access_token: string; + }; + analysis?: { + mecab_command?: string; + }; + + /** + * Service Worker + */ + sw?: { + public_key: string; + private_key: string; + }; + + google_maps_api_key: string; +}; + +/** + * Misskeyが自動的に(ユーザーが設定した情報から推論して)設定する情報 + */ +export type Mixin = { + host: string; + hostname: string; + scheme: string; + ws_scheme: string; + api_url: string; + ws_url: string; + auth_url: string; + docs_url: string; + stats_url: string; + status_url: string; + dev_url: string; + drive_url: string; +}; + +export type Config = Source & Mixin; diff --git a/src/db/elasticsearch.ts b/src/db/elasticsearch.ts index 75054a31c2..957b7ad97d 100644 --- a/src/db/elasticsearch.ts +++ b/src/db/elasticsearch.ts @@ -1,5 +1,5 @@ import * as elasticsearch from 'elasticsearch'; -import config from '../conf'; +import config from '../config'; // Init ElasticSearch connection const client = new elasticsearch.Client({ diff --git a/src/db/mongodb.ts b/src/db/mongodb.ts index 233f2f3d79..05bb72bfde 100644 --- a/src/db/mongodb.ts +++ b/src/db/mongodb.ts @@ -1,4 +1,4 @@ -import config from '../conf'; +import config from '../config'; const u = config.mongodb.user ? encodeURIComponent(config.mongodb.user) : null; const p = config.mongodb.pass ? encodeURIComponent(config.mongodb.pass) : null; diff --git a/src/db/redis.ts b/src/db/redis.ts index 2e0867de61..f8d66ebda0 100644 --- a/src/db/redis.ts +++ b/src/db/redis.ts @@ -1,5 +1,5 @@ import * as redis from 'redis'; -import config from '../conf'; +import config from '../config'; export default redis.createClient( config.redis.port, diff --git a/src/drive/add-file.ts b/src/drive/add-file.ts index f356af00d8..db13e04be0 100644 --- a/src/drive/add-file.ts +++ b/src/drive/add-file.ts @@ -15,7 +15,7 @@ import DriveFolder from '../models/drive-folder'; import { pack } from '../models/drive-file'; import event, { publishDriveStream } from '../event'; import getAcct from '../user/get-acct'; -import config from '../conf'; +import config from '../config'; const gm = _gm.subClass({ imageMagick: true diff --git a/src/event.ts b/src/event.ts index df79f0c90d..81876b3cf4 100644 --- a/src/event.ts +++ b/src/event.ts @@ -1,7 +1,7 @@ import * as mongo from 'mongodb'; import * as redis from 'redis'; import swPush from './push-sw'; -import config from './conf'; +import config from './config'; type ID = string | mongo.ObjectID; diff --git a/src/index.ts b/src/index.ts index f86c768fdc..b43e152854 100644 --- a/src/index.ts +++ b/src/index.ts @@ -4,7 +4,6 @@ Error.stackTraceLimit = Infinity; -import * as fs from 'fs'; import * as os from 'os'; import * as cluster from 'cluster'; import * as debug from 'debug'; @@ -21,8 +20,8 @@ import MachineInfo from './utils/machineInfo'; import DependencyInfo from './utils/dependencyInfo'; import stats from './utils/stats'; -import { Config, path as configPath } from './config'; -import loadConfig from './config'; +import loadConfig from './config/load'; +import { Config } from './config/types'; import parseOpt from './parse-opt'; @@ -116,11 +115,17 @@ async function init(): Promise { new DependencyInfo().showAll(); const configLogger = new Logger('Config'); - if (!fs.existsSync(configPath)) { - throw 'Configuration not found - Please run "npm run config" command.'; - } + let config; - const config = loadConfig(); + try { + config = loadConfig(); + } catch (exception) { + if (exception.code === 'ENOENT') { + throw 'Configuration not found - Please run "npm run config" command.'; + } + + throw exception; + } configLogger.info('Successfully loaded'); configLogger.info(`maintainer: ${config.maintainer}`); diff --git a/src/models/app.ts b/src/models/app.ts index 3b80a1602f..83162a6b99 100644 --- a/src/models/app.ts +++ b/src/models/app.ts @@ -2,7 +2,7 @@ import * as mongo from 'mongodb'; import deepcopy = require('deepcopy'); import AccessToken from './access-token'; import db from '../db/mongodb'; -import config from '../conf'; +import config from '../config'; const App = db.get('apps'); App.createIndex('nameId'); diff --git a/src/models/drive-file.ts b/src/models/drive-file.ts index 9e0df58c45..fba1aebdae 100644 --- a/src/models/drive-file.ts +++ b/src/models/drive-file.ts @@ -1,7 +1,7 @@ import * as mongodb from 'mongodb'; import deepcopy = require('deepcopy'); import { pack as packFolder } from './drive-folder'; -import config from '../conf'; +import config from '../config'; import monkDb, { nativeDbConn } from '../db/mongodb'; const DriveFile = monkDb.get('driveFiles.files'); diff --git a/src/models/user.ts b/src/models/user.ts index c9a25a35e9..d3c94cab3e 100644 --- a/src/models/user.ts +++ b/src/models/user.ts @@ -6,7 +6,7 @@ import { IPost, pack as packPost } from './post'; import Following from './following'; import Mute from './mute'; import getFriends from '../server/api/common/get-friends'; -import config from '../conf'; +import config from '../config'; const User = db.get('users'); diff --git a/src/othello/ai/back.ts b/src/othello/ai/back.ts index a67f6fe835..d6704b1750 100644 --- a/src/othello/ai/back.ts +++ b/src/othello/ai/back.ts @@ -8,7 +8,7 @@ import * as request from 'request-promise-native'; import Othello, { Color } from '../core'; -import conf from '../../conf'; +import conf from '../../config'; let game; let form; diff --git a/src/othello/ai/front.ts b/src/othello/ai/front.ts index 3414d64340..9d0b5f9800 100644 --- a/src/othello/ai/front.ts +++ b/src/othello/ai/front.ts @@ -10,7 +10,7 @@ import * as childProcess from 'child_process'; const WebSocket = require('ws'); import * as ReconnectingWebSocket from 'reconnecting-websocket'; import * as request from 'request-promise-native'; -import conf from '../../conf'; +import conf from '../../config'; // 設定 //////////////////////////////////////////////////////// diff --git a/src/processor/http/follow.ts b/src/processor/http/follow.ts index e81b410cb7..d6ce00006f 100644 --- a/src/processor/http/follow.ts +++ b/src/processor/http/follow.ts @@ -7,7 +7,7 @@ import event from '../../event'; import notify from '../../notify'; import context from '../../remote/activitypub/renderer/context'; import render from '../../remote/activitypub/renderer/follow'; -import config from '../../conf'; +import config from '../../config'; export default ({ data }, done) => Following.findOne({ _id: data.following }).then(({ followerId, followeeId }) => { const promisedFollower: Promise = User.findOne({ _id: followerId }); diff --git a/src/push-sw.ts b/src/push-sw.ts index 5b5ec52071..fcef7796d9 100644 --- a/src/push-sw.ts +++ b/src/push-sw.ts @@ -1,7 +1,7 @@ const push = require('web-push'); import * as mongo from 'mongodb'; import Subscription from './models/sw-subscription'; -import config from './conf'; +import config from './config'; if (config.sw) { // アプリケーションの連絡先と、サーバーサイドの鍵ペアの情報を登録 diff --git a/src/queue.ts b/src/queue.ts index 6089e0a7f1..08ea13c2a3 100644 --- a/src/queue.ts +++ b/src/queue.ts @@ -1,5 +1,5 @@ import { createQueue } from 'kue'; -import config from './conf'; +import config from './config'; export default createQueue({ redis: { diff --git a/src/remote/activitypub/create.ts b/src/remote/activitypub/create.ts index 6d135685ef..8ea8a85fd8 100644 --- a/src/remote/activitypub/create.ts +++ b/src/remote/activitypub/create.ts @@ -1,5 +1,5 @@ import { JSDOM } from 'jsdom'; -import config from '../../conf'; +import config from '../../config'; import Post from '../../models/post'; import RemoteUserObject, { IRemoteUserObject } from '../../models/remote-user-object'; import uploadFromUrl from '../../drive/upload-from-url'; diff --git a/src/remote/activitypub/renderer/document.ts b/src/remote/activitypub/renderer/document.ts index fdd52c1b6c..91a9f7df38 100644 --- a/src/remote/activitypub/renderer/document.ts +++ b/src/remote/activitypub/renderer/document.ts @@ -1,4 +1,4 @@ -import config from '../../../conf'; +import config from '../../../config'; export default ({ _id, contentType }) => ({ type: 'Document', diff --git a/src/remote/activitypub/renderer/follow.ts b/src/remote/activitypub/renderer/follow.ts index c99bc375a2..6d1ded9a95 100644 --- a/src/remote/activitypub/renderer/follow.ts +++ b/src/remote/activitypub/renderer/follow.ts @@ -1,4 +1,4 @@ -import config from '../../../conf'; +import config from '../../../config'; import { IRemoteUser } from '../../../models/user'; export default ({ username }, followee: IRemoteUser) => ({ diff --git a/src/remote/activitypub/renderer/hashtag.ts b/src/remote/activitypub/renderer/hashtag.ts index c2d261ed21..cf0b07b48a 100644 --- a/src/remote/activitypub/renderer/hashtag.ts +++ b/src/remote/activitypub/renderer/hashtag.ts @@ -1,4 +1,4 @@ -import config from '../../../conf'; +import config from '../../../config'; export default tag => ({ type: 'Hashtag', diff --git a/src/remote/activitypub/renderer/image.ts b/src/remote/activitypub/renderer/image.ts index 3d1c71cb95..d671a57e7c 100644 --- a/src/remote/activitypub/renderer/image.ts +++ b/src/remote/activitypub/renderer/image.ts @@ -1,4 +1,4 @@ -import config from '../../../conf'; +import config from '../../../config'; export default ({ _id }) => ({ type: 'Image', diff --git a/src/remote/activitypub/renderer/key.ts b/src/remote/activitypub/renderer/key.ts index 904a69e081..85be7b1367 100644 --- a/src/remote/activitypub/renderer/key.ts +++ b/src/remote/activitypub/renderer/key.ts @@ -1,4 +1,4 @@ -import config from '../../../conf'; +import config from '../../../config'; import { extractPublic } from '../../../crypto_key'; import { ILocalUser } from '../../../models/user'; diff --git a/src/remote/activitypub/renderer/note.ts b/src/remote/activitypub/renderer/note.ts index 74806f14b4..43531b121a 100644 --- a/src/remote/activitypub/renderer/note.ts +++ b/src/remote/activitypub/renderer/note.ts @@ -1,6 +1,6 @@ import renderDocument from './document'; import renderHashtag from './hashtag'; -import config from '../../../conf'; +import config from '../../../config'; import DriveFile from '../../../models/drive-file'; import Post from '../../../models/post'; import User from '../../../models/user'; diff --git a/src/remote/activitypub/renderer/person.ts b/src/remote/activitypub/renderer/person.ts index c6c7893165..7ea6f532fb 100644 --- a/src/remote/activitypub/renderer/person.ts +++ b/src/remote/activitypub/renderer/person.ts @@ -1,6 +1,6 @@ import renderImage from './image'; import renderKey from './key'; -import config from '../../../conf'; +import config from '../../../config'; export default user => { const id = `${config.url}/@${user.username}`; diff --git a/src/server/activitypub/outbox.ts b/src/server/activitypub/outbox.ts index 2018d8797b..9ecb0c0711 100644 --- a/src/server/activitypub/outbox.ts +++ b/src/server/activitypub/outbox.ts @@ -2,7 +2,7 @@ import * as express from 'express'; import context from '../../remote/activitypub/renderer/context'; import renderNote from '../../remote/activitypub/renderer/note'; import renderOrderedCollection from '../../remote/activitypub/renderer/ordered-collection'; -import config from '../../conf'; +import config from '../../config'; import Post from '../../models/post'; import withUser from './with-user'; diff --git a/src/server/activitypub/publickey.ts b/src/server/activitypub/publickey.ts index a23d11c156..c564c437e6 100644 --- a/src/server/activitypub/publickey.ts +++ b/src/server/activitypub/publickey.ts @@ -1,7 +1,7 @@ import * as express from 'express'; import context from '../../remote/activitypub/renderer/context'; import render from '../../remote/activitypub/renderer/key'; -import config from '../../conf'; +import config from '../../config'; import withUser from './with-user'; const app = express(); diff --git a/src/server/activitypub/user.ts b/src/server/activitypub/user.ts index 0d0b84bad4..baf2dc9a05 100644 --- a/src/server/activitypub/user.ts +++ b/src/server/activitypub/user.ts @@ -1,5 +1,5 @@ import * as express from 'express'; -import config from '../../conf'; +import config from '../../config'; import context from '../../remote/activitypub/renderer/context'; import render from '../../remote/activitypub/renderer/person'; import withUser from './with-user'; diff --git a/src/server/api/bot/interfaces/line.ts b/src/server/api/bot/interfaces/line.ts index 847b13aa78..27248c9b9d 100644 --- a/src/server/api/bot/interfaces/line.ts +++ b/src/server/api/bot/interfaces/line.ts @@ -3,7 +3,7 @@ import * as express from 'express'; import * as request from 'request'; import * as crypto from 'crypto'; import User from '../../../../models/user'; -import config from '../../../../conf'; +import config from '../../../../config'; import BotCore from '../core'; import _redis from '../../../../db/redis'; import prominence = require('prominence'); diff --git a/src/server/api/common/signin.ts b/src/server/api/common/signin.ts index a11ea56c0c..f9688790c4 100644 --- a/src/server/api/common/signin.ts +++ b/src/server/api/common/signin.ts @@ -1,4 +1,4 @@ -import config from '../../../conf'; +import config from '../../../config'; export default function(res, user, redirect: boolean) { const expires = 1000 * 60 * 60 * 24 * 365; // One Year diff --git a/src/server/api/endpoints/auth/session/generate.ts b/src/server/api/endpoints/auth/session/generate.ts index ad03e538ce..9857b31d87 100644 --- a/src/server/api/endpoints/auth/session/generate.ts +++ b/src/server/api/endpoints/auth/session/generate.ts @@ -5,7 +5,7 @@ import * as uuid from 'uuid'; import $ from 'cafy'; import App from '../../../../../models/app'; import AuthSess from '../../../../../models/auth-session'; -import config from '../../../../../conf'; +import config from '../../../../../config'; /** * @swagger diff --git a/src/server/api/endpoints/i/2fa/register.ts b/src/server/api/endpoints/i/2fa/register.ts index d2683fb617..dc7fb959bb 100644 --- a/src/server/api/endpoints/i/2fa/register.ts +++ b/src/server/api/endpoints/i/2fa/register.ts @@ -6,7 +6,7 @@ import * as bcrypt from 'bcryptjs'; import * as speakeasy from 'speakeasy'; import * as QRCode from 'qrcode'; import User from '../../../../../models/user'; -import config from '../../../../../conf'; +import config from '../../../../../config'; module.exports = async (params, user) => new Promise(async (res, rej) => { // Get 'password' parameter diff --git a/src/server/api/endpoints/i/update.ts b/src/server/api/endpoints/i/update.ts index 76ceede8d9..c4ec413399 100644 --- a/src/server/api/endpoints/i/update.ts +++ b/src/server/api/endpoints/i/update.ts @@ -4,7 +4,7 @@ import $ from 'cafy'; import User, { isValidName, isValidDescription, isValidLocation, isValidBirthday, pack } from '../../../../models/user'; import event from '../../../../event'; -import config from '../../../../conf'; +import config from '../../../../config'; /** * Update myself diff --git a/src/server/api/endpoints/messaging/messages/create.ts b/src/server/api/endpoints/messaging/messages/create.ts index e0ec1f216d..604da32d2e 100644 --- a/src/server/api/endpoints/messaging/messages/create.ts +++ b/src/server/api/endpoints/messaging/messages/create.ts @@ -13,7 +13,7 @@ import publishUserStream from '../../../../../event'; import { publishMessagingStream, publishMessagingIndexStream, pushSw } from '../../../../../event'; import html from '../../../../../text/html'; import parse from '../../../../../text/parse'; -import config from '../../../../../conf'; +import config from '../../../../../config'; /** * Create a message diff --git a/src/server/api/endpoints/meta.ts b/src/server/api/endpoints/meta.ts index 4f0ae2a60a..70ae7e99cc 100644 --- a/src/server/api/endpoints/meta.ts +++ b/src/server/api/endpoints/meta.ts @@ -3,7 +3,7 @@ */ import * as os from 'os'; import version from '../../../version'; -import config from '../../../conf'; +import config from '../../../config'; import Meta from '../../../models/meta'; /** diff --git a/src/server/api/endpoints/posts/create.ts b/src/server/api/endpoints/posts/create.ts index e74ac7582e..bf08fe2839 100644 --- a/src/server/api/endpoints/posts/create.ts +++ b/src/server/api/endpoints/posts/create.ts @@ -19,7 +19,7 @@ import event, { pushSw, publishChannelStream } from '../../../../event'; import notify from '../../../../notify'; import getAcct from '../../../../user/get-acct'; import parseAcct from '../../../../user/parse-acct'; -import config from '../../../../conf'; +import config from '../../../../config'; /** * Create a post diff --git a/src/server/api/endpoints/users/search.ts b/src/server/api/endpoints/users/search.ts index 335043b02e..da30f47c2a 100644 --- a/src/server/api/endpoints/users/search.ts +++ b/src/server/api/endpoints/users/search.ts @@ -4,7 +4,7 @@ import * as mongo from 'mongodb'; import $ from 'cafy'; import User, { pack } from '../../../../models/user'; -import config from '../../../../conf'; +import config from '../../../../config'; const escapeRegexp = require('escape-regexp'); /** diff --git a/src/server/api/private/signin.ts b/src/server/api/private/signin.ts index 99ba51b416..bf883ee27a 100644 --- a/src/server/api/private/signin.ts +++ b/src/server/api/private/signin.ts @@ -5,7 +5,7 @@ import User, { ILocalUser } from '../../../models/user'; import Signin, { pack } from '../../../models/signin'; import event from '../../../event'; import signin from '../common/signin'; -import config from '../../../conf'; +import config from '../../../config'; export default async (req: express.Request, res: express.Response) => { res.header('Access-Control-Allow-Origin', config.url); diff --git a/src/server/api/private/signup.ts b/src/server/api/private/signup.ts index 45b978d0b6..4203ce526d 100644 --- a/src/server/api/private/signup.ts +++ b/src/server/api/private/signup.ts @@ -5,7 +5,7 @@ import { generate as generateKeypair } from '../../../crypto_key'; import recaptcha = require('recaptcha-promise'); import User, { IUser, validateUsername, validatePassword, pack } from '../../../models/user'; import generateUserToken from '../common/generate-native-user-token'; -import config from '../../../conf'; +import config from '../../../config'; recaptcha.init({ secret_key: config.recaptcha.secret_key diff --git a/src/server/api/service/github.ts b/src/server/api/service/github.ts index b4068c729e..4fd59c2a94 100644 --- a/src/server/api/service/github.ts +++ b/src/server/api/service/github.ts @@ -2,7 +2,7 @@ import * as EventEmitter from 'events'; import * as express from 'express'; //const crypto = require('crypto'); import User from '../../../models/user'; -import config from '../../../conf'; +import config from '../../../config'; import queue from '../../../queue'; module.exports = async (app: express.Application) => { diff --git a/src/server/api/service/twitter.ts b/src/server/api/service/twitter.ts index 61a1d56397..69fa5f3c67 100644 --- a/src/server/api/service/twitter.ts +++ b/src/server/api/service/twitter.ts @@ -7,7 +7,7 @@ import autwh from 'autwh'; import redis from '../../../db/redis'; import User, { pack } from '../../../models/user'; import event from '../../../event'; -import config from '../../../conf'; +import config from '../../../config'; import signin from '../common/signin'; module.exports = (app: express.Application) => { diff --git a/src/server/api/streaming.ts b/src/server/api/streaming.ts index c86c6a8b4a..edcf505d24 100644 --- a/src/server/api/streaming.ts +++ b/src/server/api/streaming.ts @@ -1,7 +1,7 @@ import * as http from 'http'; import * as websocket from 'websocket'; import * as redis from 'redis'; -import config from '../../conf'; +import config from '../../config'; import { default as User, IUser } from '../../models/user'; import AccessToken from '../../models/access-token'; import isNativeToken from './common/is-native-token'; diff --git a/src/server/index.ts b/src/server/index.ts index 1874790116..61a8739b43 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -12,7 +12,7 @@ import Accesses from 'accesses'; import activityPub from './activitypub'; import webFinger from './webfinger'; import log from './log-request'; -import config from '../conf'; +import config from '../config'; /** * Init app diff --git a/src/server/webfinger.ts b/src/server/webfinger.ts index 7a28b1bec8..3b0f416d6f 100644 --- a/src/server/webfinger.ts +++ b/src/server/webfinger.ts @@ -1,4 +1,4 @@ -import config from '../conf'; +import config from '../config'; import parseAcct from '../user/parse-acct'; import User from '../models/user'; const express = require('express'); diff --git a/webpack.config.ts b/webpack.config.ts index d486e100a2..60dbfd2ff7 100644 --- a/webpack.config.ts +++ b/webpack.config.ts @@ -14,7 +14,7 @@ const ProgressBarPlugin = require('progress-bar-webpack-plugin'); import I18nReplacer from './src/build/i18n'; import { pattern as faPattern, replacement as faReplacement } from './src/build/fa'; const constants = require('./src/const.json'); -import config from './src/conf'; +import config from './src/config'; import { licenseHtml } from './src/build/license'; import locales from './locales'; -- cgit v1.2.3-freya From 5ceb808c6f91a76e16e30b4c7b9efbac5f3b6931 Mon Sep 17 00:00:00 2001 From: syuilo Date: Mon, 2 Apr 2018 18:59:48 +0900 Subject: :v: --- package.json | 1 + src/client/docs/api/gulpfile.ts | 2 +- src/client/docs/vars.ts | 4 ++-- src/index.ts | 3 +++ 4 files changed, 7 insertions(+), 3 deletions(-) (limited to 'src/client/docs/api') diff --git a/package.json b/package.json index 07b30e16b3..8020743656 100644 --- a/package.json +++ b/package.json @@ -56,6 +56,7 @@ "@types/is-root": "1.0.0", "@types/is-url": "1.2.28", "@types/js-yaml": "3.11.1", + "@types/kue": "^0.11.8", "@types/license-checker": "15.0.0", "@types/mkdirp": "0.5.2", "@types/mocha": "5.0.0", diff --git a/src/client/docs/api/gulpfile.ts b/src/client/docs/api/gulpfile.ts index c986e0353a..9980ede231 100644 --- a/src/client/docs/api/gulpfile.ts +++ b/src/client/docs/api/gulpfile.ts @@ -151,7 +151,7 @@ gulp.task('doc:api:entities', async () => { return; } files.forEach(file => { - const entity = yaml.safeLoad(fs.readFileSync(file, 'utf-8')); + const entity = yaml.safeLoad(fs.readFileSync(file, 'utf-8')) as any; const vars = { name: entity.name, desc: entity.desc, diff --git a/src/client/docs/vars.ts b/src/client/docs/vars.ts index dbdc880611..32b961aaa9 100644 --- a/src/client/docs/vars.ts +++ b/src/client/docs/vars.ts @@ -15,13 +15,13 @@ export default async function(): Promise<{ [key: string]: any }> { const endpoints = glob.sync('./src/client/docs/api/endpoints/**/*.yaml'); vars['endpoints'] = endpoints.map(ep => { - const _ep = yaml.safeLoad(fs.readFileSync(ep, 'utf-8')); + const _ep = yaml.safeLoad(fs.readFileSync(ep, 'utf-8')) as any; return _ep.endpoint; }); const entities = glob.sync('./src/client/docs/api/entities/**/*.yaml'); vars['entities'] = entities.map(x => { - const _x = yaml.safeLoad(fs.readFileSync(x, 'utf-8')); + const _x = yaml.safeLoad(fs.readFileSync(x, 'utf-8')) as any; return _x.name; }); diff --git a/src/index.ts b/src/index.ts index b43e152854..29c4f3431a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -30,6 +30,9 @@ const ev = new Xev(); process.title = 'Misskey'; +// https://github.com/Automattic/kue/issues/822 +require('events').EventEmitter.prototype._maxListeners = 256; + // Start app main(); -- cgit v1.2.3-freya From a1b490afa756a71b9cef4afa424575bc223bc612 Mon Sep 17 00:00:00 2001 From: syuilo Date: Sun, 8 Apr 2018 02:30:37 +0900 Subject: Post --> Note Closes #1411 --- locales/en.yml | 86 +-- locales/ja.yml | 74 +-- src/client/app/auth/views/form.vue | 2 +- src/client/app/ch/tags/channel.tag | 68 +-- src/client/app/common/mios.ts | 4 +- .../app/common/scripts/compose-notification.ts | 10 +- .../app/common/scripts/parse-search-query.ts | 4 +- src/client/app/common/views/components/index.ts | 4 +- .../views/components/messaging-room.message.vue | 2 +- .../app/common/views/components/note-html.ts | 157 ++++++ .../app/common/views/components/note-menu.vue | 141 +++++ src/client/app/common/views/components/poll.vue | 8 +- .../app/common/views/components/post-html.ts | 157 ------ .../app/common/views/components/post-menu.vue | 141 ----- .../common/views/components/reaction-picker.vue | 6 +- .../common/views/components/reactions-viewer.vue | 4 +- .../common/views/components/welcome-timeline.vue | 26 +- src/client/app/desktop/api/post.ts | 8 +- src/client/app/desktop/script.ts | 16 +- .../desktop/views/components/activity.calendar.vue | 2 +- .../desktop/views/components/activity.chart.vue | 16 +- src/client/app/desktop/views/components/index.ts | 32 +- .../app/desktop/views/components/mentions.vue | 24 +- .../desktop/views/components/note-detail.sub.vue | 130 +++++ .../app/desktop/views/components/note-detail.vue | 448 ++++++++++++++++ .../app/desktop/views/components/note-preview.vue | 107 ++++ .../desktop/views/components/notes.note.sub.vue | 116 ++++ .../app/desktop/views/components/notes.note.vue | 596 +++++++++++++++++++++ src/client/app/desktop/views/components/notes.vue | 89 +++ .../app/desktop/views/components/notifications.vue | 54 +- .../desktop/views/components/post-detail.sub.vue | 130 ----- .../app/desktop/views/components/post-detail.vue | 84 +-- .../desktop/views/components/post-form-window.vue | 6 +- .../app/desktop/views/components/post-form.vue | 32 +- .../app/desktop/views/components/post-preview.vue | 107 ---- .../desktop/views/components/posts.post.sub.vue | 116 ---- .../app/desktop/views/components/posts.post.vue | 104 ++-- src/client/app/desktop/views/components/posts.vue | 89 --- .../views/components/renote-form-window.vue | 42 ++ .../app/desktop/views/components/renote-form.vue | 131 +++++ .../views/components/repost-form-window.vue | 6 +- .../app/desktop/views/components/repost-form.vue | 26 +- .../desktop/views/components/sub-note-content.vue | 44 ++ .../desktop/views/components/sub-post-content.vue | 44 -- .../app/desktop/views/components/timeline.vue | 42 +- .../desktop/views/components/ui.header.post.vue | 6 +- .../app/desktop/views/components/user-preview.vue | 2 +- src/client/app/desktop/views/pages/home.vue | 12 +- src/client/app/desktop/views/pages/note.vue | 67 +++ src/client/app/desktop/views/pages/post.vue | 67 --- src/client/app/desktop/views/pages/search.vue | 32 +- .../app/desktop/views/pages/user/user.home.vue | 2 +- .../app/desktop/views/pages/user/user.photos.vue | 8 +- .../app/desktop/views/pages/user/user.profile.vue | 2 +- .../app/desktop/views/pages/user/user.timeline.vue | 24 +- .../desktop/views/widgets/channel.channel.form.vue | 4 +- .../desktop/views/widgets/channel.channel.note.vue | 75 +++ .../desktop/views/widgets/channel.channel.post.vue | 75 --- .../app/desktop/views/widgets/channel.channel.vue | 36 +- src/client/app/desktop/views/widgets/polls.vue | 8 +- src/client/app/desktop/views/widgets/post-form.vue | 4 +- src/client/app/desktop/views/widgets/trends.vue | 24 +- src/client/app/dev/views/new-app.vue | 2 +- src/client/app/mobile/api/post.ts | 20 +- src/client/app/mobile/script.ts | 4 +- .../app/mobile/views/components/activity.vue | 16 +- src/client/app/mobile/views/components/index.ts | 24 +- .../app/mobile/views/components/note-card.vue | 93 ++++ .../mobile/views/components/note-detail.sub.vue | 113 ++++ .../app/mobile/views/components/note-detail.vue | 462 ++++++++++++++++ .../app/mobile/views/components/note-preview.vue | 110 ++++ .../app/mobile/views/components/note.sub.vue | 119 ++++ src/client/app/mobile/views/components/note.vue | 540 +++++++++++++++++++ src/client/app/mobile/views/components/notes.vue | 111 ++++ .../views/components/notification-preview.vue | 42 +- .../app/mobile/views/components/notification.vue | 38 +- .../app/mobile/views/components/post-card.vue | 93 ---- .../mobile/views/components/post-detail.sub.vue | 113 ---- .../app/mobile/views/components/post-detail.vue | 80 +-- .../app/mobile/views/components/post-form.vue | 10 +- .../app/mobile/views/components/post-preview.vue | 110 ---- .../app/mobile/views/components/post.sub.vue | 119 ---- src/client/app/mobile/views/components/post.vue | 90 ++-- src/client/app/mobile/views/components/posts.vue | 111 ---- .../mobile/views/components/sub-note-content.vue | 43 ++ .../mobile/views/components/sub-post-content.vue | 43 -- .../app/mobile/views/components/timeline.vue | 38 +- .../app/mobile/views/components/user-timeline.vue | 32 +- src/client/app/mobile/views/pages/home.vue | 12 +- src/client/app/mobile/views/pages/note.vue | 85 +++ src/client/app/mobile/views/pages/post.vue | 85 --- src/client/app/mobile/views/pages/search.vue | 30 +- src/client/app/mobile/views/pages/user.vue | 8 +- .../app/mobile/views/pages/user/home.notes.vue | 57 ++ .../app/mobile/views/pages/user/home.photos.vue | 12 +- .../app/mobile/views/pages/user/home.posts.vue | 57 -- src/client/app/mobile/views/pages/user/home.vue | 14 +- src/client/app/stats/tags/index.tag | 28 +- src/client/docs/api/endpoints/notes/create.yaml | 59 ++ src/client/docs/api/endpoints/notes/timeline.yaml | 32 ++ src/client/docs/api/endpoints/posts/create.yaml | 59 -- src/client/docs/api/endpoints/posts/timeline.yaml | 32 -- src/client/docs/api/entities/note.yaml | 174 ++++++ src/client/docs/api/entities/post.yaml | 40 +- src/client/docs/api/entities/user.yaml | 16 +- src/client/docs/mute.ja.pug | 2 +- src/client/docs/search.ja.pug | 16 +- src/models/favorite.ts | 2 +- src/models/note-reaction.ts | 53 ++ src/models/note-watching.ts | 13 + src/models/note.ts | 241 +++++++++ src/models/notification.ts | 14 +- src/models/poll-vote.ts | 2 +- src/models/post-reaction.ts | 53 -- src/models/post-watching.ts | 13 - src/models/post.ts | 241 --------- src/models/user.ts | 18 +- src/othello/ai/back.ts | 10 +- src/othello/ai/front.ts | 18 +- src/publishers/stream.ts | 6 +- src/queue/processors/http/report-github-failure.ts | 4 +- src/remote/activitypub/act/create/note.ts | 22 +- src/remote/activitypub/act/delete/index.ts | 6 +- src/remote/activitypub/act/delete/note.ts | 12 +- src/remote/activitypub/act/like.ts | 12 +- src/remote/activitypub/renderer/like.ts | 4 +- src/remote/activitypub/renderer/note.ts | 28 +- src/remote/activitypub/resolve-person.ts | 4 +- src/renderers/get-note-summary.ts | 45 ++ src/renderers/get-notification-summary.ts | 16 +- src/renderers/get-post-summary.ts | 45 -- src/renderers/get-user-summary.ts | 2 +- src/server/activitypub/index.ts | 4 +- src/server/activitypub/note.ts | 43 ++ src/server/activitypub/outbox.ts | 8 +- src/server/activitypub/post.ts | 43 -- src/server/api/bot/core.ts | 22 +- src/server/api/bot/interfaces/line.ts | 18 +- src/server/api/endpoints.ts | 54 +- .../api/endpoints/aggregation/notes/reaction.ts | 76 +++ .../api/endpoints/aggregation/notes/reactions.ts | 72 +++ .../api/endpoints/aggregation/notes/reply.ts | 75 +++ .../api/endpoints/aggregation/notes/repost.ts | 75 +++ src/server/api/endpoints/aggregation/posts.ts | 22 +- .../api/endpoints/aggregation/posts/reaction.ts | 76 --- .../api/endpoints/aggregation/posts/reactions.ts | 72 --- .../api/endpoints/aggregation/posts/reply.ts | 75 --- .../api/endpoints/aggregation/posts/repost.ts | 75 --- .../api/endpoints/aggregation/users/activity.ts | 20 +- src/server/api/endpoints/aggregation/users/post.ts | 22 +- .../api/endpoints/aggregation/users/reaction.ts | 2 +- src/server/api/endpoints/app/create.ts | 2 +- src/server/api/endpoints/app/name_id/available.ts | 2 +- src/server/api/endpoints/app/show.ts | 2 +- src/server/api/endpoints/auth/accept.ts | 2 +- src/server/api/endpoints/auth/session/generate.ts | 2 +- src/server/api/endpoints/auth/session/show.ts | 2 +- src/server/api/endpoints/auth/session/userkey.ts | 2 +- src/server/api/endpoints/channels/posts.ts | 10 +- src/server/api/endpoints/i/favorites.ts | 4 +- src/server/api/endpoints/i/pin.ts | 20 +- src/server/api/endpoints/meta.ts | 2 +- src/server/api/endpoints/notes/context.ts | 63 +++ src/server/api/endpoints/notes/create.ts | 251 +++++++++ src/server/api/endpoints/notes/favorites/create.ts | 48 ++ src/server/api/endpoints/notes/favorites/delete.ts | 46 ++ src/server/api/endpoints/notes/mentions.ts | 78 +++ .../api/endpoints/notes/polls/recommendation.ts | 59 ++ src/server/api/endpoints/notes/polls/vote.ts | 115 ++++ src/server/api/endpoints/notes/reactions.ts | 57 ++ src/server/api/endpoints/notes/reactions/create.ts | 47 ++ src/server/api/endpoints/notes/reactions/delete.ts | 60 +++ src/server/api/endpoints/notes/replies.ts | 53 ++ src/server/api/endpoints/notes/reposts.ts | 73 +++ src/server/api/endpoints/notes/search.ts | 364 +++++++++++++ src/server/api/endpoints/notes/show.ts | 32 ++ src/server/api/endpoints/notes/timeline.ts | 132 +++++ src/server/api/endpoints/notes/trend.ts | 79 +++ src/server/api/endpoints/posts.ts | 18 +- src/server/api/endpoints/posts/context.ts | 63 --- src/server/api/endpoints/posts/create.ts | 108 ++-- src/server/api/endpoints/posts/favorites/create.ts | 48 -- src/server/api/endpoints/posts/favorites/delete.ts | 46 -- src/server/api/endpoints/posts/mentions.ts | 78 --- .../api/endpoints/posts/polls/recommendation.ts | 59 -- src/server/api/endpoints/posts/polls/vote.ts | 115 ---- src/server/api/endpoints/posts/reactions.ts | 57 -- src/server/api/endpoints/posts/reactions/create.ts | 47 -- src/server/api/endpoints/posts/reactions/delete.ts | 60 --- src/server/api/endpoints/posts/replies.ts | 53 -- src/server/api/endpoints/posts/reposts.ts | 73 --- src/server/api/endpoints/posts/search.ts | 364 ------------- src/server/api/endpoints/posts/show.ts | 32 -- src/server/api/endpoints/posts/timeline.ts | 132 ----- src/server/api/endpoints/posts/trend.ts | 79 --- src/server/api/endpoints/stats.ts | 12 +- .../users/get_frequently_replied_users.ts | 18 +- src/server/api/endpoints/users/posts.ts | 10 +- src/server/api/private/signup.ts | 2 +- src/server/api/service/github.ts | 2 +- src/server/api/stream/home.ts | 24 +- src/services/note/create.ts | 357 ++++++++++++ src/services/note/reaction/create.ts | 94 ++++ src/services/note/watch.ts | 26 + src/services/post/create.ts | 358 ------------- src/services/post/reaction/create.ts | 94 ---- src/services/post/watch.ts | 26 - tools/migration/nighthike/11.js | 35 ++ 208 files changed, 7945 insertions(+), 5267 deletions(-) create mode 100644 src/client/app/common/views/components/note-html.ts create mode 100644 src/client/app/common/views/components/note-menu.vue delete mode 100644 src/client/app/common/views/components/post-html.ts delete mode 100644 src/client/app/common/views/components/post-menu.vue create mode 100644 src/client/app/desktop/views/components/note-detail.sub.vue create mode 100644 src/client/app/desktop/views/components/note-detail.vue create mode 100644 src/client/app/desktop/views/components/note-preview.vue create mode 100644 src/client/app/desktop/views/components/notes.note.sub.vue create mode 100644 src/client/app/desktop/views/components/notes.note.vue create mode 100644 src/client/app/desktop/views/components/notes.vue delete mode 100644 src/client/app/desktop/views/components/post-detail.sub.vue delete mode 100644 src/client/app/desktop/views/components/post-preview.vue delete mode 100644 src/client/app/desktop/views/components/posts.post.sub.vue delete mode 100644 src/client/app/desktop/views/components/posts.vue create mode 100644 src/client/app/desktop/views/components/renote-form-window.vue create mode 100644 src/client/app/desktop/views/components/renote-form.vue create mode 100644 src/client/app/desktop/views/components/sub-note-content.vue delete mode 100644 src/client/app/desktop/views/components/sub-post-content.vue create mode 100644 src/client/app/desktop/views/pages/note.vue delete mode 100644 src/client/app/desktop/views/pages/post.vue create mode 100644 src/client/app/desktop/views/widgets/channel.channel.note.vue delete mode 100644 src/client/app/desktop/views/widgets/channel.channel.post.vue create mode 100644 src/client/app/mobile/views/components/note-card.vue create mode 100644 src/client/app/mobile/views/components/note-detail.sub.vue create mode 100644 src/client/app/mobile/views/components/note-detail.vue create mode 100644 src/client/app/mobile/views/components/note-preview.vue create mode 100644 src/client/app/mobile/views/components/note.sub.vue create mode 100644 src/client/app/mobile/views/components/note.vue create mode 100644 src/client/app/mobile/views/components/notes.vue delete mode 100644 src/client/app/mobile/views/components/post-card.vue delete mode 100644 src/client/app/mobile/views/components/post-detail.sub.vue delete mode 100644 src/client/app/mobile/views/components/post-preview.vue delete mode 100644 src/client/app/mobile/views/components/post.sub.vue delete mode 100644 src/client/app/mobile/views/components/posts.vue create mode 100644 src/client/app/mobile/views/components/sub-note-content.vue delete mode 100644 src/client/app/mobile/views/components/sub-post-content.vue create mode 100644 src/client/app/mobile/views/pages/note.vue delete mode 100644 src/client/app/mobile/views/pages/post.vue create mode 100644 src/client/app/mobile/views/pages/user/home.notes.vue delete mode 100644 src/client/app/mobile/views/pages/user/home.posts.vue create mode 100644 src/client/docs/api/endpoints/notes/create.yaml create mode 100644 src/client/docs/api/endpoints/notes/timeline.yaml delete mode 100644 src/client/docs/api/endpoints/posts/create.yaml delete mode 100644 src/client/docs/api/endpoints/posts/timeline.yaml create mode 100644 src/client/docs/api/entities/note.yaml create mode 100644 src/models/note-reaction.ts create mode 100644 src/models/note-watching.ts create mode 100644 src/models/note.ts delete mode 100644 src/models/post-reaction.ts delete mode 100644 src/models/post-watching.ts delete mode 100644 src/models/post.ts create mode 100644 src/renderers/get-note-summary.ts delete mode 100644 src/renderers/get-post-summary.ts create mode 100644 src/server/activitypub/note.ts delete mode 100644 src/server/activitypub/post.ts create mode 100644 src/server/api/endpoints/aggregation/notes/reaction.ts create mode 100644 src/server/api/endpoints/aggregation/notes/reactions.ts create mode 100644 src/server/api/endpoints/aggregation/notes/reply.ts create mode 100644 src/server/api/endpoints/aggregation/notes/repost.ts delete mode 100644 src/server/api/endpoints/aggregation/posts/reaction.ts delete mode 100644 src/server/api/endpoints/aggregation/posts/reactions.ts delete mode 100644 src/server/api/endpoints/aggregation/posts/reply.ts delete mode 100644 src/server/api/endpoints/aggregation/posts/repost.ts create mode 100644 src/server/api/endpoints/notes/context.ts create mode 100644 src/server/api/endpoints/notes/create.ts create mode 100644 src/server/api/endpoints/notes/favorites/create.ts create mode 100644 src/server/api/endpoints/notes/favorites/delete.ts create mode 100644 src/server/api/endpoints/notes/mentions.ts create mode 100644 src/server/api/endpoints/notes/polls/recommendation.ts create mode 100644 src/server/api/endpoints/notes/polls/vote.ts create mode 100644 src/server/api/endpoints/notes/reactions.ts create mode 100644 src/server/api/endpoints/notes/reactions/create.ts create mode 100644 src/server/api/endpoints/notes/reactions/delete.ts create mode 100644 src/server/api/endpoints/notes/replies.ts create mode 100644 src/server/api/endpoints/notes/reposts.ts create mode 100644 src/server/api/endpoints/notes/search.ts create mode 100644 src/server/api/endpoints/notes/show.ts create mode 100644 src/server/api/endpoints/notes/timeline.ts create mode 100644 src/server/api/endpoints/notes/trend.ts delete mode 100644 src/server/api/endpoints/posts/context.ts delete mode 100644 src/server/api/endpoints/posts/favorites/create.ts delete mode 100644 src/server/api/endpoints/posts/favorites/delete.ts delete mode 100644 src/server/api/endpoints/posts/mentions.ts delete mode 100644 src/server/api/endpoints/posts/polls/recommendation.ts delete mode 100644 src/server/api/endpoints/posts/polls/vote.ts delete mode 100644 src/server/api/endpoints/posts/reactions.ts delete mode 100644 src/server/api/endpoints/posts/reactions/create.ts delete mode 100644 src/server/api/endpoints/posts/reactions/delete.ts delete mode 100644 src/server/api/endpoints/posts/replies.ts delete mode 100644 src/server/api/endpoints/posts/reposts.ts delete mode 100644 src/server/api/endpoints/posts/search.ts delete mode 100644 src/server/api/endpoints/posts/show.ts delete mode 100644 src/server/api/endpoints/posts/timeline.ts delete mode 100644 src/server/api/endpoints/posts/trend.ts create mode 100644 src/services/note/create.ts create mode 100644 src/services/note/reaction/create.ts create mode 100644 src/services/note/watch.ts delete mode 100644 src/services/post/create.ts delete mode 100644 src/services/post/reaction/create.ts delete mode 100644 src/services/post/watch.ts create mode 100644 tools/migration/nighthike/11.js (limited to 'src/client/docs/api') diff --git a/locales/en.yml b/locales/en.yml index 2cc857f698..900571124f 100644 --- a/locales/en.yml +++ b/locales/en.yml @@ -33,7 +33,7 @@ common: confused: "Confused" pudding: "Pudding" - post_categories: + note_categories: music: "Music" game: "Video 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 category" @@ -211,7 +211,7 @@ ch: textarea: "Write here" upload: "Upload" drive: "Drive" - post: "Do" + note: "Do" posting: "Doing" desktop: @@ -304,8 +304,8 @@ desktop: settings: "Settings" signout: "Sign out" - mk-ui-header-post-button: - post: "Compose new Post" + mk-ui-header-note-button: + note: "Compose new Post" mk-ui-header-notifications: title: "Notifications" @@ -350,18 +350,18 @@ desktop: no-users: "No muted users" mk-post-form: - post-placeholder: "What's happening?" - reply-placeholder: "Reply to this post..." - quote-placeholder: "Quote this post..." - post: "Post" + note-placeholder: "What's happening?" + reply-placeholder: "Reply to this note..." + quote-placeholder: "Quote this note..." + note: "Post" reply: "Reply" - repost: "Repost" + renote: "Renote" posted: "Posted!" replied: "Replied!" reposted: "Reposted!" - post-failed: "Failed to post" + note-failed: "Failed to note" reply-failed: "Failed to reply" - repost-failed: "Failed to repost" + renote-failed: "Failed to renote" posting: "Posting" attach-media-from-local: "Attach media from your pc" attach-media-from-drive: "Attach media from the drive" @@ -371,14 +371,14 @@ desktop: text-remain: "{} chars remaining" mk-post-form-window: - post: "New post" + note: "New note" reply: "Reply" attaches: "{} media attached" uploading-media: "Uploading {} media" - mk-post-page: - prev: "Previous post" - next: "Next post" + mk-note-page: + prev: "Previous note" + next: "Next note" mk-settings: profile: "Profile" @@ -390,10 +390,10 @@ desktop: other: "Other" license: "License" - mk-timeline-post: + mk-timeline-note: reposted-by: "Reposted by {}" reply: "Reply" - repost: "Repost" + renote: "Renote" add-reaction: "Add your reaction" detail: "Show detail" @@ -448,7 +448,7 @@ desktop: mk-post-form-home-widget: title: "Post" - post: "Post" + note: "Post" placeholder: "What's happening?" 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: "Reposted!" - failure: "Failed to Repost" + failure: "Failed to Renote" - mk-repost-form-window: - title: "Are you sure you want to repost this post?" + mk-renote-form-window: + title: "Are you sure you want to renote this note?" mk-user: last-used-at: "Last used at" @@ -541,10 +541,10 @@ mobile: notifications: "Notifications" read-all: "Are you sure you want to mark all unread notifications as read?" - mk-post-page: + mk-note-page: title: "Post" - prev: "Previous post" - next: "Next post" + prev: "Previous note" + next: "Next note" mk-search-page: search: "Search" @@ -606,33 +606,33 @@ mobile: unfollow: "Unfollow" mk-home-timeline: - empty-timeline: "There is no posts" + empty-timeline: "There is no notes" mk-notifications: more: "More" empty: "No notifications" - mk-post-detail: + mk-note-detail: reply: "Reply" reaction: "Reaction" mk-post-form: submit: "Post" - reply-placeholder: "Reply to this post..." - post-placeholder: "What's happening?" + reply-placeholder: "Reply to this note..." + note-placeholder: "What's happening?" - mk-search-posts: - empty: "There is no post related to the 「{}」" + mk-search-notes: + empty: "There is no note related to the 「{}」" - mk-sub-post-content: + mk-sub-note-content: media-count: "{} media" poll: "Poll" - mk-timeline-post: + mk-timeline-note: reposted-by: "Reposted by {}" mk-timeline: - empty: "No posts" + empty: "No notes" load-more: "More" mk-ui-nav: @@ -652,21 +652,21 @@ mobile: no-users: "No following." mk-user-timeline: - no-posts: "This user seems never post" - no-posts-with-media: "There is no posts with media" + no-notes: "This user seems never note" + no-notes-with-media: "There is no notes with media" load-more: "More" mk-user: follows-you: "Follows you" following: "Following" followers: "Followers" - posts: "Posts" + notes: "Posts" overview: "Overview" timeline: "Timeline" media: "Media" mk-user-overview: - recent-posts: "Recent posts" + recent-notes: "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 posts" + no-notes: "No notes" mk-user-overview-photos: loading: "Loading" @@ -703,7 +703,7 @@ mobile: load-more: "More" stats: - posts-count: "Number of all posts" + notes-count: "Number of all notes" users-count: "Number of all users" status: diff --git a/locales/ja.yml b/locales/ja.yml index fd140ecc30..84694e3c74 100644 --- a/locales/ja.yml +++ b/locales/ja.yml @@ -33,7 +33,7 @@ common: confused: "こまこまのこまり" pudding: "Pudding" - post_categories: + note_categories: music: "音楽" game: "ゲーム" anime: "アニメ" @@ -124,7 +124,7 @@ common: show-result: "結果を見る" voted: "投票済み" - mk-post-menu: + mk-note-menu: pin: "ピン留め" pinned: "ピン留めしました" select: "カテゴリを選択" @@ -211,7 +211,7 @@ ch: textarea: "書いて" upload: "アップロード" drive: "ドライブ" - post: "やる" + note: "やる" posting: "やってます" desktop: @@ -304,8 +304,8 @@ desktop: settings: "設定" signout: "サインアウト" - mk-ui-header-post-button: - post: "新規投稿" + mk-ui-header-note-button: + note: "新規投稿" mk-ui-header-notifications: title: "通知" @@ -350,18 +350,18 @@ desktop: no-users: "ミュートしているユーザーはいません" mk-post-form: - post-placeholder: "いまどうしてる?" + note-placeholder: "いまどうしてる?" reply-placeholder: "この投稿への返信..." quote-placeholder: "この投稿を引用..." - post: "投稿" + note: "投稿" reply: "返信" - repost: "Repost" + renote: "Renote" posted: "投稿しました!" replied: "返信しました!" - reposted: "Repostしました!" - post-failed: "投稿に失敗しました" + reposted: "Renoteしました!" + note-failed: "投稿に失敗しました" reply-failed: "返信に失敗しました" - repost-failed: "Repostに失敗しました" + renote-failed: "Renoteに失敗しました" posting: "投稿中" attach-media-from-local: "PCからメディアを添付" attach-media-from-drive: "ドライブからメディアを添付" @@ -371,12 +371,12 @@ desktop: text-remain: "のこり{}文字" mk-post-form-window: - post: "新規投稿" + note: "新規投稿" reply: "返信" attaches: "添付: {}メディア" uploading-media: "{}個のメディアをアップロード中" - mk-post-page: + mk-note-page: prev: "前の投稿" next: "次の投稿" @@ -390,10 +390,10 @@ desktop: other: "その他" license: "ライセンス" - mk-timeline-post: - reposted-by: "{}がRepost" + mk-timeline-note: + reposted-by: "{}がRenote" reply: "返信" - repost: "Repost" + renote: "Renote" add-reaction: "リアクション" detail: "詳細" @@ -448,7 +448,7 @@ desktop: mk-post-form-home-widget: title: "投稿" - post: "投稿" + note: "投稿" placeholder: "いまどうしてる?" mk-access-log-home-widget: @@ -463,16 +463,16 @@ desktop: have-a-nice-day: "良い一日を!" next: "次" - mk-repost-form: + mk-renote-form: quote: "引用する..." cancel: "キャンセル" - repost: "Repost" + renote: "Renote" reposting: "しています..." - success: "Repostしました!" - failure: "Repostに失敗しました" + success: "Renoteしました!" + failure: "Renoteに失敗しました" - mk-repost-form-window: - title: "この投稿をRepostしますか?" + mk-renote-form-window: + title: "この投稿をRenoteしますか?" mk-user: last-used-at: "最終アクセス" @@ -541,7 +541,7 @@ mobile: notifications: "通知" read-all: "すべての通知を既読にしますか?" - mk-post-page: + mk-note-page: title: "投稿" prev: "前の投稿" next: "次の投稿" @@ -612,24 +612,24 @@ mobile: more: "もっと見る" empty: "ありません!" - mk-post-detail: + mk-note-detail: reply: "返信" reaction: "リアクション" mk-post-form: submit: "投稿" reply-placeholder: "この投稿への返信..." - post-placeholder: "いまどうしてる?" + note-placeholder: "いまどうしてる?" - mk-search-posts: + mk-search-notes: empty: "「{}」に関する投稿は見つかりませんでした。" - mk-sub-post-content: + mk-sub-note-content: media-count: "{}個のメディア" poll: "投票" - mk-timeline-post: - reposted-by: "{}がRepost" + mk-timeline-note: + reposted-by: "{}がRenote" mk-timeline: empty: "表示するものがありません" @@ -652,21 +652,21 @@ mobile: no-users: "フォロー中のユーザーはいないようです。" mk-user-timeline: - no-posts: "このユーザーはまだ投稿していないようです。" - no-posts-with-media: "メディア付き投稿はありません。" + no-notes: "このユーザーはまだ投稿していないようです。" + no-notes-with-media: "メディア付き投稿はありません。" load-more: "もっとみる" mk-user: follows-you: "フォローされています" following: "フォロー" followers: "フォロワー" - posts: "投稿" + notes: "投稿" overview: "概要" timeline: "タイムライン" media: "メディア" mk-user-overview: - recent-posts: "最近の投稿" + recent-notes: "最近の投稿" images: "画像" activity: "アクティビティ" keywords: "キーワード" @@ -675,9 +675,9 @@ mobile: followers-you-know: "知り合いのフォロワー" last-used-at: "最終ログイン" - mk-user-overview-posts: + mk-user-overview-notes: loading: "読み込み中" - no-posts: "投稿はありません" + no-notes: "投稿はありません" mk-user-overview-photos: loading: "読み込み中" @@ -703,7 +703,7 @@ mobile: load-more: "もっと" stats: - posts-count: "投稿の数" + notes-count: "投稿の数" users-count: "アカウントの数" status: diff --git a/src/client/app/auth/views/form.vue b/src/client/app/auth/views/form.vue index eb55b9035b..b323907eb0 100644 --- a/src/client/app/auth/views/form.vue +++ b/src/client/app/auth/views/form.vue @@ -16,7 +16,7 @@ @@ -27,7 +27,7 @@ export default Vue.extend({ moreFetching: false, mode: 'default', unreadCount: 0, - posts: [], + notes: [], date: null }; }, @@ -38,7 +38,7 @@ export default Vue.extend({ }, computed: { empty(): boolean { - return this.posts.length == 0; + return this.notes.length == 0; } }, mounted() { @@ -60,26 +60,26 @@ export default Vue.extend({ } }, fetch(cb?) { - (this as any).api('users/posts', { + (this as any).api('users/notes', { userId: this.user.id, untilDate: this.date ? this.date.getTime() : undefined, with_replies: this.mode == 'with-replies' - }).then(posts => { - this.posts = posts; + }).then(notes => { + this.notes = notes; this.fetching = false; if (cb) cb(); }); }, 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 as any).api('users/posts', { + (this as any).api('users/notes', { userId: this.user.id, with_replies: this.mode == 'with-replies', - untilId: this.posts[this.posts.length - 1].id - }).then(posts => { + untilId: this.notes[this.notes.length - 1].id + }).then(notes => { this.moreFetching = false; - this.posts = this.posts.concat(posts); + this.notes = this.notes.concat(notes); }); }, onScroll() { diff --git a/src/client/app/desktop/views/widgets/channel.channel.form.vue b/src/client/app/desktop/views/widgets/channel.channel.form.vue index aaf327f1ef..f2744268bb 100644 --- a/src/client/app/desktop/views/widgets/channel.channel.form.vue +++ b/src/client/app/desktop/views/widgets/channel.channel.form.vue @@ -24,11 +24,11 @@ export default Vue.extend({ if (/^>>([0-9]+) /.test(this.text)) { 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 as any).api('posts/create', { + (this as any).api('notes/create', { text: this.text, replyId: reply ? reply.id : undefined, channelId: (this.$parent as any).channel.id diff --git a/src/client/app/desktop/views/widgets/channel.channel.note.vue b/src/client/app/desktop/views/widgets/channel.channel.note.vue new file mode 100644 index 0000000000..313a2e3f4f --- /dev/null +++ b/src/client/app/desktop/views/widgets/channel.channel.note.vue @@ -0,0 +1,75 @@ + + + + + diff --git a/src/client/app/desktop/views/widgets/channel.channel.post.vue b/src/client/app/desktop/views/widgets/channel.channel.post.vue deleted file mode 100644 index fa6d8c34a5..0000000000 --- a/src/client/app/desktop/views/widgets/channel.channel.post.vue +++ /dev/null @@ -1,75 +0,0 @@ - - - - - diff --git a/src/client/app/desktop/views/widgets/channel.channel.vue b/src/client/app/desktop/views/widgets/channel.channel.vue index e9fb9e3fd7..ea4d8f8454 100644 --- a/src/client/app/desktop/views/widgets/channel.channel.vue +++ b/src/client/app/desktop/views/widgets/channel.channel.vue @@ -1,9 +1,9 @@ - +
@@ -36,7 +36,7 @@ export default define({ post() { this.posting = true; - (this as any).api('posts/create', { + (this as any).api('notes/create', { text: this.text }).then(data => { this.clear(); diff --git a/src/client/app/desktop/views/widgets/trends.vue b/src/client/app/desktop/views/widgets/trends.vue index 27c1860b32..c2c7636bb3 100644 --- a/src/client/app/desktop/views/widgets/trends.vue +++ b/src/client/app/desktop/views/widgets/trends.vue @@ -5,8 +5,8 @@

%fa:spinner .pulse .fw%%i18n:common.loading%

-
-

{{ post.text }}

+
+

{{ note.text }}

@{{ acct }}

%i18n:desktop.tags.mk-trends-home-widget.nothing%

@@ -25,12 +25,12 @@ export default define({ }).extend({ computed: { acct() { - return getAcct(this.post.user); + return getAcct(this.note.user); }, }, data() { return { - post: null, + note: null, fetching: true, offset: 0 }; @@ -44,23 +44,23 @@ export default define({ }, fetch() { this.fetching = true; - this.post = null; + this.note = null; - (this as any).api('posts/trend', { + (this as any).api('notes/trend', { limit: 1, offset: this.offset, - repost: false, + renote: false, reply: false, media: false, poll: false - }).then(posts => { - const post = posts ? posts[0] : null; - if (post == null) { + }).then(notes => { + const note = notes ? notes[0] : null; + if (note == null) { this.offset = 0; } else { this.offset++; } - this.post = post; + this.note = note; this.fetching = false; }); } @@ -103,7 +103,7 @@ export default define({ &:active color #999 - > .post + > .note padding 16px font-size 12px font-style oblique diff --git a/src/client/app/dev/views/new-app.vue b/src/client/app/dev/views/new-app.vue index e407ca00d7..c9d5971395 100644 --- a/src/client/app/dev/views/new-app.vue +++ b/src/client/app/dev/views/new-app.vue @@ -27,7 +27,7 @@ アカウントの情報を見る。 アカウントの情報を操作する。 - 投稿する。 + 投稿する。 リアクションしたりリアクションをキャンセルする。 フォローしたりフォロー解除する。 ドライブを見る。 diff --git a/src/client/app/mobile/api/post.ts b/src/client/app/mobile/api/post.ts index 98309ba8de..72919c6505 100644 --- a/src/client/app/mobile/api/post.ts +++ b/src/client/app/mobile/api/post.ts @@ -1,24 +1,24 @@ import PostForm from '../views/components/post-form.vue'; -//import RepostForm from '../views/components/repost-form.vue'; -import getPostSummary from '../../../../renderers/get-post-summary'; +//import RenoteForm from '../views/components/renote-form.vue'; +import getNoteSummary from '../../../../renderers/get-note-summary'; export default (os) => (opts) => { const o = opts || {}; - if (o.repost) { - /*const vm = new RepostForm({ + if (o.renote) { + /*const vm = new RenoteForm({ propsData: { - repost: o.repost + renote: o.renote } }).$mount(); vm.$once('cancel', recover); - vm.$once('post', recover); + vm.$once('note', recover); 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; - os.api('posts/create', { - repostId: o.repost.id, + os.api('notes/create', { + renoteId: o.renote.id, text: text == '' ? undefined : text }); } else { @@ -36,7 +36,7 @@ export default (os) => (opts) => { } }).$mount(); vm.$once('cancel', recover); - vm.$once('post', recover); + vm.$once('note', recover); document.body.appendChild(vm.$el); (vm as any).focus(); } diff --git a/src/client/app/mobile/script.ts b/src/client/app/mobile/script.ts index 4776fccddb..6265d0d45f 100644 --- a/src/client/app/mobile/script.ts +++ b/src/client/app/mobile/script.ts @@ -25,7 +25,7 @@ import MkDrive from './views/pages/drive.vue'; import MkNotifications from './views/pages/notifications.vue'; import MkMessaging from './views/pages/messaging.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 MkFollowers from './views/pages/followers.vue'; import MkFollowing from './views/pages/following.vue'; @@ -68,7 +68,7 @@ init((launch) => { { path: '/@:user', component: MkUser }, { path: '/@:user/followers', component: MkFollowers }, { path: '/@:user/following', component: MkFollowing }, - { path: '/@:user/:post', component: MkPost } + { path: '/@:user/:note', component: MkNote } ] }); diff --git a/src/client/app/mobile/views/components/activity.vue b/src/client/app/mobile/views/components/activity.vue index 2e44017e77..dcd319cb69 100644 --- a/src/client/app/mobile/views/components/activity.vue +++ b/src/client/app/mobile/views/components/activity.vue @@ -2,14 +2,14 @@
- - @@ -32,12 +32,12 @@ export default Vue.extend({ userId: this.user.id, limit: 30 }).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)); data.forEach(d => { - d.postsH = d.posts / this.peak; + d.notesH = d.notes / this.peak; d.repliesH = d.replies / this.peak; - d.repostsH = d.reposts / this.peak; + d.renotesH = d.renotes / this.peak; }); data.reverse(); this.data = data; diff --git a/src/client/app/mobile/views/components/index.ts b/src/client/app/mobile/views/components/index.ts index fb8f65f47d..9346700304 100644 --- a/src/client/app/mobile/views/components/index.ts +++ b/src/client/app/mobile/views/components/index.ts @@ -2,16 +2,16 @@ import Vue from 'vue'; import ui from './ui.vue'; import timeline from './timeline.vue'; -import post from './post.vue'; -import posts from './posts.vue'; +import note from './note.vue'; +import notes from './notes.vue'; import mediaImage from './media-image.vue'; import mediaVideo from './media-video.vue'; import drive from './drive.vue'; -import postPreview from './post-preview.vue'; -import subPostContent from './sub-post-content.vue'; -import postCard from './post-card.vue'; +import notePreview from './note-preview.vue'; +import subNoteContent from './sub-note-content.vue'; +import noteCard from './note-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 friendsMaker from './friends-maker.vue'; import notification from './notification.vue'; @@ -25,16 +25,16 @@ import widgetContainer from './widget-container.vue'; Vue.component('mk-ui', ui); Vue.component('mk-timeline', timeline); -Vue.component('mk-post', post); -Vue.component('mk-posts', posts); +Vue.component('mk-note', note); +Vue.component('mk-notes', notes); Vue.component('mk-media-image', mediaImage); Vue.component('mk-media-video', mediaVideo); Vue.component('mk-drive', drive); -Vue.component('mk-post-preview', postPreview); -Vue.component('mk-sub-post-content', subPostContent); -Vue.component('mk-post-card', postCard); +Vue.component('mk-note-preview', notePreview); +Vue.component('mk-sub-note-content', subNoteContent); +Vue.component('mk-note-card', noteCard); 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-friends-maker', friendsMaker); Vue.component('mk-notification', notification); diff --git a/src/client/app/mobile/views/components/note-card.vue b/src/client/app/mobile/views/components/note-card.vue new file mode 100644 index 0000000000..9ad0d3e294 --- /dev/null +++ b/src/client/app/mobile/views/components/note-card.vue @@ -0,0 +1,93 @@ + + + + + diff --git a/src/client/app/mobile/views/components/note-detail.sub.vue b/src/client/app/mobile/views/components/note-detail.sub.vue new file mode 100644 index 0000000000..38aea4ba20 --- /dev/null +++ b/src/client/app/mobile/views/components/note-detail.sub.vue @@ -0,0 +1,113 @@ + + + + + + diff --git a/src/client/app/mobile/views/components/note-detail.vue b/src/client/app/mobile/views/components/note-detail.vue new file mode 100644 index 0000000000..e1682e58ed --- /dev/null +++ b/src/client/app/mobile/views/components/note-detail.vue @@ -0,0 +1,462 @@ + + + + + + + diff --git a/src/client/app/mobile/views/components/note-preview.vue b/src/client/app/mobile/views/components/note-preview.vue new file mode 100644 index 0000000000..8c8d8645bb --- /dev/null +++ b/src/client/app/mobile/views/components/note-preview.vue @@ -0,0 +1,110 @@ + + + + + diff --git a/src/client/app/mobile/views/components/note.sub.vue b/src/client/app/mobile/views/components/note.sub.vue new file mode 100644 index 0000000000..a37d0dea08 --- /dev/null +++ b/src/client/app/mobile/views/components/note.sub.vue @@ -0,0 +1,119 @@ + + + + + + diff --git a/src/client/app/mobile/views/components/note.vue b/src/client/app/mobile/views/components/note.vue new file mode 100644 index 0000000000..4b33c6f071 --- /dev/null +++ b/src/client/app/mobile/views/components/note.vue @@ -0,0 +1,540 @@ + + + + + + + diff --git a/src/client/app/mobile/views/components/notes.vue b/src/client/app/mobile/views/components/notes.vue new file mode 100644 index 0000000000..573026d53e --- /dev/null +++ b/src/client/app/mobile/views/components/notes.vue @@ -0,0 +1,111 @@ + + + + + diff --git a/src/client/app/mobile/views/components/notification-preview.vue b/src/client/app/mobile/views/components/notification-preview.vue index 0492c5d86c..79ca3321e4 100644 --- a/src/client/app/mobile/views/components/notification-preview.vue +++ b/src/client/app/mobile/views/components/notification-preview.vue @@ -4,23 +4,23 @@ avatar

{{ name }}

-

%fa:quote-left%{{ getPostSummary(notification.post) }}%fa:quote-right%

+

%fa:quote-left%{{ getNoteSummary(notification.note) }}%fa:quote-right%

- @@ -51,7 +51,7 @@ avatar

%fa:chart-pie%{{ name }}

-

%fa:quote-left%{{ getPostSummary(notification.post) }}%fa:quote-right%

+

%fa:quote-left%{{ getNoteSummary(notification.note) }}%fa:quote-right%

@@ -59,7 +59,7 @@ - - diff --git a/src/client/app/mobile/views/components/post-detail.sub.vue b/src/client/app/mobile/views/components/post-detail.sub.vue deleted file mode 100644 index 98d6a14cac..0000000000 --- a/src/client/app/mobile/views/components/post-detail.sub.vue +++ /dev/null @@ -1,113 +0,0 @@ - - - - - - diff --git a/src/client/app/mobile/views/components/post-detail.vue b/src/client/app/mobile/views/components/post-detail.vue index 0226ce081a..e1682e58ed 100644 --- a/src/client/app/mobile/views/components/post-detail.vue +++ b/src/client/app/mobile/views/components/post-detail.vue @@ -1,5 +1,5 @@
- +
- +
-
+

- avatar + avatar %fa:retweet% {{ name }} - がRepost + がRenote

@@ -38,33 +38,33 @@
- +
{{ tag }}
- + %fa:map-marker-alt% 位置情報
-
- +
+
- - - -
- +
@@ -84,9 +84,9 @@ import getAcct from '../../../../../acct/render'; import getUserName from '../../../../../renderers/get-user-name'; 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 XSub from './post-detail.sub.vue'; +import XSub from './note-detail.sub.vue'; export default Vue.extend({ components: { @@ -94,7 +94,7 @@ export default Vue.extend({ }, props: { - post: { + note: { type: Object, required: true }, @@ -113,10 +113,10 @@ export default Vue.extend({ computed: { acct(): string { - return getAcct(this.post.user); + return getAcct(this.note.user); }, name(): string { - return getUserName(this.post.user); + return getUserName(this.note.user); }, pAcct(): string { return getAcct(this.p.user); @@ -124,14 +124,14 @@ export default Vue.extend({ pName(): string { return getUserName(this.p.user); }, - isRepost(): boolean { - return (this.post.repost && - this.post.text == null && - this.post.mediaIds == null && - this.post.poll == null); + isRenote(): boolean { + return (this.note.renote && + this.note.text == null && + this.note.mediaIds == null && + this.note.poll == null); }, p(): any { - return this.isRepost ? this.post.repost : this.post; + return this.isRenote ? this.note.renote : this.note; }, reactionsCount(): number { return this.p.reactionCounts @@ -155,8 +155,8 @@ export default Vue.extend({ mounted() { // Get replies if (!this.compact) { - (this as any).api('posts/replies', { - postId: this.p.id, + (this as any).api('notes/replies', { + noteId: this.p.id, limit: 8 }).then(replies => { this.replies = replies; @@ -187,8 +187,8 @@ export default Vue.extend({ this.contextFetching = true; // Fetch context - (this as any).api('posts/context', { - postId: this.p.replyId + (this as any).api('notes/context', { + noteId: this.p.replyId }).then(context => { this.contextFetching = false; this.context = context.reverse(); @@ -199,22 +199,22 @@ export default Vue.extend({ reply: this.p }); }, - repost() { + renote() { (this as any).apis.post({ - repost: this.p + renote: this.p }); }, react() { (this as any).os.new(MkReactionPicker, { source: this.$refs.reactButton, - post: this.p, + note: this.p, compact: true }); }, menu() { - (this as any).os.new(MkPostMenu, { + (this as any).os.new(MkNoteMenu, { source: this.$refs.menuButton, - post: this.p, + note: this.p, compact: true }); } @@ -225,7 +225,7 @@ export default Vue.extend({ diff --git a/src/client/app/mobile/views/components/post.sub.vue b/src/client/app/mobile/views/components/post.sub.vue deleted file mode 100644 index 909d5cb597..0000000000 --- a/src/client/app/mobile/views/components/post.sub.vue +++ /dev/null @@ -1,119 +0,0 @@ - - - - - - diff --git a/src/client/app/mobile/views/components/post.vue b/src/client/app/mobile/views/components/post.vue index eee1e80fd3..4b33c6f071 100644 --- a/src/client/app/mobile/views/components/post.vue +++ b/src/client/app/mobile/views/components/post.vue @@ -1,19 +1,19 @@ @@ -25,7 +25,7 @@ export default Vue.extend({ return { fetching: true, existMore: false, - posts: [], + notes: [], offset: 0 }; }, @@ -48,30 +48,30 @@ export default Vue.extend({ this.fetching = true; Progress.start(); - (this as any).api('posts/search', Object.assign({ + (this as any).api('notes/search', Object.assign({ limit: limit + 1 - }, parse(this.q))).then(posts => { - if (posts.length == limit + 1) { - posts.pop(); + }, parse(this.q))).then(notes => { + if (notes.length == limit + 1) { + notes.pop(); this.existMore = true; } - this.posts = posts; + this.notes = notes; this.fetching = false; Progress.done(); }); }, more() { this.offset += limit; - (this as any).api('posts/search', Object.assign({ + (this as any).api('notes/search', Object.assign({ limit: limit + 1, offset: this.offset - }, parse(this.q))).then(posts => { - if (posts.length == limit + 1) { - posts.pop(); + }, parse(this.q))).then(notes => { + if (notes.length == limit + 1) { + notes.pop(); } else { this.existMore = false; } - this.posts = this.posts.concat(posts); + this.notes = this.notes.concat(notes); }); } } @@ -79,7 +79,7 @@ export default Vue.extend({ diff --git a/src/client/app/mobile/views/pages/user/home.photos.vue b/src/client/app/mobile/views/pages/user/home.photos.vue index ecf5082072..1c59260812 100644 --- a/src/client/app/mobile/views/pages/user/home.photos.vue +++ b/src/client/app/mobile/views/pages/user/home.photos.vue @@ -5,7 +5,7 @@

%i18n:mobile.tags.mk-user-overview-photos.no-photos%

@@ -28,15 +28,15 @@ export default Vue.extend({ getAcct }, mounted() { - (this as any).api('users/posts', { + (this as any).api('users/notes', { userId: this.user.id, withMedia: true, limit: 6 - }).then(posts => { - posts.forEach(post => { - post.media.forEach(media => { + }).then(notes => { + notes.forEach(note => { + note.media.forEach(media => { if (this.images.length < 9) this.images.push({ - post, + note, media }); }); diff --git a/src/client/app/mobile/views/pages/user/home.posts.vue b/src/client/app/mobile/views/pages/user/home.posts.vue deleted file mode 100644 index 654f7f63e0..0000000000 --- a/src/client/app/mobile/views/pages/user/home.posts.vue +++ /dev/null @@ -1,57 +0,0 @@ - - - - - diff --git a/src/client/app/mobile/views/pages/user/home.vue b/src/client/app/mobile/views/pages/user/home.vue index 1afcd1f5ba..2554084969 100644 --- a/src/client/app/mobile/views/pages/user/home.vue +++ b/src/client/app/mobile/views/pages/user/home.vue @@ -1,10 +1,10 @@