diff --git a/admin/app/admin/generic_state.rb b/admin/app/admin/generic_state.rb new file mode 100644 index 000000000..02bfedf6b --- /dev/null +++ b/admin/app/admin/generic_state.rb @@ -0,0 +1,18 @@ +ActiveAdmin.register JamRuby::GenericState, :as => 'GenericState' do + menu :parent => 'Operations' + + config.clear_action_items! + filter :env + permit_params :top_message + + actions :all, :except => [:destroy] + + index do + selectable_column + column :env + actions + end + + form :partial => 'form' + +end diff --git a/admin/app/views/admin/generic_states/_form.html.erb b/admin/app/views/admin/generic_states/_form.html.erb new file mode 100644 index 000000000..9778f56dc --- /dev/null +++ b/admin/app/views/admin/generic_states/_form.html.erb @@ -0,0 +1,7 @@ +<%= semantic_form_for([:admin, resource], :url => resource.new_record? ? admin_generic_states_path : "/admin/generic_states/#{resource.id}") do |f| %> + <%= f.semantic_errors *f.object.errors.keys %> + <%= f.inputs do %> + <%= f.input(:top_message, :input_html => {:maxlength => 10000, :rows=>10, :class => 'autogrow'}) %> + <% end %> + <%= f.actions %> +<% end %> \ No newline at end of file diff --git a/db/manifest b/db/manifest index 3eafba777..8ed347f9c 100755 --- a/db/manifest +++ b/db/manifest @@ -393,4 +393,4 @@ store_backend_details_rate_session.sql invited_user_receiver.sql live_streams.sql live_stream_notify.sql -find_sessions_2020.sql +find_sessions_2020.sql \ No newline at end of file diff --git a/db/up/find_sessions_2020.sql b/db/up/find_sessions_2020.sql index a36638b4c..1b19ed3fc 100644 --- a/db/up/find_sessions_2020.sql +++ b/db/up/find_sessions_2020.sql @@ -31,4 +31,8 @@ CREATE TABLE arses ( active BOOLEAN DEFAULT TRUE, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP -); \ No newline at end of file +); + +------ + +ALTER TABLE generic_state ADD COLUMN top_message VARCHAR(100000); \ No newline at end of file diff --git a/ruby/lib/jam_ruby/models/generic_state.rb b/ruby/lib/jam_ruby/models/generic_state.rb index 5722a9424..fdf87055c 100644 --- a/ruby/lib/jam_ruby/models/generic_state.rb +++ b/ruby/lib/jam_ruby/models/generic_state.rb @@ -2,6 +2,9 @@ module JamRuby class GenericState < ActiveRecord::Base + + attr_accessible :top_message, as: :admin + self.table_name = 'generic_state' validates :env, :inclusion => {:in => ['development', 'staging', 'production', 'test']} @@ -31,6 +34,10 @@ module JamRuby GenericState.singleton.bounce_check_at end + def self.top_message + GenericState.singleton.top_message + end + def self.singleton GenericState.find('default') end diff --git a/web/app/assets/javascripts/jam_rest.js b/web/app/assets/javascripts/jam_rest.js index 5f5a823f9..71fd20294 100644 --- a/web/app/assets/javascripts/jam_rest.js +++ b/web/app/assets/javascripts/jam_rest.js @@ -2697,6 +2697,20 @@ }); } + function getConfigClient(options) { + + if (!options) { + options = {} + } + + return $.ajax({ + type: "GET", + url: "/api/config/client?" + $.param(options), + dataType: "json", + contentType: 'application/json' + }); + } + function createReview(options) { return $.ajax({ @@ -3113,6 +3127,7 @@ this.updateOnboarding = updateOnboarding; this.findFriendSessions = findFriendSessions; this.findPublicSessions = findPublicSessions; + this.getConfigClient = getConfigClient; return this; }; })(window, jQuery); diff --git a/web/app/assets/javascripts/layout.js b/web/app/assets/javascripts/layout.js index 5e4723659..aec9eecca 100644 --- a/web/app/assets/javascripts/layout.js +++ b/web/app/assets/javascripts/layout.js @@ -239,15 +239,25 @@ var broadcastWidth = findCardLayout.width + feedCardLayout.width; //+ opts.gridPadding * 2; - layoutBroadcast(broadcastWidth, findCardLayout.left); + //layoutBroadcast(broadcastWidth, findCardLayout.left); + layoutTopMessage(broadcastWidth, findCardLayout.left) } + /** function layoutBroadcast(width, left) { var css = { width:width + opts.gridPadding * 2, left:left } $('[data-react-class="BroadcastHolder"]').animate(css, opts.animationDuration) + }*/ + + function layoutTopMessage(width, left) { + var css = { + width:width + opts.gridPadding * 2, + left:left + } + $('[data-react-class="TopMessageHolder"]').animate(css, opts.animationDuration) } function layoutSidebar(screenWidth, screenHeight) { diff --git a/web/app/assets/javascripts/react-components/TopMessageHolder.js.jsx.coffee b/web/app/assets/javascripts/react-components/TopMessageHolder.js.jsx.coffee new file mode 100644 index 000000000..488f100d8 --- /dev/null +++ b/web/app/assets/javascripts/react-components/TopMessageHolder.js.jsx.coffee @@ -0,0 +1,34 @@ +context = window +ConfigStore = context.ConfigStore + +ReactCSSTransitionGroup = React.addons.CSSTransitionGroup; + +@TopMessageHolder = React.createClass( + { + displayName: 'Top Message Holder', + + mixins: [Reflux.listenTo(ConfigStore, "onConfig")] + + getInitialState: () -> + {} + + onConfig: (configs) -> + + if configs.top_message + @setState({top_message: configs.top_message}) + + componentDidUpdate:() -> + $root = $(this.getDOMNode()) + context.JK.popExternalLinks($root) + return false + + render: () -> + if @state.top_message + `
+
+ +
+
` + else + return null + }); diff --git a/web/app/assets/javascripts/react-components/stores/ConfigStore.js.coffee b/web/app/assets/javascripts/react-components/stores/ConfigStore.js.coffee new file mode 100644 index 000000000..b4d56947a --- /dev/null +++ b/web/app/assets/javascripts/react-components/stores/ConfigStore.js.coffee @@ -0,0 +1,31 @@ +$ = jQuery +context = window +logger = context.JK.logger +rest = new context.JK.Rest() + +@ConfigStore = Reflux.createStore( + { + top_message: null + + #listenables: @ConfigActions + + init: -> + this.listenTo(context.AppStore, this.onAppInit) + + onAppInit: (@app) -> + setTimeout((() => + rest.getConfigClient() + .done((response) => + @top_message = response.top_message + @changed() + ) + .fail((jqXHR) => + console.log("failed to fetch config") + ) + ), 2000) + + changed:() -> + @trigger({top_message: @top_message}) + + } +) diff --git a/web/app/assets/stylesheets/client/react-components/broadcast.scss b/web/app/assets/stylesheets/client/react-components/broadcast.scss index 46dbd84f4..ce2cd17b1 100644 --- a/web/app/assets/stylesheets/client/react-components/broadcast.scss +++ b/web/app/assets/stylesheets/client/react-components/broadcast.scss @@ -12,7 +12,7 @@ } } -[data-react-class="BroadcastHolder"] { +[data-react-class="BroadcastHolder"], [data-react-class="TopMessageHolder"] { position:relative; min-height: 125px; bottom: 60px; @@ -31,6 +31,10 @@ @include border_box_sizing; + &.config { + border-width:0; + } + &.lesson { p { text-align:center; diff --git a/web/app/controllers/api_configs_controller.rb b/web/app/controllers/api_configs_controller.rb index a69cc53a7..8aaa92c5a 100644 --- a/web/app/controllers/api_configs_controller.rb +++ b/web/app/controllers/api_configs_controller.rb @@ -8,8 +8,17 @@ class ApiConfigsController < ApiController websocket_gateway_uri: APP_CONFIG.websocket_gateway_uri, websocket_gateway_uri_ssl: APP_CONFIG.websocket_gateway_uri_ssl } + render :json => configs, :status => 200 end + # should be nginx cached on 5 minutes + def client + configs = + { + top_message: GenericState.top_message + } + render :json => configs, :status => 200 + end end diff --git a/web/app/views/clients/_header.html.erb b/web/app/views/clients/_header.html.erb index 4d42c73d8..5c7e5f6e0 100644 --- a/web/app/views/clients/_header.html.erb +++ b/web/app/views/clients/_header.html.erb @@ -12,8 +12,8 @@ <%= render "users/user_dropdown" %> - <%= react_component 'BroadcastHolder', {is_guitar_center: @is_guitar_center} %> - + + <%= react_component 'TopMessageHolder', {is_guitar_center: @is_guitar_center} %>