bible270 1.3.1 → 1.4.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 +4 -4
- data/CHANGELOG.md +6 -0
- data/app/controllers/bible270/application_controller.rb +2 -5
- data/app/controllers/bible270/checkoffs_controller.rb +29 -10
- data/app/controllers/bible270/days_controller.rb +1 -0
- data/app/views/bible270/checkoffs/toggle.turbo_stream.erb +2 -1
- data/app/views/bible270/days/_day_progress.html.erb +5 -2
- data/app/views/bible270/days/show.html.erb +3 -1
- data/app/views/bible270/shared/_day_completion_dove.html.erb +17 -0
- data/app/views/bible270/shared/_day_completion_effect.html.erb +67 -0
- data/app/views/bible270/shared/_interaction_ui.html.erb +25 -9
- data/app/views/bible270/shared/_styles.html.erb +14 -1
- data/lib/bible270/version.rb +1 -1
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8975632aa3e90165634356f25fc0c8cff2909e84b3177a58e192b16ee325b745
|
|
4
|
+
data.tar.gz: 543aa029054018c26f87d2d1836302ab671de51b70caa7a531ed0011a405f5e6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c76e854eb6449addef5b1deb2066d70cc6c51e8650817efa7c4fef8efa0d847d0b1e85c75d9faa91dde497495a01c980c674342148326f8c43f146a3321175d4
|
|
7
|
+
data.tar.gz: 80761d784e504c167954827dbe3e3e8193e571e91eee67b8bd9aa12b7990b9845020f20399c3367f63b41d441107fa15748e6e4199d327ce2588d1976a6227ca
|
data/CHANGELOG.md
CHANGED
|
@@ -4,9 +4,15 @@ All notable changes to bible270. Format follows [Keep a Changelog](https://keepa
|
|
|
4
4
|
|
|
5
5
|
## Unreleased Changes
|
|
6
6
|
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- mark a newly completed day with a brief, translucent dove animation that begins immediately on the final checkoff in JavaScript-capable browsers and remains available after Turbo or HTML-fallback responses; it respects reduced-motion preferences, replaces the final generic success message, and never blocks interaction
|
|
10
|
+
|
|
7
11
|
### Fixed
|
|
8
12
|
|
|
9
13
|
- correct Day 27's New Testament reading to include Matthew 26:75
|
|
14
|
+
- silently recover all Turbo interactions from stale sessions, retry once with a fresh CSRF token, and reload rather than submit with a known-stale token if refreshing fails
|
|
15
|
+
- keep repeated or concurrent explicit checkoffs idempotent instead of raising a duplicate-validation Rails 422 error
|
|
10
16
|
|
|
11
17
|
**[Full Changelog](https://github.com/avonderluft/bible270/compare/v1.3.0...main)**
|
|
12
18
|
|
|
@@ -116,18 +116,15 @@ module Bible270
|
|
|
116
116
|
reason = error ? error.class : 'an unverified request'
|
|
117
117
|
Rails.logger.info("[bible270] silently refreshing a page after #{reason}")
|
|
118
118
|
|
|
119
|
-
if
|
|
119
|
+
if request.format.turbo_stream?
|
|
120
120
|
response.set_header('X-Bible270-Refresh-Session', 'true')
|
|
121
|
+
response.set_header('Cache-Control', 'no-store')
|
|
121
122
|
head :conflict
|
|
122
123
|
else
|
|
123
124
|
redirect_back fallback_location: root_path, allow_other_host: false, status: :see_other
|
|
124
125
|
end
|
|
125
126
|
end
|
|
126
127
|
|
|
127
|
-
def stale_turbo_checkoff?
|
|
128
|
-
controller_name == 'checkoffs' && action_name == 'toggle' && request.format.turbo_stream?
|
|
129
|
-
end
|
|
130
|
-
|
|
131
128
|
# Guard participation (checking off / commenting). Viewing is always open.
|
|
132
129
|
def require_reader!
|
|
133
130
|
return true if signed_in?
|
|
@@ -18,9 +18,10 @@ module Bible270
|
|
|
18
18
|
requested_state = params[:checked] if params.key?(:checked)
|
|
19
19
|
head :bad_request and return if params.key?(:checked) && !%w[0 1].include?(requested_state)
|
|
20
20
|
|
|
21
|
-
marked_read = apply_checkoff(requested_state)
|
|
21
|
+
marked_read, checkoff_changed = apply_checkoff(requested_state)
|
|
22
22
|
|
|
23
23
|
@reader = current_reader.reload.reload_progress
|
|
24
|
+
@day_just_completed = marked_read && checkoff_changed && @reader.day_complete?(@day)
|
|
24
25
|
@reader_tracks = @reader.read_tracks_for(@day)
|
|
25
26
|
@readings = Plan.readings_for(@day)
|
|
26
27
|
|
|
@@ -34,8 +35,12 @@ module Bible270
|
|
|
34
35
|
respond_to do |format|
|
|
35
36
|
format.turbo_stream
|
|
36
37
|
format.html do
|
|
37
|
-
|
|
38
|
-
|
|
38
|
+
if @day_just_completed
|
|
39
|
+
redirect_to day_path(@day), flash: { b270_day_just_completed: @day }
|
|
40
|
+
else
|
|
41
|
+
message = marked_read ? 'Reading marked read.' : 'Reading marked unread.'
|
|
42
|
+
redirect_to day_path(@day), flash: { b270_interaction_status: message }
|
|
43
|
+
end
|
|
39
44
|
end
|
|
40
45
|
end
|
|
41
46
|
end
|
|
@@ -43,19 +48,33 @@ module Bible270
|
|
|
43
48
|
private
|
|
44
49
|
|
|
45
50
|
def apply_checkoff(requested_state)
|
|
46
|
-
existing =
|
|
51
|
+
existing = find_checkoff
|
|
47
52
|
if requested_state == '1'
|
|
48
53
|
current_reader.ensure_started!
|
|
49
|
-
|
|
50
|
-
true
|
|
54
|
+
[true, existing.nil? && create_checkoff]
|
|
51
55
|
elsif requested_state == '0' || existing
|
|
52
|
-
existing
|
|
53
|
-
false
|
|
56
|
+
[false, existing ? existing.destroy.present? : false]
|
|
54
57
|
else
|
|
55
58
|
current_reader.ensure_started!
|
|
56
|
-
|
|
57
|
-
true
|
|
59
|
+
[true, create_checkoff]
|
|
58
60
|
end
|
|
59
61
|
end
|
|
62
|
+
|
|
63
|
+
def find_checkoff
|
|
64
|
+
Checkoff.uncached do
|
|
65
|
+
Checkoff.find_by(reader_id: current_reader.id, day: @day, track: @track, part: @part)
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def create_checkoff
|
|
70
|
+
Checkoff.create!(reader: current_reader, day: @day, track: @track, part: @part)
|
|
71
|
+
true
|
|
72
|
+
rescue ActiveRecord::RecordInvalid, ActiveRecord::RecordNotUnique
|
|
73
|
+
# A repeated or concurrent desired-state request is already successful. Do
|
|
74
|
+
# not hide any other validation failure under the same recovery.
|
|
75
|
+
raise unless find_checkoff
|
|
76
|
+
|
|
77
|
+
false
|
|
78
|
+
end
|
|
60
79
|
end
|
|
61
80
|
end
|
|
@@ -20,6 +20,7 @@ module Bible270
|
|
|
20
20
|
@editing_comment_id = editing_comment_id
|
|
21
21
|
@new_comment = Comment.new(day: @day, parent_id: reply_parent&.id)
|
|
22
22
|
@reader_tracks = current_reader ? current_reader.read_tracks_for(@day) : []
|
|
23
|
+
@day_just_completed = flash[:b270_day_just_completed].to_i == @day
|
|
23
24
|
|
|
24
25
|
# Every box on the day, not every track: an Old Testament reading of three
|
|
25
26
|
# chapters is three rows, so counting tracks would call someone finished
|
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
<% end %>
|
|
5
5
|
|
|
6
6
|
<%= turbo_stream.replace "day_progress_#{@day}" do %>
|
|
7
|
-
<%= render "bible270/days/day_progress", day: @day, reader: @reader
|
|
7
|
+
<%= render "bible270/days/day_progress", day: @day, reader: @reader,
|
|
8
|
+
just_completed: @day_just_completed %>
|
|
8
9
|
<% end %>
|
|
9
10
|
|
|
10
11
|
<%= turbo_stream.replace "completers_#{@day}" do %>
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
<%# locals: (day:, reader:) %>
|
|
1
|
+
<%# locals: (day:, reader:, just_completed: false) %>
|
|
2
2
|
<%
|
|
3
3
|
previous_day = day > 1 ? day - 1 : nil
|
|
4
4
|
next_day = day < Bible270::Plan::DAYS ? day + 1 : nil
|
|
5
5
|
total_parts = Bible270::Plan.total_parts(day)
|
|
6
6
|
complete = reader&.day_complete?(day)
|
|
7
|
+
completion_data = just_completed ? { b270_day_just_completed: true, b270_day: day } : {}
|
|
7
8
|
%>
|
|
8
9
|
<div id="day_progress_<%= day %>">
|
|
9
10
|
<div class="b270-daynav">
|
|
@@ -32,7 +33,9 @@
|
|
|
32
33
|
|
|
33
34
|
|
|
34
35
|
<% if complete %>
|
|
35
|
-
|
|
36
|
+
<%= render "bible270/shared/day_completion_dove", day: day if just_completed %>
|
|
37
|
+
<div class="b270-day-complete" role="status" aria-live="polite"
|
|
38
|
+
<%= tag.attributes(data: completion_data) %>>
|
|
36
39
|
<div>
|
|
37
40
|
<strong>Day <%= day %> complete</strong>
|
|
38
41
|
<span>All <%= total_parts %> portions are checked off.</span>
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
<% content_for :title, "Day #{@day} — #{Bible270.config.app_name}" %>
|
|
2
2
|
|
|
3
3
|
<div class="b270-day-page">
|
|
4
|
-
<%= render "bible270/days/day_progress", day: @day, reader: current_reader
|
|
4
|
+
<%= render "bible270/days/day_progress", day: @day, reader: current_reader,
|
|
5
|
+
just_completed: @day_just_completed %>
|
|
5
6
|
|
|
6
7
|
<%= render "bible270/shared/interaction_status" %>
|
|
7
8
|
|
|
@@ -33,3 +34,4 @@
|
|
|
33
34
|
</div>
|
|
34
35
|
|
|
35
36
|
<%= render "bible270/shared/interaction_ui" %>
|
|
37
|
+
<%= render "bible270/shared/day_completion_effect", day: @day %>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<%# locals: (day:) %>
|
|
2
|
+
<svg class="b270-completion-dove" data-b270-completion-dove data-b270-day="<%= day %>"
|
|
3
|
+
viewBox="0 0 260 200" aria-hidden="true" focusable="false">
|
|
4
|
+
<defs>
|
|
5
|
+
<radialGradient id="b270-dove-light" cx="50%" cy="43%" r="68%">
|
|
6
|
+
<stop offset="0%" stop-color="#fff" />
|
|
7
|
+
<stop offset="58%" stop-color="#fffef8" />
|
|
8
|
+
<stop offset="100%" stop-color="#fff9e2" />
|
|
9
|
+
</radialGradient>
|
|
10
|
+
</defs>
|
|
11
|
+
<g class="b270-dove-silhouette">
|
|
12
|
+
<path d="M122 94C103 82 87 69 72 53L39 17l11 28-23-23 15 32-23-20 19 30-24-16 22 26-25-12 27 23-24-7 30 19-22-3 31 17c20 11 42 16 59 8 5-9 8-18 10-25Z" />
|
|
13
|
+
<path d="M138 94c19-12 35-25 50-41l33-36-11 28 23-23-15 32 23-20-19 30 24-16-22 26 25-12-27 23 24-7-30 19 22-3-31 17c-20 11-42 16-59 8-5-9-8-18-10-25Z" />
|
|
14
|
+
<path d="M148 140c6 7 16 14 24 19 2 2 1 5-2 6-5 2-10-1-14-5 4 8 7 17 4 22-2 3-5 1-7-2l-6-11c3 10 2 19-3 24-2 3-5 1-6-3l-4-15c0 11-3 21-8 24-3 2-5-1-6-5l-1-19c-3 10-6 17-11 20-3 2-5-1-5-5l2-19c-4 8-9 13-14 14-3 1-5-2-4-6l6-18c-5 5-11 7-16 4-3-1-3-4 0-7 7-7 17-13 26-19Z" />
|
|
15
|
+
<path d="M108 116c5-10 9-17 14-17-1-11-2-23 4-29 5-4 12-3 15 1 2 3 2 6 1 9 3 .2 6 .8 8 1.5-2.5 1.3-5.5 2.1-8 2.5-1 5-3 10-5 15 6 3 11 9 15 17-1 15-6 28-16 39-3 4-7 7-10 9-4-2-8-5-11-9-9-11-14-25-11-37 1-1 2-2 4-2Z" />
|
|
16
|
+
</g>
|
|
17
|
+
</svg>
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
<%# locals: (day:) %>
|
|
2
|
+
<template data-b270-completion-dove-template data-b270-day="<%= day %>">
|
|
3
|
+
<%= render "bible270/shared/day_completion_dove", day: day %>
|
|
4
|
+
</template>
|
|
5
|
+
<script nonce="<%= content_security_policy_nonce %>">
|
|
6
|
+
(() => {
|
|
7
|
+
if (window.Bible270DayCompletionEffect) return;
|
|
8
|
+
|
|
9
|
+
function willCompleteDay(form) {
|
|
10
|
+
const desiredState = form.querySelector("input[name='checked']");
|
|
11
|
+
if (!desiredState || desiredState.value !== "1") return false;
|
|
12
|
+
|
|
13
|
+
const unfinished = Array.from(
|
|
14
|
+
document.querySelectorAll("form[data-b270-checkoff='true'] input[name='checked'][value='1']")
|
|
15
|
+
);
|
|
16
|
+
return unfinished.length === 1 && unfinished[0].form === form;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function startDove(day) {
|
|
20
|
+
if (document.querySelector("[data-b270-completion-dove][data-b270-immediate]")) return;
|
|
21
|
+
|
|
22
|
+
const template = document.querySelector("[data-b270-completion-dove-template]");
|
|
23
|
+
if (!template || template.dataset.b270Day !== day) return;
|
|
24
|
+
|
|
25
|
+
const dove = template.content.querySelector("[data-b270-completion-dove]").cloneNode(true);
|
|
26
|
+
dove.dataset.b270Immediate = "true";
|
|
27
|
+
document.body.append(dove);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function removeServerDuplicate() {
|
|
31
|
+
const immediate = document.querySelector("[data-b270-completion-dove][data-b270-immediate]");
|
|
32
|
+
if (!immediate) return;
|
|
33
|
+
|
|
34
|
+
document.querySelectorAll("[data-b270-completion-dove]").forEach((dove) => {
|
|
35
|
+
if (dove !== immediate) dove.remove();
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
document.addEventListener("submit", (event) => {
|
|
40
|
+
const form = event.target.closest("form[data-b270-checkoff='true']");
|
|
41
|
+
const check = form && form.querySelector(".b270-check");
|
|
42
|
+
if (!check) return;
|
|
43
|
+
|
|
44
|
+
const bounds = check.getBoundingClientRect();
|
|
45
|
+
document.documentElement.style.setProperty(
|
|
46
|
+
"--b270-dove-origin-x",
|
|
47
|
+
`${bounds.left + bounds.width / 2}px`
|
|
48
|
+
);
|
|
49
|
+
document.documentElement.style.setProperty(
|
|
50
|
+
"--b270-dove-origin-y",
|
|
51
|
+
`${bounds.top + bounds.height / 2}px`
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
if (willCompleteDay(form)) startDove(form.action.match(/\/day\/(\d+)/)?.[1] || "");
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
document.addEventListener("animationend", (event) => {
|
|
58
|
+
if (event.target.matches("[data-b270-completion-dove]")) event.target.remove();
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
new MutationObserver(removeServerDuplicate).observe(document.documentElement, {
|
|
62
|
+
childList: true,
|
|
63
|
+
subtree: true
|
|
64
|
+
});
|
|
65
|
+
window.Bible270DayCompletionEffect = true;
|
|
66
|
+
})();
|
|
67
|
+
</script>
|
|
@@ -35,6 +35,7 @@
|
|
|
35
35
|
let sessionRefreshedAt = Date.now();
|
|
36
36
|
let sessionRefresh = null;
|
|
37
37
|
let hiddenAt = null;
|
|
38
|
+
const submitters = new WeakMap();
|
|
38
39
|
|
|
39
40
|
function updateAuthenticityToken(token) {
|
|
40
41
|
const meta = document.querySelector("meta[name='csrf-token']");
|
|
@@ -97,6 +98,9 @@
|
|
|
97
98
|
});
|
|
98
99
|
|
|
99
100
|
document.addEventListener("submit", (event) => {
|
|
101
|
+
const submittedForm = event.target.closest("form");
|
|
102
|
+
if (submittedForm && event.submitter) submitters.set(submittedForm, event.submitter);
|
|
103
|
+
|
|
100
104
|
const form = event.target.closest("form[data-b270-submit='true']");
|
|
101
105
|
if (!form) return;
|
|
102
106
|
|
|
@@ -111,8 +115,7 @@
|
|
|
111
115
|
form.requestSubmit(submitter || undefined);
|
|
112
116
|
}).catch(() => {
|
|
113
117
|
delete form.dataset.b270AwaitingSession;
|
|
114
|
-
|
|
115
|
-
form.requestSubmit(submitter || undefined);
|
|
118
|
+
window.location.reload();
|
|
116
119
|
});
|
|
117
120
|
return;
|
|
118
121
|
}
|
|
@@ -134,17 +137,22 @@
|
|
|
134
137
|
|
|
135
138
|
function staleSessionResponse(fetchResponse) {
|
|
136
139
|
if (!fetchResponse) return false;
|
|
137
|
-
if (fetchResponse.statusCode === 422) return true;
|
|
138
140
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
+
const response = fetchResponse.response;
|
|
142
|
+
if (fetchResponse.statusCode === 409 &&
|
|
143
|
+
response.headers.get("X-Bible270-Refresh-Session") === "true") return true;
|
|
144
|
+
|
|
145
|
+
// Older deployments can still return Rails' HTML 422 page. Do not confuse
|
|
146
|
+
// an intentional Turbo Stream validation response with an expired session.
|
|
147
|
+
const contentType = response.headers.get("Content-Type") || "";
|
|
148
|
+
return fetchResponse.statusCode === 422 && contentType.includes("text/html");
|
|
141
149
|
}
|
|
142
150
|
|
|
143
151
|
document.addEventListener("turbo:before-fetch-response", (event) => {
|
|
144
152
|
const target = event.target;
|
|
145
|
-
const form = target.closest && target.closest("form
|
|
153
|
+
const form = target.closest && target.closest("form");
|
|
146
154
|
const response = event.detail.fetchResponse;
|
|
147
|
-
if (!form || !staleSessionResponse(response)) return;
|
|
155
|
+
if (!form || form.method.toLowerCase() === "get" || !staleSessionResponse(response)) return;
|
|
148
156
|
|
|
149
157
|
event.preventDefault();
|
|
150
158
|
if (form.dataset.b270RetriedAfterStaleSession === "true") {
|
|
@@ -152,9 +160,12 @@
|
|
|
152
160
|
return;
|
|
153
161
|
}
|
|
154
162
|
|
|
163
|
+
const submitter = submitters.get(form);
|
|
155
164
|
form.dataset.b270RetriedAfterStaleSession = "true";
|
|
156
165
|
resetForm(form);
|
|
157
|
-
refreshSession()
|
|
166
|
+
refreshSession()
|
|
167
|
+
.then(() => form.requestSubmit(submitter || undefined))
|
|
168
|
+
.catch(() => window.location.reload());
|
|
158
169
|
});
|
|
159
170
|
|
|
160
171
|
document.addEventListener("turbo:submit-end", (event) => {
|
|
@@ -165,7 +176,12 @@
|
|
|
165
176
|
if (form.dataset.b270RetriedAfterStaleSession === "true" &&
|
|
166
177
|
staleSessionResponse(event.detail.fetchResponse)) return;
|
|
167
178
|
|
|
168
|
-
|
|
179
|
+
delete form.dataset.b270RetriedAfterStaleSession;
|
|
180
|
+
const completedDay = form.dataset.b270Checkoff === "true" &&
|
|
181
|
+
document.querySelector("[data-b270-day-just-completed='true']");
|
|
182
|
+
if (event.detail.success && completedDay) {
|
|
183
|
+
announce("");
|
|
184
|
+
} else if (event.detail.success) {
|
|
169
185
|
announce(form.dataset.b270Success || "Saved.", "success");
|
|
170
186
|
} else {
|
|
171
187
|
announce(form.dataset.b270Failure || "Couldn’t save that. Try again.", "error");
|
|
@@ -147,6 +147,18 @@
|
|
|
147
147
|
.b270-day-complete strong{font-family:"Spectral",serif;font-size:16px}
|
|
148
148
|
.b270-day-complete span{font-size:12px;color:var(--muted)}
|
|
149
149
|
.b270 .b270-day-complete .b270-btn{flex:none;margin:0 0 0 auto}
|
|
150
|
+
.b270-completion-dove{position:fixed;z-index:1000;top:var(--b270-dove-origin-y,72vh);left:var(--b270-dove-origin-x,50vw);
|
|
151
|
+
width:min(75vw,97.5vh);height:auto;overflow:visible;pointer-events:none;opacity:0;
|
|
152
|
+
filter:drop-shadow(0 0 8px rgba(255,255,255,1)) drop-shadow(0 0 24px rgba(255,252,220,1)) drop-shadow(0 0 52px rgba(225,191,92,.82));
|
|
153
|
+
will-change:top,left,transform,opacity;animation:b270-dove-flight 1.6s cubic-bezier(.18,.72,.28,1) both}
|
|
154
|
+
.b270-dove-silhouette{fill:url(#b270-dove-light);stroke:none}
|
|
155
|
+
@keyframes b270-dove-flight{
|
|
156
|
+
0%{top:var(--b270-dove-origin-y,72vh);left:var(--b270-dove-origin-x,50vw);opacity:.22;visibility:visible;transform:translate(-50%,-50%) scale(.02) rotate(-8deg)}
|
|
157
|
+
8%{top:calc(var(--b270-dove-origin-y,72vh) - 10px);left:var(--b270-dove-origin-x,50vw);opacity:.78;transform:translate(-50%,-50%) scale(.08) rotate(-5deg)}
|
|
158
|
+
64%{top:38vh;left:52vw;opacity:.92;transform:translate(-50%,-50%) scale(1) rotate(4deg)}
|
|
159
|
+
99%{visibility:visible}
|
|
160
|
+
100%{top:-25vh;left:115vw;opacity:0;visibility:hidden;transform:translate(-50%,-50%) scale(1.55) rotate(12deg)}
|
|
161
|
+
}
|
|
150
162
|
|
|
151
163
|
/* readings */
|
|
152
164
|
.b270-readings{background:var(--card);border:1px solid var(--line);border-radius:14px;box-shadow:var(--shadow);overflow:hidden}
|
|
@@ -156,7 +168,7 @@
|
|
|
156
168
|
.b270-reading.rest{opacity:.65}
|
|
157
169
|
.b270-toggle{border:none;background:none;padding:0;cursor:pointer}
|
|
158
170
|
.b270-toggle form{margin:0}
|
|
159
|
-
.b270-check{width:26px;height:26px;border-radius:8px;border:2px solid var(--line);flex:none;display:grid;place-items:center;background:#fff;transition:
|
|
171
|
+
.b270-check{width:26px;height:26px;border-radius:8px;border:2px solid var(--line);flex:none;display:grid;place-items:center;background:#fff;transition:background-color .18s,border-color .18s,opacity .18s}
|
|
160
172
|
.b270-check svg{width:15px;height:15px;stroke:#fff;stroke-width:3;fill:none;stroke-linecap:round;stroke-linejoin:round;opacity:0}
|
|
161
173
|
.b270-reading.done.ot .b270-check{background:var(--teal);border-color:var(--teal)}
|
|
162
174
|
.b270-reading.done.nt .b270-check{background:var(--oxblood);border-color:var(--oxblood)}
|
|
@@ -366,6 +378,7 @@
|
|
|
366
378
|
.b270 .b270-day-complete .b270-btn{margin:3px 0 0}
|
|
367
379
|
}
|
|
368
380
|
@media (prefers-reduced-motion:reduce){
|
|
381
|
+
.b270-completion-dove{display:none!important}
|
|
369
382
|
.b270 *,.b270 *::before,.b270 *::after{scroll-behavior:auto!important;transition-duration:.01ms!important;animation-duration:.01ms!important;animation-iteration-count:1!important}
|
|
370
383
|
}
|
|
371
384
|
</style>
|
data/lib/bible270/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: bible270
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Andrew vonderLuft
|
|
@@ -138,6 +138,8 @@ files:
|
|
|
138
138
|
- app/views/bible270/sessions/new.html.erb
|
|
139
139
|
- app/views/bible270/shared/_comment_drafts.html.erb
|
|
140
140
|
- app/views/bible270/shared/_comment_notification_choices.html.erb
|
|
141
|
+
- app/views/bible270/shared/_day_completion_dove.html.erb
|
|
142
|
+
- app/views/bible270/shared/_day_completion_effect.html.erb
|
|
141
143
|
- app/views/bible270/shared/_day_index.html.erb
|
|
142
144
|
- app/views/bible270/shared/_footer.html.erb
|
|
143
145
|
- app/views/bible270/shared/_grid_legend.html.erb
|