From b9681fd41833b6f0a68fb3eda62f115502b16043 Mon Sep 17 00:00:00 2001 From: Seth Call Date: Thu, 16 Apr 2020 11:26:18 -0500 Subject: [PATCH] livestream beta commit --- admin/Gemfile | 10 +- admin/Gemfile.lock | 26 +-- admin/app/admin/event_brite_order.rb | 10 + admin/app/admin/event_brite_order_uploads.rb | 51 ++++ admin/app/admin/live_stream.rb | 21 ++ admin/app/assets/stylesheets/custom.css.scss | 10 +- db/manifest | 3 +- db/up/live_streams.sql | 47 ++++ ruby/lib/jam_ruby.rb | 3 + ruby/lib/jam_ruby/models/event_brite_order.rb | 22 ++ .../models/event_brite_order_upload.rb | 5 + ruby/lib/jam_ruby/models/live_stream.rb | 37 +++ .../events/authorized_event_storage.js | 81 +++++++ web/app/assets/javascripts/events/events.js | 9 + web/app/assets/javascripts/events/jam_rest.js | 43 ++++ .../javascripts/events/react-components.js | 3 + .../events/react-components/EventList.js.jsx | 115 +++++++++ .../events/react-components/EventPage.js.jsx | 220 ++++++++++++++++++ .../events/react-components/EventsPage.js.jsx | 87 +++++++ .../actions/EventActions.js.coffee | 6 + .../stores/EventStore.js.coffee | 70 ++++++ .../assets/javascripts/spikes/responsive.js | 125 ++++++++++ .../javascripts/wizard/gear/gear_wizard.js | 5 +- .../assets/stylesheets/events/constants.scss | 21 ++ web/app/assets/stylesheets/events/events.scss | 26 +++ .../events/react-components/EventList.scss | 81 +++++++ .../events/react-components/EventPage.scss | 114 +++++++++ .../events/react-components/EventsPage.scss | 169 ++++++++++++++ .../api_live_streams_controller.rb | 47 ++++ web/app/controllers/events_controller.rb | 13 ++ web/app/controllers/spikes_controller.rb | 4 + web/app/views/api_live_streams/index.rabl | 4 + web/app/views/api_live_streams/show.rabl | 3 + web/app/views/events/event.slim | 6 + .../{event.html.haml => event_old.html.haml} | 0 web/app/views/events/events.slim | 6 + web/app/views/layouts/events.html.erb | 36 +++ web/app/views/spikes/responsive.html.erb | 70 ++++++ web/config/application.rb | 2 +- web/config/routes.rb | 7 + 40 files changed, 1587 insertions(+), 31 deletions(-) create mode 100644 admin/app/admin/event_brite_order.rb create mode 100644 admin/app/admin/event_brite_order_uploads.rb create mode 100644 admin/app/admin/live_stream.rb create mode 100644 db/up/live_streams.sql create mode 100644 ruby/lib/jam_ruby/models/event_brite_order.rb create mode 100644 ruby/lib/jam_ruby/models/event_brite_order_upload.rb create mode 100644 ruby/lib/jam_ruby/models/live_stream.rb create mode 100644 web/app/assets/javascripts/events/authorized_event_storage.js create mode 100644 web/app/assets/javascripts/events/events.js create mode 100644 web/app/assets/javascripts/events/jam_rest.js create mode 100644 web/app/assets/javascripts/events/react-components.js create mode 100644 web/app/assets/javascripts/events/react-components/EventList.js.jsx create mode 100644 web/app/assets/javascripts/events/react-components/EventPage.js.jsx create mode 100644 web/app/assets/javascripts/events/react-components/EventsPage.js.jsx create mode 100644 web/app/assets/javascripts/events/react-components/actions/EventActions.js.coffee create mode 100644 web/app/assets/javascripts/events/react-components/stores/EventStore.js.coffee create mode 100644 web/app/assets/javascripts/spikes/responsive.js create mode 100644 web/app/assets/stylesheets/events/constants.scss create mode 100644 web/app/assets/stylesheets/events/events.scss create mode 100644 web/app/assets/stylesheets/events/react-components/EventList.scss create mode 100644 web/app/assets/stylesheets/events/react-components/EventPage.scss create mode 100644 web/app/assets/stylesheets/events/react-components/EventsPage.scss create mode 100644 web/app/controllers/api_live_streams_controller.rb create mode 100644 web/app/views/api_live_streams/index.rabl create mode 100644 web/app/views/api_live_streams/show.rabl create mode 100644 web/app/views/events/event.slim rename web/app/views/events/{event.html.haml => event_old.html.haml} (100%) create mode 100644 web/app/views/events/events.slim create mode 100644 web/app/views/layouts/events.html.erb create mode 100644 web/app/views/spikes/responsive.html.erb diff --git a/admin/Gemfile b/admin/Gemfile index be68523a5..bddf8b3c5 100644 --- a/admin/Gemfile +++ b/admin/Gemfile @@ -64,7 +64,7 @@ gem 'resque-lonely_job', '~> 1.0.0' gem 'eventmachine', '1.2.3' gem 'amqp', '0.9.8' #gem 'logging-rails', :require => 'logging/rails' -gem 'pg_migrate' +gem 'pg_migrate', '0.1.14' gem 'ruby-protocol-buffers', '1.2.2' gem 'sendgrid', '1.2.0' gem 'geokit-rails' @@ -84,7 +84,7 @@ gem 'stripe' gem 'zip-codes' gem 'email_validator' gem 'best_in_place' #, github: 'bernat/best_in_place' - +gem 'auto_strip_attributes', '2.6.0' #group :libv8 do @@ -126,9 +126,9 @@ end group :test do gem 'simplecov', '~> 0.7.1' gem 'simplecov-rcov' - gem 'capybara-webkit' - gem 'capybara-screenshot', '0.3.22' # 1.0.0 broke compat with rspec. maybe we need newer rspec - gem 'poltergeist' +# gem 'capybara-webkit' +# gem 'capybara-screenshot', '0.3.22' # 1.0.0 broke compat with rspec. maybe we need newer rspec +# gem 'poltergeist' end gem 'pry' diff --git a/admin/Gemfile.lock b/admin/Gemfile.lock index a946d9be3..4d567f4de 100644 --- a/admin/Gemfile.lock +++ b/admin/Gemfile.lock @@ -87,6 +87,8 @@ GEM arel (6.0.4) arr-pm (0.0.10) cabin (> 0) + auto_strip_attributes (2.6.0) + activerecord (>= 4.0) aws-sdk (1.67.0) aws-sdk-v1 (= 1.67.0) aws-sdk-v1 (1.67.0) @@ -114,13 +116,6 @@ GEM rack (>= 1.0.0) rack-test (>= 0.5.4) xpath (~> 2.0) - capybara-screenshot (0.3.22) - capybara (>= 1.0, < 3) - colored - launchy - capybara-webkit (1.14.0) - capybara (>= 2.3.0, < 2.14.0) - json carrierwave (0.11.2) activemodel (>= 3.2.0) activesupport (>= 3.2.0) @@ -134,7 +129,6 @@ GEM childprocess (0.8.0) ffi (~> 1.0, >= 1.0.11) clamp (1.0.1) - cliver (0.3.2) cocoon (1.2.11) coderay (1.1.2) coffee-rails (4.2.2) @@ -144,7 +138,6 @@ GEM coffee-script-source execjs coffee-script-source (1.12.2) - colored (1.2) concurrent-ruby (1.0.5) country-select (1.1.1) crass (1.0.3) @@ -403,10 +396,6 @@ GEM insist mustache (= 0.99.8) stud - poltergeist (1.17.0) - capybara (~> 2.1) - cliver (~> 0.3.1) - websocket-driver (>= 0.2.0) polyamorous (1.3.3) activerecord (>= 3.0) postgres-copy (0.6.0) @@ -610,9 +599,6 @@ GEM rack (>= 1.0.0) warden (1.2.7) rack (>= 1.0) - websocket-driver (0.7.0) - websocket-extensions (>= 0.1.0) - websocket-extensions (0.1.3) will_paginate (3.1.6) xdan-datetimepicker-rails (2.5.4) jquery-rails @@ -629,6 +615,7 @@ DEPENDENCIES activeadmin activeadmin_addons amqp (= 0.9.8) + auto_strip_attributes (= 2.6.0) aws-sdk (~> 1) bcrypt-ruby (= 3.0.1) best_in_place @@ -636,8 +623,6 @@ DEPENDENCIES bootstrap-will_paginate (= 0.0.6) bugsnag capybara - capybara-screenshot (= 0.3.22) - capybara-webkit carrierwave (= 0.11.2) carrierwave_direct cocoon @@ -669,8 +654,7 @@ DEPENDENCIES launchy mime-types (= 1.25) net-ssh - pg_migrate - poltergeist + pg_migrate (= 0.1.14) postgres-copy (= 0.6.0) postgres_ext protected_attributes @@ -708,4 +692,4 @@ DEPENDENCIES zip-codes BUNDLED WITH - 1.13.7 + 1.17.1 diff --git a/admin/app/admin/event_brite_order.rb b/admin/app/admin/event_brite_order.rb new file mode 100644 index 000000000..ad45eedbb --- /dev/null +++ b/admin/app/admin/event_brite_order.rb @@ -0,0 +1,10 @@ +ActiveAdmin.register JamRuby::EventBriteOrder, :as => 'EventBriteOrder' do + menu :parent => 'Misc' + + config.sort_order = 'created_at DESC' + + filter :live_stream + filter :email + filter :order_id + +end diff --git a/admin/app/admin/event_brite_order_uploads.rb b/admin/app/admin/event_brite_order_uploads.rb new file mode 100644 index 000000000..984a2cdf2 --- /dev/null +++ b/admin/app/admin/event_brite_order_uploads.rb @@ -0,0 +1,51 @@ +ActiveAdmin.register_page "EventBriteOrderUploads" do + + menu :label => 'Event Brite Order Upload', :parent => 'Misc' + + page_action :upload_eventbriteorders, :method => :post do + EventBriteOrder.transaction do + + puts params + + + live_stream = LiveStream.find_by_id!(params[:jam_ruby_event_brite_order][:live_stream_id]) + + file = params[:jam_ruby_event_brite_order][:csv] + + upload = EventBriteOrderUpload.new + upload.upload_file_name = file.original_filename + upload.save! + + array_of_arrays = CSV.read(file.tempfile.path, headers:true) + array_of_arrays.each do |row| + order_id = row['Order ID'] + + event_brite_order = EventBriteOrder.find_by_order_id(order_id) + if event_brite_order.nil? + event_brite_order = EventBriteOrder.new + end + event_brite_order.event_brite_order_upload = upload + event_brite_order.live_stream = live_stream + event_brite_order.event_name = row['Event Name'] + event_brite_order.order_id = order_id + event_brite_order.ticket_count = row['Tickets'] + event_brite_order.ticket_type = row['Type'] + event_brite_order.first_name = row['First Name'] + event_brite_order.last_name = row['Last Name'] + event_brite_order.email = row['Email Address'] + event_brite_order.save! + end + + redirect_to admin_eventbriteorderuploads_path, :notice => "Created #{array_of_arrays.length} event brite orders!" + end + end + content do + active_admin_form_for EventBriteOrder.new, :url => admin_eventbriteorderuploads_upload_eventbriteorders_path, :builder => ActiveAdmin::FormBuilder do |f| + f.inputs "Upload Event Brite Orders" do + f.input :csv, as: :file, required: true, :label => "An event brite order CSV exactly as exported from Eventbrite" + f.input :live_stream, required:true, as: :select, :collection => LiveStream.upcoming + end + f.actions + end + end +end diff --git a/admin/app/admin/live_stream.rb b/admin/app/admin/live_stream.rb new file mode 100644 index 000000000..a1bdc2617 --- /dev/null +++ b/admin/app/admin/live_stream.rb @@ -0,0 +1,21 @@ +ActiveAdmin.register JamRuby::LiveStream, :as => 'LiveStream' do + menu :parent => 'Misc' + + config.sort_order = 'created_at DESC' + + filter :listed + filter :event_id + + before_build do |record| + if !record.event_type.nil? + puts "escape" + else + record.slug = 'please-do-this-sort-of-thing' + record.starts_at = 10.days.from_now.midnight + record.ends_at = 11.days.from_now.midnight + record.img_width = 200 + record.event_type = 'eventbrite' + end + end + +end diff --git a/admin/app/assets/stylesheets/custom.css.scss b/admin/app/assets/stylesheets/custom.css.scss index 19e45fee7..ec1f940e8 100644 --- a/admin/app/assets/stylesheets/custom.css.scss +++ b/admin/app/assets/stylesheets/custom.css.scss @@ -29,4 +29,12 @@ background-color:white; border:1px solid gray; padding:5px; -} \ No newline at end of file +} +.datetime_select li.fragment { + + + label { + display: block; + width:auto; + } +} diff --git a/db/manifest b/db/manifest index b8b9f7843..266c37788 100755 --- a/db/manifest +++ b/db/manifest @@ -390,4 +390,5 @@ onboarding_emails.sql limit_counter_reminders.sql amazon_v2.sql store_backend_details_rate_session.sql -invited_user_receiver.sql \ No newline at end of file +invited_user_receiver.sql +live_streams.sql \ No newline at end of file diff --git a/db/up/live_streams.sql b/db/up/live_streams.sql new file mode 100644 index 000000000..c5c3679f2 --- /dev/null +++ b/db/up/live_streams.sql @@ -0,0 +1,47 @@ +CREATE TABLE live_streams ( + id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4(), + slug VARCHAR(512) NOT NULL UNIQUE, + title TEXT, + description TEXT, + social_description TEXT, + listed BOOLEAN NOT NULL DEFAULT FALSE, + starts_at TIMESTAMP, + ends_at TIMESTAMP, + img_url VARCHAR(1024), + img_width INTEGER, + img_height INTEGER, + youtube_code VARCHAR(1024), + eventbriteid VARCHAR(1024), + event_type VARCHAR(100), + event_brite_registration_url VARCHAR(1024), + allow_in BOOLEAN NOT NULL DEFAULT FALSE, + white_label_player BOOLEAN NOT NULL DEFAULT TRUE, + user_id VARCHAR(64) REFERENCES users(id) ON DELETE SET NULL, + band_id VARCHAR(64) REFERENCES bands(id) ON DELETE SET NULL, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +CREATE TABLE event_brite_orders ( + id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4(), + live_stream_id VARCHAR(64) REFERENCES live_streams(id) ON DELETE CASCADE, + event_brite_order_upload_id VARCHAR(64) REFERENCES event_brite_order_uploads(id) ON DELETE CASCADE, + event_name VARCHAR(100) NOT NULL, + order_id VARCHAR(100) NOT NULL UNIQUE, + ticket_count INTEGER, + ticket_type VARCHAR(100), + first_name VARCHAR(100), + last_name VARCHAR(100), + email VARCHAR(200), + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + times_claimed INTEGER NOT NULL DEFAULT 0 +); + + +CREATE TABLE event_brite_order_uploads ( + id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4(), + upload_file_name VARCHAR(500) NOT NULL, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP +); \ No newline at end of file diff --git a/ruby/lib/jam_ruby.rb b/ruby/lib/jam_ruby.rb index cc2662810..8a6531664 100755 --- a/ruby/lib/jam_ruby.rb +++ b/ruby/lib/jam_ruby.rb @@ -241,6 +241,9 @@ require "jam_ruby/models/jam_track_file" require "jam_ruby/models/jam_track_mixdown" require "jam_ruby/models/jam_track_mixdown_package" require "jam_ruby/models/genre_jam_track" +require "jam_ruby/models/live_stream" +require "jam_ruby/models/event_brite_order" +require "jam_ruby/models/event_brite_order_upload" require "jam_ruby/app/mailers/async_mailer" require "jam_ruby/app/mailers/batch_mailer" require "jam_ruby/app/mailers/progress_mailer" diff --git a/ruby/lib/jam_ruby/models/event_brite_order.rb b/ruby/lib/jam_ruby/models/event_brite_order.rb new file mode 100644 index 000000000..1861c2f1c --- /dev/null +++ b/ruby/lib/jam_ruby/models/event_brite_order.rb @@ -0,0 +1,22 @@ +class JamRuby::EventBriteOrder < ActiveRecord::Base + + + belongs_to :live_stream, class_name: 'JamRuby::LiveStream' + belongs_to :event_brite_order_upload, class_name: 'JamRuby::EventBriteOrderUpload' + + validates :event_name, presence: true + validates :order_id, presence: true + validates :ticket_count, presence: true + validates :ticket_type, presence: true + + before_validation :sanitize + + def sanitize + self.first_name.strip! if self.first_name + self.last_name.strip! if self.last_name + self.email.strip! if self.email + self.ticket_type.strip! if self.ticket_type + self.order_id.strip! if self.order_id + self.event_name.strip! if self.event_name + end +end diff --git a/ruby/lib/jam_ruby/models/event_brite_order_upload.rb b/ruby/lib/jam_ruby/models/event_brite_order_upload.rb new file mode 100644 index 000000000..c2d3a599d --- /dev/null +++ b/ruby/lib/jam_ruby/models/event_brite_order_upload.rb @@ -0,0 +1,5 @@ +class JamRuby::EventBriteOrderUpload < ActiveRecord::Base + has_many :event_brite_orders, class_name: 'JamRuby::EventBriteOrder' + + validates :upload_file_name, presence: true +end diff --git a/ruby/lib/jam_ruby/models/live_stream.rb b/ruby/lib/jam_ruby/models/live_stream.rb new file mode 100644 index 000000000..9e0b3cf74 --- /dev/null +++ b/ruby/lib/jam_ruby/models/live_stream.rb @@ -0,0 +1,37 @@ +class JamRuby::LiveStream < ActiveRecord::Base + + attr_accessible :user_id, :band_id, :starts_at, :ends_at, :img_url, :slug, :title, :description, :listed, :allow_in, :white_label_player, :youtube_code, :eventbriteid, :event_type, :social_description, :event_brite_registration_url, as: :admin + + #belongs_to :user, class_name: 'JamRuby::User' + #belongs_to :band, class_name: 'JamRuby::Band' + + #validate :one_of_user_band + validates :slug, uniqueness: true, presence: true + + before_validation :sanitize_active_admin + + def ready_display + self.starts_at && self.ends_at && (self.user_id || self.band_id) + end + + def self.upcoming + LiveStream.where(listed: true).where("starts_at > ?", 2.days.ago).order('starts_at DESC') + end + + def sanitize_active_admin + self.img_url = nil if self.img_url == '' + self.user_id = nil if self.user_id == '' + self.band_id = nil if self.band_id == '' + self.social_description = nil if self.social_description == '' + end + + def one_of_user_band + if band && user + errors.add(:user, 'specify band, or user. not both') + end + end + + def admin_name + "#{title} EB:#{eventbriteid} #{starts_at}" + end +end diff --git a/web/app/assets/javascripts/events/authorized_event_storage.js b/web/app/assets/javascripts/events/authorized_event_storage.js new file mode 100644 index 000000000..c6a2d098a --- /dev/null +++ b/web/app/assets/javascripts/events/authorized_event_storage.js @@ -0,0 +1,81 @@ +(function (context) { + + /** + * Javascript wrappers for the REST API + */ + + "use strict"; + + var AuthorizedEventStorage = {} + context.JK = context.JK || {}; + context.JK.AuthorizedEventStorage = AuthorizedEventStorage + + var key = 'authorized_events' + + AuthorizedEventStorage.storeAuthorizedEvent = (authorizedEvent) => { + console.log("storing authorized event", authorizedEvent) + var events = fetchFromStorage() + if(!events) { + events = {} + } + if(!events.list) { + events.list = {} + } + + if(!authorizedEvent.event_id) { + console.error("no event_id on new event", authorizedEvent) + alert("Unable to store order (no event_id)! Please contact support@jamkazam.com.") + return + } + if(!authorizedEvent.order_id) { + console.error("no order_id on new event", authorizedEvent) + alert("Unable to store order (no order_id)! Please contact support@jamkazam.com.") + return + } + + events.list[authorizedEvent.event_id] = authorizedEvent + + try { + window.localStorage.setItem(key, JSON.stringify(events) ) + } + catch(e) { + console.error("can't update localStorage") + alert("Could not update local storage ith order.\nPlease try a different browser. Please do not use incognito mode.") + } + + } + + AuthorizedEventStorage.locateEvent = (eventId) => { + var events = fetchFromStorage() + if(events && events.list) { + return events.list[eventId] + } + else { + console.log("no events found in storage") + return null + } + } + + AuthorizedEventStorage.listAuthorizedEvents = () => { + var events = fetchFromStorage() + if(events) { + return events.list + } + else { + return {} + } + } + + function fetchFromStorage() { + var events = window.localStorage.getItem(key) + if(events){ + try { + return JSON.parse(events) + } + catch(e){ + console.log("events not parseable", events) + return null; + } + } + } +})(window); diff --git a/web/app/assets/javascripts/events/events.js b/web/app/assets/javascripts/events/events.js new file mode 100644 index 000000000..5879e313d --- /dev/null +++ b/web/app/assets/javascripts/events/events.js @@ -0,0 +1,9 @@ +//= require ./jam_rest +//= require ./authorized_event_storage +//= require reflux +//= require react +//= require react_ujs +//= require react-init +//= require react-input-autosize +//= require react-select +//= require ./react-components \ No newline at end of file diff --git a/web/app/assets/javascripts/events/jam_rest.js b/web/app/assets/javascripts/events/jam_rest.js new file mode 100644 index 000000000..beb114692 --- /dev/null +++ b/web/app/assets/javascripts/events/jam_rest.js @@ -0,0 +1,43 @@ +(function (context) { + + /** + * Javascript wrappers for the REST API + */ + + "use strict"; + + var Rest2 = {} + context.JK = context.JK || {}; + context.JK.Rest2 = Rest2 + + Rest2.listLiveStreams = () => { + return fetch('/api/live_streams', { + method: 'get', + credentials: 'same-origin', // include, *same-origin, omit + headers: { + 'Content-Type': 'application/json' + }, + }) + } + Rest2.getLiveStream = (id) => { + return fetch('/api/live_streams/' + id, { + method: 'get', + credentials: 'same-origin', // include, *same-origin, omit + headers: { + 'Content-Type': 'application/json' + }, + }) + } + + Rest2.authorizeLiveStream = (data) => { + return fetch('/api/live_streams/claim', { + method: 'post', + credentials: 'same-origin', // include, *same-origin, omit + headers: { + 'Content-Type': 'application/json' + }, + cache: 'no-cache', + body: JSON.stringify(data) + }) + } +})(window); diff --git a/web/app/assets/javascripts/events/react-components.js b/web/app/assets/javascripts/events/react-components.js new file mode 100644 index 000000000..7a16dee63 --- /dev/null +++ b/web/app/assets/javascripts/events/react-components.js @@ -0,0 +1,3 @@ +//= require_directory ./react-components/actions +//= require ./react-components/stores/EventStore +//= require_directory ./react-components \ No newline at end of file diff --git a/web/app/assets/javascripts/events/react-components/EventList.js.jsx b/web/app/assets/javascripts/events/react-components/EventList.js.jsx new file mode 100644 index 000000000..465cc9fa6 --- /dev/null +++ b/web/app/assets/javascripts/events/react-components/EventList.js.jsx @@ -0,0 +1,115 @@ +context = window +EventActions = context.EventActions + +context.EventList = React.createClass({ + + mixins: [Reflux.listenTo(EventStore, "onEventsChanged")], + + events: null, + + getInitialState: function () { + return {events: null} + }, + + componentDidMount: function () { + }, + + isModeYours: function() { + return this.props.mode == 'yours' + }, + + render: function () { + return this.renderAll(); + }, + + + renderEvents() { + if(!this.state.events || this.state.events.length == 0) { + return null; + } + + var items = [] + + + var isModeYours = this.isModeYours(); + for(var i = 0; i < this.state.events.length; i++) { + var event = this.state.events[i] + + if(isModeYours && (!!event.authorization || event.allow_in) || !isModeYours && (!event.authorization && !event.allow_in)) { + + var dynamic = null + var thumb = null + if(event.img_url) { + thumb = + dynamic = 'event' + } + else { + dynamic = 'event no-image' + } + + var title = event.title ? event.title : 'Missing event title' + title = {title} + var when = event.starts_at ? new Date(event.starts_at).toLocaleString() : 'Unknown start time' + var item =
+ {thumb} +
+
{title}
+
{when}
+
+
+ items.push(item) + } + } + return items + }, + + renderAll: function() { + var events = this.renderEvents() + var headerText = this.isModeYours() ? 'Your Registered Events' : 'All Upcoming Events' + + if(!events || events.length == 0) { + return null; + } + + if(events) { + var response =
+
+ {headerText} +
+ +
+ {events} +
+
+ return response + } + else { + return
+
+ {headerText} +
+ +
+ Loading ... +
+
; + } + }, + + onEventsChanged: function (allEvents) { + var scopedEvents = [] + if(this.isModeYours()) { + allEvents.events.entries.forEach(function(event) { + if (event.authorization || event.allow_in) { + scopedEvents.push(event) + } + }) + } + else { + scopedEvents = allEvents.events.entries + } + + + this.setState({events: scopedEvents}) + } +}) \ No newline at end of file diff --git a/web/app/assets/javascripts/events/react-components/EventPage.js.jsx b/web/app/assets/javascripts/events/react-components/EventPage.js.jsx new file mode 100644 index 000000000..0417246d6 --- /dev/null +++ b/web/app/assets/javascripts/events/react-components/EventPage.js.jsx @@ -0,0 +1,220 @@ +context = window +EventActions = context.EventActions + +context.EventPage = React.createClass({ + + mixins: [Reflux.listenTo(EventStore, "onEventsChanged")], + + + getInitialState: function () { + return {submitting: false, error: null, event: null} + }, + + parseSlug: function() { + var path = window.location.pathname + + console.log("slug path: ", path) + + var pathPart = path.substring('/events/'.length) + console.log("slug part", pathPart) + + var query = pathPart.indexOf('?') + if(query > -1) { + var slug = pathPart.substring(0, query) + } + else { + var slug = pathPart + } + + + console.log("slug", slug) + return slug; + + }, + + componentDidMount: function () { + EventActions.single(this.parseSlug()) + // new Plyr('#video'); + }, + + componentDidUpdate: function() { + + }, + + authorizeDone: function(response) { + this.setState({submitting:false}) + EventActions.addAuthorization(response) + }, + + authorizeFailed: function(e) { + if(e instanceof SyntaxError) { + this.setState({error: 'Server error. Please try again or contact support@jamkazam.com.'}) + } + else if(e instanceof Error) { + this.setState({error: 'Please enter a valid Eventbrite Order ID'}) + } + else { + console.log("heheh", e) + } + this.setState({submitting:false}) + }, + + handleSubmit: function(event) { + var value = document.getElementById("order-input").value + if(value) { + context.JK.Rest2.authorizeLiveStream({order: value}).then((response) => { + if (!response.ok) { + throw Error(response.statusText); + } + return response.json() + }).then((response) => this.authorizeDone(response)).catch((jqXHR) => this.authorizeFailed(jqXHR)) + this.setState({submitting: true, error:null}) + } + event.preventDefault(); + }, + + isErrored() { + return !!this.state.error + }, + + isReady() { + return !!this.state.event + }, + + isAuthorized() { + return this.state.event.authorization || this.state.event.allow_in + }, + + isNotAuthorized() { + return false + }, + + videoCode() { + return this.state.event.youtube_code + }, + + title() { + return this.state.event.title ? this.state.event.title : 'Unknown title' + }, + + description() { + return this.state.event.description ? this.state.event.description : '' + }, + + startsAt() { + return this.state.event.starts_at? "Starts at " + new Date(this.state.event.starts_at).toLocaleString() : 'Unknown start time' + }, + + header() { + + if(this.isErrored()) { + return
+
No event found.
+
+ } + else if(this.isReady()) { + var title = this.title() + var description = this.description() + var startsAt = this.startsAt() + + return
+
{title}
+
{description}
+
{startsAt}
+
+ } + else { + return
+
Loading ...
+
+ } + }, + + body: function() { + var video = null + if(this.isErrored()) { + video = null + } + else if(this.isReady()) { + if(this.isAuthorized()) { + //video =
+ //
+ //
+ + var videoCode = this.videoCode() + if(videoCode) { + var src = "https://www.youtube.com/embed/" + this.videoCode() + "?modestbranding=true&autoplay=0&rel=0" + video =
+ +
+ } + else { + video =
No video yet
+ } + + } + else { + var notRegistered = You are not registered for this event! + var onceDone =
  • Enter your EventBrite order code at the JamKazam Event Registration page when done registering.
  • + + var eventBriteUrl = this.state.event.event_brite_registration_url + if(eventBriteUrl) { + var meat =
    + {notRegistered} +
      +
    1. Please register at EventBrite to see this video
    2. + {onceDone} +
    +
    + } + else { + var meat =
    + {notRegistered} +
      +
    1. Please find your event at EventBrite and register for this event.
    2. + {onceDone} +
    +
    + } + video =
    + {meat} +
    + } + } + else { + video =
    + } + + return video + }, + + render: function () { + + var header = this.header() + + var body = this.body() + + var response =
    + +
    + {header} +
    +
    + {body} +
    +
    + return response + }, + + onEventsChanged: function (allEvents) { + var event = null; + if(allEvents && allEvents.events && allEvents.events.entries && allEvents.events.entries.length > 0){ + event = allEvents.events.entries[0] + } + + console.log("event change", event, allEvents) + this.setState({event: event, error:allEvents.events_error}) + } +}) \ No newline at end of file diff --git a/web/app/assets/javascripts/events/react-components/EventsPage.js.jsx b/web/app/assets/javascripts/events/react-components/EventsPage.js.jsx new file mode 100644 index 000000000..67b673499 --- /dev/null +++ b/web/app/assets/javascripts/events/react-components/EventsPage.js.jsx @@ -0,0 +1,87 @@ +context = window +EventActions = context.EventActions + +context.EventsPage = React.createClass({ + + getInitialState: function () { + return {submitting: false, error: null} + }, + + componentDidMount: function () { + EventActions.refresh() + }, + + authorizeDone: function(response) { + this.setState({submitting:false}) + EventActions.addAuthorization(response) + }, + + authorizeFailed: function(e) { + if(e instanceof SyntaxError) { + this.setState({error: 'Server error. Please try again or contact support@jamkazam.com.'}) + } + else if(e instanceof Error) { + this.setState({error: 'Please enter a valid Eventbrite Order ID'}) + } + else { + console.log("heheh", e) + } + this.setState({submitting:false}) + }, + + handleSubmit: function(event) { + var value = document.getElementById("order-input").value + if(value) { + context.JK.Rest2.authorizeLiveStream({order: value}).then((response) => { + if (!response.ok) { + throw Error(response.statusText); + } + return response.json() + }).then((response) => this.authorizeDone(response)).catch((jqXHR) => this.authorizeFailed(jqXHR)) + this.setState({submitting: true, error:null}) + } + event.preventDefault(); + }, + render: function () { + + var ctaButtonClasses = "cta-button" + if(this.state.submitting) { + ctaButtonClasses = ctaButtonClasses + " submitting" + } + var errorClasses = "error" + if(this.state.error) { + errorClasses = errorClasses + " active" + } + var response =
    + +
    +
    +

    Enter your order number frombelow:

    +
    +
    +
    +
    + + +
    + {this.state.error} +
    +
    +
    +
    +
    +
    + +
    +
    + +
    +
    +
    +
    + return response + }, + +}) \ No newline at end of file diff --git a/web/app/assets/javascripts/events/react-components/actions/EventActions.js.coffee b/web/app/assets/javascripts/events/react-components/actions/EventActions.js.coffee new file mode 100644 index 000000000..69025d513 --- /dev/null +++ b/web/app/assets/javascripts/events/react-components/actions/EventActions.js.coffee @@ -0,0 +1,6 @@ + +@EventActions = Reflux.createActions({ + refresh: {}, + single: {} + addAuthorization: {} +}) \ No newline at end of file diff --git a/web/app/assets/javascripts/events/react-components/stores/EventStore.js.coffee b/web/app/assets/javascripts/events/react-components/stores/EventStore.js.coffee new file mode 100644 index 000000000..1f49243ae --- /dev/null +++ b/web/app/assets/javascripts/events/react-components/stores/EventStore.js.coffee @@ -0,0 +1,70 @@ + +context = window + +console.log(@EventActions) +console.log(window.EventActions) + +@EventStore = Reflux.createStore( + { + listenables: window.EventActions + + events: null + eventsError: null + eventStorage: null + AuthorizedEventsStorage: null + + init: -> + @AuthorizedEventStorage = context.JK.AuthorizedEventStorage + + changed: () -> + state = @getState() + @trigger(state) + + addAuthorization: (authorization) -> + @AuthorizedEventStorage.storeAuthorizedEvent(authorization) + @collateEvents() + + onRefresh: () -> + context.JK.Rest2.listLiveStreams().then((response) => response.json()).then((response) => @refreshDone(response)).catch((jqXHR) => @refreshFailed(jqXHR)) + + onSingle: (slug) -> + context.JK.Rest2.getLiveStream(slug).then((response) => response.json()).then((response) => + console.log("single", response) + if(response.errors) + @refreshFailed(response) + else + matchedFormat = {entries:[ response ]} + @refreshDone(matchedFormat)) + + .catch((jqXHR) => @refreshFailed(jqXHR)) + + refreshDone: (events) -> + + @eventsError = false + @events = events + @collateEvents() + + refreshFailed: (events) -> + console.log("events error", events) + @eventsError = true + @changed() + + collateEvents: () -> + authorizedEvents = @AuthorizedEventStorage.listAuthorizedEvents() + + for event in @events.entries + + authorization = authorizedEvents[event.id] + if authorization + console.log("event is authorized", event) + event.authorization = authorization + else + event.authorization = null + + @changed() + + getState:() -> + {events: @events, events_error: @eventsError} + + } +) diff --git a/web/app/assets/javascripts/spikes/responsive.js b/web/app/assets/javascripts/spikes/responsive.js new file mode 100644 index 000000000..9aea01813 --- /dev/null +++ b/web/app/assets/javascripts/spikes/responsive.js @@ -0,0 +1,125 @@ +var defaultURL = 'http://127.0.0.1:3000/signup'; //<---- CHANGE TO YOUR WEBSITE URL + +//show loading graphic +function showLoader(id) { + $('#' + id + ' img').fadeIn('slow'); +} + +//hdie loading graphic +function hideLoader(id) { + $('#' + id + ' img').fadeOut('slow'); +} + +//function to check load state of each frame +function allLoaded(){ + var results = []; + $('iframe').each(function(){ + if(!$(this).data('loaded')){results.push(false)} + }); + var result = (results.length > 0) ? false : true; + return result; +}; + +function loadPage($frame, url) { + if ( url.substr(0,7) !== 'http://' && url.substr(0,8) !== 'https://' && url.substr(0, 7) !== 'file://' ) { + url = 'http://'+url; + } + $('iframe').not($frame).each(function(){showLoader($(this).parent().attr('id'));}) + $('iframe').not($frame).data('loaded', false); + $('iframe').not($frame).attr('src', url); +} + +$('.frame').each(function(){showLoader($(this).attr('id'))}); + + +//when document loads +$(document).ready(function(){ + + loadPage('', defaultURL); + + //query string + var qsArray = window.location.href.split('?'); + var qs = qsArray[qsArray.length-1]; + + if(qs != '' && qsArray.length > 1){ + $('#url input[type=text]').val(qs); + loadPage('', qs); + } + + //set slidable div width + $('#frames #inner').css('width', function(){ + var width = 0; + $('.frame').each(function(){width += $(this).outerWidth() + 20}); + return width; + }); + + //add event handlers for options radio buttons + $('input[type=radio]').change(function(){ + $frames = $('#frames'); + $inputs = $('input[type=radio]:checked').val(); + + if($inputs == '1'){ + $frames.addClass('widthOnly'); + } else { + $frames.removeClass('widthOnly'); + } + }); + + //add event handlers for scrollbars checkbox + $('input[type=checkbox]').change(function(){ + var scrollBarWidth = 15; + $frames = $('#frames'); + $inputs = $('#scrollbar:checked'); + + if( $inputs.length == 0 ){ + scrollBarWidth = -15; + } + + $frames.find('iframe').each(function(i,el) { + $(el).attr('width', parseInt($(el).attr('width')) + scrollBarWidth); + }); + }); + + //when the url textbox is used + $('form').submit(function(){ + loadPage('' , $('#url input[type=text]').val()); + return false; + }); + + //when frame loads + $('iframe').load(function(){ + + var $this = $(this); + var url = ''; + var error = false; + + try{ + url = $this.contents().get(0).location.href; + } catch(e) { + error = true; + if($('#url input[type=text]').val() != ''){ + url = $('#url input[type=text]').val(); + } else { + url = defaultURL; + } + } + + //load other pages with the same URL + if(allLoaded()){ + if(error){ + alert('Browsers prevent navigation from inside iframes across domains.\nPlease use the textbox at the top for external sites.'); + loadPage('', defaultURL); + }else{ + loadPage($this, url); + } + } + + //when frame loads, hide loader graphic + else{ + error = false; + hideLoader($(this).parent().attr('id')); + $(this).data('loaded',true); + } + }); + +}); \ No newline at end of file diff --git a/web/app/assets/javascripts/wizard/gear/gear_wizard.js b/web/app/assets/javascripts/wizard/gear/gear_wizard.js index 5066986d0..ebf6202dd 100644 --- a/web/app/assets/javascripts/wizard/gear/gear_wizard.js +++ b/web/app/assets/javascripts/wizard/gear/gear_wizard.js @@ -232,8 +232,9 @@ } if(networkTest.length) { - stepNetworkTest.initialize($wizardSteps.filter($('.network-test')), self); - STEPS[dynamicStepCount++] = stepNetworkTest + // skip network test always + //stepNetworkTest.initialize($wizardSteps.filter($('.network-test')), self); + //STEPS[dynamicStepCount++] = stepNetworkTest } STEPS[dynamicStepCount++]=stepSuccess diff --git a/web/app/assets/stylesheets/events/constants.scss b/web/app/assets/stylesheets/events/constants.scss new file mode 100644 index 000000000..b0befe2fd --- /dev/null +++ b/web/app/assets/stylesheets/events/constants.scss @@ -0,0 +1,21 @@ + +$base-font-family: 'arial', sans-serif; + +$large-font-size: 1.5rem; +$standard-font-size: 1rem; +$smaller-font-size: .8rem; +$small-font-size: .7rem; +$input-font-size: 1.0rem; +$submit-button-width:9rem; +$checkbox-label-width:14rem; +$create-account-width:10rem; +$start-promo-btn-width:13rem; +$jamkazam-background:#323232; + +$copy-color-on-dark: #b9b9b9; +$copy-color-on-white: #575757; +$cta-color: #e03d04; +$chunkyBorderWidth: 6px; + + + diff --git a/web/app/assets/stylesheets/events/events.scss b/web/app/assets/stylesheets/events/events.scss new file mode 100644 index 000000000..05719c098 --- /dev/null +++ b/web/app/assets/stylesheets/events/events.scss @@ -0,0 +1,26 @@ +/** + +*= require dialogs/banner +*= require_directory ./react-components +*/ + +@import "events/constants"; + +html, +body { + min-height: 100%; + overflow:auto; + background-color:$jamkazam-background; + color:white; +} + +body { + min-height: 100%; + margin: 0; + padding: 0; + + font-size: 16px; + line-height: 1.2; + font-family: Raleway, Arial, Helvetica, sans-serif; + +} diff --git a/web/app/assets/stylesheets/events/react-components/EventList.scss b/web/app/assets/stylesheets/events/react-components/EventList.scss new file mode 100644 index 000000000..09cde5513 --- /dev/null +++ b/web/app/assets/stylesheets/events/react-components/EventList.scss @@ -0,0 +1,81 @@ +@import "events/constants"; +@import "client/common.scss"; + +.EventList { + + .event-header { + background-color: $cta-color; + color: white; + font-size: 24px; + text-align: center; + padding: 20px 0; + border-width: $chunkyBorderWidth 0 $chunkyBorderWidth; + border-style: solid; + border-color: $copy-color-on-dark; + } + + .event-section { + padding: 10px; + //s border-width: 0 0 $chunkyBorderWidth; + //border-style: solid; + //border-color: $copy-color-on-dark; + + &.loading { + text-align:center; + } + } + + .event { + display:grid; + grid-template-columns: 10rem 20rem; + align-content:center; + height:8rem; + border-style: solid; + border-width: 2px 0 2px 0; + border-color: $copy-color-on-dark; + + &:first-child { + border-width:0 0 1px 0; + } + &:last-child { + border-width:1px 0 0 0; + } + &:only-child { + border-width:0; + } + + &.no-image { + grid-template-columns: 30rem; + } + } + + .title-link { + color:white; + } + img.thumb { + width:100%; + max-height:400px; + } + div.thumb { + display: grid; + justify-content: center; + align-content: center; + text-transform: uppercase; + padding-right:1rem; + } + + .detail { + display:grid; + justify-content: center; + padding-left:1rem; + align-content:center; + } + + .title { + font-size:1.5rem; + } + + .when { + font-size:1rem; + } +} \ No newline at end of file diff --git a/web/app/assets/stylesheets/events/react-components/EventPage.scss b/web/app/assets/stylesheets/events/react-components/EventPage.scss new file mode 100644 index 000000000..cb5abbda7 --- /dev/null +++ b/web/app/assets/stylesheets/events/react-components/EventPage.scss @@ -0,0 +1,114 @@ +@import "events/constants"; +@import "client/common.scss"; + + +[data-react-class="EventPage"] { + width:100%; + + .EventPage { + width:100%; + } + + #header { + display:grid; + justify-content: center; + width:100%; + background-color:$jamkazam-background; + padding:1.5rem 0; + } + + .title { + font-size:2rem; + } + + .description { + color:gray; + font-size:1rem; + margin-top:.2rem; + } + + .starts-at { + color:gray; + font-size:1rem; + margin-top:.2rem; + } + #root { + min-height: 100%; + + align-items: center; + //justify-content: center; + min-width: 50%; + display: flex; + flex-direction: column; + } + + #player, #no-player { + position:relative; + padding-bottom:56.25%; + padding-top:30px; + height:0; + overflow:hidden; + top:-5rem; + border-width: $chunkyBorderWidth; + border-style: solid; + border-color: $copy-color-on-dark; + a { + color:#fc0; + } + } + + #no-player { + display:grid; + align-content: center; + } + + .no-register { + text-decoration: underline; + font-weight:bold; + margin-top:2rem; + display:block; + } + + .no-code { + background-color: black; + padding-bottom:0 !important; + height:auto !important; + min-height:200px !important; + padding-top:0 !important; + text-align:center; + font-size:1rem; + ul,ol { + text-align:left; + } + } + #top-container { + background-color:black; + display: grid; + justify-content:center; + padding:2rem 0 7rem 0; + text-align:center; + } + + div.root { + flex-grow: 1; + overflow: auto; + } + + html { + font-size: 100%; + } + + .listing { + width:50%; + margin:auto; + //display:grid; + //justify-content: center; + } + + + @media (min-width:1px) and (max-width: 1199px) { + html { + font-size: 100%; + } + } +} \ No newline at end of file diff --git a/web/app/assets/stylesheets/events/react-components/EventsPage.scss b/web/app/assets/stylesheets/events/react-components/EventsPage.scss new file mode 100644 index 000000000..2c50fdcda --- /dev/null +++ b/web/app/assets/stylesheets/events/react-components/EventsPage.scss @@ -0,0 +1,169 @@ +@import "events/constants"; +@import "client/common.scss"; + + +[data-react-class="EventsPage"] { + width:100%; + + .EventsPage { + width:100%; + } + + #header { + display:grid; + justify-content: center; + width:100%; + background-color:$jamkazam-background; + padding:1.5rem 0; + } + + #root { + min-height: 100%; + + align-items: center; + //justify-content: center; + min-width: 50%; + display: flex; + flex-direction: column; + } + + + + .order-input { + font-size:1rem; + padding:5px; + } + + .cta-button { + cursor:pointer; + font-size: 1rem; + color: white; + background-color: $cta-color; + text-align: center; + padding: 5px; + margin-left:5px; + border-radius: 8px; + border: 1px outset buttonface; + font-family: Raleway, Arial, Helvetica, sans-serif; + + &.submitting { + background-color: gray; + } + } + .error { + margin-top: 1rem; + #position: absolute; + display:none; + color:red; + font-weight:bold; + &.active { + display:inline; + } + } + .eventbrite-logo { + height:2rem; + vertical-align: middle; + } + #top-container { + background-color:black; + display: grid; + justify-content:center; + padding:5rem 0 7rem 0; + } + + div.root { + flex-grow: 1; + overflow: auto; + } + + .logo-holder { + max-width: 300px; + min-width:0; + img { + width: 100%; + } + } + + + .submit-row { + text-align:center; + } + p { + margin:.5rem 0; + } + p:first-child { + margin-top:0; + } + p.last-child { + margin-bottom:0; + } + + .header { + font-size: $large-font-size; + margin-top: 1rem; + width: 100%; + max-width: 700px; + text-align: center; + } + + html { + font-size: 100%; + } + + .listings { + display:grid; + justify-content: center; + width:100%; + } + + .preview-and-action-box { + background-color: black; + position:relative; + top: -2rem; + min-width: 400px; + @include border_box_sizing; + border-width: 0 $chunkyBorderWidth $chunkyBorderWidth; + border-style: solid; + border-color: $copy-color-on-dark; + z-index: 1; + } + + @media (min-width:1px) and (max-width: 1199px) { + html { + font-size: 100%; + } + .code-input { + max-width:$submit-button-width; + } + .code-input input { + font-size: $standard-font-size; + } + .code-instructions { + width:$submit-button-width; + } + + .text-input input { + width:$create-account-width; + } + .select-input select{ + width:$create-account-width; + } + + .instructions.create-account { + margin-top:1rem; + text-align:justify; + } + .success-msg { + margin-top: 1rem; + } + .logo-holder.create-account { + margin-top:2rem; + } + .code-form.create-account { + margin-top:1rem; + } + .powered-by.create-account { + margin-top:0.5rem; + } + } +} \ No newline at end of file diff --git a/web/app/controllers/api_live_streams_controller.rb b/web/app/controllers/api_live_streams_controller.rb new file mode 100644 index 000000000..2993171fd --- /dev/null +++ b/web/app/controllers/api_live_streams_controller.rb @@ -0,0 +1,47 @@ + +class ApiLiveStreamsController < ApiController + + + respond_to :json + + def log + @log || Logging.logger[ApiLiveStreamsController] + end + + + def index + @live_streams = LiveStream.upcoming + + render "api_live_streams/index", :layout => nil + end + + def claim + order = params[:order] + if order.nil? + render :json => {}, :status => 404, layout: nil + return + end + + order.strip! + + if order.start_with? "#" + order = order[1..-1] + end + + event_brite_order = EventBriteOrder.find_by_order_id(order) + + if event_brite_order.nil? + render :json => {}, :status => 404, layout: nil + else + EventBriteOrder.where(id: event_brite_order.id).update_all(times_claimed: event_brite_order.times_claimed + 1) + render :json => {event_id: event_brite_order.live_stream.id, order_id: event_brite_order.order_id, event_type: 'eventbrite'}, :status => :ok, layout: nil + end + end + + def show + slug = params[:slug] + @live_stream = LiveStream.find_by_slug!(slug) + render "api_live_streams/show", :layout => nil + end + +end diff --git a/web/app/controllers/events_controller.rb b/web/app/controllers/events_controller.rb index 71b5288fb..f5866f775 100644 --- a/web/app/controllers/events_controller.rb +++ b/web/app/controllers/events_controller.rb @@ -2,7 +2,20 @@ class EventsController < ApplicationController respond_to :html + + def list + render 'events', layout: 'events' + end + + def show + @event = LiveStream.find_by_slug(params[:slug]) + + render 'event', :layout => 'events' + end + + # 2014 lulz + def show_old @event = Event.find_by_slug!(params[:slug]) render 'event', :layout => "web" end diff --git a/web/app/controllers/spikes_controller.rb b/web/app/controllers/spikes_controller.rb index 069a2d757..ba4580614 100644 --- a/web/app/controllers/spikes_controller.rb +++ b/web/app/controllers/spikes_controller.rb @@ -31,6 +31,10 @@ class SpikesController < ApplicationController render :layout => false end + def responsive + render :layout => false + end + def subscription #Notification.send_reload(MessageFactory::ALL_NATIVE_CLIENTS) diff --git a/web/app/views/api_live_streams/index.rabl b/web/app/views/api_live_streams/index.rabl new file mode 100644 index 000000000..2d451824c --- /dev/null +++ b/web/app/views/api_live_streams/index.rabl @@ -0,0 +1,4 @@ + +node :entries do |page| + partial "api_live_streams/show", object: @live_streams +end \ No newline at end of file diff --git a/web/app/views/api_live_streams/show.rabl b/web/app/views/api_live_streams/show.rabl new file mode 100644 index 000000000..7c5ef608a --- /dev/null +++ b/web/app/views/api_live_streams/show.rabl @@ -0,0 +1,3 @@ +object @live_stream + +attributes :id, :slug, :title, :description, :starts_at, :allow_in, :ends_at, :img_url, :img_width, :img_height, :youtube_code, :eventbriteid, :event_type, :event_brite_registration_url, :white_label_player \ No newline at end of file diff --git a/web/app/views/events/event.slim b/web/app/views/events/event.slim new file mode 100644 index 000000000..7ddad31ba --- /dev/null +++ b/web/app/views/events/event.slim @@ -0,0 +1,6 @@ +- provide(:page_name, 'JamKazam Event') +- provide(:description, @description) +- provide(:title, @title) + +#root + = react_component 'EventPage' diff --git a/web/app/views/events/event.html.haml b/web/app/views/events/event_old.html.haml similarity index 100% rename from web/app/views/events/event.html.haml rename to web/app/views/events/event_old.html.haml diff --git a/web/app/views/events/events.slim b/web/app/views/events/events.slim new file mode 100644 index 000000000..e499e9061 --- /dev/null +++ b/web/app/views/events/events.slim @@ -0,0 +1,6 @@ +- provide(:page_name, 'Upcoming JamKazam Events') +- provide(:description, @description) +- provide(:title, @title) + +#root + = react_component 'EventsPage' diff --git a/web/app/views/layouts/events.html.erb b/web/app/views/layouts/events.html.erb new file mode 100644 index 000000000..1f7d46b6b --- /dev/null +++ b/web/app/views/layouts/events.html.erb @@ -0,0 +1,36 @@ + + + + <%= full_title(yield(:title)) %> + + + + + <%= stylesheet_link_tag "events/events", media: "all" %> + <%= include_gon(:init => true) %> + <%= csrf_meta_tags %> + + + <% if content_for?(:social_meta) %> + <%= yield(:social_meta) %> + <% else %> + <%= render "layouts/social_meta" %> + <% end %> + <%= render "shared/ad_sense" %> + + +
    + + <%= javascript_include_tag "events/events" %> +
    + <%= yield %> +
    +
    +
    +<%= render "clients/help" %> +<%= render 'dialogs/banner' %> +<%= render "shared/ga" %> + + \ No newline at end of file diff --git a/web/app/views/spikes/responsive.html.erb b/web/app/views/spikes/responsive.html.erb new file mode 100644 index 000000000..0a5e88171 --- /dev/null +++ b/web/app/views/spikes/responsive.html.erb @@ -0,0 +1,70 @@ + + + + + + Responsive Design Testing + + + +
    +
    + + +
    +
    +
    + +
    +
    +
    +
    +
    +
    +

    240 x 320 (small phone)

    + +
    +
    +

    320 x 480 (iPhone)

    + +
    +
    +

    480 x 640 (small tablet)

    + +
    +
    +

    768 x 1024 (iPad - Portrait)

    + +
    +
    +

    1024 x 768 (iPad - Landscape)

    + +
    +
    +
    +A tool by Matt Kersley - Fork it on Github
    +Note: The content width may not be pixel perfect - I have added 15px to the iframes to cater for the scrollbars + + +<%= javascript_include_tag "spikes/responsive.js" %> + + + \ No newline at end of file diff --git a/web/config/application.rb b/web/config/application.rb index eae92ffc2..171b42b88 100644 --- a/web/config/application.rb +++ b/web/config/application.rb @@ -469,5 +469,5 @@ if defined?(Bundler) config.rating_dialog_min_time = 60 config.rating_dialog_min_num = 1 - end + end end diff --git a/web/config/routes.rb b/web/config/routes.rb index efceaf231..bdba4420c 100644 --- a/web/config/routes.rb +++ b/web/config/routes.rb @@ -131,6 +131,7 @@ Rails.application.routes.draw do get '/gmail_contacts', to: 'gmail#gmail_contacts' get '/events/:slug', to: 'events#show', :as => 'event' + get '/events', to: 'events#list', as: 'events' get '/endorse/:id/:service', to: 'users#endorse', :as => 'endorse' @@ -143,6 +144,7 @@ Rails.application.routes.draw do require 'resque-retry/server' mount Resque::Server.new, :at => "/resque" if Rails.env == "development" + get '/responsive', to: 'spikes#responsive' # route to spike controller (proof-of-concepts) get '/listen_in', to: 'spikes#listen_in' get '/facebook_invite', to: 'spikes#facebook_invite' @@ -229,6 +231,11 @@ Rails.application.routes.draw do post '/auths/login' => 'api_auths#login' + # live streams + match '/live_streams' => 'api_live_streams#index', :via => :get + match '/live_streams/claim' => 'api_live_streams#claim', :via => :post + match '/live_streams/:slug' => 'api_live_streams#show', :via => :get + # music sessions match '/sessions/:id/participants/legacy' => 'api_music_sessions#participant_create_legacy', :via => :post # can be removed when new Create Session comes in match '/sessions/:id/participants' => 'api_music_sessions#participant_create', :via => :post