diff --git a/Gemfile.lock b/Gemfile.lock index 2058664..1e08f97 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -83,7 +83,7 @@ GEM activerecord (>= 5.2.6) base64 (0.2.0) bcrypt (3.1.20) - bigdecimal (3.1.7) + bigdecimal (3.1.8) bindex (0.8.1) bootsnap (1.18.3) msgpack (~> 1.2) @@ -110,9 +110,9 @@ GEM irb (~> 1.10) reline (>= 0.3.8) dkim (1.1.0) - dotenv (3.1.1) - dotenv-rails (3.1.1) - dotenv (= 3.1.1) + dotenv (3.1.2) + dotenv-rails (3.1.2) + dotenv (= 3.1.2) railties (>= 6.1) drb (2.2.1) erubi (1.12.0) @@ -120,7 +120,7 @@ GEM globalid (1.2.1) activesupport (>= 6.1) htmlentities (4.3.4) - i18n (1.14.4) + i18n (1.14.5) concurrent-ruby (~> 1.0) image_processing (1.12.2) mini_magick (>= 4.9.5, < 5) @@ -130,7 +130,7 @@ GEM activesupport (>= 6.0.0) railties (>= 6.0.0) io-console (0.7.2) - irb (1.13.0) + irb (1.13.1) rdoc (>= 4.0.0) reline (>= 0.4.2) jbuilder (2.12.0) @@ -166,7 +166,7 @@ GEM request_store (~> 1.0) msgpack (1.7.2) mutex_m (0.2.0) - net-imap (0.4.10) + net-imap (0.4.11) date net-protocol net-pop (0.1.2) @@ -175,7 +175,7 @@ GEM timeout net-smtp (0.5.0) net-protocol - nio4r (2.7.1) + nio4r (2.7.3) nokogiri (1.16.4-aarch64-linux) racc (~> 1.4) nokogiri (1.16.4-arm-linux) @@ -256,7 +256,7 @@ GEM redis-client (0.22.1) connection_pool regexp_parser (2.9.0) - reline (0.5.5) + reline (0.5.6) io-console (~> 0.5) request_store (1.7.0) rack (>= 1.4) diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css index 1605931..5054dbf 100644 --- a/app/assets/stylesheets/application.css +++ b/app/assets/stylesheets/application.css @@ -97,6 +97,7 @@ body { gap: 0; min-height: 100svh; position: relative; + -webkit-font-smoothing: antialiased; } ul[class] { @@ -105,7 +106,6 @@ ul[class] { list-style: none; } - header { display: flex; justify-content: space-between; @@ -132,8 +132,6 @@ header { main { margin: 0 1rem; - - } .msg__container { @@ -249,7 +247,6 @@ main { height: 62px; --opacity: 1.0; - &::before { content: attr(data-label); color: var(--clr-white); @@ -269,11 +266,11 @@ main { margin: 0; } } + input[type=range] { appearance: none; width: 100%; background-color: transparent; - height: 62px; box-sizing: border-box; border: 8px solid transparent; @@ -318,44 +315,46 @@ input[type=range] { pointer-events: none; display: block; } -} -#windmill { - width: 33%; - min-width: 60px; - max-width: 163px; - height: auto; - rotate: -20deg; - right: 8vw; - top: 10px; -} + & img:nth-child(1) { + width: 33%; + min-width: 60px; + max-width: 163px; + height: auto; + rotate: -20deg; + right: 8vw; + top: 10px; + } -#wase { - width: 15%; - min-width: 126px; - max-width: 218px; - height: auto; - rotate: 10deg; - left: 10vw; - bottom: -40px; -} + & img:nth-child(2) { + width: 15%; + min-width: 126px; + max-width: 218px; + height: auto; + rotate: 10deg; + left: 10vw; + bottom: 0; + transform: translateY(10%); + } -#arrows { - width: 20%; - min-width: 92px; - max-width: 161px; - height: auto; - rotate: 10deg; - right: -3vw; - bottom: 10svh; + & img:nth-child(3) { + width: 20%; + min-width: 92px; + max-width: 161px; + height: auto; + rotate: 10deg; + right: -3vw; + bottom: 10svh; + } } - .cards__container, .link__container { margin-top: 2.4em; margin-bottom: 2.4em; } + + .link__container { max-width: 735; margin-left: auto; @@ -369,6 +368,8 @@ ul.card__stack { margin-bottom: 24px; aspect-ratio: 0.72972972972973; position: relative; + width: 100%; + margin-bottom: 60px; & a { text-decoration: none; @@ -504,15 +505,40 @@ ul.card__stack { .cards__ctas { max-width: 440px; + width: 100%; } +@media (orientation: portrait) { + + main:has(.cards__container) { + display: flex; + flex-direction: column; + flex-grow: 1; + } -@media (min-width: 480px) { + .cards__container { + display: flex; + flex-direction: column; + justify-content: space-between; + flex-grow: 1; + margin-top: 0; + } + + .card__stack-container { + flex-grow: 1; + display: flex; + align-items: center; + justify-content: center; + } ul.card__stack { - margin-bottom: 60px; - } + margin-top: 0; + } + +} + +@media (min-width: 480px) { .link { & img { aspect-ratio: 1.536363636363636; diff --git a/app/controllers/admin/assets_controller.rb b/app/controllers/admin/assets_controller.rb index 5ad60c3..269dc4c 100644 --- a/app/controllers/admin/assets_controller.rb +++ b/app/controllers/admin/assets_controller.rb @@ -75,7 +75,8 @@ private # Only allow a list of trusted parameters through. def asset_params params.require(:asset).permit( - :title + :title, + tags: [] ) end diff --git a/app/javascript/application.js b/app/javascript/application.js index 7036b4e..9a721b7 100644 --- a/app/javascript/application.js +++ b/app/javascript/application.js @@ -7,14 +7,21 @@ function rand_in_range(from, to) { } - function shuffle_cards() { const cards = document.querySelectorAll('.card') - const random_index = Math.floor(Math.random() * cards.length); + const card_ids = Array.from(cards).map(card => card.getAttribute('data-id')) + let shown_cards = JSON.parse(localStorage.getItem('shown_cards')) || []; + let cards_not_shown = card_ids.filter(id => !shown_cards.includes(id)); + + if (cards_not_shown.length <= 0) { + cards_not_shown = card_ids + shown_cards = [] + } + const random_id = cards_not_shown[Math.floor(Math.random() * cards_not_shown.length)]; - cards.forEach((card, index) => { - if (index == random_index) { + cards.forEach((card) => { + if (card.getAttribute('data-id') == random_id) { card.classList.add('active') card.style.zIndex = '1' card.style.transform = `none` @@ -25,6 +32,8 @@ function shuffle_cards() { } }) + shown_cards.push(random_id); + localStorage.setItem('shown_cards', JSON.stringify(shown_cards)); } document.querySelectorAll('.button_shuffle_cards').forEach((item, index) => { diff --git a/app/models/asset.rb b/app/models/asset.rb index 8ddae57..7da1f0d 100644 --- a/app/models/asset.rb +++ b/app/models/asset.rb @@ -1,10 +1,12 @@ class Asset < ApplicationRecord + STICKER = 'Sticker' + has_one_attached :file include PgSearch::Model pg_search_scope :pg_search, - against: [:title], + against: [:title, :tags], associated_against: { file_blob: [:filename, :content_type] }, @@ -12,8 +14,21 @@ class Asset < ApplicationRecord tsearch: { prefix: true } } + before_validation :remove_empty_tags + + scope :stickets, ->() { where("tags @> ?", "{#{STICKER}}") } + scope :by_last_modified, ->(rev) { order(updated_at: rev ? :asc : :desc, id: rev ? :desc : :asc) } scope :by_filename, ->(rev) { order(title: rev ? :desc : :asc) } scope :simple_search, ->(q) { pg_search(q) unless q.blank? } + +private + + def remove_empty_tags + %w"tags".map do |k| + self.send "#{k}=", self.send(k).reject { |v| v.blank? } unless self.send(k).blank? + end + end + end diff --git a/app/views/admin/assets/_asset.html.erb b/app/views/admin/assets/_asset.html.erb index f0c55f8..6be1dfd 100644 --- a/app/views/admin/assets/_asset.html.erb +++ b/app/views/admin/assets/_asset.html.erb @@ -1,7 +1,7 @@ <%= tag.div class: 'asset', id: dom_id(asset) do %>
-
<%= image_tag rails_storage_proxy_path(asset.file.representation(resize_to_limit: [320,320], format: :jpg)) if asset.file.representable? %>
+
<%= image_tag rails_storage_proxy_path(asset.file.representation(resize_to_limit: [320,320])) if asset.file.representable? %>
<%= tag.div asset.title, class: 'asset__title' %> @@ -23,7 +23,7 @@
<%= link_to 'edit', edit_admin_asset_path(asset), data: {turbo_frame: 'main', turbo_action: 'advance'}, title: t('ui.edit') %> - <%= link_to 'download', rails_blob_path(asset.file, disposition: "attachment"), title: t('ui.download') %> + <%= link_to 'download', rails_storage_proxy_path(asset.file, disposition: "attachment", locale: nil), title: t('ui.download') %>
<%= link_to 'delete_forever', admin_asset_path(asset), data: { turbo_confirm: t(:'ui.are_you_sure'), turbo_method: :delete }, title: t('ui.delete') %>
diff --git a/app/views/admin/assets/_form.html.erb b/app/views/admin/assets/_form.html.erb index f8afae6..d04473e 100644 --- a/app/views/admin/assets/_form.html.erb +++ b/app/views/admin/assets/_form.html.erb @@ -1,8 +1,16 @@ - <%= form_with(model: [ :admin, asset ], class: 'form-plain') do |form| %> + <%= form_with(model: [ :admin, asset ], class: 'form-plain has--key-ctrls') do |form| %>
<%= render partial: 'material/text_field', locals: { f: form, attr: :title } %> + + <%= render partial: 'material/tom_select_field', + locals: { + f: form, + attr: :tags, + choices: [Asset::STICKER], + multiple: true + } %>
diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index adccc0d..ec07c92 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -1,5 +1,5 @@ -"> - + + <%= content_for?(:title) ? yield(:title) : t(:project_name) %> diff --git a/app/views/site/tmpl_index.html.erb b/app/views/site/tmpl_index.html.erb index a06168a..ce9056d 100644 --- a/app/views/site/tmpl_index.html.erb +++ b/app/views/site/tmpl_index.html.erb @@ -13,8 +13,18 @@
- <%= image_tag 'windmill.png', width: 630, height: 963, loading: 'lazy', id: 'windmill' %> - <%= image_tag 'wase.png', width: 1024, height: 1024, id: 'wase' %> + <% Asset.stickets.order("RANDOM()").limit(3).each do |asset| %> + <%= image_tag rails_storage_proxy_url(asset.file.variant(resize_to_limit: [160,160])), + alt: asset.title, + srcset: " + #{rails_storage_proxy_url(asset.file.variant(resize_to_limit: [320,320]))} 2x, + #{rails_storage_proxy_url(asset.file.variant(resize_to_limit: [640,640]))} 3x + ", + loading: 'lazy' if asset&.file&.image? %> + <% end %> + + <%# image_tag 'windmill.png', width: 630, height: 963, loading: 'lazy', id: 'windmill' %> + <%# image_tag 'wase.png', width: 1024, height: 1024, id: 'wase' %> - <%= image_tag 'ico-arrow-updown.svg', width: 124, height: 103, id: 'arrows' %> + <%# image_tag 'ico-arrow-updown.svg', width: 124, height: 103, id: 'arrows' %>
\ No newline at end of file diff --git a/app/views/site/tmpl_list.html.erb b/app/views/site/tmpl_list.html.erb index ffd11b1..ed5ae73 100644 --- a/app/views/site/tmpl_list.html.erb +++ b/app/views/site/tmpl_list.html.erb @@ -5,20 +5,22 @@
- +