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
8 changes: 8 additions & 0 deletions app/assets/stylesheets/card-perma.css
Original file line number Diff line number Diff line change
Expand Up @@ -543,4 +543,12 @@
}
}
}

.card-perma__account-limit-message {
background-color: var(--color-canvas);
border: 2px solid var(--color-container);
border-radius: 4px;
margin-block-start: calc(var(--padding-block) / -2);
padding: 1ch 2ch;
}
}
120 changes: 70 additions & 50 deletions app/assets/stylesheets/comments.css
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
@layer components {
/* List
/* ------------------------------------------------------------------------ */

.comments {
--avatar-size: 2.33em;
--comment-padding-block: var(--block-space-half);
Expand All @@ -21,10 +24,8 @@
}
}

.comments__subscribers {
max-inline-size: var(--comment-max);
padding-inline: calc(var(--comment-padding-block) + var(--inline-space-double));
}
/* Comment
/* ------------------------------------------------------------------------ */

.comment {
/* Distinguish from the .comment class used for code formatting without extra specificity */
Expand All @@ -35,52 +36,6 @@
position: relative;
}

.comment-by-system & {
--comment-padding-block: var(--block-space-half);

text-align: center;

&::before {
/* Make up space for lack of avatar */
content: "";
display: flex;
inline-size: calc(var(--comment-padding-inline) * 0.9);
}

.comment__avatar {
display: none;
}

.comment__author {
a { margin: 0 auto; }
h3 { margin-inline: auto; }
strong { display: none; }
}

.comment__body {
padding: 0;
text-align: center;
}

.comment__content {
--stripe-color: var(--color-ink-lightest);

background-image: repeating-linear-gradient(
45deg in srgb,
var(--color-canvas) 0 1px,
var(--stripe-color) 1px 10px);
padding-inline: var(--comment-padding-inline);

.comments--system-expanded .comment-by-system & {
--stripe-color: color-mix(in srgb, var(--card-color) 10%, var(--color-canvas));
}
}

.reactions {
display: none !important;
}
}

.reactions {
margin-block-start: var(--block-space-half);
margin-inline: calc(var(--column-gap) / -1);
Expand Down Expand Up @@ -195,14 +150,63 @@
}
}

/* System comment
/* ------------------------------------------------------------------------ */

.comment-by-system {
--stripe-color: var(--color-ink-lightest);

display: none;
transition: var(--dialog-duration) allow-discrete;
transition-property: display;

.comments--system-expanded & {
display: contents;
}

.comment {
--comment-padding-block: var(--block-space-half);

text-align: center;

&::before {
/* Make up space for lack of avatar */
content: "";
display: flex;
inline-size: calc(var(--comment-padding-inline) * 0.9);
}
}

.comment__avatar {
display: none;
}

.comment__author {
a { margin: 0 auto; }
h3 { margin-inline: auto; }
strong { display: none; }
}

.comment__body {
padding: 0;
text-align: center;
}

.comment__content {
background-image: repeating-linear-gradient(
45deg in srgb,
var(--color-canvas) 0 1px,
var(--stripe-color) 1px 10px);
padding-inline: var(--comment-padding-inline);

.comments--system-expanded .comment-by-system & {
--stripe-color: color-mix(in srgb, var(--card-color) 10%, var(--color-canvas));
}
}

.reactions {
display: none !important;
}
}

/* Show the last system comment */
Expand All @@ -220,4 +224,20 @@
display: none;
}
}

.comment-by-system--account-limit {
--stripe-color: oklch(var(--lch-blue-lightest));

.comment__content {
padding: 3ch;
}
}

/* Subscribers
/* ------------------------------------------------------------------------ */

.comments__subscribers {
max-inline-size: var(--comment-max);
padding-inline: calc(var(--comment-padding-block) + var(--inline-space-double));
}
}
6 changes: 5 additions & 1 deletion app/views/cards/_messages.html.erb
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
<%= messages_tag(card) do %>
<% if card.published? %>
<%= render partial: "cards/comments/comment", collection: card.comments.preloaded.chronologically, cached: true %>
<%= render "cards/comments/new", card: card %>
<% if Fizzy.saas? %>
<%= render "cards/comments/saas/new", card: card %>
<% else %>
<%= render "cards/comments/new", card: card %>
<% end %>

<%= render "cards/comments/watchers", card: card %>
<% end %>
Expand Down
2 changes: 2 additions & 0 deletions app/views/cards/container/footer/_create.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,7 @@
<span>Create and add another</span>
<% end %>
</div>

<%= render "cards/container/footer/saas/storage_limit_notice" if Fizzy.saas? %>
</div>

6 changes: 5 additions & 1 deletion app/views/cards/drafts/_container.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,9 @@
</div>
<% end %>

<%= render "cards/container/footer/create", card: card %>
<% if Fizzy.saas? %>
<%= render "cards/container/footer/saas/create", card: card %>
<% else %>
<%= render "cards/container/footer/create", card: card %>
<% end %>
</section>
8 changes: 8 additions & 0 deletions saas/app/controllers/concerns/card/storage_limited.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module Card::StorageLimited
extend ActiveSupport::Concern

private
def ensure_within_storage_limit
head :forbidden if Current.account.exceeding_storage_limit?
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module Card::StorageLimited::Commenting
extend ActiveSupport::Concern

included do
include Card::StorageLimited

before_action :ensure_within_storage_limit, only: :create
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module Card::StorageLimited::Creation
extend ActiveSupport::Concern

included do
include Card::StorageLimited

before_action :ensure_within_storage_limit, only: :create, if: -> { request.format.json? }
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module Card::StorageLimited::Publishing
extend ActiveSupport::Concern

included do
include Card::StorageLimited

before_action :ensure_within_storage_limit, only: :create
end
end
14 changes: 14 additions & 0 deletions saas/app/models/account/storage_limited.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
module Account::StorageLimited
extend ActiveSupport::Concern

STORAGE_LIMIT = 1.gigabyte
NEAR_STORAGE_LIMIT_THRESHOLD = 500.megabytes

def exceeding_storage_limit?
bytes_used > STORAGE_LIMIT
end

def nearing_storage_limit?
!exceeding_storage_limit? && bytes_used > STORAGE_LIMIT - NEAR_STORAGE_LIMIT_THRESHOLD
end
end
5 changes: 5 additions & 0 deletions saas/app/views/cards/comments/saas/_new.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<% if Current.account.exceeding_storage_limit? %>
<%= render "cards/comments/saas/storage_limit_exceeded" %>
<% else %>
<%= render "cards/comments/new", card: card %>
<% end %>
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<div class="comment-by-system comment-by-system--account-limit">
<div class="comment align-start full-width">
<div class="comment__content flex flex-column flex-item-grow full-width">
<div class="comment__body lexxy-content">
<div class="action-text-content">
<strong>This account has used all the included free storage (<%= number_to_human_size(Account::StorageLimited::STORAGE_LIMIT) %>).</strong>
<div><%= link_to "Self-host Fizzy", "https://github.com/basecamp/fizzy", target: "_blank" %> for unlimited storage.</div>
</div>
</div>
</div>
</div>
</div>
5 changes: 5 additions & 0 deletions saas/app/views/cards/container/footer/saas/_create.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<% if Current.account.exceeding_storage_limit? %>
<%= render "cards/container/footer/saas/storage_limit_exceeded" %>
<% else %>
<%= render "cards/container/footer/create", card: card %>
<% end %>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<div class="card-perma__notch card-perma__notch--bottom">
<div class="card-perma__account-limit-message">
<strong>This account has used all the included free storage (<%= number_to_human_size(Account::StorageLimited::STORAGE_LIMIT) %>).</strong>
<div>
<%= link_to "Self-host Fizzy", "https://github.com/basecamp/fizzy", target: "_blank" %> for unlimited storage.
</div>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<% if Current.account.nearing_storage_limit? %>
<div class="txt-small">
This account has used <strong><%= number_to_human_size(Current.account.bytes_used) %></strong> of <strong><%= number_to_human_size(Account::StorageLimited::STORAGE_LIMIT) %></strong> storage.
<div><%= link_to "Self-host Fizzy", "https://github.com/basecamp/fizzy", class: "txt-current txt-underline", target: "_blank" %> for unlimited storage.</div>
</div>
<% end %>
4 changes: 4 additions & 0 deletions saas/lib/fizzy/saas/engine.rb
Original file line number Diff line number Diff line change
Expand Up @@ -133,10 +133,14 @@ class Engine < ::Rails::Engine
end

config.to_prepare do
::Account.include Account::StorageLimited
::Identity.include Authorization::Identity, Identity::Devices
::Session.include Session::Devices
::Signup.prepend Signup
ApplicationController.include Authorization::Controller
CardsController.include(Card::StorageLimited::Creation)
Cards::CommentsController.include(Card::StorageLimited::Commenting)
Cards::PublishesController.include(Card::StorageLimited::Publishing)

Notification.register_push_target(:native)

Expand Down
29 changes: 29 additions & 0 deletions saas/test/controllers/card/storage_limited/commenting_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
require "test_helper"

class Card::StorageLimited::CommentingTest < ActionDispatch::IntegrationTest
test "cannot create comments when storage limit exceeded" do
sign_in_as :david

Account.any_instance.stubs(:bytes_used).returns(1.gigabyte + 1)

assert_no_difference -> { Comment.count } do
post card_comments_path(cards(:logo), script_name: accounts(:"37s").slug),
params: { comment: { body: "Blocked comment" } },
as: :turbo_stream
end

assert_response :forbidden
end

test "can create comments when under storage limit" do
sign_in_as :david

assert_difference -> { Comment.count } do
post card_comments_path(cards(:logo), script_name: accounts(:"37s").slug),
params: { comment: { body: "Allowed comment" } },
as: :turbo_stream
end

assert_response :success
end
end
47 changes: 47 additions & 0 deletions saas/test/controllers/card/storage_limited/creation_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
require "test_helper"

class Card::StorageLimited::CreationTest < ActionDispatch::IntegrationTest
test "cannot create cards via JSON when storage limit exceeded" do
sign_in_as :mike

Account.any_instance.stubs(:bytes_used).returns(1.gigabyte + 1)

assert_no_difference -> { Card.count } do
post board_cards_path(boards(:miltons_wish_list), script_name: accounts(:initech).slug),
params: { card: { title: "Blocked card" } },
as: :json
end

assert_response :forbidden
end

test "can create cards via HTML when storage limit exceeded since they become drafts" do
sign_in_as :mike

Account.any_instance.stubs(:bytes_used).returns(1.gigabyte + 1)
accounts(:initech).update_column(:cards_count, 100)
boards(:miltons_wish_list).cards.drafted.where(creator: users(:mike)).destroy_all

assert_difference -> { Card.count } do
post board_cards_path(boards(:miltons_wish_list), script_name: accounts(:initech).slug)
end

assert_response :redirect
assert Card.last.drafted?
end

test "can create cards via JSON when under storage limit" do
sign_in_as :mike

Account.any_instance.stubs(:bytes_used).returns(500.megabytes)
accounts(:initech).update_column(:cards_count, 100)

assert_difference -> { Card.count } do
post board_cards_path(boards(:miltons_wish_list), script_name: accounts(:initech).slug),
params: { card: { title: "Allowed card" } },
as: :json
end

assert_response :created
end
end
Loading
Loading