diff --git a/Gemfile.lock b/Gemfile.lock index db50a3b..e0e4daf 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -3,29 +3,29 @@ GEM specs: action_text-trix (2.1.17) railties - actioncable (8.1.2) - actionpack (= 8.1.2) - activesupport (= 8.1.2) + actioncable (8.1.2.1) + actionpack (= 8.1.2.1) + activesupport (= 8.1.2.1) nio4r (~> 2.0) websocket-driver (>= 0.6.1) zeitwerk (~> 2.6) - actionmailbox (8.1.2) - actionpack (= 8.1.2) - activejob (= 8.1.2) - activerecord (= 8.1.2) - activestorage (= 8.1.2) - activesupport (= 8.1.2) + actionmailbox (8.1.2.1) + actionpack (= 8.1.2.1) + activejob (= 8.1.2.1) + activerecord (= 8.1.2.1) + activestorage (= 8.1.2.1) + activesupport (= 8.1.2.1) mail (>= 2.8.0) - actionmailer (8.1.2) - actionpack (= 8.1.2) - actionview (= 8.1.2) - activejob (= 8.1.2) - activesupport (= 8.1.2) + actionmailer (8.1.2.1) + actionpack (= 8.1.2.1) + actionview (= 8.1.2.1) + activejob (= 8.1.2.1) + activesupport (= 8.1.2.1) mail (>= 2.8.0) rails-dom-testing (~> 2.2) - actionpack (8.1.2) - actionview (= 8.1.2) - activesupport (= 8.1.2) + actionpack (8.1.2.1) + actionview (= 8.1.2.1) + activesupport (= 8.1.2.1) nokogiri (>= 1.8.5) rack (>= 2.2.4) rack-session (>= 1.0.1) @@ -33,36 +33,36 @@ GEM rails-dom-testing (~> 2.2) rails-html-sanitizer (~> 1.6) useragent (~> 0.16) - actiontext (8.1.2) + actiontext (8.1.2.1) action_text-trix (~> 2.1.15) - actionpack (= 8.1.2) - activerecord (= 8.1.2) - activestorage (= 8.1.2) - activesupport (= 8.1.2) + actionpack (= 8.1.2.1) + activerecord (= 8.1.2.1) + activestorage (= 8.1.2.1) + activesupport (= 8.1.2.1) globalid (>= 0.6.0) nokogiri (>= 1.8.5) - actionview (8.1.2) - activesupport (= 8.1.2) + actionview (8.1.2.1) + activesupport (= 8.1.2.1) builder (~> 3.1) erubi (~> 1.11) rails-dom-testing (~> 2.2) rails-html-sanitizer (~> 1.6) - activejob (8.1.2) - activesupport (= 8.1.2) + activejob (8.1.2.1) + activesupport (= 8.1.2.1) globalid (>= 0.3.6) - activemodel (8.1.2) - activesupport (= 8.1.2) - activerecord (8.1.2) - activemodel (= 8.1.2) - activesupport (= 8.1.2) + activemodel (8.1.2.1) + activesupport (= 8.1.2.1) + activerecord (8.1.2.1) + activemodel (= 8.1.2.1) + activesupport (= 8.1.2.1) timeout (>= 0.4.0) - activestorage (8.1.2) - actionpack (= 8.1.2) - activejob (= 8.1.2) - activerecord (= 8.1.2) - activesupport (= 8.1.2) + activestorage (8.1.2.1) + actionpack (= 8.1.2.1) + activejob (= 8.1.2.1) + activerecord (= 8.1.2.1) + activesupport (= 8.1.2.1) marcel (~> 1.0) - activesupport (8.1.2) + activesupport (8.1.2.1) base64 bigdecimal concurrent-ruby (~> 1.0, >= 1.3.1) @@ -138,9 +138,6 @@ GEM actionview (>= 7.0.0) activesupport (>= 7.0.0) json (2.19.2) - json-schema (6.2.0) - addressable (~> 2.8) - bigdecimal (>= 3.1, < 5) kaminari (1.2.2) activesupport (>= 4.1.0) kaminari-actionview (= 1.2.2) @@ -168,8 +165,6 @@ GEM net-pop net-smtp marcel (1.1.0) - mcp (0.9.0) - json-schema (>= 4.1) mini_magick (5.3.1) logger mini_mime (1.1.5) @@ -252,20 +247,20 @@ GEM rack (>= 1.3) rackup (2.3.1) rack (>= 3) - rails (8.1.2) - actioncable (= 8.1.2) - actionmailbox (= 8.1.2) - actionmailer (= 8.1.2) - actionpack (= 8.1.2) - actiontext (= 8.1.2) - actionview (= 8.1.2) - activejob (= 8.1.2) - activemodel (= 8.1.2) - activerecord (= 8.1.2) - activestorage (= 8.1.2) - activesupport (= 8.1.2) + rails (8.1.2.1) + actioncable (= 8.1.2.1) + actionmailbox (= 8.1.2.1) + actionmailer (= 8.1.2.1) + actionpack (= 8.1.2.1) + actiontext (= 8.1.2.1) + actionview (= 8.1.2.1) + activejob (= 8.1.2.1) + activemodel (= 8.1.2.1) + activerecord (= 8.1.2.1) + activestorage (= 8.1.2.1) + activesupport (= 8.1.2.1) bundler (>= 1.15.0) - railties (= 8.1.2) + railties (= 8.1.2.1) rails-dom-testing (2.3.0) activesupport (>= 5.0.0) minitest @@ -273,9 +268,9 @@ GEM rails-html-sanitizer (1.7.0) loofah (~> 2.25) nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0) - railties (8.1.2) - actionpack (= 8.1.2) - activesupport (= 8.1.2) + railties (8.1.2.1) + actionpack (= 8.1.2.1) + activesupport (= 8.1.2.1) irb (~> 1.13) rackup (>= 1.0.0) rake (>= 12.2) @@ -297,11 +292,10 @@ GEM io-console (~> 0.5) request_store (1.7.0) rack (>= 1.4) - rubocop (1.85.1) + rubocop (1.86.0) json (~> 2.3) language_server-protocol (~> 3.17.0.2) lint_roller (~> 1.1.0) - mcp (~> 0.6) parallel (~> 1.10) parser (>= 3.3.0.2) rainbow (>= 2.2.2, < 4.0) @@ -411,17 +405,17 @@ DEPENDENCIES CHECKSUMS action_text-trix (2.1.17) sha256=b44691639d77e67169dc054ceacd1edc04d44dc3e4c6a427aa155a2beb4cc951 - actioncable (8.1.2) sha256=dc31efc34cca9cdefc5c691ddb8b4b214c0ea5cd1372108cbc1377767fb91969 - actionmailbox (8.1.2) sha256=058b2fb1980e5d5a894f675475fcfa45c62631103d5a2596d9610ec81581889b - actionmailer (8.1.2) sha256=f4c1d2060f653bfe908aa7fdc5a61c0e5279670de992146582f2e36f8b9175e9 - actionpack (8.1.2) sha256=ced74147a1f0daafaa4bab7f677513fd4d3add574c7839958f7b4f1de44f8423 - actiontext (8.1.2) sha256=0bf57da22a9c19d970779c3ce24a56be31b51c7640f2763ec64aa72e358d2d2d - actionview (8.1.2) sha256=80455b2588911c9b72cec22d240edacb7c150e800ef2234821269b2b2c3e2e5b - activejob (8.1.2) sha256=908dab3713b101859536375819f4156b07bdf4c232cc645e7538adb9e302f825 - activemodel (8.1.2) sha256=e21358c11ce68aed3f9838b7e464977bc007b4446c6e4059781e1d5c03bcf33e - activerecord (8.1.2) sha256=acfbe0cadfcc50fa208011fe6f4eb01cae682ebae0ef57145ba45380c74bcc44 - activestorage (8.1.2) sha256=8a63a48c3999caeee26a59441f813f94681fc35cc41aba7ce1f836add04fba76 - activesupport (8.1.2) sha256=88842578ccd0d40f658289b0e8c842acfe9af751afee2e0744a7873f50b6fdae + actioncable (8.1.2.1) sha256=a2f88cecce148b3fcb63d2e517d7694e119830a85baa7d6cf59e5453dcf32e8d + actionmailbox (8.1.2.1) sha256=c2e45c0c1e5687e35e050838c94a8aed0d954c56a32ea411d54cd848c338c54e + actionmailer (8.1.2.1) sha256=d7d62fbc2197f1a7006bb5af4c665edf999adf79ab6c10337c088d27e6622071 + actionpack (8.1.2.1) sha256=a6b69cd10ec4c8d978c8eee51206e34152b1c1be017e534236dbc89a3d00ffb8 + actiontext (8.1.2.1) sha256=1e503ce600a6ab2e12a46f999959a7d8e2fdaff910ca01dcf3b968934b55d957 + actionview (8.1.2.1) sha256=38daa7b87bca427e2967f139e5b7f0d1081271bdafd0e015d8ef97a006f570a6 + activejob (8.1.2.1) sha256=c89c311d07fd358b76c581ed8fee87c5b4351fb44994f3389385c014d22182fe + activemodel (8.1.2.1) sha256=8f31a6f9c12fecb8e5a0fce8a8950cfd94f0d75829322935f99e8217a3e5f3c6 + activerecord (8.1.2.1) sha256=3f79140318ff6d23376f5d9b1b5b5e2c7d3cc8979dd71367e9a8394378ca630a + activestorage (8.1.2.1) sha256=36794c9b8853ac9276b0386cb1f8973374d8e71e8a9666bb02e70f5b7c9c5391 + activesupport (8.1.2.1) sha256=beec20ced12ad569194554399449a6372fdab03061b8f48a9ed6ef9b7dc251b2 acts_as_list (1.2.6) sha256=8345380900b7bee620c07ad00991ccee59af3d8c9e8574f426e321da2865fdc8 addressable (2.8.9) sha256=cc154fcbe689711808a43601dee7b980238ce54368d23e127421753e46895485 ancestry (5.1.0) sha256=8a073cf6f7e306eeed36af72595abd19602ef4a197bf4beda2f31cf8f55de27b @@ -461,7 +455,6 @@ CHECKSUMS irb (1.17.0) sha256=168c4ddb93d8a361a045c41d92b2952c7a118fa73f23fe14e55609eb7a863aae jbuilder (2.14.1) sha256=4eb26376ff60ef100cb4fd6fd7533cd271f9998327e86adf20fd8c0e69fabb42 json (2.19.2) sha256=e7e1bd318b2c37c4ceee2444841c86539bc462e81f40d134cf97826cb14e83cf - json-schema (6.2.0) sha256=e8bff46ed845a22c1ab2bd0d7eccf831c01fe23bb3920caa4c74db4306813666 kaminari (1.2.2) sha256=c4076ff9adccc6109408333f87b5c4abbda5e39dc464bd4c66d06d9f73442a3e kaminari-actionview (1.2.2) sha256=1330f6fc8b59a4a4ef6a549ff8a224797289ebf7a3a503e8c1652535287cc909 kaminari-activerecord (1.2.2) sha256=0dd3a67bab356a356f36b3b7236bcb81cef313095365befe8e98057dd2472430 @@ -473,7 +466,6 @@ CHECKSUMS loofah (2.25.1) sha256=d436c73dbd0c1147b16c4a41db097942d217303e1f7728704b37e4df9f6d2e04 mail (2.9.0) sha256=6fa6673ecd71c60c2d996260f9ee3dd387d4673b8169b502134659ece6d34941 marcel (1.1.0) sha256=fdcfcfa33cc52e93c4308d40e4090a5d4ea279e160a7f6af988260fa970e0bee - mcp (0.9.0) sha256=a0a3737b0ac9df0772f4ef7e2b013c260ddbcf217a5d50a66bff0baeddf03e47 mini_magick (5.3.1) sha256=29395dfd76badcabb6403ee5aff6f681e867074f8f28ce08d78661e9e4a351c4 mini_mime (1.1.5) sha256=8681b7e2e4215f2a159f9400b5816d85e9d8c6c6b491e96a12797e798f8bccef minitest (6.0.2) sha256=db6e57956f6ecc6134683b4c87467d6dd792323c7f0eea7b93f66bd284adbc3d @@ -517,10 +509,10 @@ CHECKSUMS rack-session (2.1.1) sha256=0b6dc07dea7e4b583f58a48e8b806d4c9f1c6c9214ebc202ec94562cbea2e4e9 rack-test (2.2.0) sha256=005a36692c306ac0b4a9350355ee080fd09ddef1148a5f8b2ac636c720f5c463 rackup (2.3.1) sha256=6c79c26753778e90983761d677a48937ee3192b3ffef6bc963c0950f94688868 - rails (8.1.2) sha256=5069061b23dfa8706b9f0159ae8b9d35727359103178a26962b868a680ba7d95 + rails (8.1.2.1) sha256=93ebf1efc792c9bc47e9795259c920312d3920008dad3ae634b7a0457ffe0af8 rails-dom-testing (2.3.0) sha256=8acc7953a7b911ca44588bf08737bc16719f431a1cc3091a292bca7317925c1d rails-html-sanitizer (1.7.0) sha256=28b145cceaf9cc214a9874feaa183c3acba036c9592b19886e0e45efc62b1e89 - railties (8.1.2) sha256=1289ece76b4f7668fc46d07e55cc992b5b8751f2ad85548b7da351b8c59f8055 + railties (8.1.2.1) sha256=f4d902869541af4e5b5552d726062fa59ec0fd9078f7ab87720dbd93f22c43ee rainbow (3.1.1) sha256=039491aa3a89f42efa1d6dec2fc4e62ede96eb6acd95e52f1ad581182b79bc6a rake (13.3.1) sha256=8c9e89d09f66a26a01264e7e3480ec0607f0c497a861ef16063604b1b08eb19c rdoc (7.2.0) sha256=8650f76cd4009c3b54955eb5d7e3a075c60a57276766ebf36f9085e8c9f23192 @@ -529,7 +521,7 @@ CHECKSUMS regexp_parser (2.11.3) sha256=ca13f381a173b7a93450e53459075c9b76a10433caadcb2f1180f2c741fc55a4 reline (0.6.3) sha256=1198b04973565b36ec0f11542ab3f5cfeeec34823f4e54cebde90968092b1835 request_store (1.7.0) sha256=e1b75d5346a315f452242a68c937ef8e48b215b9453a77a6c0acdca2934c88cb - rubocop (1.85.1) sha256=3dbcf9e961baa4c376eeeb2a03913dca5e3987033b04d38fa538aa1e7406cc77 + rubocop (1.86.0) sha256=4ff1186fe16ebe9baff5e7aad66bb0ad4cabf5cdcd419f773146dbba2565d186 rubocop-ast (1.49.1) sha256=4412f3ee70f6fe4546cc489548e0f6fcf76cafcfa80fa03af67098ffed755035 rubocop-performance (1.26.1) sha256=cd19b936ff196df85829d264b522fd4f98b6c89ad271fa52744a8c11b8f71834 rubocop-rails (2.34.3) sha256=10d37989024865ecda8199f311f3faca990143fbac967de943f88aca11eb9ad2 diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css index 0049354..452c93c 100644 --- a/app/assets/stylesheets/application.css +++ b/app/assets/stylesheets/application.css @@ -100,8 +100,7 @@ bottom: 0; left: 0; font: 0.75rem ui-monospace; - padding: 1em; - max-width: 40ch; + padding: 1em; } .turbo-progress-bar { diff --git a/app/controllers/admin/nodes_controller.rb b/app/controllers/admin/nodes_controller.rb index b3b6234..bac66fa 100644 --- a/app/controllers/admin/nodes_controller.rb +++ b/app/controllers/admin/nodes_controller.rb @@ -33,7 +33,7 @@ class Admin::NodesController < Admin::AdminController # POST /nodes or /nodes.json def create @node = Node.new(node_params) - @node.status = :status_draft + @node.status = :status_published base_title = t("ui.untitled") title = base_title diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 983eb41..aff3ca0 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,9 +1,6 @@ class ApplicationController < ActionController::Base - - http_basic_authenticate_with name: "stupid", password: "studio" if Rails.env.production? - private @@ -13,8 +10,6 @@ private def not_found - raise ActionController::RoutingError.new('Not Found') + raise ActionController::RoutingError.new("Not Found") end - - end diff --git a/app/controllers/concerns/quiz_helper_methods.rb b/app/controllers/concerns/quiz_helper_methods.rb index dbf51ec..adf70aa 100644 --- a/app/controllers/concerns/quiz_helper_methods.rb +++ b/app/controllers/concerns/quiz_helper_methods.rb @@ -14,7 +14,7 @@ private def require_player! unless player_present? - redirect_to url_for(controller: "languages", action: "index") + redirect_to url_for(controller: "languages", action: "index") and return end end diff --git a/app/controllers/players_controller.rb b/app/controllers/players_controller.rb index 2a8526c..3f6cc6f 100644 --- a/app/controllers/players_controller.rb +++ b/app/controllers/players_controller.rb @@ -1,8 +1,9 @@ class PlayersController < ApplicationController include QuizHelperMethods - skip_before_action :require_player! + skip_before_action :require_player!, only: [ :create ] + # POST def create # reset_session @@ -11,4 +12,15 @@ class PlayersController < ApplicationController redirect_to stage_path(id: 1) end + + # POST + def go_again + @player = Player.create( + locale: current_player.locale, + extra_lives_stage: current_player.extra_lives_stage + ) + session[:player_id] = @player.id + + redirect_to stage_path(id: 1) + end end diff --git a/app/controllers/stages_controller.rb b/app/controllers/stages_controller.rb index db01513..dd46b61 100644 --- a/app/controllers/stages_controller.rb +++ b/app/controllers/stages_controller.rb @@ -4,19 +4,29 @@ class StagesController < ApplicationController before_action :set_locale before_action :require_player! - before_action :set_stage + before_action :check_stage_access, expect: [ :game_over ] + before_action :set_stage, expect: [ :game_over ] + before_action :set_outcome, only: [ :reveal, :pick, :result ] + helper_method :stage_index + # GET def show end + # GET + def game_over + @node = Node.at_depth(1).game_over.viewable&.first + end + + # POST def flip outcome = rand < 0.5 ? "chance" : "choice" - current_player.record_flip(params[:id], outcome) + current_player.record_flip(stage_index, outcome) redirect_to url_for(action: "reveal") end @@ -38,13 +48,13 @@ class StagesController < ApplicationController @node = @stage.children.viewable.find_by(template: @outcome) possible_answers = @node.children.viewable - if @outcome == "chance" result = possible_answers.pluck(:template).sample elsif @outcome == "choice" result = possible_answers.find_by(id: params[:option])&.template end - current_player.record_pick_result(params[:id], result) unless result.blank? + current_player.record_pick_result(stage_index, result) unless result.blank? + redirect_to action: "result" end @@ -54,30 +64,90 @@ class StagesController < ApplicationController def result redirect_to root_path and return if @outcome.blank? - @result = current_player.progress[params[:id].to_s]["result"] + @result = current_player.progress[stage_index.to_s]["result"] + @node = @stage.children.viewable.find_by(template: @outcome)&.children&.viewable&.find_by(template: @result) - question_node = @stage.children.viewable.find_by(template: @outcome) - @node = question_node.children.viewable.find_by(template: @result) + current_player.update(is_done: true) if @node.game_over? + redirect_to root_path and return if @node.blank? + end - logger.info(current_player.inspect) + + # POST + def next + if player_can_proceed? + current_player.update(current_stage: stage_index + 1) + redirect_to stage_path(id: current_player.current_stage) + else + redirect_to root_path + end end -private + # POST + def bonus + @result = current_player.progress[stage_index.to_s]["result"] + redirect_to root_path unless @result == "bonus" + # Set extra lives stage + current_player.update( + extra_lives_stage: ((rand < 0.5) ? [ stage_index - 1, 0 ].max : stage_index), + is_done: true + ) - def set_stage - @stage = Node.at_depth(1).viewable.ordered[params[:id].to_i - 1] + # Reset progress + # current_player.reset_when_game_over - if @stage.nil? - redirect_to root_path + redirect_to action: "game_over" + end + + + # POST + def get_extra_life + current_player.update( + extra_lives_stage: [ stage_index - 1, 0 ].max, + is_done: true + ) + + redirect_to action: "game_over" + end + + + +private + + + def check_stage_access + if stage_index > current_player.current_stage + redirect_to root_path and return end end + + def player_can_proceed? + result = current_player.progress[stage_index.to_s]&.dig("result") + + return false if stage_index != current_player.current_stage + + return true if %w[good best].include?(result) + return true if result == "bad" and current_player.extra_lives_stage >= stage_index + + false + end + + + def set_stage + @stage = Node.at_depth(1).stage.viewable.ordered[stage_index - 1] + redirect_to root_path and return if @stage.nil? + end + + def set_outcome - @outcome ||= current_player.progress[params[:id].to_s]["flip"] + @outcome ||= current_player.progress[stage_index.to_s]&.dig("flip") end + def stage_index + @stage_index ||= params[:id].to_i + end end diff --git a/app/javascript/image_controller.js b/app/javascript/image_controller.js deleted file mode 100644 index ec9d568..0000000 --- a/app/javascript/image_controller.js +++ /dev/null @@ -1,23 +0,0 @@ -import { Controller } from "@hotwired/stimulus" - -export default class extends Controller { - //static targets = ["img"] - - - connect() { - - if (this.element.complete && this.element.naturalHeight !== 0) { - this.element.classList.add('loaded') - } else { - this.element.addEventListener('load', function() { - this.classList.add('loaded') - }) - } - - } - - disconnect() { - } - - -} \ No newline at end of file diff --git a/app/javascript/quiz_preloader.js b/app/javascript/quiz_preloader.js deleted file mode 100644 index e7c267c..0000000 --- a/app/javascript/quiz_preloader.js +++ /dev/null @@ -1,117 +0,0 @@ -export default class QuizImagePreloader { - constructor() { - this.isPreloading = false - this.preloadedImages = new Map() - } - - init() { - // Start preloading on initial page load - document.addEventListener('DOMContentLoaded', () => { - sessionStorage.removeItem('quiz_images_preloaded') - // this.startPreloadingFromHeader() - // console.info('startPreloadingFromHeader'); - }) - - // Continue preloading on Turbo navigation - document.addEventListener('turbo:load', () => { - this.startPreloadingFromHeader() - console.info('startPreloadingFromHeader'); - }) - - - } - - startPreloadingFromHeader() { - - if (sessionStorage.getItem('quiz_images_preloaded') === 'true') { - return - } - - // Skip if already preloading - if (this.isPreloading) { - return - } - - const imageUrls = this.getImageUrlsFromHeader() - - if (imageUrls.length > 0) { - this.preloadImages(imageUrls) - } - } - - getImageUrlsFromHeader() { - const headerElement = document.querySelector('header[data-quiz-images]') || - document.querySelector('[data-quiz-images]') - - if (!headerElement) return [] - - - try { - const urls = JSON.parse(headerElement.dataset.quizImages || '[]') - return urls.filter(url => url && url.trim()) - } catch (e) { - console.warn('Failed to parse quiz image URLs from header:', e) - return [] - } - } - - preloadImages(imageUrls) { - this.isPreloading = true - let loadedCount = 0 - const totalImages = imageUrls.length - - imageUrls.forEach((url, index) => { - // Skip if already preloaded - if (this.preloadedImages.has(url)) { - loadedCount++ - if (loadedCount === totalImages) { - this.onPreloadComplete() - } - return - } - - const img = new Image() - - img.onload = () => { - this.preloadedImages.set(url, img) - loadedCount++ - - if (loadedCount === totalImages) { - this.onPreloadComplete() - } - } - - img.onerror = () => { - console.warn(`Failed to preload image: ${url}`) - loadedCount++ - - if (loadedCount === totalImages) { - this.onPreloadComplete() - } - } - - // Stagger requests to avoid overwhelming server - setTimeout(() => { - img.src = url - }, index * 30) - }) - } - - onPreloadComplete() { - this.isPreloading = false - sessionStorage.setItem('quiz_images_preloaded', 'true') - - console.info('PreloadComplete'); - // Dispatch completion event for components that need to know - document.dispatchEvent(new CustomEvent('quiz:preload:complete', { - detail: { totalImages: this.preloadedImages.size } - })) - } - - // Check if preloading is complete - isComplete() { - return sessionStorage.getItem('quiz_images_preloaded') === 'true' - } - - -} \ No newline at end of file diff --git a/app/models/node.rb b/app/models/node.rb index e4cee29..e6a8d3a 100644 --- a/app/models/node.rb +++ b/app/models/node.rb @@ -42,12 +42,12 @@ class Node < ApplicationRecord case depth when 1 - [ :stage ] + [ :stage, :game_over ] when 2 - [ :choice, :chance, :bonus ] + [ :choice, :chance ] when 3 if parent&.chance? - [ :good, :bad, :game_over ] + [ :good, :bad, :game_over, :bonus ] elsif parent&.choice? [ :best, :good, :bad ] else @@ -66,6 +66,7 @@ class Node < ApplicationRecord } before_validation :remove_empty_tags + before_validation :normalize_translations validates_presence_of :title @@ -161,4 +162,14 @@ private self.send "#{k}=", self.send(k).reject { |v| v.blank? } unless self.send(k).blank? end end + + + def normalize_translations + %w[title slug].each do |attr| + value = read_attribute(attr) + if value.is_a?(Hash) + write_attribute(attr, value.transform_values { |v| v.is_a?(String) ? v.strip : v }) + end + end + end end diff --git a/app/models/player.rb b/app/models/player.rb index ebeaefb..f126d23 100644 --- a/app/models/player.rb +++ b/app/models/player.rb @@ -1,7 +1,23 @@ class Player < ApplicationRecord - attribute :progress, :json, default: {} attribute :current_stage, :integer, default: 1 - attribute :score, :integer, default: 0 + attribute :extra_lives_stage, :integer, default: 0 + attribute :progress, :json, default: {} + attribute :is_done, :boolean, default: false + + scope :playing, -> { where(is_done: false) } + scope :done, -> { where(is_done: true) } + + + def is_playing? + !self.is_done? + end + + + def reset_when_game_over + self.progress = {} + self.current_stage = 1 + self.save + end # stage_index: 1, 2, 3... # outcome: 'chance' or 'choice' @@ -11,13 +27,7 @@ class Player < ApplicationRecord save end - # stage_index: 1, 2, 3... - # option_index: 1, 2, or 3 - def record_option(stage_index, option_index) - self.progress[stage_index.to_s] ||= {} - self.progress[stage_index.to_s]["option"] = option_index - save - end + # stage_index: 1, 2, 3... # result: 'good', 'bad', 'game_over' @@ -27,8 +37,5 @@ class Player < ApplicationRecord save end - def advance_to_next_stage! - self.current_stage += 1 - save - end + end diff --git a/app/models/user.rb b/app/models/user.rb index d244176..10542bb 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1,10 +1,9 @@ class User < ApplicationRecord - - enum :role, { user: 'user', admin: 'admin' }, suffix: true + enum :role, { user: "user", admin: "admin" }, suffix: true include PgSearch::Model - pg_search_scope :pg_search, against: [:lastname, :firstname, :email, :phone, :title], - using: {tsearch: {dictionary: "danish"}} + pg_search_scope :pg_search, against: [ :lastname, :firstname, :email, :phone, :title ], + using: { tsearch: { dictionary: "danish" } } has_secure_password has_many :verification_codes, dependent: :destroy @@ -16,7 +15,7 @@ class User < ApplicationRecord validates_uniqueness_of :email validates_format_of :email, with: URI::MailTo::EMAIL_REGEXP - normalizes :email, with: -> email { email.strip.downcase } + normalizes :email, with: ->(email) { email.strip.downcase } validate :cant_change_admin, on: :update @@ -31,18 +30,18 @@ class User < ApplicationRecord def su? - email == 'mattias@oncotype.dk' + email == "mattias@oncotype.dk" end def name return email if lastname.blank? and firstname.blank? - [firstname, lastname].select{ |v| !v.blank? }.join(' ') + [ firstname, lastname ].select { |v| !v.blank? }.join(" ") end def initials - name.split(' ').map{ |s| s[0] }.join('').mb_chars.upcase + name.split(" ").map { |s| s[0] }.join("").mb_chars.upcase end @@ -50,21 +49,21 @@ class User < ApplicationRecord !self.enabled_at.nil? end + protected - #Prevent the user admins from beeing changed + # Prevent the user admins from beeing changed def cant_change_admin user = self.class.find(self.id) - errors.add(:email, I18n.t(:you_cant_change_the_email_on_this_user, scope: 'users')) if user.su? and self.email != user.email - errors.add(:email, I18n.t(:you_cant_change_this_on_this_user, scope: 'users')) if user.su? and !self.admin_role? and self.role_changed? - errors.add(:email, I18n.t(:you_cant_disable_this_user, scope: 'users')) if user.su? and self.enabled_at.nil? + errors.add(:email, I18n.t(:you_cant_change_the_email_on_this_user, scope: "users")) if user.su? and self.email != user.email + errors.add(:email, I18n.t(:you_cant_change_this_on_this_user, scope: "users")) if user.su? and !self.admin_role? and self.role_changed? + errors.add(:email, I18n.t(:you_cant_disable_this_user, scope: "users")) if user.su? and self.enabled_at.nil? end # Prevents the super user admin to be removed" def dont_destroy_admin - raise I18n.t(:cant_destroy_this_user, scope: 'users') if self.su? + raise I18n.t(:cant_destroy_this_user, scope: "users") if self.su? end - end diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index ba1216c..fe911e6 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -8,7 +8,6 @@ <%= tag.meta name: 'description', content: content_for?(:meta_description) ? yield(:meta_description) : (@node.present?? @node.page_description : '') %> - <%= csrf_meta_tags %> <%= csp_meta_tag %> @@ -16,11 +15,8 @@ - <%= stylesheet_link_tag "reset", "application" %> - - + <%= stylesheet_link_tag "reset", "application" %> -