diff --git a/app/controllers/notifications/readings_controller.rb b/app/controllers/notifications/readings_controller.rb deleted file mode 100644 index a5d0f7f11..000000000 --- a/app/controllers/notifications/readings_controller.rb +++ /dev/null @@ -1,5 +0,0 @@ -class Notifications::ReadingsController < ApplicationController - def create - Current.user.notifications.find(params[:notification_id]).update!(read: true) - end -end diff --git a/app/controllers/readings_controller.rb b/app/controllers/readings_controller.rb new file mode 100644 index 000000000..5fc57ec9c --- /dev/null +++ b/app/controllers/readings_controller.rb @@ -0,0 +1,8 @@ +class ReadingsController < ApplicationController + include BubbleScoped, BucketScoped + + def create + @notifications = Current.user.notifications.where(bubble: @bubble) + @notifications.update(read: true) + end +end diff --git a/app/helpers/notifications_helper.rb b/app/helpers/notifications_helper.rb index 60a442ebd..8c2208a14 100644 --- a/app/helpers/notifications_helper.rb +++ b/app/helpers/notifications_helper.rb @@ -1,10 +1,6 @@ module NotificationsHelper def notification_tag(notification, &) link_to notification.resource, id: dom_id(notification), class: "notification", - data: { - turbo_frame: "_top", - action: "turbo:click->notifications--readings#record", - notifications__readings_url_param: notification_readings_url(notification) - }, & + data: { turbo_frame: "_top", }, & end end diff --git a/app/javascript/controllers/notifications/readings_controller.js b/app/javascript/controllers/notifications/readings_controller.js deleted file mode 100644 index 5ec9ffca1..000000000 --- a/app/javascript/controllers/notifications/readings_controller.js +++ /dev/null @@ -1,18 +0,0 @@ -import { Controller } from "@hotwired/stimulus" - -export default class extends Controller { - record({ target, params: { url } }) { - navigator.sendBeacon(url, this.#csrfPayload()) - target.remove() - } - - #csrfPayload() { - const data = new FormData() - data.append("authenticity_token", this.#csrfToken()) - return data - } - - #csrfToken() { - return document.querySelector('meta[name="csrf-token"]').content - } -} diff --git a/app/javascript/controllers/readings_controller.js b/app/javascript/controllers/readings_controller.js new file mode 100644 index 000000000..07182bb36 --- /dev/null +++ b/app/javascript/controllers/readings_controller.js @@ -0,0 +1,10 @@ +import { post } from "@rails/request.js" +import { Controller } from "@hotwired/stimulus" + +export default class extends Controller { + static values = { url: String } + + connect() { + post(this.urlValue, { responseKind: "turbo-stream" }) + } +} diff --git a/app/views/bubbles/_bubble.html.erb b/app/views/bubbles/_bubble.html.erb index 033364ed5..f00d502ff 100644 --- a/app/views/bubbles/_bubble.html.erb +++ b/app/views/bubbles/_bubble.html.erb @@ -2,7 +2,9 @@
+ data-animation-play-class="bubble--wobble" + data-animation-play-on-load-value="true" + data-action="mouseover->animation#play">

diff --git a/app/views/bubbles/show.html.erb b/app/views/bubbles/show.html.erb index 371600f9d..6c65a65bf 100644 --- a/app/views/bubbles/show.html.erb +++ b/app/views/bubbles/show.html.erb @@ -65,7 +65,7 @@

-
+
<%= render "bubbles/bubble", bubble: @bubble, editing: params[:editing] %>
diff --git a/app/views/notifications/_tray.html.erb b/app/views/notifications/_tray.html.erb index 9b9d91c40..af1c2d452 100644 --- a/app/views/notifications/_tray.html.erb +++ b/app/views/notifications/_tray.html.erb @@ -1,5 +1,5 @@ <%= turbo_stream_from Current.user, :notifications %> -<%= tag.div id: "notification-tray", class: "notification-tray", data: { turbo_permanent: true, controller: "notifications--readings" } do %> +<%= tag.div id: "notification-tray", class: "notification-tray", data: { turbo_permanent: true } do %> <%= turbo_frame_tag("notifications", src: notifications_path) %> <% end %> diff --git a/app/views/readings/create.turbo_stream.erb b/app/views/readings/create.turbo_stream.erb new file mode 100644 index 000000000..5d7fe7801 --- /dev/null +++ b/app/views/readings/create.turbo_stream.erb @@ -0,0 +1,3 @@ +<% @notifications.each do |notification| %> + <%= turbo_stream.remove notification %> +<% end %> diff --git a/config/importmap.rb b/config/importmap.rb index bf42e394c..87974cc7f 100644 --- a/config/importmap.rb +++ b/config/importmap.rb @@ -4,6 +4,7 @@ pin "application" pin "@hotwired/turbo-rails", to: "turbo.min.js" pin "@hotwired/stimulus", to: "stimulus.min.js" pin "@hotwired/stimulus-loading", to: "stimulus-loading.js" +pin "@rails/request.js", to: "@rails--request.js" # @0.0.11 pin "house", to: "house.min.js" pin_all_from "app/javascript/controllers", under: "controllers" diff --git a/config/routes.rb b/config/routes.rb index 329e8fc7f..ad0d9a2cf 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -18,16 +18,13 @@ Rails.application.routes.draw do end resources :bubbles - resources :notifications do - scope module: :notifications do - resource :readings, only: :create - end - end + resources :notifications resources :buckets do resources :bubbles do resources :boosts resources :comments + resource :readings, only: :create scope module: :bubbles do resource :image diff --git a/test/controllers/notifications/readings_controller_test.rb b/test/controllers/notifications/readings_controller_test.rb deleted file mode 100644 index cce305a3d..000000000 --- a/test/controllers/notifications/readings_controller_test.rb +++ /dev/null @@ -1,7 +0,0 @@ -require "test_helper" - -class Notifications::ReadingsControllerTest < ActionDispatch::IntegrationTest - # test "the truth" do - # assert true - # end -end diff --git a/test/fixtures/notifications.yml b/test/fixtures/notifications.yml index bb0035b95..802dcf68b 100644 --- a/test/fixtures/notifications.yml +++ b/test/fixtures/notifications.yml @@ -10,7 +10,7 @@ logo_created_kevin: layout_created_kevin: user: kevin creator: david - bubble: logo + bubble: layout resource: layout_overflowing_david (Comment) read: false body: "re: Layout is broken" diff --git a/vendor/javascript/@rails--request.js b/vendor/javascript/@rails--request.js new file mode 100644 index 000000000..b85d5c78a --- /dev/null +++ b/vendor/javascript/@rails--request.js @@ -0,0 +1,4 @@ +// @rails/request.js@0.0.11 downloaded from https://ga.jspm.io/npm:@rails/request.js@0.0.11/src/index.js + +class FetchResponse{constructor(t){this.response=t}get statusCode(){return this.response.status}get redirected(){return this.response.redirected}get ok(){return this.response.ok}get unauthenticated(){return this.statusCode===401}get unprocessableEntity(){return this.statusCode===422}get authenticationURL(){return this.response.headers.get("WWW-Authenticate")}get contentType(){const t=this.response.headers.get("Content-Type")||"";return t.replace(/;.*$/,"")}get headers(){return this.response.headers}get html(){return this.contentType.match(/^(application|text)\/(html|xhtml\+xml)$/)?this.text:Promise.reject(new Error(`Expected an HTML response but got "${this.contentType}" instead`))}get json(){return this.contentType.match(/^application\/.*json$/)?this.responseJson||(this.responseJson=this.response.json()):Promise.reject(new Error(`Expected a JSON response but got "${this.contentType}" instead`))}get text(){return this.responseText||(this.responseText=this.response.text())}get isTurboStream(){return this.contentType.match(/^text\/vnd\.turbo-stream\.html/)}get isScript(){return this.contentType.match(/\b(?:java|ecma)script\b/)}async renderTurboStream(){if(!this.isTurboStream)return Promise.reject(new Error(`Expected a Turbo Stream response but got "${this.contentType}" instead`));window.Turbo?await window.Turbo.renderStreamMessage(await this.text):console.warn("You must set `window.Turbo = Turbo` to automatically process Turbo Stream events with request.js")}async activeScript(){if(!this.isScript)return Promise.reject(new Error(`Expected a Script response but got "${this.contentType}" instead`));{const t=document.createElement("script");const e=document.querySelector("meta[name=csp-nonce]");const n=e&&e.content;n&&t.setAttribute("nonce",n);t.innerHTML=await this.text;document.body.appendChild(t)}}}class RequestInterceptor{static register(t){this.interceptor=t}static get(){return this.interceptor}static reset(){this.interceptor=void 0}}function getCookie(t){const e=document.cookie?document.cookie.split("; "):[];const n=`${encodeURIComponent(t)}=`;const s=e.find((t=>t.startsWith(n)));if(s){const t=s.split("=").slice(1).join("=");if(t)return decodeURIComponent(t)}}function compact(t){const e={};for(const n in t){const s=t[n];s!==void 0&&(e[n]=s)}return e}function metaContent(t){const e=document.head.querySelector(`meta[name="${t}"]`);return e&&e.content}function stringEntriesFromFormData(t){return[...t].reduce(((t,[e,n])=>t.concat(typeof n==="string"?[[e,n]]:[])),[])}function mergeEntries(t,e){for(const[n,s]of e)if(!(s instanceof window.File))if(t.has(n)&&!n.includes("[]")){t.delete(n);t.set(n,s)}else t.append(n,s)}class FetchRequest{constructor(t,e,n={}){this.method=t;this.options=n;this.originalUrl=e.toString()}async perform(){try{const t=RequestInterceptor.get();t&&await t(this)}catch(t){console.error(t)}const t=this.responseKind==="turbo-stream"&&window.Turbo?window.Turbo.fetch:window.fetch;const e=new FetchResponse(await t(this.url,this.fetchOptions));if(e.unauthenticated&&e.authenticationURL)return Promise.reject(window.location.href=e.authenticationURL);e.isScript&&await e.activeScript();const n=e.ok||e.unprocessableEntity;n&&e.isTurboStream&&await e.renderTurboStream();return e}addHeader(t,e){const n=this.additionalHeaders;n[t]=e;this.options.headers=n}sameHostname(){if(!this.originalUrl.startsWith("http:"))return true;try{return new URL(this.originalUrl).hostname===window.location.hostname}catch(t){return true}}get fetchOptions(){return{method:this.method.toUpperCase(),headers:this.headers,body:this.formattedBody,signal:this.signal,credentials:this.credentials,redirect:this.redirect}}get headers(){const t={"X-Requested-With":"XMLHttpRequest","Content-Type":this.contentType,Accept:this.accept};this.sameHostname()&&(t["X-CSRF-Token"]=this.csrfToken);return compact(Object.assign(t,this.additionalHeaders))}get csrfToken(){return getCookie(metaContent("csrf-param"))||metaContent("csrf-token")}get contentType(){return this.options.contentType?this.options.contentType:this.body==null||this.body instanceof window.FormData?void 0:this.body instanceof window.File?this.body.type:"application/json"}get accept(){switch(this.responseKind){case"html":return"text/html, application/xhtml+xml";case"turbo-stream":return"text/vnd.turbo-stream.html, text/html, application/xhtml+xml";case"json":return"application/json, application/vnd.api+json";case"script":return"text/javascript, application/javascript";default:return"*/*"}}get body(){return this.options.body}get query(){const t=(this.originalUrl.split("?")[1]||"").split("#")[0];const e=new URLSearchParams(t);let n=this.options.query;n=n instanceof window.FormData?stringEntriesFromFormData(n):n instanceof window.URLSearchParams?n.entries():Object.entries(n||{});mergeEntries(e,n);const s=e.toString();return s.length>0?`?${s}`:""}get url(){return this.originalUrl.split("?")[0].split("#")[0]+this.query}get responseKind(){return this.options.responseKind||"html"}get signal(){return this.options.signal}get redirect(){return this.options.redirect||"follow"}get credentials(){return this.options.credentials||"same-origin"}get additionalHeaders(){return this.options.headers||{}}get formattedBody(){const t=Object.prototype.toString.call(this.body)==="[object String]";const e=this.headers["Content-Type"]==="application/json";return e&&!t?JSON.stringify(this.body):this.body}}async function get(t,e){const n=new FetchRequest("get",t,e);return n.perform()}async function post(t,e){const n=new FetchRequest("post",t,e);return n.perform()}async function put(t,e){const n=new FetchRequest("put",t,e);return n.perform()}async function patch(t,e){const n=new FetchRequest("patch",t,e);return n.perform()}async function destroy(t,e){const n=new FetchRequest("delete",t,e);return n.perform()}export{FetchRequest,FetchResponse,RequestInterceptor,destroy,get,patch,post,put}; +