From 7d92c8970ee3eb8af3a51632242462a0fcae49a6 Mon Sep 17 00:00:00 2001 From: Dhamari Trice-Hanson <39872667+dhamariT@users.noreply.github.com> Date: Thu, 2 Jul 2026 14:32:14 -0400 Subject: [PATCH 1/3] =?UTF-8?q?Hardware=E2=86=92Outpost:=20in-app=20notifi?= =?UTF-8?q?cation=20instead=20of=20the=20after-recording=20popup?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per review, recording a timelapse on a hardware project shouldn't throw a modal — it should drop a notification in the inbox instead. - New Notifications::HardwareMovedToOutpost type (inbox-only/low priority, aggregatable so repeat recordings keep one unread notice), registered + given an alert-triangle icon, with an inbox partial linking to Outpost. - Fire it from LookoutSessionsController#create for hardware projects (flag hardware_to_outpost), after the session is created so recording is unaffected. - Remove the on-recorder popup: the record.html.erb render + the capture controller's showHardwareOutpostNotice trigger/method. Project *creation* (new hardware project / hardware mission) still shows the Outpost popup up front — that's unchanged. Notification links to outpost.hackclub.com. Gated by hardware_to_outpost (+ the notification system's own week_2_release rollout flag). --- .../notifications/item_component.rb | 3 ++- .../projects/lookout_sessions_controller.rb | 7 +++++++ .../controllers/lookout_capture_controller.js | 9 --------- .../hardware_moved_to_outpost.rb | 19 +++++++++++++++++++ app/models/notifications/registry.rb | 1 + .../inbox/_hardware_moved_to_outpost.html.erb | 4 ++++ .../projects/lookout_sessions/record.html.erb | 6 ------ 7 files changed, 33 insertions(+), 16 deletions(-) create mode 100644 app/models/notifications/hardware_moved_to_outpost.rb create mode 100644 app/views/notifications/inbox/_hardware_moved_to_outpost.html.erb diff --git a/app/components/notifications/item_component.rb b/app/components/notifications/item_component.rb index 2c09f46be..712d6c938 100644 --- a/app/components/notifications/item_component.rb +++ b/app/components/notifications/item_component.rb @@ -19,7 +19,8 @@ class ItemComponent < ViewComponent::Base "Notifications::Missions::SubmissionApproved" => "check-circle", "Notifications::Missions::SubmissionRejected" => "alert-triangle", "Notifications::Missions::SubmissionPendingForReviewer" => "clipboard", - "Notifications::ShopOrders::StatusChanged" => "bag" + "Notifications::ShopOrders::StatusChanged" => "bag", + "Notifications::HardwareMovedToOutpost" => "alert-triangle" }.freeze attr_reader :notification diff --git a/app/controllers/projects/lookout_sessions_controller.rb b/app/controllers/projects/lookout_sessions_controller.rb index a4114f4cf..2fce586bb 100644 --- a/app/controllers/projects/lookout_sessions_controller.rb +++ b/app/controllers/projects/lookout_sessions_controller.rb @@ -34,6 +34,13 @@ def create started_at: Time.current ) + # Hardware projects live on Outpost now — nudge the builder there with an + # inbox notification rather than the old on-recorder popup, without blocking + # the recording. + if @project.hardware? && Flipper.enabled?(:hardware_to_outpost, current_user) + Notifications::HardwareMovedToOutpost.notify(recipient: current_user, record: @project) + end + render json: session_json(@session), status: :created end diff --git a/app/javascript/controllers/lookout_capture_controller.js b/app/javascript/controllers/lookout_capture_controller.js index 9d32665a7..7ec477e90 100644 --- a/app/javascript/controllers/lookout_capture_controller.js +++ b/app/javascript/controllers/lookout_capture_controller.js @@ -434,7 +434,6 @@ export default class extends Controller { this.setText(statusEl, "Your timelapse is ready!"); } if (showVideo) await this.revealVideo(); - this.showHardwareOutpostNotice(); return; } if (data.status === "failed") { @@ -480,14 +479,6 @@ export default class extends Controller { } } - // Hardware moved to Outpost — once the timelapse is done, let the builder know. - // The notice is only rendered on the page for hardware projects with the flag - // on, so this is a no-op everywhere else. - showHardwareOutpostNotice() { - const dialog = document.getElementById("hardware-outpost-modal"); - if (dialog && !dialog.open) dialog.showModal(); - } - close(event) { if (event) event.preventDefault(); this.cleanup(); diff --git a/app/models/notifications/hardware_moved_to_outpost.rb b/app/models/notifications/hardware_moved_to_outpost.rb new file mode 100644 index 000000000..562ebdf1a --- /dev/null +++ b/app/models/notifications/hardware_moved_to_outpost.rb @@ -0,0 +1,19 @@ +module Notifications + # Heads-up that hardware projects live on Hack Club Outpost now, surfaced when + # a hardware project records a Lookout timelapse (replaces the old on-recorder + # popup). Inbox-only (low priority — no Slack/email), and aggregatable so a + # builder who records repeatedly keeps a single unread notice rather than a + # pile of them. + class HardwareMovedToOutpost < ::Notification + self.default_priority = :low + self.aggregatable = true + self.category_key = :hardware_moved_to_outpost + self.category_label = "Hardware moved to Outpost" + self.category_description = "Hardware projects have moved to Hack Club Outpost" + self.category_group = "General" + + def preview_text + "Building hardware? It all happens over on Hack Club Outpost now." + end + end +end diff --git a/app/models/notifications/registry.rb b/app/models/notifications/registry.rb index dbe3b0894..52c0a1b98 100644 --- a/app/models/notifications/registry.rb +++ b/app/models/notifications/registry.rb @@ -22,6 +22,7 @@ module Registry Notifications::Payouts::ShipEventIssued Notifications::Payouts::VoteDeficitBlocked Notifications::ShopOrders::StatusChanged + Notifications::HardwareMovedToOutpost ].freeze module_function diff --git a/app/views/notifications/inbox/_hardware_moved_to_outpost.html.erb b/app/views/notifications/inbox/_hardware_moved_to_outpost.html.erb new file mode 100644 index 000000000..2e82275f0 --- /dev/null +++ b/app/views/notifications/inbox/_hardware_moved_to_outpost.html.erb @@ -0,0 +1,4 @@ +Hardware projects have moved to +<%= link_to "Hack Club Outpost", "https://outpost.hackclub.com", + class: "notifications-item__actor", + target: "_blank", rel: "noopener" %> diff --git a/app/views/projects/lookout_sessions/record.html.erb b/app/views/projects/lookout_sessions/record.html.erb index f8ee1cc32..13991bcab 100644 --- a/app/views/projects/lookout_sessions/record.html.erb +++ b/app/views/projects/lookout_sessions/record.html.erb @@ -175,9 +175,3 @@ hidden: true %> - -<%# Hardware moved to Outpost — once this timelapse finishes, the capture - controller pops this notice (rendered only for hardware projects). %> -<% if @project.hardware? && Flipper.enabled?(:hardware_to_outpost, current_user) %> - <%= render "projects/hardware_outpost_modal" %> -<% end %> From 0870119a90f4c63049185f4b9de814e4284d6a6e Mon Sep 17 00:00:00 2001 From: Dhamari Trice-Hanson <39872667+dhamariT@users.noreply.github.com> Date: Thu, 2 Jul 2026 15:11:53 -0400 Subject: [PATCH 2/3] =?UTF-8?q?Hardware=E2=86=92Outpost=20notif:=20backfil?= =?UTF-8?q?l=20all=20hardware=20owners=20+=20Outpost=20flame=20icon?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two follow-ups on the notification: 1) Notify everyone who already has a hardware project, not only those who record a timelapse. New OneTime::BackfillHardwareOutpostNotificationsJob sweeps every member of a hardware project (dry-run by default; the notif is aggregatable + self-gates on week_2_release, so no dupes/spam). New hardware projects can't be created on Stardance anymore, so this audience is fixed. 2) Prettier icon: instead of the generic alert-triangle line icon, show a colour chip cropped from the Outpost flame logo (borrowed from the sidebar widget). item_component gains optional image icons (TYPE_ICON_IMAGES); the notifications SCSS crops the wide wordmark to its flame side as a rounded, full-colour badge. --- .../stylesheets/pages/_notifications.scss | 16 +++++++++ .../notifications/item_component.html.erb | 8 +++-- .../notifications/item_component.rb | 14 ++++++-- ...fill_hardware_outpost_notifications_job.rb | 35 +++++++++++++++++++ 4 files changed, 69 insertions(+), 4 deletions(-) create mode 100644 app/jobs/one_time/backfill_hardware_outpost_notifications_job.rb diff --git a/app/assets/stylesheets/pages/_notifications.scss b/app/assets/stylesheets/pages/_notifications.scss index 1fc51c9f9..020e9f854 100644 --- a/app/assets/stylesheets/pages/_notifications.scss +++ b/app/assets/stylesheets/pages/_notifications.scss @@ -98,6 +98,22 @@ display: block; } + // Full-colour brand chip (e.g. the Outpost flame) instead of a muted line icon. + &__icon--image { + opacity: 1; + } + + &__icon-img { + width: 28px; + height: 28px; + // The Outpost logo is a wide wordmark; crop to its flame side so the chip + // reads as a little colourful badge rather than a squished wordmark. + object-fit: cover; + object-position: left center; + border-radius: 6px; + display: block; + } + &__main { flex: 1 1 auto; display: flex; diff --git a/app/components/notifications/item_component.html.erb b/app/components/notifications/item_component.html.erb index 6d501ea52..d0b680922 100644 --- a/app/components/notifications/item_component.html.erb +++ b/app/components/notifications/item_component.html.erb @@ -3,8 +3,12 @@ (card_data wires the Stimulus controller + click action; links are ignored by the controller). Reading is handled by opening the inbox. %> <%= tag.li class: li_classes, data: card_data do %> -