From 481aa178860cb56897909e1128fc5fdf2378049d Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 18 Aug 2020 22:44:21 +0900 Subject: [PATCH] Channel (#6621) * wip * wip * wip * wip * wip * wip * wip * wip * wop * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * add notes * wip * wip * wip * wip * sound * wip * add kick_gaba2 * wip --- locales/ja-JP.yml | 17 ++ migration/1596548170836-channel.ts | 58 ++++++ migration/1596786425167-channel2.ts | 14 ++ migration/1597459042300-channel-unread.ts | 27 +++ .../assets/sounds/noizenecio/kick_gaba2.mp3 | 3 + src/client/assets/sounds/syuilo/reverved.mp3 | 3 + src/client/assets/sounds/syuilo/ryukyu.mp3 | 3 + .../assets/sounds/syuilo/square-pico.mp3 | 3 + .../components/channel-follow-button.vue | 141 +++++++++++++ src/client/components/channel-preview.vue | 144 +++++++++++++ src/client/components/note.vue | 16 +- src/client/components/post-form.vue | 73 ++++--- src/client/components/timeline.vue | 19 ++ src/client/init.ts | 14 ++ src/client/pages/channel-editor.vue | 128 ++++++++++++ src/client/pages/channel.vue | 190 ++++++++++++++++++ src/client/pages/channels.vue | 86 ++++++++ src/client/pages/index.home.vue | 66 ++++-- .../pages/messaging/messaging-room.form.vue | 8 +- src/client/pages/my-settings/index.vue | 24 +-- src/client/pages/preferences/index.vue | 20 +- src/client/router.ts | 4 + src/client/store.ts | 8 +- src/client/themes/_dark.json5 | 1 + src/client/themes/_light.json5 | 1 + src/db/postgre.ts | 8 +- src/misc/api-permissions.ts | 2 + src/models/entities/channel-following.ts | 43 ++++ src/models/entities/channel-note-pining.ts | 35 ++++ src/models/entities/channel.ts | 74 +++++++ src/models/entities/note-unread.ts | 30 ++- src/models/entities/note.ts | 16 +- ...er-note-pinings.ts => user-note-pining.ts} | 0 src/models/index.ts | 8 +- src/models/repositories/channel.ts | 101 ++++++++++ src/models/repositories/note.ts | 25 ++- src/models/repositories/user.ts | 35 ++-- src/remote/activitypub/models/person.ts | 2 +- .../api/common/generate-channel-query.ts | 24 +++ src/server/api/endpoints/channels/create.ts | 68 +++++++ src/server/api/endpoints/channels/featured.ts | 28 +++ src/server/api/endpoints/channels/follow.ts | 45 +++++ src/server/api/endpoints/channels/followed.ts | 28 +++ src/server/api/endpoints/channels/owned.ts | 28 +++ src/server/api/endpoints/channels/pin-note.ts | 0 src/server/api/endpoints/channels/show.ts | 43 ++++ src/server/api/endpoints/channels/timeline.ts | 99 +++++++++ src/server/api/endpoints/channels/unfollow.ts | 42 ++++ src/server/api/endpoints/channels/update.ts | 93 +++++++++ src/server/api/endpoints/i.ts | 1 - src/server/api/endpoints/notes/create.ts | 32 ++- .../api/endpoints/notes/global-timeline.ts | 1 + .../api/endpoints/notes/hybrid-timeline.ts | 2 + .../api/endpoints/notes/local-timeline.ts | 2 + src/server/api/endpoints/notes/timeline.ts | 2 + src/server/api/stream/channel.ts | 4 + src/server/api/stream/channels/channel.ts | 49 +++++ .../api/stream/channels/global-timeline.ts | 1 + .../api/stream/channels/home-timeline.ts | 8 +- .../api/stream/channels/hybrid-timeline.ts | 12 +- src/server/api/stream/channels/index.ts | 2 + .../api/stream/channels/local-timeline.ts | 1 + src/server/api/stream/index.ts | 21 +- src/server/web/index.ts | 26 ++- src/server/web/views/channel.pug | 21 ++ src/services/add-note-to-antenna.ts | 1 + src/services/i/pin.ts | 2 +- src/services/note/create.ts | 75 ++++++- src/services/note/read.ts | 141 ++++++++----- src/services/note/unread.ts | 35 ++-- 70 files changed, 2203 insertions(+), 184 deletions(-) create mode 100644 migration/1596548170836-channel.ts create mode 100644 migration/1596786425167-channel2.ts create mode 100644 migration/1597459042300-channel-unread.ts create mode 100644 src/client/assets/sounds/noizenecio/kick_gaba2.mp3 create mode 100644 src/client/assets/sounds/syuilo/reverved.mp3 create mode 100644 src/client/assets/sounds/syuilo/ryukyu.mp3 create mode 100644 src/client/assets/sounds/syuilo/square-pico.mp3 create mode 100644 src/client/components/channel-follow-button.vue create mode 100644 src/client/components/channel-preview.vue create mode 100644 src/client/pages/channel-editor.vue create mode 100644 src/client/pages/channel.vue create mode 100644 src/client/pages/channels.vue create mode 100644 src/models/entities/channel-following.ts create mode 100644 src/models/entities/channel-note-pining.ts create mode 100644 src/models/entities/channel.ts rename src/models/entities/{user-note-pinings.ts => user-note-pining.ts} (100%) create mode 100644 src/models/repositories/channel.ts create mode 100644 src/server/api/common/generate-channel-query.ts create mode 100644 src/server/api/endpoints/channels/create.ts create mode 100644 src/server/api/endpoints/channels/featured.ts create mode 100644 src/server/api/endpoints/channels/follow.ts create mode 100644 src/server/api/endpoints/channels/followed.ts create mode 100644 src/server/api/endpoints/channels/owned.ts create mode 100644 src/server/api/endpoints/channels/pin-note.ts create mode 100644 src/server/api/endpoints/channels/show.ts create mode 100644 src/server/api/endpoints/channels/timeline.ts create mode 100644 src/server/api/endpoints/channels/unfollow.ts create mode 100644 src/server/api/endpoints/channels/update.ts create mode 100644 src/server/api/stream/channels/channel.ts create mode 100644 src/server/web/views/channel.pug diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 96398db6a..678f20e8c 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -564,6 +564,19 @@ overview: "概要" logs: "ログ" delayed: "遅延" database: "データベース" +channel: "チャンネル" +create: "作成" + +_channel: + create: "チャンネルを作成" + edit: "チャンネルを編集" + setBanner: "バナーを設定" + removeBanner: "バナーを削除" + featured: "トレンド" + owned: "管理中" + following: "フォロー中" + usersCount: "{n}人が参加中" + notesCount: "{n}投稿があります" _sidebar: full: "フル" @@ -660,6 +673,7 @@ _sfx: chat: "チャット" chatBg: "チャット(バックグラウンド)" antenna: "アンテナ受信" + channel: "チャンネル通知" _ago: unknown: "謎" @@ -740,6 +754,8 @@ _permissions: "write:page-likes": "ページのいいねを操作する" "read:user-groups": "ユーザーグループを見る" "write:user-groups": "ユーザーグループを操作する" + "read:channels": "チャンネルを見る" + "write:channels": "チャンネルを操作する" _auth: shareAccess: "「{name}」がアカウントにアクセスすることを許可しますか?" @@ -822,6 +838,7 @@ _visibility: _postForm: replyPlaceholder: "このノートに返信..." quotePlaceholder: "このノートを引用..." + channelPlaceholder: "チャンネルに投稿..." _placeholders: a: "いまどうしてる?" b: "何かありましたか?" diff --git a/migration/1596548170836-channel.ts b/migration/1596548170836-channel.ts new file mode 100644 index 000000000..4e3ebb330 --- /dev/null +++ b/migration/1596548170836-channel.ts @@ -0,0 +1,58 @@ +import {MigrationInterface, QueryRunner} from "typeorm"; + +export class channel1596548170836 implements MigrationInterface { + name = 'channel1596548170836' + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`CREATE TABLE "channel" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "lastNotedAt" TIMESTAMP WITH TIME ZONE, "userId" character varying(32) NOT NULL, "name" character varying(128) NOT NULL, "description" character varying(2048), "bannerId" character varying(32), "notesCount" integer NOT NULL DEFAULT 0, "usersCount" integer NOT NULL DEFAULT 0, CONSTRAINT "PK_590f33ee6ee7d76437acf362e39" PRIMARY KEY ("id"))`); + await queryRunner.query(`CREATE INDEX "IDX_71cb7b435b7c0d4843317e7e16" ON "channel" ("createdAt") `); + await queryRunner.query(`CREATE INDEX "IDX_29ef80c6f13bcea998447fce43" ON "channel" ("lastNotedAt") `); + await queryRunner.query(`CREATE INDEX "IDX_823bae55bd81b3be6e05cff438" ON "channel" ("userId") `); + await queryRunner.query(`CREATE INDEX "IDX_0f58c11241e649d2a638a8de94" ON "channel" ("notesCount") `); + await queryRunner.query(`CREATE INDEX "IDX_094b86cd36bb805d1aa1e8cc9a" ON "channel" ("usersCount") `); + await queryRunner.query(`CREATE TABLE "channel_following" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "followeeId" character varying(32) NOT NULL, "followerId" character varying(32) NOT NULL, CONSTRAINT "PK_8b104be7f7415113f2a02cd5bdd" PRIMARY KEY ("id"))`); + await queryRunner.query(`CREATE INDEX "IDX_11e71f2511589dcc8a4d3214f9" ON "channel_following" ("createdAt") `); + await queryRunner.query(`CREATE INDEX "IDX_0e43068c3f92cab197c3d3cd86" ON "channel_following" ("followeeId") `); + await queryRunner.query(`CREATE INDEX "IDX_6d8084ec9496e7334a4602707e" ON "channel_following" ("followerId") `); + await queryRunner.query(`CREATE UNIQUE INDEX "IDX_2e230dd45a10e671d781d99f3e" ON "channel_following" ("followerId", "followeeId") `); + await queryRunner.query(`CREATE TABLE "channel_note_pining" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "channelId" character varying(32) NOT NULL, "noteId" character varying(32) NOT NULL, CONSTRAINT "PK_44f7474496bcf2e4b741681146d" PRIMARY KEY ("id"))`); + await queryRunner.query(`CREATE INDEX "IDX_8125f950afd3093acb10d2db8a" ON "channel_note_pining" ("channelId") `); + await queryRunner.query(`CREATE UNIQUE INDEX "IDX_f36fed37d6d4cdcc68c803cd9c" ON "channel_note_pining" ("channelId", "noteId") `); + await queryRunner.query(`ALTER TABLE "note" ADD "channelId" character varying(32) DEFAULT null`); + await queryRunner.query(`CREATE INDEX "IDX_f22169eb10657bded6d875ac8f" ON "note" ("channelId") `); + await queryRunner.query(`ALTER TABLE "channel" ADD CONSTRAINT "FK_823bae55bd81b3be6e05cff4383" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE SET NULL ON UPDATE NO ACTION`); + await queryRunner.query(`ALTER TABLE "channel" ADD CONSTRAINT "FK_999da2bcc7efadbfe0e92d3bc19" FOREIGN KEY ("bannerId") REFERENCES "drive_file"("id") ON DELETE SET NULL ON UPDATE NO ACTION`); + await queryRunner.query(`ALTER TABLE "note" ADD CONSTRAINT "FK_f22169eb10657bded6d875ac8f9" FOREIGN KEY ("channelId") REFERENCES "channel"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); + await queryRunner.query(`ALTER TABLE "channel_following" ADD CONSTRAINT "FK_0e43068c3f92cab197c3d3cd86e" FOREIGN KEY ("followeeId") REFERENCES "channel"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); + await queryRunner.query(`ALTER TABLE "channel_following" ADD CONSTRAINT "FK_6d8084ec9496e7334a4602707e1" FOREIGN KEY ("followerId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); + await queryRunner.query(`ALTER TABLE "channel_note_pining" ADD CONSTRAINT "FK_8125f950afd3093acb10d2db8a8" FOREIGN KEY ("channelId") REFERENCES "channel"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); + await queryRunner.query(`ALTER TABLE "channel_note_pining" ADD CONSTRAINT "FK_10b19ef67d297ea9de325cd4502" FOREIGN KEY ("noteId") REFERENCES "note"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "channel_note_pining" DROP CONSTRAINT "FK_10b19ef67d297ea9de325cd4502"`); + await queryRunner.query(`ALTER TABLE "channel_note_pining" DROP CONSTRAINT "FK_8125f950afd3093acb10d2db8a8"`); + await queryRunner.query(`ALTER TABLE "channel_following" DROP CONSTRAINT "FK_6d8084ec9496e7334a4602707e1"`); + await queryRunner.query(`ALTER TABLE "channel_following" DROP CONSTRAINT "FK_0e43068c3f92cab197c3d3cd86e"`); + await queryRunner.query(`ALTER TABLE "note" DROP CONSTRAINT "FK_f22169eb10657bded6d875ac8f9"`); + await queryRunner.query(`ALTER TABLE "channel" DROP CONSTRAINT "FK_999da2bcc7efadbfe0e92d3bc19"`); + await queryRunner.query(`ALTER TABLE "channel" DROP CONSTRAINT "FK_823bae55bd81b3be6e05cff4383"`); + await queryRunner.query(`DROP INDEX "IDX_f22169eb10657bded6d875ac8f"`); + await queryRunner.query(`ALTER TABLE "note" DROP COLUMN "channelId"`); + await queryRunner.query(`DROP INDEX "IDX_f36fed37d6d4cdcc68c803cd9c"`); + await queryRunner.query(`DROP INDEX "IDX_8125f950afd3093acb10d2db8a"`); + await queryRunner.query(`DROP TABLE "channel_note_pining"`); + await queryRunner.query(`DROP INDEX "IDX_2e230dd45a10e671d781d99f3e"`); + await queryRunner.query(`DROP INDEX "IDX_6d8084ec9496e7334a4602707e"`); + await queryRunner.query(`DROP INDEX "IDX_0e43068c3f92cab197c3d3cd86"`); + await queryRunner.query(`DROP INDEX "IDX_11e71f2511589dcc8a4d3214f9"`); + await queryRunner.query(`DROP TABLE "channel_following"`); + await queryRunner.query(`DROP INDEX "IDX_094b86cd36bb805d1aa1e8cc9a"`); + await queryRunner.query(`DROP INDEX "IDX_0f58c11241e649d2a638a8de94"`); + await queryRunner.query(`DROP INDEX "IDX_823bae55bd81b3be6e05cff438"`); + await queryRunner.query(`DROP INDEX "IDX_29ef80c6f13bcea998447fce43"`); + await queryRunner.query(`DROP INDEX "IDX_71cb7b435b7c0d4843317e7e16"`); + await queryRunner.query(`DROP TABLE "channel"`); + } + +} diff --git a/migration/1596786425167-channel2.ts b/migration/1596786425167-channel2.ts new file mode 100644 index 000000000..0233f7ab0 --- /dev/null +++ b/migration/1596786425167-channel2.ts @@ -0,0 +1,14 @@ +import {MigrationInterface, QueryRunner} from "typeorm"; + +export class channel21596786425167 implements MigrationInterface { + name = 'channel21596786425167' + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "channel_following" ADD "readCursor" TIMESTAMP WITH TIME ZONE NOT NULL`); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "channel_following" DROP COLUMN "readCursor"`); + } + +} diff --git a/migration/1597459042300-channel-unread.ts b/migration/1597459042300-channel-unread.ts new file mode 100644 index 000000000..a0f862114 --- /dev/null +++ b/migration/1597459042300-channel-unread.ts @@ -0,0 +1,27 @@ +import {MigrationInterface, QueryRunner} from "typeorm"; + +export class channelUnread1597459042300 implements MigrationInterface { + name = 'channelUnread1597459042300' + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`TRUNCATE TABLE "note_unread"`, undefined); + await queryRunner.query(`ALTER TABLE "channel_following" DROP COLUMN "readCursor"`); + await queryRunner.query(`ALTER TABLE "note_unread" ADD "isMentioned" boolean NOT NULL`); + await queryRunner.query(`ALTER TABLE "note_unread" ADD "noteChannelId" character varying(32)`); + await queryRunner.query(`CREATE INDEX "IDX_25b1dd384bec391b07b74b861c" ON "note_unread" ("isMentioned") `); + await queryRunner.query(`CREATE INDEX "IDX_89a29c9237b8c3b6b3cbb4cb30" ON "note_unread" ("isSpecified") `); + await queryRunner.query(`CREATE INDEX "IDX_29e8c1d579af54d4232939f994" ON "note_unread" ("noteUserId") `); + await queryRunner.query(`CREATE INDEX "IDX_6a57f051d82c6d4036c141e107" ON "note_unread" ("noteChannelId") `); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`DROP INDEX "IDX_6a57f051d82c6d4036c141e107"`); + await queryRunner.query(`DROP INDEX "IDX_29e8c1d579af54d4232939f994"`); + await queryRunner.query(`DROP INDEX "IDX_89a29c9237b8c3b6b3cbb4cb30"`); + await queryRunner.query(`DROP INDEX "IDX_25b1dd384bec391b07b74b861c"`); + await queryRunner.query(`ALTER TABLE "note_unread" DROP COLUMN "noteChannelId"`); + await queryRunner.query(`ALTER TABLE "note_unread" DROP COLUMN "isMentioned"`); + await queryRunner.query(`ALTER TABLE "channel_following" ADD "readCursor" TIMESTAMP WITH TIME ZONE NOT NULL`); + } + +} diff --git a/src/client/assets/sounds/noizenecio/kick_gaba2.mp3 b/src/client/assets/sounds/noizenecio/kick_gaba2.mp3 new file mode 100644 index 000000000..5b2794104 --- /dev/null +++ b/src/client/assets/sounds/noizenecio/kick_gaba2.mp3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:93c9aabc4d23bae420527930936c823f94b29528fb83789502d056afc79a9eff +size 27144 diff --git a/src/client/assets/sounds/syuilo/reverved.mp3 b/src/client/assets/sounds/syuilo/reverved.mp3 new file mode 100644 index 000000000..473d6b510 --- /dev/null +++ b/src/client/assets/sounds/syuilo/reverved.mp3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dd91fe33b1b158643794db0ac7a44b5792860f0450d22d9a1f87249fb1081229 +size 276480 diff --git a/src/client/assets/sounds/syuilo/ryukyu.mp3 b/src/client/assets/sounds/syuilo/ryukyu.mp3 new file mode 100644 index 000000000..e01f13634 --- /dev/null +++ b/src/client/assets/sounds/syuilo/ryukyu.mp3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e5fd322114c786ade25e34a0b5e11cc789a419ec0986094f6295d18e557f5380 +size 139200 diff --git a/src/client/assets/sounds/syuilo/square-pico.mp3 b/src/client/assets/sounds/syuilo/square-pico.mp3 new file mode 100644 index 000000000..20eee06e0 --- /dev/null +++ b/src/client/assets/sounds/syuilo/square-pico.mp3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1aecd011cba14582ccf65b8bdfb33163a8196fdb7f35e038bd9d053bc24299fe +size 19200 diff --git a/src/client/components/channel-follow-button.vue b/src/client/components/channel-follow-button.vue new file mode 100644 index 000000000..3b83865b5 --- /dev/null +++ b/src/client/components/channel-follow-button.vue @@ -0,0 +1,141 @@ + + + + + diff --git a/src/client/components/channel-preview.vue b/src/client/components/channel-preview.vue new file mode 100644 index 000000000..bef475957 --- /dev/null +++ b/src/client/components/channel-preview.vue @@ -0,0 +1,144 @@ + + + + + diff --git a/src/client/components/note.vue b/src/client/components/note.vue index 99a088b3e..a731f9a36 100644 --- a/src/client/components/note.vue +++ b/src/client/components/note.vue @@ -57,6 +57,7 @@
+ {{ appearNote.channel.name }}