From 242c808add74d8c3dcc0c522484495acd3eeb3b0 Mon Sep 17 00:00:00 2001 From: Zsolt Tasnadi Date: Sun, 2 Aug 2026 22:54:37 +0200 Subject: [PATCH] web payable and asset view --- apps/api/app/admin/softwares.rb | 3 +- apps/api/app/models/release.rb | 4 +- .../app/services/software_response_builder.rb | 7 +- ...00003_remove_web_playable_from_releases.rb | 5 + apps/api/db/schema.rb | 3 +- apps/frontend/src/i18n/locales/en.ts | 9 +- apps/frontend/src/i18n/locales/hu.ts | 9 +- .../src/page/catalog/CatalogShowPage.vue | 125 +++++++++--------- 8 files changed, 86 insertions(+), 79 deletions(-) create mode 100644 apps/api/db/migrate/20260802000003_remove_web_playable_from_releases.rb diff --git a/apps/api/app/admin/softwares.rb b/apps/api/app/admin/softwares.rb index e80a657..94ec875 100644 --- a/apps/api/app/admin/softwares.rb +++ b/apps/api/app/admin/softwares.rb @@ -3,7 +3,7 @@ ActiveAdmin.register Software do :license, :platform, :status, :highlighted, software_images_attributes: [ :id, :image_id, :is_default, :position, :file_upload, :_destroy ], external_links_attributes: [ :id, :label, :url, :_destroy ], - releases_attributes: [ :id, :version, :web_playable, :_destroy, + releases_attributes: [ :id, :version, :_destroy, { release_assets_attributes: [ :id, :kind, :path, :_destroy ] } ] menu priority: 2, label: "🎮 Softwares" @@ -127,7 +127,6 @@ ActiveAdmin.register Software do f.inputs "Releases" do f.has_many :releases, allow_destroy: true, new_record: true do |r| r.input :version - r.input :web_playable r.has_many :release_assets, allow_destroy: true, new_record: true do |ra| ra.input :kind, as: :select, collection: ReleaseAsset::KINDS, include_blank: false ra.input :path diff --git a/apps/api/app/models/release.rb b/apps/api/app/models/release.rb index 8eeac44..5ec553f 100644 --- a/apps/api/app/models/release.rb +++ b/apps/api/app/models/release.rb @@ -18,13 +18,13 @@ class Release < ApplicationRecord return unless software&.platform == "c64" has_html = release_assets.reject(&:marked_for_destruction?).any? { |a| a.kind == "html" } - if has_html || web_playable? + if has_html errors.add(:base, "C64 releases cannot be web playable (download only)") end end def self.ransackable_attributes(auth_object = nil) - %w[created_at deleted_at id software_id updated_at version web_playable] + %w[created_at deleted_at id software_id updated_at version] end def self.ransackable_associations(auth_object = nil) diff --git a/apps/api/app/services/software_response_builder.rb b/apps/api/app/services/software_response_builder.rb index 7c673de..d8da64d 100644 --- a/apps/api/app/services/software_response_builder.rb +++ b/apps/api/app/services/software_response_builder.rb @@ -2,9 +2,10 @@ module SoftwareResponseBuilder private def build_response(software, releases) - sorted = releases.sort_by { |r| r.created_at || Time.at(0) }.reverse - latest = sorted.reject { |r| r.version.to_s.start_with?("dev-") }.first - web_playable = sorted.find { |r| r.release_assets.any? { |a| a.kind == "html" } } + sorted = releases.sort_by { |r| r.created_at || Time.at(0) }.reverse + latest = sorted.reject { |r| r.version.to_s.start_with?("dev-") }.first + # web-playable, ha az utolsó (stabil) release-nek van webes assetje + web_playable = latest if latest&.release_assets&.any? { |a| a.kind == "html" } total_downloads = releases.sum { |r| r.association(:downloads).loaded? ? r.downloads.size : 0 } SoftwareDetailSerializer.render_as_hash(software, diff --git a/apps/api/db/migrate/20260802000003_remove_web_playable_from_releases.rb b/apps/api/db/migrate/20260802000003_remove_web_playable_from_releases.rb new file mode 100644 index 0000000..acebf35 --- /dev/null +++ b/apps/api/db/migrate/20260802000003_remove_web_playable_from_releases.rb @@ -0,0 +1,5 @@ +class RemoveWebPlayableFromReleases < ActiveRecord::Migration[8.1] + def change + remove_column :releases, :web_playable, :boolean, default: false + end +end diff --git a/apps/api/db/schema.rb b/apps/api/db/schema.rb index 88353f7..01bc6e1 100644 --- a/apps/api/db/schema.rb +++ b/apps/api/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[8.1].define(version: 2026_08_02_000002) do +ActiveRecord::Schema[8.1].define(version: 2026_08_02_000003) do create_table "admin_users", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.datetime "created_at", null: false t.string "email", default: "", null: false @@ -188,7 +188,6 @@ ActiveRecord::Schema[8.1].define(version: 2026_08_02_000002) do t.bigint "software_id", unsigned: true t.datetime "updated_at", precision: 3 t.string "version", limit: 64 - t.boolean "web_playable", default: false t.index ["deleted_at"], name: "idx_releases_deleted_at" t.index ["software_id"], name: "idx_releases_software_id" end diff --git a/apps/frontend/src/i18n/locales/en.ts b/apps/frontend/src/i18n/locales/en.ts index d68ec08..2f8a7e2 100644 --- a/apps/frontend/src/i18n/locales/en.ts +++ b/apps/frontend/src/i18n/locales/en.ts @@ -146,8 +146,13 @@ export default { play: '▶ Play', downloads: 'downloads', links: 'Links', - binariesTitle: 'Download for desktop', - binary: { + assetsTitle: 'Downloads', + assetDownload: 'Download', + assetOpen: 'Open', + assetKind: { + cartridge: 'Game file', + source: 'Source code', + docs: 'Documentation', win_x86: 'Windows (32-bit)', win_x64: 'Windows (64-bit)', linux_x86: 'Linux (32-bit)', diff --git a/apps/frontend/src/i18n/locales/hu.ts b/apps/frontend/src/i18n/locales/hu.ts index 0e88e6b..6356e3e 100644 --- a/apps/frontend/src/i18n/locales/hu.ts +++ b/apps/frontend/src/i18n/locales/hu.ts @@ -146,8 +146,13 @@ export default { play: '▶ Játék', downloads: 'letöltés', links: 'Linkek', - binariesTitle: 'Letöltés asztali gépre', - binary: { + assetsTitle: 'Letöltések', + assetDownload: 'Letöltés', + assetOpen: 'Megnyitás', + assetKind: { + cartridge: 'Játékfájl', + source: 'Forráskód', + docs: 'Dokumentáció', win_x86: 'Windows (32 bit)', win_x64: 'Windows (64 bit)', linux_x86: 'Linux (32 bit)', diff --git a/apps/frontend/src/page/catalog/CatalogShowPage.vue b/apps/frontend/src/page/catalog/CatalogShowPage.vue index 34fa7d7..fc622fd 100644 --- a/apps/frontend/src/page/catalog/CatalogShowPage.vue +++ b/apps/frontend/src/page/catalog/CatalogShowPage.vue @@ -92,27 +92,22 @@

{{ t('catalogShow.released') }} {{ formatDateTime(latestStable.UpdatedAt) }}

-
- {{ t('catalogShow.playNow') }} - {{ t('catalogShow.download') }} - {{ t('catalogShow.source') }} - {{ t('catalogShow.docs') }} -
+ {{ t('catalogShow.playNow') }} -
-
{{ t('catalogShow.binariesTitle') }}
- +
+
{{ t('catalogShow.assetsTitle') }}
+ + + + + + + +
{{ t('catalogShow.assetKind.' + asset.kind) }} + {{ t('catalogShow.assetOpen') }} + {{ t('catalogShow.assetDownload') }} +
@@ -137,13 +132,18 @@ {{ release.version }} ↓ {{ release.downloadCount }} -
- {{ t('catalogShow.play') }} - {{ t('catalogShow.download') }} - {{ t('catalogShow.source') }} - {{ t('catalogShow.docs') }} - 💾 {{ t('catalogShow.binary.' + asset.kind) }} -
+ {{ t('catalogShow.play') }} + + + + + + + +
{{ t('catalogShow.assetKind.' + asset.kind) }} + {{ t('catalogShow.assetOpen') }} + {{ t('catalogShow.assetDownload') }} +
{{ formatDateTime(release.UpdatedAt) }} @@ -155,11 +155,18 @@
{{ release.version }}
-
- {{ t('catalogShow.play') }} - {{ t('catalogShow.download') }} - 💾 {{ t('catalogShow.binary.' + asset.kind) }} -
+ {{ t('catalogShow.play') }} + + + + + + + +
{{ t('catalogShow.assetKind.' + asset.kind) }} + {{ t('catalogShow.assetOpen') }} + {{ t('catalogShow.assetDownload') }} +
{{ formatDateTime(release.UpdatedAt) }} @@ -181,13 +188,18 @@ Dev -
- {{ t('catalogShow.play') }} - {{ t('catalogShow.download') }} - {{ t('catalogShow.source') }} - {{ t('catalogShow.docs') }} - 💾 {{ t('catalogShow.binary.' + asset.kind) }} -
+ {{ t('catalogShow.play') }} + + + + + + + +
{{ t('catalogShow.assetKind.' + asset.kind) }} + {{ t('catalogShow.assetOpen') }} + {{ t('catalogShow.assetDownload') }} +
@@ -265,36 +277,17 @@ const latestStable = computed(() => stableReleases.value[0] ?? null) const downloadUrl = (path: string) => `/api/download?path=${encodeURIComponent(path.replace('/file/', ''))}` -const BINARY_KINDS = ['win_x64', 'win_x86', 'linux_x64', 'linux_x86', 'mac_universal', 'mac_arm64', 'mac_x64'] +// az asset-táblázat sorai és sorrendjük; a html nem letöltés, azt a Play gomb viszi +const TABLE_KIND_ORDER = [ + 'cartridge', 'source', 'docs', + 'win_x64', 'win_x86', 'linux_x64', 'linux_x86', + 'mac_universal', 'mac_arm64', 'mac_x64', +] -// a látogató OS-éhez illő targetek, preferencia-sorrendben -function osPreferredKinds(): string[] { - const ua = navigator.userAgent - if (/Windows/i.test(ua)) { - return /WOW64|Win64|x64|amd64/i.test(ua) ? ['win_x64', 'win_x86'] : ['win_x86', 'win_x64'] - } - if (/Macintosh|Mac OS X/i.test(ua)) return ['mac_universal', 'mac_arm64', 'mac_x64'] - if (/Linux/i.test(ua)) return ['linux_x64', 'linux_x86'] - return [] -} - -const preferredKinds = osPreferredKinds() - -const kindRank = (kind: string) => { - const preferred = preferredKinds.indexOf(kind) - return preferred >= 0 ? preferred : preferredKinds.length + BINARY_KINDS.indexOf(kind) -} - -const binaryAssets = (release: Release) => +const tableAssets = (release: Release) => (release.assets ?? []) - .filter(a => BINARY_KINDS.includes(a.kind)) - .sort((a, b) => kindRank(a.kind) - kindRank(b.kind)) - -// a kiemelt gomb: a látogató OS-éhez legjobban illő elérhető bináris -const primaryBinaryKind = (release: Release) => { - const first = binaryAssets(release)[0] - return first && preferredKinds.includes(first.kind) ? first.kind : null -} + .filter(a => TABLE_KIND_ORDER.includes(a.kind)) + .sort((a, b) => TABLE_KIND_ORDER.indexOf(a.kind) - TABLE_KIND_ORDER.indexOf(b.kind)) onMounted(async () => { try {