class AppAnnouncement < ApplicationRecord KINDS = %w[info maintenance update].freeze SEVERITIES = %w[info warning critical].freeze STATUSES = %w[draft published archived].freeze CHANNELS = { app: :show_on_app, web_public: :show_on_web_public, web_private: :show_on_web_private }.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, length: { maximum: 120 } validates :body, length: { maximum: 2000 } validates :action_label, length: { maximum: 40 }, allow_blank: true validates :action_url, format: { with: /\Ahttps?:\/\/.+/i, allow_blank: true } 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 for_channel(:app) 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: title, body: body, dismissible: dismissible, action_url: resolved_action_url(platform), action_label: action_label.presence, starts_at: starts_at&.iso8601, ends_at: ends_at&.iso8601 } end def resolved_action_url(platform) 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 at_least_one_channel return if show_on_app? || 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