spectator_sport 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 04ef1fe5b218478993467e2b24f8279ce6385978ae57800ecc27dd8a98afa9af
4
- data.tar.gz: b3730a2d748d434c36264ae7018a3c40a217c16dca9ba12c2956143f01a535f7
3
+ metadata.gz: ce7e9837080e5b1f96455b0f135fda0aff65832e57ef01c9cc42763be9579a46
4
+ data.tar.gz: be3679d719d26e852867510b223983e763de348c5c3c44bb58592e4960ae7fd2
5
5
  SHA512:
6
- metadata.gz: bdd63ac25ccfcf26e8e237ae925793ce186a3b1c13882fdb07f598d53f677661dca139e4f4085529b4214ea34547d812e0a80b531415c43c76d48752aad02cfb
7
- data.tar.gz: b02788e838fc6d7d5190bfa0e47f70a02d78c919bf80f3166d7a332187f8e487a039380d3ebce8a07dd0fcccf5ff6eb0d31f0d6d7b6b4ffae9d78be4bb7fed88
6
+ metadata.gz: 3c076b0fd70e3667bf2d8b19f939995589332ee853d63dd30d0281248416df8788f627b7d5ca15bd534bdaf63d4fca377befe8a58941c69769c3d38dbd1b42d3
7
+ data.tar.gz: e8e8901c34d153bbe77a4718bc9da962d89d91e52d6f618be9d524a1bb045f2f907245a9b89888c5d4f9b7602268cf58089666aa2cbe0bb3db5b7f2d72e17de0
data/README.md CHANGED
@@ -69,6 +69,18 @@ This renders a hidden `<meta>` element signed by the server. The recording clien
69
69
 
70
70
  **Note:** this requires the `spectator_sport_session_window_tags` migration to be applied (`bin/rails spectator_sport:install:migrations && bin/rails db:migrate`). If the migration hasn't been run, the feature is silently disabled.
71
71
 
72
+ ## Stopping recording
73
+
74
+ You can pause recording for a page by calling `spectator_sport_stop_recording` in any template:
75
+
76
+ ```erb
77
+ <%= spectator_sport_stop_recording %>
78
+ ```
79
+
80
+ This renders a hidden `<meta>` element that the recording client detects on page load. When present, rrweb recording is stopped and no events are buffered or sent. Recording automatically resumes when the user navigates to a page that does not have this tag.
81
+
82
+ This is useful when navigating via Turbo to pages that shouldn't be recorded — without this tag the recorder would continue running across navigations.
83
+
72
84
  ## Dashboard authorization
73
85
 
74
86
  It is advisable to manually install and set up authorization for the **Player Dashboard** and refrain from making it public.
@@ -153,6 +165,21 @@ open http://localhost:3000
153
165
  # 6. Make changes, see the result, commit and make a PR!
154
166
  ```
155
167
 
168
+ ## Releasing a new version
169
+
170
+ 1. Update the version in `lib/spectator_sport/version.rb`
171
+ 2. Run `bundle install` to update `Gemfile.lock`
172
+ 3. Commit the version bump and updated `Gemfile.lock`:
173
+ ```bash
174
+ git add lib/spectator_sport/version.rb Gemfile.lock
175
+ git commit -m "Bump version to x.y.z"
176
+ ```
177
+ 3. Build and publish to RubyGems, tag, and push to GitHub:
178
+ ```bash
179
+ bundle exec rake release
180
+ ```
181
+ 4. Create a GitHub Release at https://github.com/bensheldon/spectator_sport/releases using the new `vx.y.z` tag.
182
+
156
183
  ## License
157
184
 
158
185
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -8,5 +8,9 @@ module SpectatorSport
8
8
  signed = Rails.application.message_verifier(:spectator_sport_tag_recording).generate(tag_value)
9
9
  tag.meta(name: "spectator-sport-recording-tag", content: signed)
10
10
  end
11
+
12
+ def spectator_sport_stop_recording
13
+ tag.meta(name: "spectator-sport-stop")
14
+ end
11
15
  end
12
16
  end
@@ -29,6 +29,8 @@ function log(...args) {
29
29
  const POST_URL = new URL("./events", document.currentScript.src).href;
30
30
  const POST_INTERVAL_SECONDS = 15;
31
31
  const KEEPALIVE_BYTE_LIMIT = 60000; // Fetch payloads >64kb cannot use keepalive: true
32
+ const RECORDING_TAG_SELECTOR = 'meta[name="spectator-sport-recording-tag"]';
33
+ const STOP_SELECTOR = 'meta[name="spectator-sport-stop"]';
32
34
 
33
35
  class Recorder {
34
36
  constructor() {
@@ -173,7 +175,7 @@ class TagWatcher {
173
175
  }
174
176
 
175
177
  start() {
176
- document.querySelectorAll('meta[name="spectator-sport-recording-tag"]').forEach(el => {
178
+ document.querySelectorAll(RECORDING_TAG_SELECTOR).forEach(el => {
177
179
  this.enqueue(el.content);
178
180
  });
179
181
 
@@ -181,10 +183,10 @@ class TagWatcher {
181
183
  for (const mutation of mutations) {
182
184
  for (const node of mutation.addedNodes) {
183
185
  if (node.nodeType !== Node.ELEMENT_NODE) continue;
184
- if (node.matches('meta[name="spectator-sport-recording-tag"]')) {
186
+ if (node.matches(RECORDING_TAG_SELECTOR)) {
185
187
  this.enqueue(node.content);
186
188
  }
187
- node.querySelectorAll('meta[name="spectator-sport-recording-tag"]').forEach(el => {
189
+ node.querySelectorAll(RECORDING_TAG_SELECTOR).forEach(el => {
188
190
  this.enqueue(el.content);
189
191
  });
190
192
  }
@@ -222,15 +224,62 @@ class TagWatcher {
222
224
  }
223
225
  }
224
226
 
227
+ class StopWatcher {
228
+ constructor(recorder) {
229
+ this.recorder = recorder;
230
+ this.observer = null;
231
+ }
232
+
233
+ start() {
234
+ this.observer = new MutationObserver((mutations) => {
235
+ let changed = false;
236
+ for (const mutation of mutations) {
237
+ for (const node of [...mutation.addedNodes, ...mutation.removedNodes]) {
238
+ if (node.nodeType !== Node.ELEMENT_NODE) continue;
239
+ if (node.matches(STOP_SELECTOR) ||
240
+ node.querySelector(STOP_SELECTOR)) {
241
+ changed = true;
242
+ }
243
+ }
244
+ }
245
+ if (changed) {
246
+ this.update();
247
+ }
248
+ });
249
+ this.observer.observe(document.documentElement, { childList: true, subtree: true });
250
+ }
251
+
252
+ update() {
253
+ if (document.querySelector(STOP_SELECTOR)) {
254
+ this.recorder.stop();
255
+ } else {
256
+ this.recorder.start();
257
+ }
258
+ }
259
+ }
260
+
261
+ function isStopped() {
262
+ return !!document.querySelector(STOP_SELECTOR);
263
+ }
264
+
225
265
  const recorder = new Recorder();
226
- recorder.start();
266
+ if (!isStopped()) {
267
+ recorder.start();
268
+ }
227
269
 
228
270
  const tagWatcher = new TagWatcher(recorder.sessionId, recorder.windowId);
229
271
  tagWatcher.start();
230
272
 
273
+ const stopWatcher = new StopWatcher(recorder);
274
+ stopWatcher.start();
275
+
231
276
  window.addEventListener("pageshow", function(_event) {
232
277
  log("pageshow");
233
- recorder.start();
278
+ if (isStopped()) {
279
+ recorder.stop();
280
+ } else {
281
+ recorder.start();
282
+ }
234
283
  });
235
284
 
236
285
  window.addEventListener("pagehide", function(_event) {
@@ -241,7 +290,9 @@ window.addEventListener("pagehide", function(_event) {
241
290
  document.addEventListener("visibilitychange", function(_event) {
242
291
  log("visibilitychange", document.visibilityState);
243
292
  if (document.visibilityState === "visible") {
244
- recorder.unpause();
293
+ if (!isStopped()) {
294
+ recorder.unpause();
295
+ }
245
296
  } else if (document.visibilityState === "hidden") {
246
297
  recorder.pause();
247
298
  }
@@ -1,3 +1,3 @@
1
1
  module SpectatorSport
2
- VERSION = "0.2.0"
2
+ VERSION = "0.3.0"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spectator_sport
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ben Sheldon