* wip
This commit is contained in:
parent
02a96e93e8
commit
ea0c2015d1
|
|
@ -22,12 +22,5 @@ ActiveAdmin.register JamRuby::BroadcastNotification, :as => 'BroadcastNotificati
|
|||
end
|
||||
end
|
||||
|
||||
controller do
|
||||
|
||||
def create
|
||||
resource_class.create(params[:broadcast_notification])
|
||||
redirect_to(admin_broadcast_notifications_path)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -16,3 +16,6 @@ PLATFORMS
|
|||
|
||||
DEPENDENCIES
|
||||
pg_migrate (= 0.1.13)
|
||||
|
||||
BUNDLED WITH
|
||||
1.10.3
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ CREATE TABLE broadcast_notification_views (
|
|||
user_id varchar(64) NOT NULL REFERENCES users(id),
|
||||
broadcast_notification_id varchar(64) NOT NULL REFERENCES broadcast_notifications(id) ON DELETE CASCADE,
|
||||
view_count INTEGER DEFAULT 0,
|
||||
active_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ module JamRuby
|
|||
class BroadcastNotification < ActiveRecord::Base
|
||||
|
||||
attr_accessible :title, :message, :button_label, :frequency, :button_url, as: :admin
|
||||
|
||||
has_many :user_views, class_name: 'JamRuby::BroadcastNotificationView', dependent: :destroy
|
||||
|
||||
validates :button_label, presence: true, length: {maximum: 14}
|
||||
|
|
@ -16,8 +17,9 @@ module JamRuby
|
|||
self.select('broadcast_notifications.*, bnv.updated_at AS bnv_updated_at')
|
||||
.joins("LEFT OUTER JOIN broadcast_notification_views AS bnv ON bnv.broadcast_notification_id = broadcast_notifications.id AND (bnv.user_id IS NULL OR (bnv.user_id = '#{user.id}'))")
|
||||
.where(['broadcast_notifications.frequency > 0'])
|
||||
.where(['bnv.user_id IS NULL OR bnv.active_at < NOW()'])
|
||||
.where(['bnv.user_id IS NULL OR broadcast_notifications.frequency > bnv.view_count'])
|
||||
.order('bnv_updated_at')
|
||||
.order('bnv_updated_at NULLS FIRST')
|
||||
end
|
||||
|
||||
def did_view(user)
|
||||
|
|
@ -25,6 +27,13 @@ module JamRuby
|
|||
.where(broadcast_notification_id: self.id, user_id: user.id)
|
||||
.limit(1)
|
||||
.first
|
||||
|
||||
unless bnv
|
||||
bnv = user_views.new()
|
||||
bnv.user = user
|
||||
bnv.active_at = Time.now - 10
|
||||
end
|
||||
|
||||
bnv = user_views.new(user: user) unless bnv
|
||||
bnv.view_count += 1
|
||||
bnv.save
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ describe BroadcastNotification do
|
|||
it 'gets view incremented' do
|
||||
bnv = broadcast1.did_view(user1)
|
||||
bnv = broadcast1.did_view(user1)
|
||||
expect(bnv.view_count).to be eq(2)
|
||||
bnv.view_count.should eq(2)
|
||||
end
|
||||
|
||||
it 'loads viewable broadcasts for a user' do
|
||||
|
|
|
|||
|
|
@ -1,26 +1,34 @@
|
|||
# one time init stuff for the /client view
|
||||
|
||||
|
||||
$ = jQuery
|
||||
context = window
|
||||
context.JK ||= {};
|
||||
|
||||
broadcastActions = BroadcastNotificationActions # require('./react-components/actions/BroadcastNotificationActions')
|
||||
broadcastActions = context.JK.Actions.broadcast
|
||||
|
||||
context.JK.ClientInit = class ClientInit
|
||||
constructor: () ->
|
||||
@logger = context.JK.logger
|
||||
@gearUtils = context.JK.GearUtils
|
||||
@ALERT_NAMES = context.JK.ALERT_NAMES;
|
||||
@lastCheckedBroadcast = null
|
||||
|
||||
init: () =>
|
||||
if context.gon.isNativeClient
|
||||
this.nativeClientInit()
|
||||
|
||||
setTimeout(this.checkBroadcastNotification, 3000)
|
||||
context.JK.onBackendEvent(@ALERT_NAMES.WINDOW_OPEN_FOREGROUND_MODE, 'client_init', @watchBroadcast);
|
||||
|
||||
checkBroadcastNotification: () =>
|
||||
this.watchBroadcast()
|
||||
|
||||
checkBroadcast: () =>
|
||||
broadcastActions.load.triggerPromise()
|
||||
|
||||
watchBroadcast: () =>
|
||||
if context.JK.currentUserId
|
||||
broadcastActions.load.triggerPromise()
|
||||
# create a 15 second buffer to not check too fast for some reason (like the client firing multiple foreground events)
|
||||
if !@lastCheckedBroadcast? || @lastCheckedBroadcast.getTime() < new Date().getTime() - 15000
|
||||
@lastCheckedBroadcast = new Date()
|
||||
setTimeout(@checkBroadcast, 3000)
|
||||
|
||||
|
||||
nativeClientInit: () =>
|
||||
|
|
|
|||
|
|
@ -64,7 +64,19 @@
|
|||
});
|
||||
}
|
||||
|
||||
function uploadMusicNotations(formData) {
|
||||
function quietBroadcastNotification(options) {
|
||||
var userId = getId(options);
|
||||
var broadcast_id = options.broadcast_id;
|
||||
return $.ajax({
|
||||
type: "POST",
|
||||
dataType: "json",
|
||||
contentType: 'application/json',
|
||||
url: "/api/users/" + userId + "/broadcast_notification/" + broadcast_id + '/quiet',
|
||||
data: JSON.stringify({}),
|
||||
});
|
||||
}
|
||||
|
||||
function uploadMusicNotations(formData) {
|
||||
return $.ajax({
|
||||
type: "POST",
|
||||
processData: false,
|
||||
|
|
@ -1789,6 +1801,7 @@
|
|||
this.uploadMusicNotations = uploadMusicNotations;
|
||||
this.getMusicNotation = getMusicNotation;
|
||||
this.getBroadcastNotification = getBroadcastNotification;
|
||||
this.quietBroadcastNotification = quietBroadcastNotification;
|
||||
this.legacyJoinSession = legacyJoinSession;
|
||||
this.joinSession = joinSession;
|
||||
this.cancelSession = cancelSession;
|
||||
|
|
|
|||
|
|
@ -202,7 +202,12 @@
|
|||
var gridRows = layout.split('x')[0];
|
||||
var gridCols = layout.split('x')[1];
|
||||
|
||||
$grid.children().each(function () {
|
||||
var gutterWidth = 0;
|
||||
|
||||
var findCardLayout;
|
||||
var feedCardLayout;
|
||||
|
||||
$grid.find('.homecard').each(function () {
|
||||
var childPosition = $(this).attr("layout-grid-position");
|
||||
var childRow = childPosition.split(',')[1];
|
||||
var childCol = childPosition.split(',')[0];
|
||||
|
|
@ -211,6 +216,13 @@
|
|||
var childLayout = me.getCardLayout(gridWidth, gridHeight, gridRows, gridCols,
|
||||
childRow, childCol, childRowspan, childColspan);
|
||||
|
||||
if($(this).is('.feed')) {
|
||||
feedCardLayout = childLayout;
|
||||
}
|
||||
else if($(this).is('.findsession')) {
|
||||
findCardLayout = childLayout;
|
||||
}
|
||||
|
||||
$(this).animate({
|
||||
width: childLayout.width,
|
||||
height: childLayout.height,
|
||||
|
|
@ -218,6 +230,18 @@
|
|||
left: childLayout.left
|
||||
}, opts.animationDuration);
|
||||
});
|
||||
|
||||
var broadcastWidth = findCardLayout.width + feedCardLayout.width;
|
||||
|
||||
layoutBroadcast(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 layoutSidebar(screenWidth, screenHeight) {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,30 @@
|
|||
//= require_self
|
||||
//= require react_ujs
|
||||
|
||||
React = require('react');
|
||||
// this pulls in react + addons (like CSS transitions)
|
||||
React = require('react/addons');
|
||||
|
||||
context = window
|
||||
|
||||
var actions = {}
|
||||
var stores = {}
|
||||
var components = {}
|
||||
|
||||
// create globally available references to all actions, stores, and components
|
||||
context.JK.Actions = actions
|
||||
context.JK.Stores = stores
|
||||
context.JK.Components = components
|
||||
|
||||
// FLUX ACTIONS
|
||||
actions.broadcast = require('./react-components/actions/BroadcastActions')
|
||||
|
||||
// FLUX STORES
|
||||
stores.broadcast = require('./react-components/stores/BroadcastStore');
|
||||
|
||||
// REACT COMPONENTS
|
||||
// NOTE: be sure to give each component a global name so that you can use the <%= react_component "ComponentName" %> directive or in JSX
|
||||
components.broadcastHolder = BroadcastHolder = require('./react-components/BroadcastHolder')
|
||||
components.broadcast = Broadcast = require('./react-components/Broadcast')
|
||||
|
||||
|
||||
|
||||
BroadcastNotificationActions = require('./react-components/actions/BroadcastNotificationActions')
|
||||
BroadcastNotification = require('./react-components/BroadcastNotification')
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
var React = require('react');
|
||||
|
||||
var broadcastActions = window.JK.Actions.broadcast;
|
||||
var rest = window.JK.Rest();
|
||||
|
||||
var Broadcast = React.createClass({
|
||||
displayName: 'Broadcast Notification',
|
||||
|
||||
handleNavigate: function (e) {
|
||||
|
||||
var href = $(e.currentTarget).attr('href')
|
||||
|
||||
if (href.indexOf('http') == 0) {
|
||||
e.preventDefault()
|
||||
window.JK.popExternalLink(href)
|
||||
}
|
||||
|
||||
broadcastActions.hide.trigger()
|
||||
},
|
||||
|
||||
notNow: function (e) {
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
rest.quietBroadcastNotification({broadcast_id: this.props.notification.id})
|
||||
|
||||
broadcastActions.hide.trigger()
|
||||
},
|
||||
|
||||
createMarkup: function() {
|
||||
return {__html: this.props.notification.message};
|
||||
},
|
||||
|
||||
render: function () {
|
||||
return <div className="broadcast-notification">
|
||||
<div className="message" dangerouslySetInnerHTML={this.createMarkup()}/>
|
||||
<div className="actions">
|
||||
<div className="actionsAligner">
|
||||
<a className="button-orange" onClick={this.handleNavigate}
|
||||
href={this.props.notification.button_url}>{this.props.notification.button_label}</a>
|
||||
<br/>
|
||||
<a className="not-now" href="#" onClick={this.notNow}>not now, thanks</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
});
|
||||
|
||||
// each file will export exactly one component
|
||||
module.exports = Broadcast;
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
var React = require('react');
|
||||
|
||||
var ReactCSSTransitionGroup = React.addons.CSSTransitionGroup;
|
||||
|
||||
var broadcastStore = window.JK.Stores.broadcast;
|
||||
|
||||
var BroadcastHolder = React.createClass({displayName: 'Broadcast Notification Holder',
|
||||
mixins: [Reflux.connect(broadcastStore, 'notification')],
|
||||
|
||||
render: function() {
|
||||
var notification = []
|
||||
if(this.state.notification) {
|
||||
notification.push(<Broadcast key={this.state.notification.id} notification={this.state.notification}/>)
|
||||
}
|
||||
|
||||
return (
|
||||
<div id="broadcast-notification-holder" className="broadcast-notification-holder" >
|
||||
<ReactCSSTransitionGroup transitionName="bn-slidedown">
|
||||
{notification}
|
||||
</ReactCSSTransitionGroup>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
});
|
||||
|
||||
// each file will export exactly one component
|
||||
module.exports = BroadcastHolder;
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
var React = require('react');
|
||||
|
||||
var BroadcastNotificationStore = require('./stores/BroadcastNotificationStore');
|
||||
|
||||
var BroadcastNotification = React.createClass({displayName: 'Broadcast Notification',
|
||||
mixins: [Reflux.connect(BroadcastNotificationStore, 'notification')],
|
||||
render: function() {
|
||||
if(!this.state.notification) {
|
||||
return <div>HAHAHAAH</div>
|
||||
}
|
||||
|
||||
return <div className="broadcast-notification">
|
||||
<div className="message" dangerouslySetInnerHTML={this.state.notification.message}/>
|
||||
<div className="actions">
|
||||
<a className="button-orange"
|
||||
href={this.state.notification.button_url}>{this.state.notification.button_label}</a>
|
||||
<a className="not-now" href="#">not now, thanks</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
// each file will export exactly one component
|
||||
module.exports = BroadcastNotification;
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
var React = require('react');
|
||||
|
||||
var DemoComponent = React.createClass({displayName: 'Demo Component',
|
||||
render: function() {
|
||||
return <div>Demo Component</div>;
|
||||
}
|
||||
});
|
||||
|
||||
// each file will export exactly one component
|
||||
module.exports = DemoComponent;
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
|
||||
BroadcastActions = Reflux.createActions({
|
||||
load: {asyncResult: true},
|
||||
hide: {}
|
||||
})
|
||||
|
||||
module.exports = BroadcastActions
|
||||
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
//var rest = context.JK.Rest()
|
||||
|
||||
var BroadcastNotificationActions = Reflux.createActions({
|
||||
load: {asyncResult: true}
|
||||
})
|
||||
|
||||
module.exports = BroadcastNotificationActions
|
||||
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
$ = jQuery
|
||||
context = window
|
||||
logger = context.JK.logger
|
||||
broadcastActions = BroadcastNotificationActions # require('../actions/BroadcastNotificationActions')
|
||||
|
||||
rest = context.JK.Rest()
|
||||
|
||||
# see if this shows up elsewhere
|
||||
broadcastActions.blah = 'hahah'
|
||||
|
||||
broadcastActions.load.listenAndPromise(rest.getBroadcastNotification);
|
||||
|
||||
console.log("broadcastActions!!", broadcastActions)
|
||||
BroadcastNotificationStore = Reflux.createStore(
|
||||
{
|
||||
listenables: broadcastActions
|
||||
|
||||
init: () =>
|
||||
logger.debug("broadcast notification store init")
|
||||
#this.listenTo(broadcastActions.load, 'onSync')
|
||||
|
||||
onLoad: () =>
|
||||
logger.debug("loading broadcast notification...")
|
||||
|
||||
onLoadCompleted: () =>
|
||||
logger.debug("broadcast notification sync completed")
|
||||
|
||||
onLoadFailed: (jqXHR) =>
|
||||
if jqXHR.status != 404
|
||||
logger.error("broadcast notification sync failed")
|
||||
}
|
||||
)
|
||||
|
||||
module.exports = BroadcastNotificationStore
|
||||
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
$ = jQuery
|
||||
context = window
|
||||
logger = context.JK.logger
|
||||
broadcastActions = context.JK.Actions.broadcast
|
||||
|
||||
rest = context.JK.Rest()
|
||||
|
||||
broadcastActions.load.listenAndPromise(rest.getBroadcastNotification);
|
||||
|
||||
BroadcastStore = Reflux.createStore(
|
||||
{
|
||||
listenables: broadcastActions
|
||||
|
||||
onLoad: () ->
|
||||
logger.debug("loading broadcast notification...")
|
||||
|
||||
onLoadCompleted: (response) ->
|
||||
logger.debug("broadcast notification sync completed")
|
||||
this.trigger(response)
|
||||
|
||||
onLoadFailed: (jqXHR) ->
|
||||
if jqXHR.status != 404
|
||||
logger.error("broadcast notification sync failed")
|
||||
|
||||
onHide: () ->
|
||||
this.trigger(null)
|
||||
}
|
||||
)
|
||||
|
||||
module.exports = BroadcastStore
|
||||
|
||||
|
|
@ -81,4 +81,5 @@
|
|||
*= require ./jamTrackPreview
|
||||
*= require users/signinCommon
|
||||
*= require landings/partner_agreement_v1
|
||||
*= require_directory ./react-components
|
||||
*/
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
@import 'client/common';
|
||||
|
||||
[data-react-class="BroadcastHolder"] {
|
||||
position:absolute;
|
||||
min-height:60px;
|
||||
top:62px;
|
||||
@include border_box_sizing;
|
||||
|
||||
.broadcast-notification {
|
||||
position:absolute;
|
||||
border-width:1px;
|
||||
border-color:$ColorScreenPrimary;
|
||||
border-style:solid;
|
||||
padding:5px 12px;
|
||||
height:100%;
|
||||
width:100%;
|
||||
left:0;
|
||||
top:0;
|
||||
overflow:hidden;
|
||||
margin-left:60px;
|
||||
@include border_box_sizing;
|
||||
|
||||
}
|
||||
.message {
|
||||
float:left;
|
||||
width:calc(100% - 150px);
|
||||
font-size:12px;
|
||||
}
|
||||
|
||||
.actions {
|
||||
float:right;
|
||||
text-align: right;
|
||||
vertical-align: middle;
|
||||
display:inline;
|
||||
height:100%;
|
||||
width:150px;
|
||||
}
|
||||
|
||||
.actionsAligner {
|
||||
display:inline-block;
|
||||
vertical-align:middle;
|
||||
text-align:center;
|
||||
}
|
||||
|
||||
a { display:inline-block; }
|
||||
|
||||
.button-orange {
|
||||
font-size:16px;
|
||||
position:relative;
|
||||
top:8px;
|
||||
}
|
||||
|
||||
.not-now {
|
||||
font-size:11px;
|
||||
top:13px;
|
||||
position:relative;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.bn-slidedown-enter {
|
||||
max-height:0;
|
||||
opacity: 0.01;
|
||||
transition: max-height .5s ease-in;
|
||||
}
|
||||
|
||||
.bn-slidedown-enter.bn-slidedown-enter-active {
|
||||
max-height:60px;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.bn-slidedown-leave {
|
||||
max-height:60px;
|
||||
transition: max-height .5s ease-in;
|
||||
}
|
||||
|
||||
.bn-slidedown-leave.bn-slidedown-leave-active {
|
||||
max-height:0;
|
||||
}
|
||||
|
|
@ -814,7 +814,18 @@ class ApiUsersController < ApiController
|
|||
else
|
||||
render json: { message: 'Not Found'}, status: 404
|
||||
end
|
||||
end
|
||||
|
||||
# used to hide a broadcast notification from rotation temporarily
|
||||
def quiet_broadcast_notification
|
||||
@broadcast = BroadcastNotificationView.find_by_broadcast_notification_id_and_user_id(params[:broadcast_id], current_user.id)
|
||||
|
||||
if @broadcast
|
||||
@broadcast.active_at = Date.today + 14 # 14 days in the future we'll re-instas
|
||||
@broadcast.save
|
||||
end
|
||||
|
||||
render json: { }, status: 200
|
||||
end
|
||||
|
||||
###################### RECORDINGS #######################
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
object @broadcast
|
||||
|
||||
attributes :message, :button_label, :button_url
|
||||
attributes :id, :message, :button_label, :button_url
|
||||
|
|
@ -10,7 +10,7 @@
|
|||
/ Grid is the count of the smallest spaces, then
|
||||
/ individual spells span those spaces
|
||||
-if @nativeClient
|
||||
.grid layout-grid="2x12"
|
||||
.grid layout-grid="2x12"
|
||||
.homecard.createsession layout-grid-columns="4" layout-grid-position="0,0" layout-grid-rows="1" layout-link="createSession" type="createSession" class="#{logged_in_not_logged_in_class}"
|
||||
h2 create session
|
||||
.homebox-info
|
||||
|
|
@ -45,7 +45,7 @@
|
|||
.homebox-info
|
||||
/! free service level
|
||||
-else
|
||||
.grid layout-grid="2x12"
|
||||
.grid layout-grid="2x12"
|
||||
.homecard.createsession layout-grid-columns="4" layout-grid-position="0,0" layout-grid-rows="1" layout-link="createSession" type="createSession" class="#{logged_in_not_logged_in_class}"
|
||||
h2 create session
|
||||
.homebox-info
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
<div class="dialog-overlay op70" style="display:none; width:100%; height:100%; z-index:99;"></div>
|
||||
|
||||
<%= render "header" %>
|
||||
<%= react_component 'BroadcastHolder', {} %>
|
||||
<%= render "home" %>
|
||||
<%= render "footer" %>
|
||||
<%= render "paginator" %>
|
||||
|
|
@ -81,7 +82,6 @@
|
|||
<%= render 'dialogs/dialogs' %>
|
||||
<div id="fb-root"></div>
|
||||
|
||||
<%= react_component 'BroadcastNotification', {} %>
|
||||
|
||||
<script type="text/javascript">
|
||||
$(function() {
|
||||
|
|
|
|||
|
|
@ -397,6 +397,6 @@ if defined?(Bundler)
|
|||
|
||||
config.react.variant = :production
|
||||
config.react.addons = true
|
||||
config.browserify_rails.commandline_options = "-t coffeeify --extension=\".js.coffee\" --transform reactify --extension=\".jsx\""
|
||||
config.browserify_rails.commandline_options = " --transform reactify --extension=\".jsx\" "
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -376,6 +376,7 @@ SampleApp::Application.routes.draw do
|
|||
|
||||
# broadcast notification
|
||||
match '/users/:id/broadcast_notification' => 'api_users#broadcast_notification', :via => :get
|
||||
match '/users/:id/broadcast_notification/:broadcast_id/quiet' => 'api_users#quiet_broadcast_notification', :via => :post
|
||||
|
||||
# session chat
|
||||
match '/chat' => 'api_chats#create', :via => :post
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
"browserify-incremental": "^1.4.0",
|
||||
"coffeeify": "~> 0.6",
|
||||
"reactify": "^0.17.1",
|
||||
"coffee-reactify": "~>3.0.0",
|
||||
"react": "^0.12.0",
|
||||
"react-tools": "^0.12.1"
|
||||
},
|
||||
|
|
|
|||
Loading…
Reference in New Issue