class AppAnnouncement < ApplicationRecord KINDS = %w[info maintenance update].freeze SEVERITIES = %w[info warning critical].freeze STATUSES = %w[draft published archived].freeze CHANNELS = { android: :show_on_android, ios: :show_on_ios, web_public: :show_on_web_public, web_private: :show_on_web_private }.freeze URL_FIELDS = %i[action_url android_action_url ios_action_url].freeze belongs_to :created_by_admin, class_name: "AdminAccount", optional: true validates :kind, :severity, :status, :title, :body, presence: true validates :kind, inclusion: { in: KINDS } validates :severity, inclusion: { in: SEVERITIES } validates :status, inclusion: { in: STATUSES } validates :title, :android_title, :ios_title, length: { maximum: 120 }, allow_blank: true validates :body, :android_body, :ios_body, length: { maximum: 2000 }, allow_blank: true validates :action_label, :android_action_label, :ios_action_label, length: { maximum: 40 }, allow_blank: true URL_FIELDS.each do |field| validates field, format: { with: /\Ahttps?:\/\/.+/i, allow_blank: true } end validate :ends_at_after_starts_at validate :at_least_one_channel scope :newest, -> { order(created_at: :desc) } scope :published, -> { where(status: "published") } scope :active_now, lambda { now = Time.current published .where("starts_at IS NULL OR starts_at <= ?", now) .where("ends_at IS NULL OR ends_at > ?", now) } def self.for_channel(channel) column = CHANNELS[channel.to_sym] raise ArgumentError, "unknown announcement channel: #{channel}" if column.blank? active_now.where(column => true) end def self.for_app active_now.where("show_on_android = TRUE OR show_on_ios = TRUE") end def self.for_platform(platform) case platform.to_s when "android" then for_channel(:android) when "ios" then for_channel(:ios) else for_app end end def published? status == "published" end def selected_channels CHANNELS.each_with_object([]) do |(key, column), keys| keys << key if public_send(column) end end def as_api_json(platform: nil) { id: id, kind: kind, severity: severity, title: copy_for(platform, :title), body: copy_for(platform, :body), dismissible: dismissible, action_url: resolved_action_url(platform), action_label: copy_for(platform, :action_label).presence, starts_at: starts_at&.iso8601, ends_at: ends_at&.iso8601 } end def resolved_action_url(platform) override = platform_override(platform, :action_url) return override if override.present? return action_url if action_url.present? return nil unless kind == "update" case platform.to_s when "android" then MatchLiveTv.play_store_url when "ios" then MatchLiveTv.app_store_url end end private def copy_for(platform, field) override = platform_override(platform, field) override.presence || public_send(field) end def platform_override(platform, field) prefix = case platform.to_s when "android" then :android when "ios" then :ios end return if prefix.blank? public_send("#{prefix}_#{field}") end def at_least_one_channel return if show_on_android? || show_on_ios? || show_on_web_public? || show_on_web_private? errors.add(:base, :no_channel) end def ends_at_after_starts_at return if starts_at.blank? || ends_at.blank? return if ends_at > starts_at errors.add(:ends_at, :invalid) end end