Mattias Bodlund 1 year ago
parent
commit
74ee217882
13 changed files with 153 additions and 73 deletions
  1. +9
    -9
      Gemfile.lock
  2. +62
    -36
      app/assets/stylesheets/application.css
  3. +2
    -1
      app/controllers/admin/assets_controller.rb
  4. +13
    -4
      app/javascript/application.js
  5. +16
    -1
      app/models/asset.rb
  6. +2
    -2
      app/views/admin/assets/_asset.html.erb
  7. +9
    -1
      app/views/admin/assets/_form.html.erb
  8. +2
    -2
      app/views/layouts/application.html.erb
  9. +13
    -3
      app/views/site/tmpl_index.html.erb
  10. +15
    -13
      app/views/site/tmpl_list.html.erb
  11. +1
    -0
      config/locales/en.yml
  12. +6
    -0
      db/migrate/20240509121618_add_tags_to_assets.rb
  13. +3
    -1
      db/schema.rb

+ 9
- 9
Gemfile.lock View File

@ -83,7 +83,7 @@ GEM
activerecord (>= 5.2.6) activerecord (>= 5.2.6)
base64 (0.2.0) base64 (0.2.0)
bcrypt (3.1.20) bcrypt (3.1.20)
bigdecimal (3.1.7)
bigdecimal (3.1.8)
bindex (0.8.1) bindex (0.8.1)
bootsnap (1.18.3) bootsnap (1.18.3)
msgpack (~> 1.2) msgpack (~> 1.2)
@ -110,9 +110,9 @@ GEM
irb (~> 1.10) irb (~> 1.10)
reline (>= 0.3.8) reline (>= 0.3.8)
dkim (1.1.0) 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) railties (>= 6.1)
drb (2.2.1) drb (2.2.1)
erubi (1.12.0) erubi (1.12.0)
@ -120,7 +120,7 @@ GEM
globalid (1.2.1) globalid (1.2.1)
activesupport (>= 6.1) activesupport (>= 6.1)
htmlentities (4.3.4) htmlentities (4.3.4)
i18n (1.14.4)
i18n (1.14.5)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
image_processing (1.12.2) image_processing (1.12.2)
mini_magick (>= 4.9.5, < 5) mini_magick (>= 4.9.5, < 5)
@ -130,7 +130,7 @@ GEM
activesupport (>= 6.0.0) activesupport (>= 6.0.0)
railties (>= 6.0.0) railties (>= 6.0.0)
io-console (0.7.2) io-console (0.7.2)
irb (1.13.0)
irb (1.13.1)
rdoc (>= 4.0.0) rdoc (>= 4.0.0)
reline (>= 0.4.2) reline (>= 0.4.2)
jbuilder (2.12.0) jbuilder (2.12.0)
@ -166,7 +166,7 @@ GEM
request_store (~> 1.0) request_store (~> 1.0)
msgpack (1.7.2) msgpack (1.7.2)
mutex_m (0.2.0) mutex_m (0.2.0)
net-imap (0.4.10)
net-imap (0.4.11)
date date
net-protocol net-protocol
net-pop (0.1.2) net-pop (0.1.2)
@ -175,7 +175,7 @@ GEM
timeout timeout
net-smtp (0.5.0) net-smtp (0.5.0)
net-protocol net-protocol
nio4r (2.7.1)
nio4r (2.7.3)
nokogiri (1.16.4-aarch64-linux) nokogiri (1.16.4-aarch64-linux)
racc (~> 1.4) racc (~> 1.4)
nokogiri (1.16.4-arm-linux) nokogiri (1.16.4-arm-linux)
@ -256,7 +256,7 @@ GEM
redis-client (0.22.1) redis-client (0.22.1)
connection_pool connection_pool
regexp_parser (2.9.0) regexp_parser (2.9.0)
reline (0.5.5)
reline (0.5.6)
io-console (~> 0.5) io-console (~> 0.5)
request_store (1.7.0) request_store (1.7.0)
rack (>= 1.4) rack (>= 1.4)


+ 62
- 36
app/assets/stylesheets/application.css View File

@ -97,6 +97,7 @@ body {
gap: 0; gap: 0;
min-height: 100svh; min-height: 100svh;
position: relative; position: relative;
-webkit-font-smoothing: antialiased;
} }
ul[class] { ul[class] {
@ -105,7 +106,6 @@ ul[class] {
list-style: none; list-style: none;
} }
header { header {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
@ -132,8 +132,6 @@ header {
main { main {
margin: 0 1rem; margin: 0 1rem;
} }
.msg__container { .msg__container {
@ -249,7 +247,6 @@ main {
height: 62px; height: 62px;
--opacity: 1.0; --opacity: 1.0;
&::before { &::before {
content: attr(data-label); content: attr(data-label);
color: var(--clr-white); color: var(--clr-white);
@ -269,11 +266,11 @@ main {
margin: 0; margin: 0;
} }
} }
input[type=range] { input[type=range] {
appearance: none; appearance: none;
width: 100%; width: 100%;
background-color: transparent; background-color: transparent;
height: 62px; height: 62px;
box-sizing: border-box; box-sizing: border-box;
border: 8px solid transparent; border: 8px solid transparent;
@ -318,44 +315,46 @@ input[type=range] {
pointer-events: none; pointer-events: none;
display: block; 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 { .cards__container, .link__container {
margin-top: 2.4em; margin-top: 2.4em;
margin-bottom: 2.4em; margin-bottom: 2.4em;
} }
.link__container { .link__container {
max-width: 735; max-width: 735;
margin-left: auto; margin-left: auto;
@ -369,6 +368,8 @@ ul.card__stack {
margin-bottom: 24px; margin-bottom: 24px;
aspect-ratio: 0.72972972972973; aspect-ratio: 0.72972972972973;
position: relative; position: relative;
width: 100%;
margin-bottom: 60px;
& a { & a {
text-decoration: none; text-decoration: none;
@ -504,15 +505,40 @@ ul.card__stack {
.cards__ctas { .cards__ctas {
max-width: 440px; 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 { ul.card__stack {
margin-bottom: 60px;
}
margin-top: 0;
}
}
@media (min-width: 480px) {
.link { .link {
& img { & img {
aspect-ratio: 1.536363636363636; aspect-ratio: 1.536363636363636;


+ 2
- 1
app/controllers/admin/assets_controller.rb View File

@ -75,7 +75,8 @@ private
# Only allow a list of trusted parameters through. # Only allow a list of trusted parameters through.
def asset_params def asset_params
params.require(:asset).permit( params.require(:asset).permit(
:title
:title,
tags: []
) )
end end


+ 13
- 4
app/javascript/application.js View File

@ -7,14 +7,21 @@ function rand_in_range(from, to) {
} }
function shuffle_cards() { function shuffle_cards() {
const cards = document.querySelectorAll('.card') 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.classList.add('active')
card.style.zIndex = '1' card.style.zIndex = '1'
card.style.transform = `none` 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) => { document.querySelectorAll('.button_shuffle_cards').forEach((item, index) => {


+ 16
- 1
app/models/asset.rb View File

@ -1,10 +1,12 @@
class Asset < ApplicationRecord class Asset < ApplicationRecord
STICKER = 'Sticker'
has_one_attached :file has_one_attached :file
include PgSearch::Model include PgSearch::Model
pg_search_scope :pg_search, pg_search_scope :pg_search,
against: [:title],
against: [:title, :tags],
associated_against: { associated_against: {
file_blob: [:filename, :content_type] file_blob: [:filename, :content_type]
}, },
@ -12,8 +14,21 @@ class Asset < ApplicationRecord
tsearch: { prefix: true } 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_last_modified, ->(rev) { order(updated_at: rev ? :asc : :desc, id: rev ? :desc : :asc) }
scope :by_filename, ->(rev) { order(title: rev ? :desc : :asc) } scope :by_filename, ->(rev) { order(title: rev ? :desc : :asc) }
scope :simple_search, ->(q) { pg_search(q) unless q.blank? } 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 end

+ 2
- 2
app/views/admin/assets/_asset.html.erb View File

@ -1,7 +1,7 @@
<%= tag.div class: 'asset', id: dom_id(asset) do %> <%= tag.div class: 'asset', id: dom_id(asset) do %>
<div class="asset__thumbnail"> <div class="asset__thumbnail">
<div><%= image_tag rails_storage_proxy_path(asset.file.representation(resize_to_limit: [320,320], format: :jpg)) if asset.file.representable? %></div>
<div><%= image_tag rails_storage_proxy_path(asset.file.representation(resize_to_limit: [320,320])) if asset.file.representable? %></div>
</div> </div>
<%= tag.div asset.title, class: 'asset__title' %> <%= tag.div asset.title, class: 'asset__title' %>
@ -23,7 +23,7 @@
<div class="asset-ctrls"> <div class="asset-ctrls">
<div> <div>
<%= link_to 'edit', edit_admin_asset_path(asset), data: {turbo_frame: 'main', turbo_action: 'advance'}, title: t('ui.edit') %> <%= 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') %>
</div> </div>
<%= link_to 'delete_forever', admin_asset_path(asset), data: { turbo_confirm: t(:'ui.are_you_sure'), turbo_method: :delete }, title: t('ui.delete') %> <%= link_to 'delete_forever', admin_asset_path(asset), data: { turbo_confirm: t(:'ui.are_you_sure'), turbo_method: :delete }, title: t('ui.delete') %>
</div> </div>


+ 9
- 1
app/views/admin/assets/_form.html.erb View File

@ -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| %>
<div class="form-section"> <div class="form-section">
<%= render partial: 'material/text_field', locals: { f: form, attr: :title } %> <%= 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
} %>
</div> </div>


+ 2
- 2
app/views/layouts/application.html.erb View File

@ -1,5 +1,5 @@
<!DOCTYPE html lang="<%= I18n.locale %>">
<html>
<!DOCTYPE html>
<html lang="<%= I18n.locale %>">
<head> <head>
<title><%= content_for?(:title) ? yield(:title) : t(:project_name) %></title> <title><%= content_for?(:title) ? yield(:title) : t(:project_name) %></title>


+ 13
- 3
app/views/site/tmpl_index.html.erb View File

@ -13,8 +13,18 @@
<div class="icon__container"> <div class="icon__container">
<%= 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' %>
</div> </div>

+ 15
- 13
app/views/site/tmpl_list.html.erb View File

@ -5,20 +5,22 @@
<div class="cards__container"> <div class="cards__container">
<ul class="card__stack">
<% @node.children.viewable.ordered.each do |node| %>
<div class="card__stack-container">
<ul class="card__stack">
<% @node.children.viewable.ordered.each do |node| %>
<li>
<%= link_to node.url do %>
<div class="card">
<%= tag.div node.tags[0] %>
<%= svg "ico-arrow-updown" %>
<%= tag.div node.tags[1] %>
</div>
<% end %>
</li>
<% end %>
</ul>
<li>
<%= link_to node.url do %>
<div class="card" data-id="<%= node.id %>">
<%= tag.div node.tags[0] %>
<%= svg "ico-arrow-updown" %>
<%= tag.div node.tags[1] %>
</div>
<% end %>
</li>
<% end %>
</ul>
</div>
<ul class="cards__ctas"> <ul class="cards__ctas">
<li> <li>


+ 1
- 0
config/locales/en.yml View File

@ -358,6 +358,7 @@ en:
title: File Name title: File Name
created_at: Created created_at: Created
updated_at: Last Updated updated_at: Last Updated
tags: Tags
user: user:
enabled_at: Active enabled_at: Active


+ 6
- 0
db/migrate/20240509121618_add_tags_to_assets.rb View File

@ -0,0 +1,6 @@
class AddTagsToAssets < ActiveRecord::Migration[7.1]
def change
add_column :assets, :tags, :text, array: true, default: []
add_index :assets, :tags, using: :gin
end
end

+ 3
- 1
db/schema.rb View File

@ -10,7 +10,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[7.1].define(version: 2024_04_24_093536) do
ActiveRecord::Schema[7.1].define(version: 2024_05_09_121618) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
@ -50,6 +50,8 @@ ActiveRecord::Schema[7.1].define(version: 2024_04_24_093536) do
t.text "title" t.text "title"
t.datetime "created_at", null: false t.datetime "created_at", null: false
t.datetime "updated_at", null: false t.datetime "updated_at", null: false
t.text "tags", default: [], array: true
t.index ["tags"], name: "index_assets_on_tags", using: :gin
end end
create_table "attachments", force: :cascade do |t| create_table "attachments", force: :cascade do |t|


Loading…
Cancel
Save