Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/assets/images/bubbles.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 12 additions & 0 deletions app/assets/stylesheets/bubble.css
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,18 @@
}
}

.bubble--golden {
&:before {
box-shadow:
0 0 0 1px color-mix(in oklch, var(--color-golden) 100%, transparent),
0 0 0.2em 0.2em color-mix(in oklch, var(--color-golden) 25%, transparent);
background: radial-gradient(
color-mix(in srgb, var(--color-golden) 8%, var(--color-canvas)) 50%,
color-mix(in srgb, var(--color-golden) 48%, var(--color-canvas)) 100%
);
}
}

.bubble__number {
display: grid;
font-size: clamp(10px, 50cqi, var(--bubble-number-max)); /* FF bug: https://app.fizzy.do/5986089/boards/2/cards/1373 */
Expand Down
52 changes: 40 additions & 12 deletions app/assets/stylesheets/card-columns.css
Original file line number Diff line number Diff line change
Expand Up @@ -789,7 +789,7 @@
--card-color: var(--color-card-complete) !important;
}

.bubble {
.bubble:not(.bubble--golden) {
display: none !important;
}
}
Expand All @@ -799,17 +799,13 @@

/* Surface a mini bubble if there are cards with bubbles inside */
.cards--maybe:has(.bubble:not([hidden])) .cards__expander-title,
.cards--maybe.is-collapsed:has(.bubble:not([hidden])) .cards__transition-container,
.cards--on-deck:has(.bubble--golden:not([hidden])) .cards__expander-title,
.cards--doing.is-collapsed:has(.bubble:not([hidden])) .cards__transition-container {
--bubble-color: var(--card-color, oklch(var(--lch-blue-medium)));
--bubble-opacity: 75%;
--bubble-shape: 54% 46% 61% 39% / 57% 49% 51% 43%;

&:before {
background: radial-gradient(
color-mix(in srgb, var(--bubble-color) calc(var(--bubble-opacity) / 5), var(--color-canvas)) 50%,
color-mix(in srgb, var(--bubble-color) var(--bubble-opacity), var(--color-canvas)) 100%
);
&:before,
&:after {
block-size: 1em;
border-radius: var(--bubble-shape);
content: "";
Expand All @@ -820,21 +816,53 @@
z-index: 1;
}

/* Regular mini bubble */
.cards:has(.bubble:not(.bubble--golden):not([hidden])) &::before {
--bubble-color: var(--card-color, oklch(var(--lch-blue-medium)));
background: radial-gradient(
color-mix(in srgb, var(--bubble-color) calc(var(--bubble-opacity) / 5), var(--color-canvas)) 50%,
color-mix(in srgb, var(--bubble-color) var(--bubble-opacity), var(--color-canvas)) 100%
);
}

/* Golden mini bubble */
.cards:has(.bubble--golden:not([hidden])) &::after {
--bubble-color: var(--color-golden);
background: radial-gradient(
color-mix(in srgb, var(--bubble-color) calc(var(--bubble-opacity) / 5), var(--color-canvas)) 50%,
color-mix(in srgb, var(--bubble-color) var(--bubble-opacity), var(--color-canvas)) 100%
);
}

/* Offset if both bubbles present */
.cards:has(.bubble:not(.bubble--golden):not([hidden])):has(.bubble--golden:not([hidden])) & {
&::before { translate: -15% -25%; }
&::after { translate: 35% -15%; }
}

/* Maybe column: position bubble relative to the title, not the container */
.cards--maybe.is-expanded & {
.cards--maybe.is-expanded &,
.cards--on-deck.is-expanded & {
overflow: visible;
position: relative;

&:before {
&:before,
&:after {
inset-block-start: 50%;
inset-inline-start: 0;
translate: -125% -75%;
translate: -135% -50%;
z-index: -1;
}

.cards:has(.bubble:not(.bubble--golden):not([hidden])):has(.bubble--golden:not([hidden])) & {
&::before { translate: -160% -50%; }
&::after { translate: -110% -50%; }
}
}

@media (max-width: 639px) {
&.cards__expander-title:before {
&.cards__expander-title:before,
&.cards__expander-title:after {
display: none;
}
}
Expand Down
2 changes: 1 addition & 1 deletion app/assets/stylesheets/card-perma.css
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@

&:has([open]) {
position: relative;
z-index: 1;
z-index: 2;
}

&:has([data-controller~="tooltip"]:hover) {
Expand Down
2 changes: 1 addition & 1 deletion app/assets/stylesheets/cards.css
Original file line number Diff line number Diff line change
Expand Up @@ -407,7 +407,7 @@
.card-perma:has(.card--postponed) {
--card-color: var(--color-card-complete) !important;

.bubble {
.bubble:not(.bubble--golden) {
display: none;
}
}
Expand Down
1 change: 1 addition & 0 deletions app/assets/stylesheets/icons.css
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
.icon--bookmark-outline { --svg: url("bookmark-outline.svg "); }
.icon--bookmark { --svg: url("bookmark.svg "); }
.icon--boost { --svg: url("boost.svg "); }
.icon--bubbles { --svg: url("bubbles.svg "); }
.icon--camera { --svg: url("camera.svg "); }
.icon--caret-down { --svg: url("caret-down.svg "); }
.icon--check { --svg: url("check.svg "); }
Expand Down
1 change: 1 addition & 0 deletions app/assets/stylesheets/popup.css
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@
text-align: start;

&:focus-visible {
outline: none;
z-index: 1;
}
}
Expand Down
21 changes: 21 additions & 0 deletions app/controllers/cards/bubble_ups_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
class Cards::BubbleUpsController < ApplicationController
include CardScoped

def create
@card.bubble_up_at TimeSlot.for(params[:slot])

respond_to do |format|
format.turbo_stream { render_card_replacement }
format.json { head :no_content }
end
end

def destroy
@card.pop

respond_to do |format|
format.turbo_stream { render_card_replacement }
format.json { head :no_content }
end
end
end
14 changes: 14 additions & 0 deletions app/helpers/bubble_up_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
module BubbleUpHelper
def bubble_up_options_for(card)
if card.bubble_up?
{
isPostponed: card.postponed?,
resurfaceAt: card.bubble_up.resurface_at.iso8601
}
end
end

def slot_too_soon(slot)
slot == "latertoday" && Time.current.hour > 16 ? true : false
end
end
30 changes: 27 additions & 3 deletions app/javascript/controllers/bubble_controller.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { Controller } from "@hotwired/stimulus"
import { signedDifferenceInDays } from "helpers/date_helpers"
import { signedDifferenceInDays, differenceInDays } from "helpers/date_helpers"

const REFRESH_INTERVAL = 3_600_000 // 1 hour (in milliseconds)

export default class extends Controller {
static targets = [ "entropy", "stalled", "top", "center", "bottom" ]
static values = { entropy: Object, stalled: Object }
static values = { entropy: Object, stalled: Object, up: Object }

#timer

Expand All @@ -23,7 +23,9 @@ export default class extends Controller {
this.#showEntropy()
} else if (this.#isStalled) {
this.#showStalled()
} else {
} else if (this.#isBubbling) {
this.#showBubbling()
}else {
this.#hide()
}
}
Expand Down Expand Up @@ -67,6 +69,28 @@ export default class extends Controller {
})
}

get #isBubbling() {
if (!this.upValue.resurfaceAt) return false
return this.upValue.isPostponed || this.#hasBubbled
}

#showBubbling() {
const dayLabel = this.#resurfaceDayCount === 1 ? "day" : "days"
this.#render({
top: this.#hasBubbled ? "Bubbled Up" : this.#resurfaceDayCount < 1 ? "Bubbles Up" : "Bubbles Up in",
center: this.#resurfaceDayCount < 1 ? "!" : this.#resurfaceDayCount,
bottom: this.#resurfaceDayCount < 1 ? "Today" : this.#hasBubbled ? `${dayLabel} ago` : dayLabel
})
}

get #hasBubbled() {
return new Date() > new Date(this.upValue.resurfaceAt)
}

get #resurfaceDayCount() {
return differenceInDays(new Date(), new Date(this.upValue.resurfaceAt))
}

#hide() {
this.element.toggleAttribute("hidden", true)
}
Expand Down
2 changes: 1 addition & 1 deletion app/models/card.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
class Card < ApplicationRecord
include Accessible, Assignable, Attachments, Broadcastable, Closeable, Colored, Commentable,
include Accessible, Assignable, Attachments, Broadcastable, BubblesUp, Closeable, Colored, Commentable,
Entropic, Eventable, Exportable, Golden, Mentions, Multistep, Pinnable, Postponable, Promptable,
Readable, Searchable, Stallable, Statuses, Storage::Tracked, Taggable, Triageable, Watchable

Expand Down
12 changes: 12 additions & 0 deletions app/models/card/bubble_up.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
class Card::BubbleUp < ApplicationRecord
belongs_to :account, default: -> { card.account }
belongs_to :card, touch: true

scope :due_to_resurface, -> { joins(card: :not_now).where(resurface_at: ..Time.now) }

def self.resurface_all_due
due_to_resurface.find_each do |bubble_up|
bubble_up.card.send_back_to_triage(skip_event: true)
end
end
end
29 changes: 29 additions & 0 deletions app/models/card/bubbles_up.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
module Card::BubblesUp
extend ActiveSupport::Concern

included do
has_one :bubble_up, dependent: :destroy, class_name: "Card::BubbleUp"
end

def bubble_up?
bubble_up.present?
end

def bubbling?
bubble_up? && Time.current.before?(bubble_up.resurface_at)
end

def bubbled?
bubble_up? && Time.current.after?(bubble_up.resurface_at)
end

def bubble_up_at(time)
postpone unless postponed?
bubble_up ||= association(:bubble_up).reader || self.build_bubble_up
bubble_up.update resurface_at: time
end

def pop
bubble_up&.destroy
end
end
1 change: 1 addition & 0 deletions app/models/card/closeable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ def close(user: Current.user)
unless closed?
transaction do
not_now&.destroy
bubble_up&.destroy
create_closure! user: user
track_event :closed, creator: user
end
Expand Down
1 change: 1 addition & 0 deletions app/models/card/postponable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ def postpone(user: Current.user, event_name: :postponed)
reopen
activity_spike&.destroy
create_not_now!(user: user) unless postponed?
pop if bubble_up?
track_event event_name, creator: user
end
end
Expand Down
1 change: 1 addition & 0 deletions app/models/card/triageable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ def triage_into(column)

transaction do
resume
pop if bubble_up?
update! column: column
track_event "triaged", particulars: { column: column.name }
end
Expand Down
36 changes: 36 additions & 0 deletions app/models/time_slot.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
class TimeSlot
attr_reader :slot

HUMAN_NAMES_BY_VALUE = {
"latertoday" => "Later Today",
"tomorrow" => "Tomorrow",
"thisweekend" => "This Weekend",
"nextweek" => "Next Week",
"surprise" => "Surprise Me"
}

class << self
def for(slot)
new.for(slot)
end

def initialize(slot)
@slot = slot
end
end

def for(slot)
case slot
when "latertoday"
Time.current.change(hour: 18)
when "tomorrow"
1.day.from_now.change(hour: 8)
when "thisweekend"
Date.current.next_occurring(:saturday).in_time_zone.change(hour: 8)
when "nextweek"
Date.current.next_occurring(:monday).in_time_zone.change(hour: 8)
when "surprise"
rand(2..14).days.from_now.change(hour: 8)
end
end
end
3 changes: 2 additions & 1 deletion app/views/cards/_container.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
<% cache card do %>
<div class="card-perma__actions card-perma__actions--left">
<%= render "cards/container/gild", card: card if card.published? && !card.closed? %>
<%= render "cards/container/bubble_up", card: card if card.published? && !card.closed? %>
<%= render "cards/container/image", card: card %>
</div>

Expand Down Expand Up @@ -32,7 +33,7 @@
</footer>
<% end %>

<% if card.entropic? %>
<% if card.entropic? || card.bubble_up? %>
<%= render "cards/display/preview/bubble", card: card %>
<% end %>
</div>
Expand Down
Loading