From e9d4a17ce4067eca3159a452ce063441b244a29c Mon Sep 17 00:00:00 2001 From: takayamaki / fusagiko <24884114+takayamaki@users.noreply.github.com> Date: Sun, 5 Jan 2025 10:19:36 +0900 Subject: [PATCH 01/20] =?UTF-8?q?favourite=5Ftags=E3=83=86=E3=83=BC?= =?UTF-8?q?=E3=83=96=E3=83=AB=E3=81=ABname=E3=82=AB=E3=83=A9=E3=83=A0?= =?UTF-8?q?=E3=82=92=E8=BF=BD=E5=8A=A0=E3=81=97=E3=80=81tag=5Fid=E3=82=AB?= =?UTF-8?q?=E3=83=A9=E3=83=A0=E3=82=92nullable=E3=81=AB=E5=A4=89=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/models/favourite_tag.rb | 1 + ...105010419_add_column_name_to_favourite_tags.rb | 15 +++++++++++++++ db/schema.rb | 5 +++-- 3 files changed, 19 insertions(+), 2 deletions(-) create mode 100644 db/migrate/20250105010419_add_column_name_to_favourite_tags.rb diff --git a/app/models/favourite_tag.rb b/app/models/favourite_tag.rb index 42c85b7464ae36..4bb7d2806c9ed1 100644 --- a/app/models/favourite_tag.rb +++ b/app/models/favourite_tag.rb @@ -11,6 +11,7 @@ # updated_at :datetime not null # visibility :integer default("public"), not null # order :integer default(0), not null +# name :string # class FavouriteTag < ApplicationRecord diff --git a/db/migrate/20250105010419_add_column_name_to_favourite_tags.rb b/db/migrate/20250105010419_add_column_name_to_favourite_tags.rb new file mode 100644 index 00000000000000..5b76201fb6b708 --- /dev/null +++ b/db/migrate/20250105010419_add_column_name_to_favourite_tags.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +require Rails.root.join('lib', 'mastodon', 'migration_helpers') + +class AddColumnNameToFavouriteTags < ActiveRecord::Migration[7.0] + include Mastodon::MigrationHelpers + disable_ddl_transaction! + + def change + safety_assured do + add_column :favourite_tags, :name, :string + change_column_null :favourite_tags, :tag_id, true + end + end +end diff --git a/db/schema.rb b/db/schema.rb index f274359b66afb6..3332a20715496d 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.0].define(version: 2023_09_07_150100) do +ActiveRecord::Schema[7.0].define(version: 2025_01_05_010419) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -457,8 +457,9 @@ t.datetime "updated_at", precision: nil, null: false t.integer "visibility", default: 0, null: false t.bigint "account_id", null: false - t.bigint "tag_id", null: false + t.bigint "tag_id" t.integer "order", default: 0, null: false + t.string "name" t.index ["account_id", "tag_id"], name: "index_favourite_tags_on_account_id_and_tag_id", unique: true end From 8727e7b95ec6ff2b7e815313421a9f2ef87abd71 Mon Sep 17 00:00:00 2001 From: takayamaki / fusagiko <24884114+takayamaki@users.noreply.github.com> Date: Sun, 5 Jan 2025 10:35:10 +0900 Subject: [PATCH 02/20] =?UTF-8?q?=E3=83=86=E3=82=B9=E3=83=88=E3=81=AE?= =?UTF-8?q?=E3=83=86=E3=82=B9=E3=83=88=E3=82=B1=E3=83=BC=E3=82=B9=E5=90=8D?= =?UTF-8?q?=E3=82=92=E6=97=A5=E6=9C=AC=E8=AA=9E=E3=81=A7=E6=9B=B8=E3=81=8D?= =?UTF-8?q?=E7=9B=B4=E3=81=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- spec/imastodon/models/favourite_tags_spec.rb | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/spec/imastodon/models/favourite_tags_spec.rb b/spec/imastodon/models/favourite_tags_spec.rb index f92acc7a35fb19..6275c4cbbbe221 100644 --- a/spec/imastodon/models/favourite_tags_spec.rb +++ b/spec/imastodon/models/favourite_tags_spec.rb @@ -7,21 +7,21 @@ let(:account) { Fabricate :account } let(:tag) { Fabricate(:tag, name: 'valid_tag') } - it 'valid visibility' do - expect(described_class.new(account: account, tag: tag, visibility: 0)).to be_valid - expect(described_class.new(account: account, tag: tag, visibility: 1)).to be_valid - expect(described_class.new(account: account, tag: tag, visibility: 2)).to be_valid - expect(described_class.new(account: account, tag: tag, visibility: 3)).to be_valid - end + describe 'visibility' do + it '値が0(public)から3(direct)ならvalid' do + expect(described_class.new(account: account, tag: tag, visibility: 0)).to be_valid + expect(described_class.new(account: account, tag: tag, visibility: 1)).to be_valid + expect(described_class.new(account: account, tag: tag, visibility: 2)).to be_valid + expect(described_class.new(account: account, tag: tag, visibility: 3)).to be_valid + end - context 'when visibility is out of ranges' do - it 'invalid visibility' do + it '値が4(enum上で未定義)ならArgumentErrorをraise' do expect { described_class.new(account: account, tag: tag, visibility: 4) }.to raise_error(ArgumentError) end end - context 'when the tag is invalid' do - it 'when tag name is invalid' do + describe 'tag' do + it 'お気に入りタグを作成しようとしたとき、そのタグの名前が不正ならinvalid' do expect(described_class.new(account: account, tag: Tag.new(name: 'test tag'), visibility: 0)).to_not be_valid end end From ef22d4376d411546397b174469b980a6fcd1c4da Mon Sep 17 00:00:00 2001 From: takayamaki / fusagiko <24884114+takayamaki@users.noreply.github.com> Date: Sun, 5 Jan 2025 10:41:46 +0900 Subject: [PATCH 03/20] =?UTF-8?q?FavouriteTag=E3=83=A2=E3=83=87=E3=83=AB?= =?UTF-8?q?=E3=81=8Cname=E3=82=92=E7=9B=B4=E6=8E=A5=E6=8C=81=E3=81=A4?= =?UTF-8?q?=E3=82=88=E3=81=86=E3=81=AA=E4=BB=95=E6=A7=98=E5=A4=89=E6=9B=B4?= =?UTF-8?q?=E3=82=92=E3=83=86=E3=82=B9=E3=83=88=E3=82=B1=E3=83=BC=E3=82=B9?= =?UTF-8?q?=E3=81=AB=E5=8F=8D=E6=98=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- spec/fabricators/favourite_tags_fabricator.rb | 2 +- spec/imastodon/models/favourite_tags_spec.rb | 25 +++++++++++-------- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/spec/fabricators/favourite_tags_fabricator.rb b/spec/fabricators/favourite_tags_fabricator.rb index fb454b0219a6a1..24abb1b724e99b 100644 --- a/spec/fabricators/favourite_tags_fabricator.rb +++ b/spec/fabricators/favourite_tags_fabricator.rb @@ -2,7 +2,7 @@ Fabricator(:favourite_tag) do account - tag + name 'test' visibility 0 order 0 end diff --git a/spec/imastodon/models/favourite_tags_spec.rb b/spec/imastodon/models/favourite_tags_spec.rb index 6275c4cbbbe221..4968acc9c8f4ac 100644 --- a/spec/imastodon/models/favourite_tags_spec.rb +++ b/spec/imastodon/models/favourite_tags_spec.rb @@ -5,24 +5,30 @@ RSpec.describe FavouriteTag, type: :model do describe 'validation' do let(:account) { Fabricate :account } - let(:tag) { Fabricate(:tag, name: 'valid_tag') } describe 'visibility' do it '値が0(public)から3(direct)ならvalid' do - expect(described_class.new(account: account, tag: tag, visibility: 0)).to be_valid - expect(described_class.new(account: account, tag: tag, visibility: 1)).to be_valid - expect(described_class.new(account: account, tag: tag, visibility: 2)).to be_valid - expect(described_class.new(account: account, tag: tag, visibility: 3)).to be_valid + expect(described_class.new(account: account, name: 'tag', visibility: 0)).to be_valid + expect(described_class.new(account: account, name: 'tag', visibility: 1)).to be_valid + expect(described_class.new(account: account, name: 'tag', visibility: 2)).to be_valid + expect(described_class.new(account: account, name: 'tag', visibility: 3)).to be_valid end it '値が4(enum上で未定義)ならArgumentErrorをraise' do - expect { described_class.new(account: account, tag: tag, visibility: 4) }.to raise_error(ArgumentError) + expect { described_class.new(account: account, name: 'tag', visibility: 4) }.to raise_error(ArgumentError) end end - describe 'tag' do + describe 'name' do + it 'nameは大文字小文字混ざりで作成できる' do + # Tagモデルは途中から英字が小文字に正規化されるようになったが、お気に入りタグは大文字小文字混ざりで作成したいという要望があるため + favourite_tag = described_class.new(account: account, name: 'Test', visibility: 0) + + expect(favourite_tag).to be_valid + end + it 'お気に入りタグを作成しようとしたとき、そのタグの名前が不正ならinvalid' do - expect(described_class.new(account: account, tag: Tag.new(name: 'test tag'), visibility: 0)).to_not be_valid + expect(described_class.new(account: account, name: 'test tag', visibility: 0)).to_not be_valid end end end @@ -53,8 +59,7 @@ describe 'expect to_json_for_api' do let(:account) { Fabricate :account } - let(:tag) { Tag.new(name: 'test_tag') } - let!(:favourite_tag) { Fabricate(:favourite_tag, account: account, tag: tag) } + let!(:favourite_tag) { Fabricate(:favourite_tag, account: account, name: 'test_tag') } it 'expect to_json_for_api' do json = favourite_tag.to_json_for_api From ecee0651509cf361fe6f422ffa807e8e1ed5cdc8 Mon Sep 17 00:00:00 2001 From: takayamaki / fusagiko <24884114+takayamaki@users.noreply.github.com> Date: Sun, 5 Jan 2025 11:02:15 +0900 Subject: [PATCH 04/20] =?UTF-8?q?FavouriteTag=E3=83=A2=E3=83=87=E3=83=AB?= =?UTF-8?q?=E3=81=8Cname=E3=82=92=E7=9B=B4=E6=8E=A5=E6=8C=81=E3=81=A4?= =?UTF-8?q?=E3=82=88=E3=81=86=E3=81=AB=E5=AE=9F=E8=A3=85=E3=82=92=E5=A4=89?= =?UTF-8?q?=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/lib/friends/favourite_tags_extension.rb | 2 +- app/models/favourite_tag.rb | 7 ++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/app/lib/friends/favourite_tags_extension.rb b/app/lib/friends/favourite_tags_extension.rb index e76ba2623b8763..1dd56515aa6ecd 100644 --- a/app/lib/friends/favourite_tags_extension.rb +++ b/app/lib/friends/favourite_tags_extension.rb @@ -17,7 +17,7 @@ module FavouriteTagsExtension def add_default_favourite_tag DEFAULT_TAGS.each_with_index do |tag_name, i| - favourite_tags.create!(visibility: 'unlisted', tag: Tag.find_or_create_by!(name: HashtagNormalizer.new.normalize(tag_name)), order: (DEFAULT_TAGS.length - i)) + favourite_tags.create!(visibility: 'unlisted', name: tag_name, order: (DEFAULT_TAGS.length - i)) end end end diff --git a/app/models/favourite_tag.rb b/app/models/favourite_tag.rb index 4bb7d2806c9ed1..84942cc9b4af23 100644 --- a/app/models/favourite_tag.rb +++ b/app/models/favourite_tag.rb @@ -18,17 +18,14 @@ class FavouriteTag < ApplicationRecord enum visibility: { public: 0, unlisted: 1, private: 2, direct: 3 }, _suffix: :visibility belongs_to :account, optional: false - belongs_to :tag, optional: false - accepts_nested_attributes_for :tag + belongs_to :tag, optional: true - validates :tag, uniqueness: { scope: :account } + validates :name, format: { with: Tag::HASHTAG_NAME_RE } validates :visibility, presence: true validates :order, presence: true, numericality: { only_integer: true, greater_than_or_equal_to: 0 } scope :with_order, -> { order(order: :desc, id: :asc) } - delegate :name, to: :tag - def to_json_for_api { id: id, From f469eb65fb9068245d15fafa33c728949db51b6e Mon Sep 17 00:00:00 2001 From: takayamaki / fusagiko <24884114+takayamaki@users.noreply.github.com> Date: Sun, 5 Jan 2025 12:09:43 +0900 Subject: [PATCH 05/20] =?UTF-8?q?=E3=81=8A=E6=B0=97=E3=81=AB=E5=85=A5?= =?UTF-8?q?=E3=82=8A=E3=82=BF=E3=82=B0=E3=82=92=E5=90=8C=E5=90=8D&?= =?UTF-8?q?=E5=90=8C=E7=AF=84=E5=9B=B2=E3=81=A7=E3=81=AF=E4=BD=9C=E6=88=90?= =?UTF-8?q?=E3=81=A7=E3=81=8D=E3=81=AA=E3=81=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 大文字小文字違いや公開範囲違いは許容する --- app/models/favourite_tag.rb | 2 +- ...ex_account_visibility_on_favourite_tags.rb | 14 ++++++++++ db/schema.rb | 3 +- spec/imastodon/models/favourite_tags_spec.rb | 28 ++++++++++++++++--- 4 files changed, 41 insertions(+), 6 deletions(-) create mode 100644 db/migrate/20250105030426_add_index_account_visibility_on_favourite_tags.rb diff --git a/app/models/favourite_tag.rb b/app/models/favourite_tag.rb index 84942cc9b4af23..f553bc67483677 100644 --- a/app/models/favourite_tag.rb +++ b/app/models/favourite_tag.rb @@ -20,7 +20,7 @@ class FavouriteTag < ApplicationRecord belongs_to :account, optional: false belongs_to :tag, optional: true - validates :name, format: { with: Tag::HASHTAG_NAME_RE } + validates :name, format: { with: Tag::HASHTAG_NAME_RE }, uniqueness: { scope: [:account, :visibility] } validates :visibility, presence: true validates :order, presence: true, numericality: { only_integer: true, greater_than_or_equal_to: 0 } diff --git a/db/migrate/20250105030426_add_index_account_visibility_on_favourite_tags.rb b/db/migrate/20250105030426_add_index_account_visibility_on_favourite_tags.rb new file mode 100644 index 00000000000000..7e2c5471ad1459 --- /dev/null +++ b/db/migrate/20250105030426_add_index_account_visibility_on_favourite_tags.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +require Rails.root.join('lib', 'mastodon', 'migration_helpers') + +class AddIndexAccountVisibilityOnFavouriteTags < ActiveRecord::Migration[7.0] + include Mastodon::MigrationHelpers + disable_ddl_transaction! + + def change + safety_assured do + add_index :favourite_tags, [:account_id, :name, :visibility], unique: true + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 3332a20715496d..205dfb37875a38 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.0].define(version: 2025_01_05_010419) do +ActiveRecord::Schema[7.0].define(version: 2025_01_05_030426) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -460,6 +460,7 @@ t.bigint "tag_id" t.integer "order", default: 0, null: false t.string "name" + t.index ["account_id", "name", "visibility"], name: "index_favourite_tags_on_account_id_and_name_and_visibility", unique: true t.index ["account_id", "tag_id"], name: "index_favourite_tags_on_account_id_and_tag_id", unique: true end diff --git a/spec/imastodon/models/favourite_tags_spec.rb b/spec/imastodon/models/favourite_tags_spec.rb index 4968acc9c8f4ac..7f5adae8dff3e0 100644 --- a/spec/imastodon/models/favourite_tags_spec.rb +++ b/spec/imastodon/models/favourite_tags_spec.rb @@ -30,6 +30,20 @@ it 'お気に入りタグを作成しようとしたとき、そのタグの名前が不正ならinvalid' do expect(described_class.new(account: account, name: 'test tag', visibility: 0)).to_not be_valid end + + it '同じ名前でも公開範囲が異なるならばvalid' do + Fabricate(:favourite_tag, account: account, name: 'test', visibility: :public) + + duplicated = described_class.new(account: account, visibility: :unlisted, order: 100, name: 'test') + expect(duplicated).to be_valid + end + + it '同じ名前、同じ公開範囲で既にお気に入りタグを作成しているならばinvalid' do + Fabricate(:favourite_tag, account: account, name: 'test') + + duplicated = described_class.new(account: account, visibility: :public, order: 100, name: 'test') + expect(duplicated).to_not be_valid + end end end @@ -48,11 +62,17 @@ it 'returns an array of recent favourite tags ordered by order and id' do specifieds = [ - Fabricate(:favourite_tag, account: account, order: 9900), - Fabricate(:favourite_tag, account: account, order: 9800), - Fabricate(:favourite_tag, account: account, order: 9800), + Fabricate(:favourite_tag, account: account, name: 'test1', order: 10), + Fabricate(:favourite_tag, account: account, name: 'test2', order: 11), + Fabricate(:favourite_tag, account: account, name: 'test3', order: 10), ] - expect(account.favourite_tags.with_order.limit(3)).to match_array(specifieds) + expect(account.favourite_tags.with_order.limit(3)).to eq( + [ + specifieds[1], + specifieds[0], + specifieds[2], + ] + ) end end end From d92a9de295277324bae66f9cc3b2c3d946943969 Mon Sep 17 00:00:00 2001 From: takayamaki / fusagiko <24884114+takayamaki@users.noreply.github.com> Date: Sun, 5 Jan 2025 12:10:29 +0900 Subject: [PATCH 06/20] =?UTF-8?q?/settings/favourite=5Ftags=E3=81=AEcontro?= =?UTF-8?q?ller=20spec=E3=82=92=E4=BB=95=E6=A7=98=E5=A4=89=E6=9B=B4?= =?UTF-8?q?=E3=81=AB=E8=BF=BD=E5=BE=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../favourite_tags_controller_spec.rb | 50 +++++-------------- 1 file changed, 12 insertions(+), 38 deletions(-) diff --git a/spec/imastodon/controllers/settings/favourite_tags_controller_spec.rb b/spec/imastodon/controllers/settings/favourite_tags_controller_spec.rb index 5fa39a2956bb9c..47146dceec79c9 100644 --- a/spec/imastodon/controllers/settings/favourite_tags_controller_spec.rb +++ b/spec/imastodon/controllers/settings/favourite_tags_controller_spec.rb @@ -26,8 +26,7 @@ end describe 'GET #edit' do - let(:tag) { Fabricate(:tag, name: 'dummy_tag') } - let!(:favourite_tag) { Fabricate(:favourite_tag, account: user.account, tag: tag) } + let!(:favourite_tag) { Fabricate(:favourite_tag, account: user.account) } context 'when the favourite tag is found.' do before do @@ -62,20 +61,12 @@ let(:params) do { favourite_tag: { - tag_attributes: { - name: tag_name, - }, + name: tag_name, visibility: 'public', order: 1, }, } end - let!(:tag) { Fabricate(:tag, name: tag_name) } - - it 'after create, tag' do - expect { subject }.to_not change(Tag, :count) - expect(response).to redirect_to(settings_favourite_tags_path) - end it 'after create, favourite tag' do expect { subject }.to change(FavouriteTag, :count).by(1) @@ -84,7 +75,7 @@ context 'when the tag has already been favourite.' do before do - Fabricate(:favourite_tag, account: user.account, tag: tag) + Fabricate(:favourite_tag, account: user.account, name: tag_name) end it 'does not create any tags and should render index template' do @@ -95,8 +86,7 @@ end describe 'PUT #update' do - let(:tag) { Fabricate(:tag, name: 'dummy_tag') } - let!(:favourite_tag) { Fabricate(:favourite_tag, account: user.account, tag: tag) } + let!(:favourite_tag) { Fabricate(:favourite_tag, account: user.account) } context 'The favourite tag can update.' do subject { put :update, params: params } @@ -105,25 +95,18 @@ { id: favourite_tag.id, favourite_tag: { - tag_attributes: { - name: "dummy_tag_#{favourite_tag.id}", - }, + name: "dummy_tag_#{favourite_tag.id}", visibility: 'unlisted', order: 2, }, } end - it 'after update, tag' do - expect { subject }.to change(Tag, :count).by(1) - expect(assigns(:favourite_tag).tag.name).to_not eq('dummy_tag') - expect(response).to redirect_to(settings_favourite_tags_path) - end - it 'after update, favourite tag' do expect { subject }.to_not change(FavouriteTag, :count) expect(assigns(:favourite_tag).visibility).to eq('unlisted') expect(assigns(:favourite_tag).order).to eq(2) + expect(assigns(:favourite_tag).name).to eq("dummy_tag_#{favourite_tag.id}") expect(response).to redirect_to(settings_favourite_tags_path) end end @@ -131,27 +114,24 @@ context 'The favourite tag could not update, because tag has already been registered.' do subject { put :update, params: params } - let(:tag_name) { 'dummy_tag2' } + let(:tag_name) { 'duplicated' } let(:params) do { id: favourite_tag.id, favourite_tag: { - tag_attributes: { - name: tag_name, - }, - visibility: 'unlisted', + name: tag_name, + visibility: 'public', order: 2, }, } end - let(:tag2) { Fabricate(:tag, name: tag_name) } before do - Fabricate(:favourite_tag, account: user.account, tag: tag2) + Fabricate(:favourite_tag, account: user.account, name: 'duplicated') end it 'does not update any tags and should render edit template' do - expect { subject }.to_not change(Tag, :count) + subject expect(response).to render_template(:edit) end end @@ -160,19 +140,13 @@ describe 'DELETE #destroy' do subject { delete :destroy, params: params } - let(:tag) { Fabricate(:tag, name: 'dummy_tag') } - let!(:favourite_tag) { Fabricate(:favourite_tag, account: user.account, tag: tag) } + let!(:favourite_tag) { Fabricate(:favourite_tag, account: user.account, name: 'dummy_tag') } let(:params) do { id: favourite_tag.id, } end - it 'after destroy, tag' do - expect { subject }.to_not change(Tag, :count) - expect(response).to redirect_to(settings_favourite_tags_path) - end - it 'after destroy, favourite tag' do expect { subject }.to change(FavouriteTag, :count).by(-1) expect(response).to redirect_to(settings_favourite_tags_path) From ecbc23f1682232a54b6ef47cbebfa519ae51a0e9 Mon Sep 17 00:00:00 2001 From: takayamaki / fusagiko <24884114+takayamaki@users.noreply.github.com> Date: Sun, 5 Jan 2025 12:11:51 +0900 Subject: [PATCH 07/20] =?UTF-8?q?/settings/favourite=5Ftags=E3=81=AE?= =?UTF-8?q?=E5=AE=9F=E8=A3=85=E3=82=92=E4=BB=95=E6=A7=98=E5=A4=89=E6=9B=B4?= =?UTF-8?q?=E3=81=AB=E8=BF=BD=E5=BE=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../settings/favourite_tags_controller.rb | 22 +++++++++---------- .../settings/favourite_tags/_form.html.haml | 3 +-- .../settings/favourite_tags/index.html.haml | 2 +- 3 files changed, 12 insertions(+), 15 deletions(-) diff --git a/app/controllers/settings/favourite_tags_controller.rb b/app/controllers/settings/favourite_tags_controller.rb index 4f6dafe2864e60..96972d75d372f0 100644 --- a/app/controllers/settings/favourite_tags_controller.rb +++ b/app/controllers/settings/favourite_tags_controller.rb @@ -8,7 +8,7 @@ class Settings::FavouriteTagsController < Settings::BaseController before_action :set_favourite_tag, only: [:edit, :update, :destroy] def index - @favourite_tag = FavouriteTag.new(tag: Tag.new, visibility: FavouriteTag.visibilities[:public]) + @favourite_tag = FavouriteTag.new(visibility: FavouriteTag.visibilities[:public]) end def edit @@ -16,9 +16,8 @@ def edit end def create - name = tag_params[:name].delete_prefix('#') - tag = Tag.find_or_initialize_by(name: name) - @favourite_tag = FavouriteTag.new(account: @account, tag: tag, visibility: favourite_tag_params[:visibility], order: favourite_tag_params[:order]) + name = create_params[:name].delete_prefix('#') + @favourite_tag = FavouriteTag.new(account: @account, name: name, order: create_params[:order], visibility: create_params[:visibility]) if @favourite_tag.save redirect_to settings_favourite_tags_path, notice: I18n.t('generic.changes_saved_msg') else @@ -27,9 +26,8 @@ def create end def update - name = tag_params[:name].delete_prefix('#') - tag = Tag.find_or_initialize_by(name: name) - if @favourite_tag.update(tag: tag, visibility: favourite_tag_params[:visibility], order: favourite_tag_params[:order]) + name = update_params[:name].delete_prefix('#') + if @favourite_tag.update(name: name, order: update_params[:order], visibility: update_params[:visibility]) redirect_to settings_favourite_tags_path, notice: I18n.t('generic.changes_saved_msg') else render :edit @@ -43,12 +41,12 @@ def destroy private - def tag_params - params.require(:favourite_tag).require(:tag_attributes).permit(:id, :name) + def create_params + params.require(:favourite_tag).permit(:name, :visibility, :order) end - def favourite_tag_params - params.require(:favourite_tag).permit(:visibility, :order, { tag_attributes: [:id, :name] }) + def update_params + params.require(:favourite_tag).permit(:name, :visibility, :order) end def set_account @@ -60,6 +58,6 @@ def set_favourite_tag end def set_favourite_tags - @favourite_tags = @account.favourite_tags.with_order.includes(:tag) + @favourite_tags = @account.favourite_tags.with_order end end diff --git a/app/views/settings/favourite_tags/_form.html.haml b/app/views/settings/favourite_tags/_form.html.haml index 0dc806213d85d0..11092d9cbe2078 100644 --- a/app/views/settings/favourite_tags/_form.html.haml +++ b/app/views/settings/favourite_tags/_form.html.haml @@ -1,7 +1,6 @@ = simple_form_for [:settings, favourite_tag] do |f| = render 'shared/error_messages', object: favourite_tag - = f.simple_fields_for :tag do |ff| - = ff.input :name, placeholder: t('favourite_tags.name_of_tag') + = f.input :name, placeholder: t('favourite_tags.name_of_tag') = f.input :visibility, label: t('simple_form.labels.defaults.setting_default_privacy'), collection: Status.visibilities.keys[0..1], diff --git a/app/views/settings/favourite_tags/index.html.haml b/app/views/settings/favourite_tags/index.html.haml index a990a51d132a40..37fe961c008a52 100644 --- a/app/views/settings/favourite_tags/index.html.haml +++ b/app/views/settings/favourite_tags/index.html.haml @@ -18,7 +18,7 @@ %tr %td = fa_icon 'tag' - = fav_tag.tag.name + = fav_tag.name %td = I18n.t("statuses.visibilities.#{fav_tag.visibility}") %td From a9a82d4f1530bcbdc160d933ee5ac91d6e45c189 Mon Sep 17 00:00:00 2001 From: takayamaki / fusagiko <24884114+takayamaki@users.noreply.github.com> Date: Mon, 6 Jan 2025 13:53:01 +0900 Subject: [PATCH 08/20] =?UTF-8?q?GET=20/api/v1/favourite=5Ftags=E3=81=AEco?= =?UTF-8?q?ntroller=20spec=E3=81=AB=E3=81=8A=E3=81=84=E3=81=A6=E3=80=81?= =?UTF-8?q?=E3=83=AC=E3=82=B9=E3=83=9D=E3=83=B3=E3=82=B9=E3=81=AE=E5=86=85?= =?UTF-8?q?=E5=AE=B9=E3=82=92=E6=A4=9C=E8=A8=BC=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/v1/favourite_tags_controller_spec.rb | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/spec/imastodon/controllers/api/v1/favourite_tags_controller_spec.rb b/spec/imastodon/controllers/api/v1/favourite_tags_controller_spec.rb index 655155d89ba92b..c776b8ed273cf4 100644 --- a/spec/imastodon/controllers/api/v1/favourite_tags_controller_spec.rb +++ b/spec/imastodon/controllers/api/v1/favourite_tags_controller_spec.rb @@ -19,6 +19,16 @@ it 'returns http success' do get :index expect(response).to have_http_status(:success) + body = JSON.parse(response.body, symbolize_names: true) + expect(body).to match( + [ + { id: be_integer, name: 'デレラジ', visibility: 'unlisted' }, + { id: be_integer, name: 'デレパ', visibility: 'unlisted' }, + { id: be_integer, name: 'imas_mor', visibility: 'unlisted' }, + { id: be_integer, name: 'millionradio', visibility: 'unlisted' }, + { id: be_integer, name: 'sidem', visibility: 'unlisted' }, + ] + ) end end From d5666136b87412e44199a684684f9ec86efafb2b Mon Sep 17 00:00:00 2001 From: takayamaki / fusagiko <24884114+takayamaki@users.noreply.github.com> Date: Mon, 6 Jan 2025 14:25:48 +0900 Subject: [PATCH 09/20] =?UTF-8?q?POST=20/api/v1/favourite=5Ftags=E3=81=AEc?= =?UTF-8?q?ontroller=20spec=E3=81=AB=E4=BB=95=E6=A7=98=E5=A4=89=E6=9B=B4?= =?UTF-8?q?=E3=82=92=E5=8F=8D=E6=98=A0=20&=20=E5=85=A8=E4=BD=93=E7=9A=84?= =?UTF-8?q?=E3=81=AB=E6=9B=B8=E3=81=8D=E7=9B=B4=E3=81=97=E3=81=A6=E4=BB=95?= =?UTF-8?q?=E6=A7=98=E3=82=92=E3=82=88=E3=82=8A=E6=98=8E=E7=A2=BA=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .rubocop.yml | 4 + .../api/v1/favourite_tags_controller_spec.rb | 146 ++++++++++++++---- 2 files changed, 122 insertions(+), 28 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index 19fdc4e42065fb..76a5f3c2e4c7c4 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -129,6 +129,10 @@ RSpec/FilePath: - 'spec/config/initializers/rack_attack_spec.rb' # namespaces usually have separate folder - 'spec/lib/sanitize_config_spec.rb' # namespaces usually have separate folder +RSpec/LetSetup: + Exclude: + - spec/imastodon/**/* + # Reason: # https://docs.rubocop.org/rubocop-rspec/cops_rspec.html#rspecnamedsubject RSpec/NamedSubject: diff --git a/spec/imastodon/controllers/api/v1/favourite_tags_controller_spec.rb b/spec/imastodon/controllers/api/v1/favourite_tags_controller_spec.rb index c776b8ed273cf4..79973bf08ed19a 100644 --- a/spec/imastodon/controllers/api/v1/favourite_tags_controller_spec.rb +++ b/spec/imastodon/controllers/api/v1/favourite_tags_controller_spec.rb @@ -35,44 +35,134 @@ describe 'POST #create' do subject { post :create, params: params } - let(:scopes) { 'write:statuses' } - let(:tag_name) { 'dummy_tag' } - let(:params) do - { - tag: tag_name, - visibility: 'public', - } + let!(:scopes) { 'write:statuses' } + + context '新しいタグをお気に入りタグに登録するとき' do + context '公開範囲をpublicで登録するとき' do + let!(:params) do + { + name: 'お気に入りタグ', + visibility: 'public', + } + end + + it '新しいお気に入りタグのレコードが記録され、ステータスコード200と、作成されたお気に入りタグがレスポンスボディとして返る' do + expect { subject }.to change { user.account.favourite_tags.count }.by(1) + + created = FavouriteTag.last + expect(created.name).to eq('お気に入りタグ') + expect(created.visibility).to eq('public') + + expect(response).to have_http_status(:success) + body = JSON.parse(response.body, symbolize_names: true) + expect(body).to match({ + id: created.id, + name: 'お気に入りタグ', + visibility: 'public', + }) + end + end + + context '公開範囲をunlistedで登録するとき' do + let!(:params) do + { + name: 'お気に入りタグ', + visibility: 'unlisted', + } + end + + it '新しいお気に入りタグのレコードが記録され、ステータスコード200と、作成されたお気に入りタグがレスポンスボディとして返る' do + expect { subject }.to change { user.account.favourite_tags.count }.by(1) + + created = FavouriteTag.last + expect(created.name).to eq('お気に入りタグ') + expect(created.visibility).to eq('unlisted') + + expect(response).to have_http_status(:success) + body = JSON.parse(response.body, symbolize_names: true) + expect(body).to match({ + id: created.id, + name: 'お気に入りタグ', + visibility: 'unlisted', + }) + end + end end - context 'when the tag is a new favourite tag' do - it 'returns http success' do - expect { subject }.to change { user.account.favourite_tags.count }.by(1) - expect(response).to have_http_status(:success) + context '登録しようとしたタグが既にお気に入りタグに登録されているとき' do + let!(:already_exists) { Fabricate(:favourite_tag, account: user.account, name: '登録済みのお気に入りタグ', visibility: 'public') } + + context '公開範囲も同じとき' do + let!(:params) do + { + name: '登録済みのお気に入りタグ', + visibility: 'public', + } + end + + it '新しいお気に入りタグのレコードは増えず、ステータスコード409と、既存のお気に入りタグがレスポンスボディとして返る' do + expect { subject }.to_not(change { user.account.favourite_tags.count }) + + expect(response).to have_http_status(409) + body = JSON.parse(response.body, symbolize_names: true) + expect(body).to match({ + id: already_exists.id, + name: '登録済みのお気に入りタグ', + visibility: 'public', + }) + end end - it 'responce has created tag' do - expect { subject }.to change { user.account.favourite_tags.count }.by(1) - expect( - JSON.parse(response.body, symbolize_names: true).except(:id) - ).to eq({ name: tag_name, visibility: 'public' }) + context '公開範囲が異なるとき' do + let!(:params) do + { + name: '登録済みのお気に入りタグ', + visibility: 'unlisted', + } + end + + it '新しいお気に入りタグのレコードが記録され、ステータスコード200と、作成されたお気に入りタグがレスポンスボディとして返る' do + expect { subject }.to change { user.account.favourite_tags.count }.by(1) + + created = FavouriteTag.last + expect(created.name).to eq('登録済みのお気に入りタグ') + expect(created.visibility).to eq('unlisted') + + expect(response).to have_http_status(:success) + body = JSON.parse(response.body, symbolize_names: true) + expect(body).to match({ + id: created.id, + name: '登録済みのお気に入りタグ', + visibility: 'unlisted', + }) + end end end - context 'when the tag has already been favourite.' do - before do - Fabricate(:favourite_tag, account: user.account, tag: tag) - end + context '英字のタグを大文字小文字違いで登録しようとしたとき' do + let!(:already_exists) { Fabricate(:favourite_tag, account: user.account, name: 'already_favourited_tag', visibility: 'public') } - it 'returns http 409' do - expect { subject }.to_not(change { user.account.favourite_tags.count }) - expect(response).to have_http_status(409) + let!(:params) do + { + name: 'Already_favourited_tag', + visibility: 'public', + } end - it 'does not create new favourite_tag' do - expect { subject }.to_not(change { user.account.favourite_tags.count }) - expect( - JSON.parse(response.body, symbolize_names: true).except(:id) - ).to eq({ name: tag_name, visibility: 'public' }) + it '新しいお気に入りタグのレコードが記録され、ステータスコード200と、作成されたお気に入りタグがレスポンスボディとして返る' do + expect { subject }.to change { user.account.favourite_tags.count }.by(1) + + created = FavouriteTag.last + expect(created.name).to eq('Already_favourited_tag') + expect(created.visibility).to eq('public') + + expect(response).to have_http_status(:success) + body = JSON.parse(response.body, symbolize_names: true) + expect(body).to match({ + id: created.id, + name: 'Already_favourited_tag', + visibility: 'public', + }) end end end From 5218cbbe03901fd79f56035aedb0ff1f033a37e4 Mon Sep 17 00:00:00 2001 From: takayamaki / fusagiko <24884114+takayamaki@users.noreply.github.com> Date: Mon, 6 Jan 2025 14:34:49 +0900 Subject: [PATCH 10/20] =?UTF-8?q?DELETE=20/api/v1/favourite=5Ftags/{id}?= =?UTF-8?q?=E3=81=AEcontroller=20spec=E3=81=AB=E4=BB=95=E6=A7=98=E5=A4=89?= =?UTF-8?q?=E6=9B=B4=E3=82=92=E5=8F=8D=E6=98=A0=20&=20=E5=85=A8=E4=BD=93?= =?UTF-8?q?=E7=9A=84=E3=81=AB=E6=9B=B8=E3=81=8D=E7=9B=B4=E3=81=97=E3=81=A6?= =?UTF-8?q?=E4=BB=95=E6=A7=98=E3=82=92=E3=82=88=E3=82=8A=E6=98=8E=E7=A2=BA?= =?UTF-8?q?=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/v1/favourite_tags_controller_spec.rb | 45 ++++++++++--------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/spec/imastodon/controllers/api/v1/favourite_tags_controller_spec.rb b/spec/imastodon/controllers/api/v1/favourite_tags_controller_spec.rb index 79973bf08ed19a..68e59ec65a0cb0 100644 --- a/spec/imastodon/controllers/api/v1/favourite_tags_controller_spec.rb +++ b/spec/imastodon/controllers/api/v1/favourite_tags_controller_spec.rb @@ -7,7 +7,6 @@ let(:user) { Fabricate(:user, account: Fabricate(:account, username: 'alice')) } let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) } - let(:tag) { Fabricate(:tag, name: tag_name) } before do allow(controller).to receive(:doorkeeper_token) { token } @@ -170,42 +169,46 @@ describe 'DELETE #destroy' do subject { delete :destroy, params: params } - let(:scopes) { 'write:statuses' } - let(:params) { { tag: tag_name } } - - context 'when try to destroy the favourite tag' do - let(:tag_name) { 'dummy_tag' } + let!(:scopes) { 'write:statuses' } - before do - Fabricate(:favourite_tag, account: user.account, tag: tag) - end + context '存在するお気に入りタグのIDを指定したとき' do + let!(:favourite_tag) { Fabricate(:favourite_tag, account: user.account) } + let!(:params) { { id: favourite_tag.id } } - it 'returns http success' do + it 'ステータスコード200が返り、お気に入りタグのレコード数が1つ減る' do expect { subject }.to change { user.account.favourite_tags.count }.by(-1) - expect(response).to have_http_status(:success) + expect(response).to have_http_status(204) end + end + + context '存在しないお気に入りタグのIDを指定したとき' do + let!(:params) { { id: 1 } } + + it 'ステータスコード404が返り、お気に入りタグのレコード数は変わらない' do + expect { subject }.to_not(change { user.account.favourite_tags.count }) + expect(response).to have_http_status(404) - it 'responce has success message by json' do - expect { subject }.to change { user.account.favourite_tags.count }.by(-1) expect( JSON.parse(response.body, symbolize_names: true) - ).to eq({ succeeded: true }) + ).to eq({ + error: 'FavouriteTag is not found', + }) end end - context 'when try to destroy an unregistered tag' do - let(:tag_name) { 'unregistered' } + context '自分以外の人のお気に入りタグのIDを指定したとき' do + let!(:favourite_tag) { Fabricate(:favourite_tag) } + let!(:params) { { id: favourite_tag.id } } - it 'returns http 404' do + it 'ステータスコード404が返り、お気に入りタグのレコード数は変わらない' do expect { subject }.to_not(change { user.account.favourite_tags.count }) expect(response).to have_http_status(404) - end - it 'responce has fail message by json' do - expect { subject }.to_not(change { user.account.favourite_tags.count }) expect( JSON.parse(response.body, symbolize_names: true) - ).to eq({ succeeded: false }) + ).to eq({ + error: 'FavouriteTag is not found', + }) end end end From 35b1f2d26da8f9439467136882cb0a1978b2ec87 Mon Sep 17 00:00:00 2001 From: takayamaki / fusagiko <24884114+takayamaki@users.noreply.github.com> Date: Mon, 6 Jan 2025 14:37:52 +0900 Subject: [PATCH 11/20] =?UTF-8?q?GET=20/api/v1/favourite=5Ftags=E3=81=AE?= =?UTF-8?q?=E5=AE=9F=E8=A3=85=E3=82=92=E3=83=AA=E3=83=95=E3=82=A1=E3=82=AF?= =?UTF-8?q?=E3=82=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/controllers/api/v1/favourite_tags_controller.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/controllers/api/v1/favourite_tags_controller.rb b/app/controllers/api/v1/favourite_tags_controller.rb index bd26bf30c061f9..c9b898c338b62e 100644 --- a/app/controllers/api/v1/favourite_tags_controller.rb +++ b/app/controllers/api/v1/favourite_tags_controller.rb @@ -9,7 +9,10 @@ class Api::V1::FavouriteTagsController < Api::BaseController respond_to :json def index - render json: current_favourite_tags + current_account = current_user.account + orderd_favourite_tags = current_account.favourite_tags.with_order + + render json: orderd_favourite_tags.map(&:to_json_for_api) end def create From bdf0ca67b2c516268396d1b5e68e8459ee1ea9d3 Mon Sep 17 00:00:00 2001 From: takayamaki / fusagiko <24884114+takayamaki@users.noreply.github.com> Date: Mon, 6 Jan 2025 15:03:36 +0900 Subject: [PATCH 12/20] =?UTF-8?q?POST=20/api/v1/favourite=5Ftags=E3=81=AE?= =?UTF-8?q?=E5=AE=9F=E8=A3=85=E3=81=AB=E4=BB=95=E6=A7=98=E5=A4=89=E6=9B=B4?= =?UTF-8?q?=E3=82=92=E5=8F=8D=E6=98=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/v1/favourite_tags_controller.rb | 20 +++++++++++-------- config/routes/api.rb | 2 +- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/app/controllers/api/v1/favourite_tags_controller.rb b/app/controllers/api/v1/favourite_tags_controller.rb index c9b898c338b62e..ecad3c1517c189 100644 --- a/app/controllers/api/v1/favourite_tags_controller.rb +++ b/app/controllers/api/v1/favourite_tags_controller.rb @@ -16,13 +16,17 @@ def index end def create - tag = find_or_init_tag - @favourite_tag = FavouriteTag.new(account: @account, tag: tag, visibility: favourite_tag_visibility) - if @favourite_tag.save - render json: @favourite_tag.to_json_for_api - else - render json: find_fav_tag_by(tag).to_json_for_api, status: 409 + current_account = current_user.account + favourite_tag = current_account.favourite_tags.find_by(name: create_params[:name], visibility: create_params[:visibility]) + + if favourite_tag.present? + render json: favourite_tag.to_json_for_api, status: 409 + return end + + favourite_tag = FavouriteTag.new(account: current_account, name: create_params[:name], visibility: create_params[:visibility]) + favourite_tag.save! + render json: favourite_tag.to_json_for_api end def destroy @@ -38,8 +42,8 @@ def destroy private - def tag_params - params.permit(:tag, :visibility) + def create_params + params.permit(:name, :visibility) end def set_account diff --git a/config/routes/api.rb b/config/routes/api.rb index 86d91aef103495..cbf4e4fb0c21b8 100644 --- a/config/routes/api.rb +++ b/config/routes/api.rb @@ -284,7 +284,7 @@ resources :tags, only: [:index, :show, :update] end - resources :favourite_tags, only: [:index, :create, :destroy], param: :tag + resources :favourite_tags, only: [:index, :create, :destroy] end namespace :v2 do From 1390de7d4def7abe4b17bb07810fb94337e5da04 Mon Sep 17 00:00:00 2001 From: takayamaki / fusagiko <24884114+takayamaki@users.noreply.github.com> Date: Mon, 6 Jan 2025 15:08:31 +0900 Subject: [PATCH 13/20] =?UTF-8?q?POST=20/api/v1/favourite=5Ftags=E3=81=AB?= =?UTF-8?q?=E3=81=8A=E3=81=84=E3=81=A6=E3=83=90=E3=83=AA=E3=83=87=E3=83=BC?= =?UTF-8?q?=E3=82=B7=E3=83=A7=E3=83=B3=E3=82=A8=E3=83=A9=E3=83=BC=E3=81=A7?= =?UTF-8?q?=E3=81=82=E3=82=8B=E5=A0=B4=E5=90=88=E3=81=AE=E3=83=86=E3=82=B9?= =?UTF-8?q?=E3=83=88=E3=82=B1=E3=83=BC=E3=82=B9=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/v1/favourite_tags_controller_spec.rb | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/spec/imastodon/controllers/api/v1/favourite_tags_controller_spec.rb b/spec/imastodon/controllers/api/v1/favourite_tags_controller_spec.rb index 68e59ec65a0cb0..799445f3883884 100644 --- a/spec/imastodon/controllers/api/v1/favourite_tags_controller_spec.rb +++ b/spec/imastodon/controllers/api/v1/favourite_tags_controller_spec.rb @@ -164,6 +164,25 @@ }) end end + + context 'validでない名前のタグを登録しようとしたとき' do + let!(:params) do + { + name: 'invalid tag name', + visibility: 'public', + } + end + + it '新しいお気に入りタグのレコードは増えず、ステータスコード422と、バリデーションエラーの内容が返る' do + expect { subject }.to_not(change { user.account.favourite_tags.count }) + + expect(response).to have_http_status(422) + body = JSON.parse(response.body, symbolize_names: true) + expect(body).to match({ + error: 'Validation failed: Name is invalid', + }) + end + end end describe 'DELETE #destroy' do From 884537dd0a1eb9bbab00aef6987d6642cf596d39 Mon Sep 17 00:00:00 2001 From: takayamaki / fusagiko <24884114+takayamaki@users.noreply.github.com> Date: Wed, 8 Jan 2025 10:39:23 +0900 Subject: [PATCH 14/20] =?UTF-8?q?DELETE=20/api/v1/favourite=5Ftags/{id}?= =?UTF-8?q?=E3=81=AE=E5=AE=9F=E8=A3=85=E3=81=AB=E4=BB=95=E6=A7=98=E5=A4=89?= =?UTF-8?q?=E6=9B=B4=E3=82=92=E5=8F=8D=E6=98=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/controllers/api/v1/favourite_tags_controller.rb | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/app/controllers/api/v1/favourite_tags_controller.rb b/app/controllers/api/v1/favourite_tags_controller.rb index ecad3c1517c189..172fdd26075c19 100644 --- a/app/controllers/api/v1/favourite_tags_controller.rb +++ b/app/controllers/api/v1/favourite_tags_controller.rb @@ -30,13 +30,12 @@ def create end def destroy - tag = find_tag - @favourite_tag = find_fav_tag_by(tag) - if @favourite_tag.nil? - render json: { succeeded: false }, status: 404 + current_account = current_user.account + favourite_tag = current_account.favourite_tags.find_by(id: params[:id]) + if favourite_tag.nil? + render json: { error: 'FavouriteTag is not found' }, status: 404 else - @favourite_tag.destroy - render json: { succeeded: true } + favourite_tag.destroy! end end From d0768d500e5d43fa90d3d04dc07c06a1548ec7b6 Mon Sep 17 00:00:00 2001 From: takayamaki / fusagiko <24884114+takayamaki@users.noreply.github.com> Date: Wed, 8 Jan 2025 10:40:00 +0900 Subject: [PATCH 15/20] =?UTF-8?q?POST=20/api/v1/favourite=5Ftags=E3=81=AEc?= =?UTF-8?q?ontroller=20spec=E3=81=AB=E5=85=AC=E9=96=8B=E7=AF=84=E5=9B=B2?= =?UTF-8?q?=E3=81=8C=E6=8C=87=E5=AE=9A=E3=81=95=E3=82=8C=E3=81=AA=E3=81=8B?= =?UTF-8?q?=E3=81=A3=E3=81=9F=E5=A0=B4=E5=90=88=E3=81=AEcontext=E3=82=92?= =?UTF-8?q?=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/v1/favourite_tags_controller_spec.rb | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/spec/imastodon/controllers/api/v1/favourite_tags_controller_spec.rb b/spec/imastodon/controllers/api/v1/favourite_tags_controller_spec.rb index 799445f3883884..a7af33cd151a0d 100644 --- a/spec/imastodon/controllers/api/v1/favourite_tags_controller_spec.rb +++ b/spec/imastodon/controllers/api/v1/favourite_tags_controller_spec.rb @@ -165,6 +165,30 @@ end end + context '公開範囲が指定されなかった場合' do + let!(:params) do + { + name: 'お気に入りタグ', + } + end + + it '新しいお気に入りタグのレコードが公開範囲publicとして記録され、ステータスコード200と、作成されたお気に入りタグがレスポンスボディとして返る' do + expect { subject }.to change { user.account.favourite_tags.count }.by(1) + + created = FavouriteTag.last + expect(created.name).to eq('お気に入りタグ') + expect(created.visibility).to eq('public') + + expect(response).to have_http_status(:success) + body = JSON.parse(response.body, symbolize_names: true) + expect(body).to match({ + id: created.id, + name: 'お気に入りタグ', + visibility: 'public', + }) + end + end + context 'validでない名前のタグを登録しようとしたとき' do let!(:params) do { From 97f8fcf0b69ac9790817638c8b56fa95b872e9ae Mon Sep 17 00:00:00 2001 From: takayamaki / fusagiko <24884114+takayamaki@users.noreply.github.com> Date: Wed, 8 Jan 2025 10:42:02 +0900 Subject: [PATCH 16/20] =?UTF-8?q?create=5Fparams=E3=81=AEvisibility?= =?UTF-8?q?=E3=81=AB=E3=83=87=E3=83=95=E3=82=A9=E3=83=AB=E3=83=88=E5=80=A4?= =?UTF-8?q?=E3=82=92=E8=A8=AD=E5=AE=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/controllers/api/v1/favourite_tags_controller.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/controllers/api/v1/favourite_tags_controller.rb b/app/controllers/api/v1/favourite_tags_controller.rb index 172fdd26075c19..94644f0a8e7012 100644 --- a/app/controllers/api/v1/favourite_tags_controller.rb +++ b/app/controllers/api/v1/favourite_tags_controller.rb @@ -43,6 +43,8 @@ def destroy def create_params params.permit(:name, :visibility) + params[:visibility] ||= 'public' + params end def set_account From 1341bd6c1f44e32a691e54a9aa22efc5ecd69937 Mon Sep 17 00:00:00 2001 From: takayamaki / fusagiko <24884114+takayamaki@users.noreply.github.com> Date: Wed, 8 Jan 2025 10:42:19 +0900 Subject: [PATCH 17/20] =?UTF-8?q?Api::V1::FavouriteTagsController=E3=81=AE?= =?UTF-8?q?=E4=B8=8D=E8=A6=81=E3=81=AB=E3=81=AA=E3=81=A3=E3=81=9F=E5=AE=9F?= =?UTF-8?q?=E8=A3=85=E3=82=92=E3=81=8A=E6=8E=83=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/v1/favourite_tags_controller.rb | 25 ------------------- 1 file changed, 25 deletions(-) diff --git a/app/controllers/api/v1/favourite_tags_controller.rb b/app/controllers/api/v1/favourite_tags_controller.rb index 94644f0a8e7012..7740e57ded799d 100644 --- a/app/controllers/api/v1/favourite_tags_controller.rb +++ b/app/controllers/api/v1/favourite_tags_controller.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true class Api::V1::FavouriteTagsController < Api::BaseController - before_action :set_account before_action -> { doorkeeper_authorize! :read, :'read:statuses' }, only: [:index] before_action -> { doorkeeper_authorize! :write, :'write:statuses' }, except: [:index] before_action :require_user! @@ -46,28 +45,4 @@ def create_params params[:visibility] ||= 'public' params end - - def set_account - @account = current_user.account - end - - def find_or_init_tag - Tag.find_or_initialize_by(name: tag_params[:tag]) - end - - def find_tag - Tag.find_by(name: tag_params[:tag]) - end - - def find_fav_tag_by(tag) - @account.favourite_tags.find_by(tag: tag) - end - - def favourite_tag_visibility - tag_params[:visibility].nil? ? 'public' : tag_params[:visibility] - end - - def current_favourite_tags - current_account.favourite_tags.with_order.includes(:tag).map(&:to_json_for_api) - end end From 6288af8d8d25fdd341e2025bc8d8b52a314111c0 Mon Sep 17 00:00:00 2001 From: takayamaki / fusagiko <24884114+takayamaki@users.noreply.github.com> Date: Thu, 9 Jan 2025 01:46:38 +0900 Subject: [PATCH 18/20] =?UTF-8?q?=E3=82=BF=E3=82=B0TL=E4=B8=8A=E9=83=A8?= =?UTF-8?q?=E3=81=AE=E3=81=8A=E6=B0=97=E3=81=AB=E5=85=A5=E3=82=8A=E3=82=BF?= =?UTF-8?q?=E3=82=B0=E7=99=BB=E9=8C=B2/=E8=A7=A3=E9=99=A4=E3=83=9C?= =?UTF-8?q?=E3=82=BF=E3=83=B3=E3=82=92API=E3=81=AE=E4=BB=95=E6=A7=98?= =?UTF-8?q?=E5=A4=89=E6=9B=B4=E3=81=AB=E8=BF=BD=E5=BE=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mastodon/actions/favourite_tags.js | 6 +-- .../components/favourite_toggle.jsx | 40 ++++++++++++------- .../containers/favourite_toggle_container.js | 7 ++-- app/javascript/mastodon/locales/en.json | 3 +- app/javascript/mastodon/locales/ja-IM.json | 3 +- app/javascript/mastodon/locales/ja.json | 3 +- 6 files changed, 38 insertions(+), 24 deletions(-) diff --git a/app/javascript/mastodon/actions/favourite_tags.js b/app/javascript/mastodon/actions/favourite_tags.js index dc358d277f3240..68f6a301623409 100644 --- a/app/javascript/mastodon/actions/favourite_tags.js +++ b/app/javascript/mastodon/actions/favourite_tags.js @@ -15,7 +15,7 @@ export function refreshFavouriteTags() { export function addFavouriteTags(tag, visibility) { return (dispatch, getState) => { api(getState).post('/api/v1/favourite_tags', { - tag: tag, + name: tag, visibility: visibility, }).then(() => { dispatch(refreshFavouriteTags()); @@ -23,9 +23,9 @@ export function addFavouriteTags(tag, visibility) { }; } -export function removeFavouriteTags(tag) { +export function removeFavouriteTags(id) { return (dispatch, getState) => { - api(getState).delete(`/api/v1/favourite_tags/${tag}`).then(() => { + api(getState).delete(`/api/v1/favourite_tags/${id}`).then(() => { dispatch(refreshFavouriteTags()); }).catch(() => {}); }; diff --git a/app/javascript/mastodon/features/hashtag_timeline/components/favourite_toggle.jsx b/app/javascript/mastodon/features/hashtag_timeline/components/favourite_toggle.jsx index 86a347856438b4..5db6a22210f85c 100644 --- a/app/javascript/mastodon/features/hashtag_timeline/components/favourite_toggle.jsx +++ b/app/javascript/mastodon/features/hashtag_timeline/components/favourite_toggle.jsx @@ -9,7 +9,8 @@ import Button from '../../../components/button'; const messages = defineMessages({ add_favourite_tags_public: { id: 'tag.add_favourite.public', defaultMessage: 'add in the favourite tags (Public)' }, add_favourite_tags_unlisted: { id: 'tag.add_favourite.unlisted', defaultMessage: 'add in the favourite tags (Unlisted)' }, - remove_favourite_tags: { id: 'tag.remove_favourite', defaultMessage: 'Remove from the favourite tags' }, + remove_favourite_tags_public: { id: 'tag.remove_favourite.public', defaultMessage: 'Remove from the favourite tags (Public)' }, + remove_favourite_tags_unlisted: { id: 'tag.remove_favourite.unlisted', defaultMessage: 'Remove from the favourite tags (Unlisted)' }, }); class FavouriteToggle extends React.PureComponent { @@ -18,7 +19,8 @@ class FavouriteToggle extends React.PureComponent { tag: PropTypes.string.isRequired, addFavouriteTags: PropTypes.func.isRequired, removeFavouriteTags: PropTypes.func.isRequired, - isRegistered: PropTypes.bool.isRequired, + unlistedId: PropTypes.number, + publicId: PropTypes.number, intl: PropTypes.object.isRequired, }; @@ -34,25 +36,33 @@ class FavouriteToggle extends React.PureComponent { this.addFavouriteTags('unlisted'); }; - removeFavouriteTags = () => { - this.props.removeFavouriteTags(this.props.tag); + removeFavouriteTags = (id) => { + this.props.removeFavouriteTags(id); }; + removePublic = () => { + this.removeFavouriteTags(this.props.publicId) + } + + removeUnlisted = () => { + this.removeFavouriteTags(this.props.unlistedId) + } + render () { - const { intl, isRegistered } = this.props; + const { intl, unlistedId, publicId } = this.props; return (
- { isRegistered ? -
-
- : -
-
- } +
+ { + publicId != null ?
); } diff --git a/app/javascript/mastodon/features/hashtag_timeline/containers/favourite_toggle_container.js b/app/javascript/mastodon/features/hashtag_timeline/containers/favourite_toggle_container.js index b2d29ed0c84d09..8abd3a74fe6ac4 100644 --- a/app/javascript/mastodon/features/hashtag_timeline/containers/favourite_toggle_container.js +++ b/app/javascript/mastodon/features/hashtag_timeline/containers/favourite_toggle_container.js @@ -4,7 +4,8 @@ import { addFavouriteTags, removeFavouriteTags } from '../../../actions/favourit import FavouriteToggle from '../components/favourite_toggle'; const mapStateToProps = (state, { tag }) => ({ - isRegistered: state.getIn(['favourite_tags', 'tags']).some(t => t.get('name') === tag), + publicId: state.getIn(['favourite_tags', 'tags']).find(t => t.get('name') === tag && t.get('visibility') === 'public')?.get('id'), + unlistedId: state.getIn(['favourite_tags', 'tags']).find(t => t.get('name') === tag && t.get('visibility') === 'unlisted')?.get('id'), }); const mapDispatchToProps = dispatch => ({ @@ -13,8 +14,8 @@ const mapDispatchToProps = dispatch => ({ dispatch(addFavouriteTags(tag, visibility)); }, - removeFavouriteTags (tag) { - dispatch(removeFavouriteTags(tag)); + removeFavouriteTags (id) { + dispatch(removeFavouriteTags(id)); }, }); diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json index 24a0002f54960f..842e08c3bfa8bd 100644 --- a/app/javascript/mastodon/locales/en.json +++ b/app/javascript/mastodon/locales/en.json @@ -681,7 +681,8 @@ "tabs_bar.notifications": "Notifications", "tag.add_favourite.public": "add in the favourite tags (Public)", "tag.add_favourite.unlisted": "add in the favourite tags (Unlisted)", - "tag.remove_favourite": "Remove from the favourite tags", + "tag.remove_favourite.public": "Remove from the favourite tagss (Public)", + "tag.remove_favourite.unlisted": "Remove from the favourite tagsags (Unlisted)", "time_remaining.days": "{number, plural, one {# day} other {# days}} left", "time_remaining.hours": "{number, plural, one {# hour} other {# hours}} left", "time_remaining.minutes": "{number, plural, one {# minute} other {# minutes}} left", diff --git a/app/javascript/mastodon/locales/ja-IM.json b/app/javascript/mastodon/locales/ja-IM.json index ccdcc3c07662a2..0195cb09a118b0 100644 --- a/app/javascript/mastodon/locales/ja-IM.json +++ b/app/javascript/mastodon/locales/ja-IM.json @@ -357,7 +357,8 @@ "tabs_bar.search": "検索", "tag.add_favourite.public": "スウィーティー☆(公開資料)", "tag.add_favourite.unlisted": "スウィーティー☆(社内資料)", - "tag.remove_favourite": "スウィーティーじゃない", + "tag.remove_favourite.public": "スウィーティーじゃない(公開)", + "tag.remove_favourite.unlisted": "スウィーティーじゃない(未収載)", "time_remaining.days": "残り{number}日", "time_remaining.hours": "残り{number}時間", "time_remaining.minutes": "残り{number}分", diff --git a/app/javascript/mastodon/locales/ja.json b/app/javascript/mastodon/locales/ja.json index 1f99ca336240fc..89f00cd1a82cb5 100644 --- a/app/javascript/mastodon/locales/ja.json +++ b/app/javascript/mastodon/locales/ja.json @@ -681,7 +681,8 @@ "tabs_bar.notifications": "通知", "tag.add_favourite.public": "お気に入り登録(公開)", "tag.add_favourite.unlisted": "お気に入り登録(未収載)", - "tag.remove_favourite": "お気に入り解除", + "tag.remove_favourite.public": "お気に入り解除(公開)", + "tag.remove_favourite.unlisted": "お気に入り解除(未収載)", "time_remaining.days": "残り{number}日", "time_remaining.hours": "残り{number}時間", "time_remaining.minutes": "残り{number}分", From 438c16c3b6cb9e34ad2301d68ba9f896d64055fa Mon Sep 17 00:00:00 2001 From: takayamaki / fusagiko <24884114+takayamaki@users.noreply.github.com> Date: Thu, 9 Jan 2025 02:28:16 +0900 Subject: [PATCH 19/20] =?UTF-8?q?=E3=83=9E=E3=82=A4=E3=82=B0=E3=83=AC?= =?UTF-8?q?=E3=83=BC=E3=82=B7=E3=83=A7=E3=83=B3=E7=94=A8=E3=81=AErake?= =?UTF-8?q?=E3=82=BF=E3=82=B9=E3=82=AF=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/models/favourite_tag.rb | 6 +++++ lib/tasks/imastodon.rake | 10 +++++++ spec/imastodon/models/favourite_tags_spec.rb | 28 ++++++++++++++++++++ 3 files changed, 44 insertions(+) create mode 100644 lib/tasks/imastodon.rake diff --git a/app/models/favourite_tag.rb b/app/models/favourite_tag.rb index f553bc67483677..fd2374a8ee56a8 100644 --- a/app/models/favourite_tag.rb +++ b/app/models/favourite_tag.rb @@ -33,4 +33,10 @@ def to_json_for_api visibility: visibility, } end + + def migrate_tag_name! + return if name.present? + + update!(name: tag&.name) + end end diff --git a/lib/tasks/imastodon.rake b/lib/tasks/imastodon.rake new file mode 100644 index 00000000000000..ba52828ba353c6 --- /dev/null +++ b/lib/tasks/imastodon.rake @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +namespace :imastodon do + task migrate_favourite_tags: :environment do + FavouriteTag.all.includes(:tag).find_in_batches do |favourite_tags| + Rails.logger.info("imastodon:migrate_favourite_tags: #{favourite_tags.first.id}..#{favourite_tags.last.id}") + favourite_tags.each(&:migrate_tag_name!) + end + end +end diff --git a/spec/imastodon/models/favourite_tags_spec.rb b/spec/imastodon/models/favourite_tags_spec.rb index 7f5adae8dff3e0..c40def5ebb0ace 100644 --- a/spec/imastodon/models/favourite_tags_spec.rb +++ b/spec/imastodon/models/favourite_tags_spec.rb @@ -88,4 +88,32 @@ expect(json[:visibility]).to eq 'public' end end + + describe 'migrate_tag_name!' do + it 'モデルのnameカラムに値が入っていない場合は関連付けのtagからnameをコピーする' do + account = Fabricate(:account) + tag = Tag.create!(name: 'tag') + favourite_tag = described_class.new(account: account, tag: tag, visibility: :public, name: nil) + favourite_tag.save!(validate: false) + + expect(favourite_tag.name).to be_nil + + favourite_tag.migrate_tag_name! + + expect(favourite_tag.name).to eq('tag') + end + + it 'モデルのnameカラムに値が入っている場合は変更しない' do + account = Fabricate(:account) + tag = Tag.create!(name: 'hoge_tag') + favourite_tag = described_class.new(account: account, tag: tag, visibility: :public, name: 'tag') + favourite_tag.save! + + expect(favourite_tag.name).to eq('tag') + + favourite_tag.migrate_tag_name! + + expect(favourite_tag.name).to eq('tag') + end + end end From 72ed467e7affec983c33d329c65c52fe00ab87b7 Mon Sep 17 00:00:00 2001 From: takayamaki / fusagiko <24884114+takayamaki@users.noreply.github.com> Date: Thu, 9 Jan 2025 09:59:34 +0900 Subject: [PATCH 20/20] =?UTF-8?q?Settings::FavouriteTagsController?= =?UTF-8?q?=E3=82=92=E3=83=AA=E3=83=95=E3=82=A1=E3=82=AF=E3=82=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 過剰なbefore_actionを削除 --- .../settings/favourite_tags_controller.rb | 41 ++++++++++--------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/app/controllers/settings/favourite_tags_controller.rb b/app/controllers/settings/favourite_tags_controller.rb index 96972d75d372f0..b552ad69953fa9 100644 --- a/app/controllers/settings/favourite_tags_controller.rb +++ b/app/controllers/settings/favourite_tags_controller.rb @@ -3,31 +3,42 @@ class Settings::FavouriteTagsController < Settings::BaseController layout 'admin' before_action :authenticate_user! - before_action :set_account - before_action :set_favourite_tags, only: [:index, :create] - before_action :set_favourite_tag, only: [:edit, :update, :destroy] def index + @favourite_tags = current_account.favourite_tags.with_order @favourite_tag = FavouriteTag.new(visibility: FavouriteTag.visibilities[:public]) end def edit - @favourite_tag + @favourite_tag = current_account.favourite_tags.find(params[:id]) end def create - name = create_params[:name].delete_prefix('#') - @favourite_tag = FavouriteTag.new(account: @account, name: name, order: create_params[:order], visibility: create_params[:visibility]) + @favourite_tag = FavouriteTag.new( + account: current_account, + name: create_params[:name].delete_prefix('#'), + order: create_params[:order], + visibility: create_params[:visibility] + ) + if @favourite_tag.save redirect_to settings_favourite_tags_path, notice: I18n.t('generic.changes_saved_msg') else + @favourite_tags = current_account.favourite_tags.with_order render :index end end def update - name = update_params[:name].delete_prefix('#') - if @favourite_tag.update(name: name, order: update_params[:order], visibility: update_params[:visibility]) + @favourite_tag = current_account.favourite_tags.find(params[:id]) + + @favourite_tag.update( + name: update_params[:name].delete_prefix('#'), + order: update_params[:order], + visibility: update_params[:visibility] + ) + + if @favourite_tag.save redirect_to settings_favourite_tags_path, notice: I18n.t('generic.changes_saved_msg') else render :edit @@ -35,7 +46,7 @@ def update end def destroy - @favourite_tag.destroy + current_account.favourite_tags.destroy(params[:id]) redirect_to settings_favourite_tags_path end @@ -49,15 +60,7 @@ def update_params params.require(:favourite_tag).permit(:name, :visibility, :order) end - def set_account - @account = current_user.account - end - - def set_favourite_tag - @favourite_tag = @account.favourite_tags.find(params[:id]) - end - - def set_favourite_tags - @favourite_tags = @account.favourite_tags.with_order + def current_account + current_user.account end end