diff --git a/app/controllers/avo/base_controller.rb b/app/controllers/avo/base_controller.rb index ffac4e01eb..c6877f58d9 100644 --- a/app/controllers/avo/base_controller.rb +++ b/app/controllers/avo/base_controller.rb @@ -357,12 +357,12 @@ def set_actions def set_applied_filters reset_filters if params[:reset_filter] - @applied_filters = Avo::Filters::BaseFilter.decode_filters(fetch_filters) + return @applied_filters = {} if (fetched_filters = fetch_filters).blank? + + @applied_filters = Avo::Filters::BaseFilter.decode_filters(fetched_filters) # Some filters react to others and will have to be merged into this @applied_filters = @applied_filters.merge reactive_filters - rescue - @applied_filters = {} end def reactive_filters diff --git a/app/javascript/js/controllers/filter_controller.js b/app/javascript/js/controllers/filter_controller.js index 6335f00250..1beb247f7b 100644 --- a/app/javascript/js/controllers/filter_controller.js +++ b/app/javascript/js/controllers/filter_controller.js @@ -25,17 +25,28 @@ export default class extends Controller { return param } - b64EncodeUnicode(str) { - // first we use encodeURIComponent to get percent-encoded UTF-8, - // then we convert the percent encodings into raw bytes which - // can be fed into btoa. - return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, - (match, p1) => String.fromCharCode(`0x${p1}`))) + decode(filters) { + return JSON.parse( + new TextDecoder().decode( + Uint8Array.from( + atob( + decodeURIComponent(filters), + ), (m) => m.codePointAt(0), + ), + ), + ) } - b64DecodeUnicode(str) { - // Going backwards: from bytestream, to percent-encoding, to original string. - return decodeURIComponent(atob(str).split('').map((c) => `%${(`00${c.charCodeAt(0).toString(16)}`).slice(-2)}`).join('')) + encode(filtered) { + return encodeURIComponent( + btoa( + String.fromCodePoint( + ...new TextEncoder().encode( + JSON.stringify(filtered), + ), + ), + ), + ) } changeFilter() { @@ -47,7 +58,7 @@ export default class extends Controller { // Decode the filters if (filters) { - filters = JSON.parse(this.b64DecodeUnicode(filters)) + filters = this.decode(filters) } else { filters = {} } @@ -68,7 +79,7 @@ export default class extends Controller { // Encode the filters and their values if (filtered && Object.keys(filtered).length > 0) { - encodedFilters = this.b64EncodeUnicode(JSON.stringify(filtered)) + encodedFilters = this.encode(filtered) } this.navigateToURLWithFilters(encodedFilters) diff --git a/lib/avo/filters/base_filter.rb b/lib/avo/filters/base_filter.rb index b0a8d957b1..6bbf659422 100644 --- a/lib/avo/filters/base_filter.rb +++ b/lib/avo/filters/base_filter.rb @@ -23,8 +23,6 @@ def current_user class << self def decode_filters(filter_params) JSON.parse(Base64.decode64(filter_params)) - rescue - {} end def encode_filters(filter_params) @@ -67,7 +65,9 @@ def applied_or_default_value(applied_filters) # Fetch the applied filters from the params def applied_filters - self.class.decode_filters params[PARAM_KEY] + return {} if (filters_from_params = params[PARAM_KEY]).blank? + + self.class.decode_filters filters_from_params end def visible_in_view(resource: nil, parent_resource: nil) diff --git a/spec/system/avo/filters/filters_spec.rb b/spec/system/avo/filters/filters_spec.rb index c5ceb8c98c..b6d65d8850 100644 --- a/spec/system/avo/filters/filters_spec.rb +++ b/spec/system/avo/filters/filters_spec.rb @@ -270,10 +270,12 @@ let!(:team_without_members) { create :team, name: "Without Members" } let!(:team_with_members) { create :team, name: "With Members" } + let!(:team) { create :team, name: "音楽 ✓" } before do team_with_members.team_members << user team_without_members.team_members << user + team.team_members << user end let(:url) { "/admin/resources/teams?view_type=table" } @@ -288,7 +290,7 @@ it "filters by name" do visit url - expect(page).to have_text("Displaying 2 item") + expect(page).to have_text("Displaying 3 item") open_filters_menu fill_in "avo_filters_name_filter", with: "With Members" @@ -302,7 +304,26 @@ click_on "Reset filters" wait_for_loaded - expect(page).to have_text("Displaying 2 item") + expect(page).to have_text("Displaying 3 item") + end + + it "filters by 音楽 ✓" do + visit url + expect(page).to have_text("Displaying 3 item") + + open_filters_menu + fill_in "avo_filters_name_filter", with: "音楽 ✓" + click_on "Filter by name" + wait_for_loaded + expect(page).to have_text("Displaying 1 item") + + open_filters_menu + expect(page).to have_text "音楽 ✓" + expect(page).to have_link("Reset filters") + + click_on "Reset filters" + wait_for_loaded + expect(page).to have_text("Displaying 3 item") end end end