From 3b1f9bf4cbc047321f7fb8886cacad2a35de51b9 Mon Sep 17 00:00:00 2001 From: Andreas Nedbal Date: Sun, 22 Jan 2023 17:43:20 +0100 Subject: [PATCH 01/29] Remove legacy cropping feature --- .../retrospring/features/settings/crop.ts | 61 ------------------- .../retrospring/features/settings/index.ts | 5 +- 2 files changed, 1 insertion(+), 65 deletions(-) delete mode 100644 app/javascript/retrospring/features/settings/crop.ts diff --git a/app/javascript/retrospring/features/settings/crop.ts b/app/javascript/retrospring/features/settings/crop.ts deleted file mode 100644 index c30e3e5f..00000000 --- a/app/javascript/retrospring/features/settings/crop.ts +++ /dev/null @@ -1,61 +0,0 @@ -import Croppr from 'croppr'; - -const readImage = (file, callback) => callback((window.URL || window.webkitURL).createObjectURL(file)); - -export function profilePictureChangeHandler(event: Event): void { - const input = event.target as HTMLInputElement; - - const cropControls = document.querySelector('#profile-picture-crop-controls'); - cropControls.classList.toggle('d-none'); - - if (input.files && input.files[0]) { - readImage(input.files[0], (src) => { - const updateValues = (data) => { - document.querySelector('#profile_picture_x').value = data.x; - document.querySelector('#profile_picture_y').value = data.y; - document.querySelector('#profile_picture_w').value = data.width; - document.querySelector('#profile_picture_h').value = data.height; - } - - const cropper = document.querySelector('#profile-picture-cropper'); - cropper.src = src; - - new Croppr(cropper, { - aspectRatio: 1, - startSize: [100, 100, '%'], - onCropStart: updateValues, - onCropMove: updateValues, - onCropEnd: updateValues - }); - }); - } -} - -export function profileHeaderChangeHandler(event: Event): void { - const input = event.target as HTMLInputElement; - - const cropControls = document.querySelector('#profile-header-crop-controls'); - cropControls.classList.toggle('d-none'); - - if (input.files && input.files[0]) { - readImage(input.files[0], (src) => { - const updateValues = (data) => { - document.querySelector('#profile_header_x').value = data.x; - document.querySelector('#profile_header_y').value = data.y; - document.querySelector('#profile_header_w').value = data.width; - document.querySelector('#profile_header_h').value = data.height; - } - - const cropper = document.querySelector('#profile-header-cropper'); - cropper.src = src; - - new Croppr(cropper, { - aspectRatio: 7/30, - startSize: [100, 100, '%'], - onCropStart: updateValues, - onCropMove: updateValues, - onCropEnd: updateValues - }); - }); - } -} \ No newline at end of file diff --git a/app/javascript/retrospring/features/settings/index.ts b/app/javascript/retrospring/features/settings/index.ts index ad6b214e..5ea4bb2d 100644 --- a/app/javascript/retrospring/features/settings/index.ts +++ b/app/javascript/retrospring/features/settings/index.ts @@ -1,11 +1,8 @@ import registerEvents from "utilities/registerEvents"; -import { profileHeaderChangeHandler, profilePictureChangeHandler } from "./crop"; import { userSubmitHandler } from "./password"; export default (): void => { registerEvents([ - { type: 'submit', target: document.querySelector('#edit_user'), handler: userSubmitHandler }, - { type: 'change', target: document.querySelector('#user_profile_picture[type=file]'), handler: profilePictureChangeHandler }, - { type: 'change', target: document.querySelector('#user_profile_header[type=file]'), handler: profileHeaderChangeHandler } + { type: 'submit', target: document.querySelector('#edit_user'), handler: userSubmitHandler } ]); } From 96ed2c864c966981bcb6158bcfc4c1bc280e8964 Mon Sep 17 00:00:00 2001 From: Andreas Nedbal Date: Sun, 22 Jan 2023 17:43:40 +0100 Subject: [PATCH 02/29] Add Stimulus cropper controller --- .../controllers/cropper_controller.ts | 49 +++++++++++++++++++ .../retrospring/initializers/stimulus.ts | 2 + 2 files changed, 51 insertions(+) create mode 100644 app/javascript/retrospring/controllers/cropper_controller.ts diff --git a/app/javascript/retrospring/controllers/cropper_controller.ts b/app/javascript/retrospring/controllers/cropper_controller.ts new file mode 100644 index 00000000..269d0066 --- /dev/null +++ b/app/javascript/retrospring/controllers/cropper_controller.ts @@ -0,0 +1,49 @@ +import { Controller } from '@hotwired/stimulus'; +import Croppr from 'croppr'; + +export default class extends Controller { + static targets = ['input', 'controls', 'cropper', 'x', 'y', 'w', 'h']; + + declare readonly inputTarget: HTMLInputElement; + declare readonly controlsTarget: HTMLElement; + declare readonly cropperTarget: HTMLImageElement; + declare readonly xTarget: HTMLInputElement; + declare readonly yTarget: HTMLInputElement; + declare readonly wTarget: HTMLInputElement; + declare readonly hTarget: HTMLInputElement; + + static values = { + aspectRatio: String + }; + + declare readonly aspectRatioValue: string; + + readImage(file, callback) { + callback((window.URL || window.webkitURL).createObjectURL(file)); + } + + updateValues(data) { + this.xTarget.value = data.x; + this.yTarget.value = data.y; + this.wTarget.value = data.width; + this.hTarget.value = data.height; + } + + change() { + this.controlsTarget.classList.toggle('d-none'); + + if (this.inputTarget.files && this.inputTarget.files[0]) { + this.readImage(this.inputTarget.files[0], (src) => { + this.cropperTarget.src = src; + + new Croppr(this.cropperTarget, { + aspectRatio: parseFloat(this.aspectRatioValue), + startSize: [100, 100, '%'], + onCropStart: this.updateValues.bind(this), + onCropMove: this.updateValues.bind(this), + onCropEnd: this.updateValues.bind(this) + }); + }); + } + } +} diff --git a/app/javascript/retrospring/initializers/stimulus.ts b/app/javascript/retrospring/initializers/stimulus.ts index 3af6306c..64ba1a50 100644 --- a/app/javascript/retrospring/initializers/stimulus.ts +++ b/app/javascript/retrospring/initializers/stimulus.ts @@ -7,6 +7,7 @@ import FormatPopupController from "retrospring/controllers/format_popup_controll import CollapseController from "retrospring/controllers/collapse_controller"; import ThemeController from "retrospring/controllers/theme_controller"; import CapabilitiesController from "retrospring/controllers/capabilities_controller"; +import CropperController from "retrospring/controllers/cropper_controller"; /** * This module sets up Stimulus and our controllers @@ -23,6 +24,7 @@ export default function (): void { window['Stimulus'].register('character-count', CharacterCountController); window['Stimulus'].register('character-count-warning', CharacterCountWarningController); window['Stimulus'].register('collapse', CollapseController); + window['Stimulus'].register('cropper', CropperController); window['Stimulus'].register('format-popup', FormatPopupController); window['Stimulus'].register('theme', ThemeController); } From 6a361e69db2db434a6ef946069514c4bf2c62915 Mon Sep 17 00:00:00 2001 From: Andreas Nedbal Date: Sun, 22 Jan 2023 17:44:08 +0100 Subject: [PATCH 03/29] Wire up cropper controller in profile settings --- app/views/settings/profile/edit.html.haml | 50 ++++++++++++----------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/app/views/settings/profile/edit.html.haml b/app/views/settings/profile/edit.html.haml index 7cd29926..f19a4a32 100644 --- a/app/views/settings/profile/edit.html.haml +++ b/app/views/settings/profile/edit.html.haml @@ -2,36 +2,38 @@ .card-body = bootstrap_form_for(current_user, url: settings_profile_picture_path, html: { multipart: true }, method: :patch, data: { turbo: false }) do |f| - .d-flex#profile-picture-media - .flex-shrink-0 - %img.avatar-lg.me-3{ src: current_user.profile_picture.url(:medium) } - .flex-grow-1 - = f.file_field :profile_picture, accept: APP_CONFIG[:accepted_image_formats].join(",") + %div{ data: { controller: "cropper", cropper_aspect_ratio_value: "1"}} + .d-flex#profile-picture-media + .flex-shrink-0 + %img.avatar-lg.me-3{ src: current_user.profile_picture.url(:medium) } + .flex-grow-1 + = f.file_field :profile_picture, accept: APP_CONFIG[:accepted_image_formats].join(","), data: { cropper_target: "input", action: "cropper#change" } - .row.d-none#profile-picture-crop-controls - .col-sm-10.col-md-8 - %strong= t(".adjust.profile_picture") - %img#profile-picture-cropper{ src: current_user.profile_picture.url(:medium) } + .row.d-none#profile-picture-crop-controls{ data: { cropper_target: "controls" } } + .col-sm-10.col-md-8 + %strong= t(".adjust.profile_picture") + %img#profile-picture-cropper{ src: current_user.profile_picture.url(:medium), data: { cropper_target: "cropper" } } - .row.mb-2#profile-header-media - .col-xs-12.col-md-6 - %img.mw-100.me-3{ src: current_user.profile_header.url(:mobile) } - .col-xs-12.col-md-6.mt-3.mt-sm-0.ps-3.pe-3 - = f.file_field :profile_header, accept: APP_CONFIG[:accepted_image_formats].join(",") + - %i[profile_picture_x profile_picture_y profile_picture_w profile_picture_h].each do |attrib| + = f.hidden_field attrib, id: attrib, data: { cropper_target: attrib.to_s.split("_").last } - .row.d-none#profile-header-crop-controls - .col-sm-10.col-md-8 - %strong= t(".adjust.profile_header") - %img#profile-header-cropper{ src: current_user.profile_header.url(:web) } + %div{ data: { controller: "cropper", cropper_aspect_ratio_value: "0.23"}} + .row.mb-2#profile-header-media + .col-xs-12.col-md-6 + %img.mw-100.me-3{ src: current_user.profile_header.url(:mobile) } + .col-xs-12.col-md-6.mt-3.mt-sm-0.ps-3.pe-3 + = f.file_field :profile_header, accept: APP_CONFIG[:accepted_image_formats].join(","), data: { cropper_target: "input", action: "cropper#change" } + + .row.d-none#profile-header-crop-controls{ data: { cropper_target: "controls" } } + .col-sm-10.col-md-8 + %strong= t(".adjust.profile_header") + %img#profile-header-cropper{ src: current_user.profile_header.url(:web), data: { cropper_target: "cropper" } } + + - %i[profile_header_x profile_header_y profile_header_w profile_header_h].each do |attrib| + = f.hidden_field attrib, id: attrib, data: { cropper_target: attrib.to_s.split("_").last } = f.check_box :show_foreign_themes - - %i[profile_picture_x profile_picture_y profile_picture_w profile_picture_h].each do |attrib| - = f.hidden_field attrib, id: attrib - - - %i[profile_header_x profile_header_y profile_header_w profile_header_h].each do |attrib| - = f.hidden_field attrib, id: attrib - = f.primary t(".submit_picture") .card .card-body From f0df18ce871a51022bd8ce36133bb984afb0224a Mon Sep 17 00:00:00 2001 From: Andreas Nedbal Date: Sun, 22 Jan 2023 17:48:53 +0100 Subject: [PATCH 04/29] Remove unused IDs on profile crop elements --- app/views/settings/profile/edit.html.haml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/views/settings/profile/edit.html.haml b/app/views/settings/profile/edit.html.haml index f19a4a32..ad75dd65 100644 --- a/app/views/settings/profile/edit.html.haml +++ b/app/views/settings/profile/edit.html.haml @@ -3,31 +3,31 @@ = bootstrap_form_for(current_user, url: settings_profile_picture_path, html: { multipart: true }, method: :patch, data: { turbo: false }) do |f| %div{ data: { controller: "cropper", cropper_aspect_ratio_value: "1"}} - .d-flex#profile-picture-media + .d-flex .flex-shrink-0 %img.avatar-lg.me-3{ src: current_user.profile_picture.url(:medium) } .flex-grow-1 = f.file_field :profile_picture, accept: APP_CONFIG[:accepted_image_formats].join(","), data: { cropper_target: "input", action: "cropper#change" } - .row.d-none#profile-picture-crop-controls{ data: { cropper_target: "controls" } } + .row.d-none{ data: { cropper_target: "controls" } } .col-sm-10.col-md-8 %strong= t(".adjust.profile_picture") - %img#profile-picture-cropper{ src: current_user.profile_picture.url(:medium), data: { cropper_target: "cropper" } } + %img{ src: current_user.profile_picture.url(:medium), data: { cropper_target: "cropper" } } - %i[profile_picture_x profile_picture_y profile_picture_w profile_picture_h].each do |attrib| = f.hidden_field attrib, id: attrib, data: { cropper_target: attrib.to_s.split("_").last } %div{ data: { controller: "cropper", cropper_aspect_ratio_value: "0.23"}} - .row.mb-2#profile-header-media + .row.mb-2 .col-xs-12.col-md-6 %img.mw-100.me-3{ src: current_user.profile_header.url(:mobile) } .col-xs-12.col-md-6.mt-3.mt-sm-0.ps-3.pe-3 = f.file_field :profile_header, accept: APP_CONFIG[:accepted_image_formats].join(","), data: { cropper_target: "input", action: "cropper#change" } - .row.d-none#profile-header-crop-controls{ data: { cropper_target: "controls" } } + .row.d-none{ data: { cropper_target: "controls" } } .col-sm-10.col-md-8 %strong= t(".adjust.profile_header") - %img#profile-header-cropper{ src: current_user.profile_header.url(:web), data: { cropper_target: "cropper" } } + %img{ src: current_user.profile_header.url(:web), data: { cropper_target: "cropper" } } - %i[profile_header_x profile_header_y profile_header_w profile_header_h].each do |attrib| = f.hidden_field attrib, id: attrib, data: { cropper_target: attrib.to_s.split("_").last } From fe156a38d33efbb4b86430c96324ba5c7c6d402d Mon Sep 17 00:00:00 2001 From: Andreas Nedbal Date: Sun, 22 Jan 2023 17:58:23 +0100 Subject: [PATCH 05/29] Appease the dog overlords --- .../retrospring/controllers/cropper_controller.ts | 6 +++--- app/views/settings/profile/edit.html.haml | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/javascript/retrospring/controllers/cropper_controller.ts b/app/javascript/retrospring/controllers/cropper_controller.ts index 269d0066..921e328c 100644 --- a/app/javascript/retrospring/controllers/cropper_controller.ts +++ b/app/javascript/retrospring/controllers/cropper_controller.ts @@ -18,18 +18,18 @@ export default class extends Controller { declare readonly aspectRatioValue: string; - readImage(file, callback) { + readImage(file: File, callback: (string) => void): void { callback((window.URL || window.webkitURL).createObjectURL(file)); } - updateValues(data) { + updateValues(data: Record): void { this.xTarget.value = data.x; this.yTarget.value = data.y; this.wTarget.value = data.width; this.hTarget.value = data.height; } - change() { + change(): void { this.controlsTarget.classList.toggle('d-none'); if (this.inputTarget.files && this.inputTarget.files[0]) { diff --git a/app/views/settings/profile/edit.html.haml b/app/views/settings/profile/edit.html.haml index ad75dd65..32721590 100644 --- a/app/views/settings/profile/edit.html.haml +++ b/app/views/settings/profile/edit.html.haml @@ -2,7 +2,7 @@ .card-body = bootstrap_form_for(current_user, url: settings_profile_picture_path, html: { multipart: true }, method: :patch, data: { turbo: false }) do |f| - %div{ data: { controller: "cropper", cropper_aspect_ratio_value: "1"}} + %div{ data: { controller: "cropper", cropper_aspect_ratio_value: "1" } } .d-flex .flex-shrink-0 %img.avatar-lg.me-3{ src: current_user.profile_picture.url(:medium) } @@ -17,7 +17,7 @@ - %i[profile_picture_x profile_picture_y profile_picture_w profile_picture_h].each do |attrib| = f.hidden_field attrib, id: attrib, data: { cropper_target: attrib.to_s.split("_").last } - %div{ data: { controller: "cropper", cropper_aspect_ratio_value: "0.23"}} + %div{ data: { controller: "cropper", cropper_aspect_ratio_value: "0.23" } } .row.mb-2 .col-xs-12.col-md-6 %img.mw-100.me-3{ src: current_user.profile_header.url(:mobile) } From b99e1b03de2841c7302e2c13c5e433faa3dd4245 Mon Sep 17 00:00:00 2001 From: Georg Gadinger Date: Mon, 23 Jan 2023 12:25:48 +0100 Subject: [PATCH 06/29] notifications: only update all new notifications --- app/views/layouts/notifications.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/layouts/notifications.html.haml b/app/views/layouts/notifications.html.haml index d7c87e7e..dceb6d47 100644 --- a/app/views/layouts/notifications.html.haml +++ b/app/views/layouts/notifications.html.haml @@ -8,5 +8,5 @@ .d-block.d-sm-none= render "shared/links" :ruby - Notification.for(current_user).update_all(new: false) + Notification.for(current_user).where(new: true).update_all(new: false) parent_layout 'base' From 58705fffba417db7210c36500487ab7b448c3196 Mon Sep 17 00:00:00 2001 From: Georg Gadinger Date: Tue, 24 Jan 2023 15:54:41 +0100 Subject: [PATCH 07/29] mark notifications as "read" in the controller and when you see them this makes it behave a bit more like the inbox --- app/controllers/notifications_controller.rb | 7 +++++++ app/views/layouts/notifications.html.haml | 1 - spec/controllers/notifications_controller_spec.rb | 4 ++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/app/controllers/notifications_controller.rb b/app/controllers/notifications_controller.rb index 6f6f0e91..0655c3c3 100644 --- a/app/controllers/notifications_controller.rb +++ b/app/controllers/notifications_controller.rb @@ -3,6 +3,8 @@ class NotificationsController < ApplicationController before_action :authenticate_user! + after_action :mark_notifications_as_read, only: %i[index] + TYPE_MAPPINGS = { "answer" => Notification::QuestionAnswered.name, "comment" => Notification::Commented.name, @@ -25,6 +27,11 @@ class NotificationsController < ApplicationController private + def mark_notifications_as_read + # using .dup to not modify @notifications -- useful in tests + @notifications&.dup&.update_all(new: false) + end + def cursored_notifications_for(type:, last_id:, size: nil) cursor_params = { last_id: last_id, size: size }.compact diff --git a/app/views/layouts/notifications.html.haml b/app/views/layouts/notifications.html.haml index dceb6d47..08d8ad1d 100644 --- a/app/views/layouts/notifications.html.haml +++ b/app/views/layouts/notifications.html.haml @@ -8,5 +8,4 @@ .d-block.d-sm-none= render "shared/links" :ruby - Notification.for(current_user).where(new: true).update_all(new: false) parent_layout 'base' diff --git a/spec/controllers/notifications_controller_spec.rb b/spec/controllers/notifications_controller_spec.rb index f89db5fb..bcae9a8d 100644 --- a/spec/controllers/notifications_controller_spec.rb +++ b/spec/controllers/notifications_controller_spec.rb @@ -33,5 +33,9 @@ describe NotificationsController do expect(response).to render_template(:index) expect(controller.instance_variable_get(:@notifications)).to have_attributes(size: 2) end + + it "marks notifications as read" do + expect { subject }.to change { Notification.for(user).where(new: true).count }.from(2).to(0) + end end end From 8cd0d481c8ca1d9fcf51a589905658c1e62b77be Mon Sep 17 00:00:00 2001 From: Georg Gadinger Date: Tue, 24 Jan 2023 15:57:34 +0100 Subject: [PATCH 08/29] bruh --- app/controllers/notifications_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/notifications_controller.rb b/app/controllers/notifications_controller.rb index 0655c3c3..4f2dfdeb 100644 --- a/app/controllers/notifications_controller.rb +++ b/app/controllers/notifications_controller.rb @@ -29,7 +29,7 @@ class NotificationsController < ApplicationController def mark_notifications_as_read # using .dup to not modify @notifications -- useful in tests - @notifications&.dup&.update_all(new: false) + @notifications&.dup&.update_all(new: false) # rubocop:disable Rails/SkipsModelValidations end def cursored_notifications_for(type:, last_id:, size: nil) From adaa4a4314577f7d971190f6abf5c3558feb6bc5 Mon Sep 17 00:00:00 2001 From: Karina Kwiatek Date: Thu, 26 Jan 2023 12:08:40 +0100 Subject: [PATCH 09/29] Only pass cached requests through service worker --- public/service_worker.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/public/service_worker.js b/public/service_worker.js index 3735a89b..8662f085 100644 --- a/public/service_worker.js +++ b/public/service_worker.js @@ -47,6 +47,9 @@ self.addEventListener('install', function (event) { }); self.addEventListener('fetch', function (event) { + const url = new URL(event.request.url); + if (event.request.method !== 'GET' || !OFFLINE_CACHE_PATHS.includes(url.pathname)) return; + event.respondWith( (async () => { try { From 9a5ce13cfde6ae96c58d68f2de7dc51888adc875 Mon Sep 17 00:00:00 2001 From: Georg Gadinger Date: Fri, 27 Jan 2023 16:16:34 +0100 Subject: [PATCH 10/29] re-add removed remove_stale rake task --- Rakefile | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Rakefile b/Rakefile index 20c3349e..17ba7dcf 100644 --- a/Rakefile +++ b/Rakefile @@ -51,6 +51,15 @@ namespace :justask do # rubocop:disable Metrics/BlockLength user.remove_role :moderator puts "#{user.screen_name} is no longer a moderator." end + + desc "Removes users whose accounts haven't been verified for over 3 months." + task remove_stale: :environment do + puts "Removing stale users…" + removed = User.where(confirmed_at: nil) + .where("confirmation_sent_at < ?", DateTime.now.utc - 3.months) + .destroy_all.count + puts "Removed #{removed} users" + end end namespace :db do From 420627f3adae872ac4902eccc12003c6703b16b1 Mon Sep 17 00:00:00 2001 From: Karina Kwiatek Date: Fri, 27 Jan 2023 17:28:13 +0100 Subject: [PATCH 11/29] Bump version to 2023.0127.0 --- lib/retrospring/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/retrospring/version.rb b/lib/retrospring/version.rb index 824e5dc3..f35b16f9 100644 --- a/lib/retrospring/version.rb +++ b/lib/retrospring/version.rb @@ -17,7 +17,7 @@ module Retrospring def month = 1 - def day = 23 + def day = 27 def patch = 0 From 3e3501d201ee4edc6dfa82b26a681d8109b9cb16 Mon Sep 17 00:00:00 2001 From: Georg Gadinger Date: Fri, 27 Jan 2023 20:31:38 +0100 Subject: [PATCH 12/29] inbox: update inbox entries in controller --- app/controllers/inbox_controller.rb | 7 +++++++ app/views/layouts/inbox.html.haml | 1 - spec/controllers/inbox_controller_spec.rb | 4 ++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/app/controllers/inbox_controller.rb b/app/controllers/inbox_controller.rb index 19c6a70d..48cc5675 100644 --- a/app/controllers/inbox_controller.rb +++ b/app/controllers/inbox_controller.rb @@ -3,6 +3,8 @@ class InboxController < ApplicationController before_action :authenticate_user! + after_action :mark_inbox_entries_as_read, only: %i[show] + def show find_author find_inbox_entries @@ -77,4 +79,9 @@ class InboxController < ApplicationController .joins(:question) .where(questions: { user: @author_user, author_is_anonymous: false }) end + + def mark_inbox_entries_as_read + # using .dup to not modify @inbox -- useful in tests + @inbox&.dup&.update_all(new: false) # rubocop:disable Rails/SkipsModelValidations + end end diff --git a/app/views/layouts/inbox.html.haml b/app/views/layouts/inbox.html.haml index b54b82b5..cb931773 100644 --- a/app/views/layouts/inbox.html.haml +++ b/app/views/layouts/inbox.html.haml @@ -9,6 +9,5 @@ = render 'shared/links' :ruby - @inbox.update_all(new: false) provide(:title, generate_title('Inbox')) parent_layout 'base' diff --git a/spec/controllers/inbox_controller_spec.rb b/spec/controllers/inbox_controller_spec.rb index 6cb2e128..bc0ba2ad 100644 --- a/spec/controllers/inbox_controller_spec.rb +++ b/spec/controllers/inbox_controller_spec.rb @@ -58,6 +58,10 @@ describe InboxController, type: :controller do end end + it "updates the inbox entry status" do + expect { subject }.to change { inbox_entry.reload.new? }.from(true).to(false) + end + context "when requested the turbo stream format" do subject { get :show, format: :turbo_stream } From b8b86b069d632285ff1fc78f020c5393130359e7 Mon Sep 17 00:00:00 2001 From: Karina Kwiatek Date: Fri, 27 Jan 2023 23:08:12 +0100 Subject: [PATCH 13/29] Cache headers for web app manifest based on user theme --- app/controllers/manifests_controller.rb | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/app/controllers/manifests_controller.rb b/app/controllers/manifests_controller.rb index 966f6164..1a66ff85 100644 --- a/app/controllers/manifests_controller.rb +++ b/app/controllers/manifests_controller.rb @@ -3,7 +3,13 @@ class ManifestsController < ApplicationController include ThemeHelper + skip_before_action :banned? + skip_before_action :find_active_announcements + def show + expires_in 1.day + return if fresh_when current_user&.theme + render json: { name: APP_CONFIG["site_name"], description: t("about.index.subtitle"), @@ -12,17 +18,19 @@ class ManifestsController < ApplicationController display: "standalone", categories: %w[social], lang: I18n.locale, - shortcuts: [ - webapp_shortcut(inbox_url, t("navigation.inbox"), "inbox") - ], + shortcuts:, icons: webapp_icons, - theme_color: theme_color, + theme_color:, background_color: mobile_theme_color } end private + def shortcuts = [ + webapp_shortcut(inbox_url, t("navigation.inbox"), "inbox") + ] + def webapp_shortcut(url, name, icon_name) { name: name, From a34a30349e329b9aec1acfde2bc18215025635e5 Mon Sep 17 00:00:00 2001 From: Karina Kwiatek Date: Fri, 27 Jan 2023 23:16:01 +0100 Subject: [PATCH 14/29] Use `skip_before_action` instead of redefining action in `AjaxController` --- app/controllers/ajax_controller.rb | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/app/controllers/ajax_controller.rb b/app/controllers/ajax_controller.rb index f6718de0..f5cc4ecd 100644 --- a/app/controllers/ajax_controller.rb +++ b/app/controllers/ajax_controller.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true class AjaxController < ApplicationController + skip_before_action :find_active_announcements before_action :build_response after_action :return_response @@ -92,10 +93,6 @@ class AjaxController < ApplicationController return_response end - def find_active_announcements - # We do not need announcements here - end - private def build_response From f6392b8ca6fa6fb64f03f7221cb45ffd0ef55b5f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 28 Jan 2023 01:21:40 +0000 Subject: [PATCH 15/29] Bump sanitize from 6.0.0 to 6.0.1 Bumps [sanitize](https://github.com/rgrove/sanitize) from 6.0.0 to 6.0.1. - [Release notes](https://github.com/rgrove/sanitize/releases) - [Changelog](https://github.com/rgrove/sanitize/blob/main/HISTORY.md) - [Commits](https://github.com/rgrove/sanitize/compare/v6.0.0...v6.0.1) --- updated-dependencies: - dependency-name: sanitize dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index c7e221d6..28e75168 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -437,7 +437,7 @@ GEM ruby-vips (2.1.4) ffi (~> 1.12) rubyzip (2.3.2) - sanitize (6.0.0) + sanitize (6.0.1) crass (~> 1.0.2) nokogiri (>= 1.12.0) sassc (2.4.0) From d45c6af8533dc7617fcb6d278b3d5b7cfc11c7c5 Mon Sep 17 00:00:00 2001 From: Karina Kwiatek Date: Sat, 28 Jan 2023 11:08:16 +0100 Subject: [PATCH 16/29] Remove page parameter from `user/friends` redirects --- config/routes.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/routes.rb b/config/routes.rb index 68958f72..7dca9c1a 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -157,14 +157,14 @@ Rails.application.routes.draw do get "/@:username/q/:id", to: "question#show", as: :question get "/@:username/followers", to: "user#followers", as: :show_user_followers get "/@:username/followings", to: "user#followings", as: :show_user_followings - get "/@:username/friends", to: redirect("/@%{username}/followings/p/%{page}") + get "/@:username/friends", to: redirect("/@%{username}/followings") get "/@:username/questions", to: "user#questions", as: :show_user_questions get "/:username", to: "user#show", as: :user_alt get "/:username/a/:id", to: "answer#show", as: :answer_alt get "/:username/q/:id", to: "question#show", as: :question_alt get "/:username/followers", to: "user#followers", as: :show_user_followers_alt get "/:username/followings", to: "user#followings", as: :show_user_followings_alt - get "/:username/friends", to: redirect("/%{username}/followings/p/%{page}") + get "/:username/friends", to: redirect("/%{username}/followings") get "/:username/questions", to: "user#questions", as: :show_user_questions_alt get "/feedback/consent", to: "feedback#consent", as: "feedback_consent" From 6e3bc40c5721533c4405b17431e73814c4e60b63 Mon Sep 17 00:00:00 2001 From: Karina Kwiatek Date: Sat, 28 Jan 2023 18:48:14 +0100 Subject: [PATCH 17/29] Move marking follow notifications as read to an after action --- app/controllers/user_controller.rb | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/app/controllers/user_controller.rb b/app/controllers/user_controller.rb index 79be3630..ed054b8d 100644 --- a/app/controllers/user_controller.rb +++ b/app/controllers/user_controller.rb @@ -3,20 +3,13 @@ class UserController < ApplicationController before_action :set_user before_action :hidden_social_graph_redirect, only: %i[followers followings] + after_action :mark_notification_as_read, only: %i[show] def show @answers = @user.cursored_answers(last_id: params[:last_id]) @answers_last_id = @answers.map(&:id).min @more_data_available = !@user.cursored_answers(last_id: @answers_last_id, size: 1).count.zero? - if user_signed_in? - notif = Notification.where(target_type: "Relationship", target_id: @user.active_follow_relationships.where(target_id: current_user.id).pluck(:id), recipient_id: current_user.id, new: true).first - unless notif.nil? - notif.new = false - notif.save - end - end - respond_to do |format| format.html format.turbo_stream @@ -66,6 +59,18 @@ class UserController < ApplicationController private + def mark_notification_as_read + return unless user_signed_in? + + Notification + .where( + target_type: "Relationship", + target_id: @user.active_follow_relationships.where(target_id: current_user.id).pluck(:id), + recipient_id: current_user.id, + new: true + ).update(new: false) + end + def set_user @user = User.where("LOWER(screen_name) = ?", params[:username].downcase).includes(:profile).first! end From e9c397a01350dfc53fd5fced64183596a7d84225 Mon Sep 17 00:00:00 2001 From: Karina Kwiatek Date: Sat, 28 Jan 2023 19:02:13 +0100 Subject: [PATCH 18/29] Replace `@title` and `@type` ivars --- app/controllers/user_controller.rb | 14 ++++---------- app/views/user/show_follow.html.haml | 6 +++--- app/views/user/show_follow.turbo_stream.haml | 4 ++-- config/locales/views.en.yml | 4 ++++ 4 files changed, 13 insertions(+), 15 deletions(-) diff --git a/app/controllers/user_controller.rb b/app/controllers/user_controller.rb index ed054b8d..ad94a2d1 100644 --- a/app/controllers/user_controller.rb +++ b/app/controllers/user_controller.rb @@ -17,36 +17,30 @@ class UserController < ApplicationController end def followers - @title = "Followers" @relationships = @user.cursored_follower_relationships(last_id: params[:last_id]) @relationships_last_id = @relationships.map(&:id).min @more_data_available = !@user.cursored_follower_relationships(last_id: @relationships_last_id, size: 1).count.zero? @users = @relationships.map(&:source) - @type = :follower respond_to do |format| - format.html { render "show_follow" } - format.turbo_stream { render "show_follow" } + format.html { render "show_follow", locals: { type: :follower } } + format.turbo_stream { render "show_follow", locals: { type: :follower } } end end def followings - @title = "Following" @relationships = @user.cursored_following_relationships(last_id: params[:last_id]) @relationships_last_id = @relationships.map(&:id).min @more_data_available = !@user.cursored_following_relationships(last_id: @relationships_last_id, size: 1).count.zero? @users = @relationships.map(&:target) - @type = :friend respond_to do |format| - format.html { render "show_follow" } - format.turbo_stream { render "show_follow" } + format.html { render "show_follow", locals: { type: :friend } } + format.turbo_stream { render "show_follow", locals: { type: :friend } } end end def questions - @title = "Questions" - @questions = @user.cursored_questions(author_is_anonymous: false, direct: direct_param, last_id: params[:last_id]) @questions_last_id = @questions.map(&:id).min @more_data_available = !@user.cursored_questions(author_is_anonymous: false, direct: direct_param, last_id: @questions_last_id, size: 1).count.zero? diff --git a/app/views/user/show_follow.html.haml b/app/views/user/show_follow.html.haml index fd341619..bcd981f0 100644 --- a/app/views/user/show_follow.html.haml +++ b/app/views/user/show_follow.html.haml @@ -1,15 +1,15 @@ .row.row-cols-1.row-cols-sm-2.row-cols-md-3#users - @users.each do |user| .col.pb-3 - = render 'shared/userbox', user: user, type: @type + = render 'shared/userbox', user:, type: - if @more_data_available .d-flex.justify-content-center.justify-content-sm-start#paginator - = button_to t("voc.load"), @type == :follower ? show_user_followers_path(@user) : show_user_followings_path(@user), + = button_to t("voc.load"), type == :follower ? show_user_followers_path(@user) : show_user_followings_path(@user), class: "btn btn-light", method: :get, params: { last_id: @relationships_last_id }, form: { data: { turbo_stream: true } } -- provide(:title, user_title(@user, 'friends and followers')) +- provide(:title, t(".title.#{type}", user: @user.profile.safe_name)) - parent_layout 'user/profile' diff --git a/app/views/user/show_follow.turbo_stream.haml b/app/views/user/show_follow.turbo_stream.haml index d36b3b2f..013d730a 100644 --- a/app/views/user/show_follow.turbo_stream.haml +++ b/app/views/user/show_follow.turbo_stream.haml @@ -1,11 +1,11 @@ = turbo_stream.append "users" do - @users.each do |user| .col.pb-3 - = render 'shared/userbox', user: user, type: @type + = render 'shared/userbox', user:, type: = turbo_stream.update "paginator" do - if @more_data_available - = button_to t("voc.load"), @type == :follower ? show_user_followers_path(@user) : show_user_followings_path(@user), + = button_to t("voc.load"), type == :follower ? show_user_followers_path(@user) : show_user_followings_path(@user), class: "btn btn-light", method: :get, params: { last_id: @relationships_last_id }, diff --git a/config/locales/views.en.yml b/config/locales/views.en.yml index 6cc8aca5..295f8aab 100644 --- a/config/locales/views.en.yml +++ b/config/locales/views.en.yml @@ -651,6 +651,10 @@ en: index: title: "Questions from %{author_identifier}" user: + show_follow: + title: + follower: "%{user}'s followers" + friend: "%{user}'s followings" actions: view_inbox: "View inbox" privilege: "Check %{user}'s privileges" From ff866f88efbc3896b3c69c6e62b4b6bb54190caf Mon Sep 17 00:00:00 2001 From: Karina Kwiatek Date: Sat, 28 Jan 2023 19:09:03 +0100 Subject: [PATCH 19/29] Appease the dog overlords --- app/views/user/show_follow.html.haml | 4 ++-- app/views/user/show_follow.turbo_stream.haml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/views/user/show_follow.html.haml b/app/views/user/show_follow.html.haml index bcd981f0..364d5dcf 100644 --- a/app/views/user/show_follow.html.haml +++ b/app/views/user/show_follow.html.haml @@ -1,7 +1,7 @@ .row.row-cols-1.row-cols-sm-2.row-cols-md-3#users - @users.each do |user| .col.pb-3 - = render 'shared/userbox', user:, type: + = render "shared/userbox", user:, type: - if @more_data_available .d-flex.justify-content-center.justify-content-sm-start#paginator @@ -12,4 +12,4 @@ form: { data: { turbo_stream: true } } - provide(:title, t(".title.#{type}", user: @user.profile.safe_name)) -- parent_layout 'user/profile' +- parent_layout "user/profile" diff --git a/app/views/user/show_follow.turbo_stream.haml b/app/views/user/show_follow.turbo_stream.haml index 013d730a..7719340e 100644 --- a/app/views/user/show_follow.turbo_stream.haml +++ b/app/views/user/show_follow.turbo_stream.haml @@ -1,7 +1,7 @@ = turbo_stream.append "users" do - @users.each do |user| .col.pb-3 - = render 'shared/userbox', user:, type: + = render "shared/userbox", user:, type: = turbo_stream.update "paginator" do - if @more_data_available From 8dd49d3a8c1c0b4c408f5530a80fa0c8ef8e0fd7 Mon Sep 17 00:00:00 2001 From: Andreas Nedbal Date: Sat, 28 Jan 2023 19:17:04 +0100 Subject: [PATCH 20/29] Remove author search handler --- app/javascript/retrospring/features/inbox/author.ts | 6 ------ app/javascript/retrospring/features/inbox/index.ts | 3 +-- 2 files changed, 1 insertion(+), 8 deletions(-) delete mode 100644 app/javascript/retrospring/features/inbox/author.ts diff --git a/app/javascript/retrospring/features/inbox/author.ts b/app/javascript/retrospring/features/inbox/author.ts deleted file mode 100644 index 456b0ed5..00000000 --- a/app/javascript/retrospring/features/inbox/author.ts +++ /dev/null @@ -1,6 +0,0 @@ -export function authorSearchHandler(event: Event): void { - event.preventDefault(); - - const author = document.querySelector('#author')?.value; - window.location.href = `/inbox/${encodeURIComponent(author)}`; -} \ No newline at end of file diff --git a/app/javascript/retrospring/features/inbox/index.ts b/app/javascript/retrospring/features/inbox/index.ts index 4235dc15..02206e9a 100644 --- a/app/javascript/retrospring/features/inbox/index.ts +++ b/app/javascript/retrospring/features/inbox/index.ts @@ -6,8 +6,7 @@ import { deleteAllAuthorQuestionsHandler, deleteAllQuestionsHandler } from './de export default (): void => { registerEvents([ { type: 'click', target: '#ib-delete-all', handler: deleteAllQuestionsHandler, global: true }, - { type: 'click', target: '#ib-delete-all-author', handler: deleteAllAuthorQuestionsHandler, global: true }, - { type: 'submit', target: '#author-form', handler: authorSearchHandler, global: true } + { type: 'click', target: '#ib-delete-all-author', handler: deleteAllAuthorQuestionsHandler, global: true } ]); registerInboxEntryEvents(); From 6c25594b88b68eea1b442c8b2553718380731717 Mon Sep 17 00:00:00 2001 From: Andreas Nedbal Date: Sat, 28 Jan 2023 19:18:02 +0100 Subject: [PATCH 21/29] Get the username of the currently searched author correctly --- app/javascript/retrospring/features/inbox/delete.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/javascript/retrospring/features/inbox/delete.ts b/app/javascript/retrospring/features/inbox/delete.ts index 6595af0b..c93534b2 100644 --- a/app/javascript/retrospring/features/inbox/delete.ts +++ b/app/javascript/retrospring/features/inbox/delete.ts @@ -62,6 +62,7 @@ export function deleteAllQuestionsHandler(event: Event): void { export function deleteAllAuthorQuestionsHandler(event: Event): void { const button = event.target as Element; const count = button.getAttribute('data-ib-count'); + const urlSearchParams = new URLSearchParams(window.location.search); swal({ title: I18n.translate('frontend.inbox.confirm_all.title', { count: count }), @@ -75,7 +76,7 @@ export function deleteAllAuthorQuestionsHandler(event: Event): void { }, (returnValue) => { if (returnValue === null) return false; - post(`/ajax/delete_all_inbox/${location.pathname.split('/')[2]}`) + post(`/ajax/delete_all_inbox/${urlSearchParams.get('author')}`) .then(async response => { const data = await response.json; @@ -89,4 +90,4 @@ export function deleteAllAuthorQuestionsHandler(event: Event): void { showErrorNotification(I18n.translate('frontend.error.message')); }); }); -} \ No newline at end of file +} From 32ba17ac72f2971a3470dfc16f88f22fb3b8b2f2 Mon Sep 17 00:00:00 2001 From: Karina Kwiatek Date: Sat, 28 Jan 2023 21:17:08 +0100 Subject: [PATCH 22/29] Eager load profiles in paginators --- app/models/user/answer_methods.rb | 3 ++- app/models/user/inbox_methods.rb | 3 ++- app/models/user/timeline_methods.rb | 8 ++++++-- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/app/models/user/answer_methods.rb b/app/models/user/answer_methods.rb index 172997d2..e40eb82b 100644 --- a/app/models/user/answer_methods.rb +++ b/app/models/user/answer_methods.rb @@ -5,10 +5,11 @@ module User::AnswerMethods define_cursor_paginator :cursored_answers, :ordered_answers + # @return [ActiveRecord::Relation] List of a user's answers def ordered_answers answers .order(:created_at) .reverse_order - .includes(comments: [:user, :smiles], question: [:user], smiles: [:user]) + .includes(comments: %i[user smiles], question: { user: :profile }, smiles: [:user]) end end diff --git a/app/models/user/inbox_methods.rb b/app/models/user/inbox_methods.rb index dea2fb36..1d961ce2 100644 --- a/app/models/user/inbox_methods.rb +++ b/app/models/user/inbox_methods.rb @@ -5,9 +5,10 @@ module User::InboxMethods define_cursor_paginator :cursored_inbox, :ordered_inbox + # @return [ActiveRecord::Relation] the user's inbox entries def ordered_inbox inboxes - .includes(:question, :user) + .includes(:question, user: :profile) .order(:created_at) .reverse_order end diff --git a/app/models/user/timeline_methods.rb b/app/models/user/timeline_methods.rb index 38f1e560..a0fa8c55 100644 --- a/app/models/user/timeline_methods.rb +++ b/app/models/user/timeline_methods.rb @@ -5,8 +5,12 @@ module User::TimelineMethods define_cursor_paginator :cursored_timeline, :timeline - # @return [Array] the users' timeline + # @return [ActiveRecord::Relation] the user's timeline def timeline - Answer.where("user_id in (?) OR user_id = ?", following_ids, id).order(:created_at).reverse_order.includes(comments: %i[user smiles], question: [:user], user: [:profile], smiles: [:user]) + Answer + .where("user_id in (?) OR user_id = ?", following_ids, id) + .order(:created_at) + .reverse_order + .includes(comments: %i[user smiles], question: { user: :profile }, user: [:profile], smiles: [:user]) end end From ab1b034cfbaa3cef962021e35a9f0e11640ff95a Mon Sep 17 00:00:00 2001 From: Karina Kwiatek Date: Sat, 28 Jan 2023 21:17:25 +0100 Subject: [PATCH 23/29] Add type hints for paginators --- app/models/user/question_methods.rb | 1 + app/models/user/relationship_methods.rb | 2 ++ 2 files changed, 3 insertions(+) diff --git a/app/models/user/question_methods.rb b/app/models/user/question_methods.rb index 0ff0db4f..e2281810 100644 --- a/app/models/user/question_methods.rb +++ b/app/models/user/question_methods.rb @@ -5,6 +5,7 @@ module User::QuestionMethods define_cursor_paginator :cursored_questions, :ordered_questions + # @return [ActiveRecord::Relation] List of questions sent by the user def ordered_questions(author_is_anonymous: nil, direct: nil) questions .where({ author_is_anonymous:, direct: }.compact) diff --git a/app/models/user/relationship_methods.rb b/app/models/user/relationship_methods.rb index faf56ef0..b70bb578 100644 --- a/app/models/user/relationship_methods.rb +++ b/app/models/user/relationship_methods.rb @@ -6,10 +6,12 @@ module User::RelationshipMethods define_cursor_paginator :cursored_following_relationships, :ordered_following_relationships define_cursor_paginator :cursored_follower_relationships, :ordered_follower_relationships + # @return [ActiveRecord::Relation] List of the user's following relationships def ordered_following_relationships active_follow_relationships.reverse_order.includes(target: [:profile]) end + # @return [ActiveRecord::Relation] List of the user's follower relationships def ordered_follower_relationships passive_follow_relationships.reverse_order.includes(source: [:profile]) end From 7d5104d09df99b90205d0a8101674cd8a1479646 Mon Sep 17 00:00:00 2001 From: Karina Kwiatek Date: Sat, 28 Jan 2023 22:10:14 +0100 Subject: [PATCH 24/29] Filter out user-facing errors from Sentry --- config/initializers/sentry.rb | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/config/initializers/sentry.rb b/config/initializers/sentry.rb index 1f4a12af..639ded81 100644 --- a/config/initializers/sentry.rb +++ b/config/initializers/sentry.rb @@ -8,4 +8,13 @@ Sentry.init do |config| # of transactions for performance monitoring. # We recommend adjusting this value in production config.traces_sample_rate = 0.25 + + config.before_send do |event, hint| + if hint[:exception].is_a?(Errors::Base) + # These are used for user-facing errors, not when something goes wrong + nil + end + + event + end end From da9a170e67e0adc02a8d66a25b4f1f58d7d49d51 Mon Sep 17 00:00:00 2001 From: Karina Kwiatek Date: Sat, 28 Jan 2023 22:14:02 +0100 Subject: [PATCH 25/29] Set event fingerprint for exceptions relating to external services --- config/initializers/sentry.rb | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/config/initializers/sentry.rb b/config/initializers/sentry.rb index 639ded81..caf6e2d9 100644 --- a/config/initializers/sentry.rb +++ b/config/initializers/sentry.rb @@ -9,12 +9,21 @@ Sentry.init do |config| # We recommend adjusting this value in production config.traces_sample_rate = 0.25 + exception_fingerprints = { + Excon::Error::ServiceUnavailable => 'external-service', + Twitter::Error::InternalServerError => 'external-service', + } config.before_send do |event, hint| if hint[:exception].is_a?(Errors::Base) # These are used for user-facing errors, not when something goes wrong nil end + exception_class = hint[:exception].class.name + if exception_fingerprints.key?(exception_class) + event.fingerprint = [exception_fingerprints[hint[:exception].class.name]] + end + event end end From 272b98c95447ac8f599df066215831558e571121 Mon Sep 17 00:00:00 2001 From: Karina Kwiatek Date: Sat, 28 Jan 2023 22:18:43 +0100 Subject: [PATCH 26/29] `before_send` should be assigned a lambda --- config/initializers/sentry.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/initializers/sentry.rb b/config/initializers/sentry.rb index caf6e2d9..2b107267 100644 --- a/config/initializers/sentry.rb +++ b/config/initializers/sentry.rb @@ -13,7 +13,7 @@ Sentry.init do |config| Excon::Error::ServiceUnavailable => 'external-service', Twitter::Error::InternalServerError => 'external-service', } - config.before_send do |event, hint| + config.before_send = lambda do |event, hint| if hint[:exception].is_a?(Errors::Base) # These are used for user-facing errors, not when something goes wrong nil From 354407cd79e2bad4fba60a57dec0a05bc40360d4 Mon Sep 17 00:00:00 2001 From: Karina Kwiatek Date: Sat, 28 Jan 2023 22:29:41 +0100 Subject: [PATCH 27/29] Use next to return out of `before_send` --- config/initializers/sentry.rb | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/config/initializers/sentry.rb b/config/initializers/sentry.rb index 2b107267..62fdaf9e 100644 --- a/config/initializers/sentry.rb +++ b/config/initializers/sentry.rb @@ -14,10 +14,8 @@ Sentry.init do |config| Twitter::Error::InternalServerError => 'external-service', } config.before_send = lambda do |event, hint| - if hint[:exception].is_a?(Errors::Base) - # These are used for user-facing errors, not when something goes wrong - nil - end + # These are used for user-facing errors, not when something goes wrong + next if hint[:exception].is_a?(Errors::Base) exception_class = hint[:exception].class.name if exception_fingerprints.key?(exception_class) From 9ef6e8fdc1309a95ea2490f217085920b5467695 Mon Sep 17 00:00:00 2001 From: Karina Kwiatek Date: Sat, 28 Jan 2023 22:30:03 +0100 Subject: [PATCH 28/29] Use class instead of class name for matching exception types --- config/initializers/sentry.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/initializers/sentry.rb b/config/initializers/sentry.rb index 62fdaf9e..1124fc37 100644 --- a/config/initializers/sentry.rb +++ b/config/initializers/sentry.rb @@ -17,9 +17,9 @@ Sentry.init do |config| # These are used for user-facing errors, not when something goes wrong next if hint[:exception].is_a?(Errors::Base) - exception_class = hint[:exception].class.name + exception_class = hint[:exception].class if exception_fingerprints.key?(exception_class) - event.fingerprint = [exception_fingerprints[hint[:exception].class.name]] + event.fingerprint = [exception_fingerprints[exception_class]] end event From a59bc3ef922b2724407dec5d85489cc83a5f13e4 Mon Sep 17 00:00:00 2001 From: Georg Gadinger Date: Sun, 29 Jan 2023 19:19:35 +0100 Subject: [PATCH 29/29] typoed_email_validator: add new endings to the typo list --- app/validators/typoed_email_validator.rb | 1 + spec/models/user_spec.rb | 1 + 2 files changed, 2 insertions(+) diff --git a/app/validators/typoed_email_validator.rb b/app/validators/typoed_email_validator.rb index cf323bc9..5cc07cff 100644 --- a/app/validators/typoed_email_validator.rb +++ b/app/validators/typoed_email_validator.rb @@ -19,6 +19,7 @@ class TypoedEmailValidator < ActiveModel::EachValidator gmaile.com gmaill.com gmali.com + gmaul.com gnail.com hotamil.com hotmai.com diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 64df9de4..af65a555 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -93,6 +93,7 @@ RSpec.describe User, type: :model do include_examples "invalid email", "fritz.fantom@gmaile.com" include_examples "invalid email", "fritz.fantom@gmaill.com" include_examples "invalid email", "fritz.fantom@gmali.com" + include_examples "invalid email", "fritz.fantom@gmaul.com" include_examples "invalid email", "fritz.fantom@gnail.com" include_examples "invalid email", "fritz.fantom@hotamil.com" include_examples "invalid email", "fritz.fantom@hotmai.com"