Revert "Stop using user agent to detect mobile devices. Use a media query and yepnope to load the appropriate css and customizations."

This commit is contained in:
Neil Lalonde 2013-12-09 16:28:11 -05:00
parent ef432902ce
commit 2596f7dec2
8 changed files with 148 additions and 95 deletions

View file

@ -5,19 +5,30 @@
@module Mobile @module Mobile
**/ **/
Discourse.Mobile = { Discourse.Mobile = {
isMobileDevice: false,
mobileView: false, mobileView: false,
init: function() { init: function() {
var $html = $('html'); var $html = $('html');
this.isMobileDevice = $html.hasClass('mobile-device');
this.mobileView = $html.hasClass('mobile-view'); this.mobileView = $html.hasClass('mobile-view');
if (localStorage && localStorage.mobileView) {
var savedValue = (localStorage.mobileView === 'true' ? true : false);
if (savedValue !== this.mobileView) {
this.reloadPage(savedValue);
}
}
}, },
toggleMobileView: function() { toggleMobileView: function() {
if (localStorage) { if (localStorage) {
localStorage.mobileView = !this.mobileView; localStorage.mobileView = !this.mobileView;
} }
window.location.reload(); this.reloadPage(!this.mobileView);
} },
reloadPage: function(mobile) {
window.location.assign(window.location.pathname + '?mobile_view=' + (mobile ? '1' : '0'));
}
}; };

View file

@ -26,6 +26,7 @@ class ApplicationController < ActionController::Base
end end
before_filter :set_locale before_filter :set_locale
before_filter :set_mobile_view
before_filter :inject_preview_style before_filter :inject_preview_style
before_filter :disable_customization before_filter :disable_customization
before_filter :block_if_maintenance_mode before_filter :block_if_maintenance_mode
@ -119,6 +120,10 @@ class ApplicationController < ActionController::Base
end end
end end
def set_mobile_view
session[:mobile_view] = params[:mobile_view] if params.has_key?(:mobile_view)
end
def inject_preview_style def inject_preview_style
style = request['preview-style'] style = request['preview-style']
session[:preview_style] = style if style session[:preview_style] = style if style

View file

@ -19,6 +19,10 @@ module ApplicationHelper
end end
end end
def html_classes
"#{mobile_view? ? 'mobile-view' : 'desktop-view'} #{mobile_device? ? 'mobile-device' : 'not-mobile-device'}"
end
def escape_unicode(javascript) def escape_unicode(javascript)
if javascript if javascript
javascript = javascript.dup.force_encoding("utf-8") javascript = javascript.dup.force_encoding("utf-8")
@ -111,8 +115,18 @@ module ApplicationHelper
"#{Discourse::base_uri}/login" "#{Discourse::base_uri}/login"
end end
def stylesheet_filenames(target=:desktop) def mobile_view?
[asset_path("#{target}.css"), customization_disabled? ? nil : SiteCustomization.custom_stylesheet_path(session[:preview_style], target)].compact return false unless SiteSetting.enable_mobile_theme
if session[:mobile_view]
session[:mobile_view] == '1'
else
mobile_device?
end
end
def mobile_device?
# TODO: this is dumb. user agent matching is a doomed approach. a better solution is coming.
request.user_agent =~ /Mobile|webOS|Nexus 7/ and !(request.user_agent =~ /iPad/)
end end
def customization_disabled? def customization_disabled?

View file

@ -83,12 +83,6 @@ class SiteCustomization < ActiveRecord::Base
style.stylesheet_link_tag(target).html_safe if style style.stylesheet_link_tag(target).html_safe if style
end end
def self.custom_stylesheet_path(preview_style, target=:desktop)
preview_style ||= enabled_style_key
style = lookup_style(preview_style)
style.stylesheet_relative_path(target) if style
end
def self.custom_header(preview_style, target=:desktop) def self.custom_header(preview_style, target=:desktop)
preview_style ||= enabled_style_key preview_style ||= enabled_style_key
style = lookup_style(preview_style) style = lookup_style(preview_style)
@ -181,18 +175,14 @@ class SiteCustomization < ActiveRecord::Base
return "" unless stylesheet.present? return "" unless stylesheet.present?
return @stylesheet_link_tag if @stylesheet_link_tag return @stylesheet_link_tag if @stylesheet_link_tag
ensure_stylesheets_on_disk! ensure_stylesheets_on_disk!
@stylesheet_link_tag = "<link class=\"custom-css\" rel=\"stylesheet\" href=\"#{stylesheet_relative_path(:desktop)}\" type=\"text/css\" media=\"screen\">" @stylesheet_link_tag = "<link class=\"custom-css\" rel=\"stylesheet\" href=\"/#{CACHE_PATH}#{stylesheet_filename}?#{stylesheet_hash}\" type=\"text/css\" media=\"screen\">"
end end
def mobile_stylesheet_link_tag def mobile_stylesheet_link_tag
return "" unless mobile_stylesheet.present? return "" unless mobile_stylesheet.present?
return @mobile_stylesheet_link_tag if @mobile_stylesheet_link_tag return @mobile_stylesheet_link_tag if @mobile_stylesheet_link_tag
ensure_stylesheets_on_disk! ensure_stylesheets_on_disk!
@mobile_stylesheet_link_tag = "<link class=\"custom-css\" rel=\"stylesheet\" href=\"#{stylesheet_relative_path(:mobile)}\" type=\"text/css\" media=\"screen\">" @mobile_stylesheet_link_tag = "<link class=\"custom-css\" rel=\"stylesheet\" href=\"/#{CACHE_PATH}#{stylesheet_filename(:mobile)}?#{stylesheet_hash(:mobile)}\" type=\"text/css\" media=\"screen\">"
end
def stylesheet_relative_path(target=:desktop)
"/#{CACHE_PATH}#{stylesheet_filename(target)}?#{stylesheet_hash(target)}"
end end
end end

View file

@ -1,42 +1,15 @@
<%- unless SiteCustomization.override_default_style(session[:preview_style]) %> <%- unless SiteCustomization.override_default_style(session[:preview_style]) %>
<% if mobile_view? %>
<script type="text/javascript"> <%= stylesheet_link_tag "mobile" %>
(function() { <% else %>
var h = document.getElementsByTagName('html')[0], <%= stylesheet_link_tag "desktop" %>
isMobileView = (localStorage && localStorage.mobileView) ? (localStorage.mobileView === 'true') : <% end %>
Modernizr.mq("only screen and (max-width: 480px), only screen and (max-device-width: 480px)");
h.className += (isMobileView ? ' mobile-view' : ' desktop-view');
Modernizr.load({
test: isMobileView,
yep: <%= stylesheet_filenames(:mobile).inspect.html_safe %>,
nope: <%= stylesheet_filenames(:desktop).inspect.html_safe %>,
complete: function() {
// CSS file(s) have loaded.
$(function() {
setTimeout(function() {
// Use setTimeout to make this happen in the next event loop.
// Trying to avoid a FOUC (flash of unstyled content).
if (isMobileView) {
$('#custom-mobile-header').show();
} else {
$('#custom-desktop-header').show();
}
$('#js-app').attr('style', ''); // Show everything.
$(window).trigger('scroll.discourse-dock'); // Calc header div positions now that they're visible.
}, 1);
});
}
});
})();
</script>
<%- end %> <%- end %>
<noscript>
<%= stylesheet_link_tag "desktop" %>
</noscript>
<%- if staff? %> <%- if staff? %>
<%= stylesheet_link_tag "admin"%> <%= stylesheet_link_tag "admin"%>
<%-end%> <%-end%>
<%- unless customization_disabled? %>
<%= SiteCustomization.custom_stylesheet(session[:preview_style], mobile_view? ? :mobile : :desktop) %>
<%- end %>

View file

@ -1,5 +1,5 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="<%= SiteSetting.default_locale %>"> <html lang="<%= SiteSetting.default_locale %>" class="<%= html_classes %>">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<title><%= content_for?(:title) ? yield(:title) + ' - ' + SiteSetting.title : SiteSetting.title %></title> <title><%= content_for?(:title) ? yield(:title) + ' - ' + SiteSetting.title : SiteSetting.title %></title>
@ -20,55 +20,48 @@
<%= javascript_include_tag "admin"%> <%= javascript_include_tag "admin"%>
<%- end %> <%- end %>
<%= render :partial => "common/discourse_stylesheet" %>
<%= render :partial => "common/special_font_face" %> <%= render :partial => "common/special_font_face" %>
<%= render :partial => "common/discourse_stylesheet" %>
<%= discourse_csrf_tags %> <%= discourse_csrf_tags %>
<%= yield :head %> <%= yield :head %>
</head> </head>
<body class="css-loading"> <body>
<!--[if IE 9]><script type="text/javascript">ie = "new";</script><![endif]--> <!--[if IE 9]><script type="text/javascript">ie = "new";</script><![endif]-->
<div id="js-app" style="display: none;"> <%- unless customization_disabled? %>
<%- unless customization_disabled? %> <%= SiteCustomization.custom_header(session[:preview_style], mobile_view? ? :mobile : :desktop) %>
<div id='custom-desktop-header' style='display: none;'> <%- end %>
<%= SiteCustomization.custom_header(session[:preview_style], :desktop) %>
</div>
<div id='custom-mobile-header' style='display: none;'>
<%= SiteCustomization.custom_header(session[:preview_style], :mobile) %>
</div>
<%- end %>
<section id='main'> <section id='main'>
</section> </section>
<% unless current_user %> <% unless current_user %>
<form id='hidden-login-form' method="post" action="<%=login_path%>" style="display: none;"> <form id='hidden-login-form' method="post" action="<%=login_path%>" style="display: none;">
<input name="username" type="text" id="signin_username"> <input name="username" type="text" id="signin_username">
<input name="password" type="password" id="signin_password"> <input name="password" type="password" id="signin_password">
<input name="redirect" type="hidden"> <input name="redirect" type="hidden">
<input type="submit" id="signin-button" value="Log In"> <input type="submit" id="signin-button" value="Log In">
</form> </form>
<% end %>
<%- if @preloaded.present? %>
<script>
<%- @preloaded.each do |key, json| %>
PreloadStore.store("<%= key %>",<%= escape_unicode(json) %>);
<% end %> <% end %>
</script>
<%- end %>
<%- if @preloaded.present? %> <%= yield :data %>
<script>
<%- @preloaded.each do |key, json| %>
PreloadStore.store("<%= key %>",<%= escape_unicode(json) %>);
<% end %>
</script>
<%- end %>
<%= yield :data %> <footer id='bottom'></footer>
<footer id='bottom'></footer> <%= render :partial => "common/discourse_javascript" %>
<%= render :partial => "common/discourse_javascript" %> <%= render_google_analytics_code %>
<%= render_google_analytics_code %>
</div>
<noscript data-path="<%= request.env['PATH_INFO'] %>"> <noscript data-path="<%= request.env['PATH_INFO'] %>">
<header class="d-header"> <header class="d-header">

View file

@ -11,4 +11,78 @@ describe ApplicationHelper do
end end
end end
describe "mobile_view?" do
context "enable_mobile_theme is true" do
before do
SiteSetting.stubs(:enable_mobile_theme).returns(true)
end
it "is true if mobile_view is '1' in the session" do
session[:mobile_view] = '1'
helper.mobile_view?.should be_true
end
it "is false if mobile_view is '0' in the session" do
session[:mobile_view] = '0'
helper.mobile_view?.should be_false
end
context "mobile_view is not set" do
it "is false if user agent is not mobile" do
controller.request.stubs(:user_agent).returns('Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.17 Safari/537.36')
helper.mobile_view?.should be_false
end
it "is true for iPhone" do
controller.request.stubs(:user_agent).returns('Mozilla/5.0 (iPhone; U; ru; CPU iPhone OS 4_2_1 like Mac OS X; ru) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8C148a Safari/6533.18.5')
helper.mobile_view?.should be_true
end
it "is false for iPad" do
controller.request.stubs(:user_agent).returns("Mozilla/5.0 (iPad; CPU OS 5_1 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9B176 Safari/7534.48.3")
helper.mobile_view?.should be_false
end
it "is false for Nexus 10 tablet" do
controller.request.stubs(:user_agent).returns("Mozilla/5.0 (Linux; Android 4.2.1; Nexus 10 Build/JOP40D) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166 Safari/535.19")
helper.mobile_view?.should be_false
end
it "is true for Nexus 7 tablet" do
controller.request.stubs(:user_agent).returns("Mozilla/5.0 (Linux; Android 4.1.2; Nexus 7 Build/JZ054K) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166 Safari/535.19")
helper.mobile_view?.should be_true
end
end
end
context "enable_mobile_theme is false" do
before do
SiteSetting.stubs(:enable_mobile_theme).returns(false)
end
it "is false if mobile_view is '1' in the session" do
session[:mobile_view] = '1'
helper.mobile_view?.should be_false
end
it "is false if mobile_view is '0' in the session" do
session[:mobile_view] = '0'
helper.mobile_view?.should be_false
end
context "mobile_view is not set" do
it "is false if user agent is not mobile" do
controller.request.stubs(:user_agent).returns('Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.17 Safari/537.36')
helper.mobile_view?.should be_false
end
it "is false for iPhone" do
controller.request.stubs(:user_agent).returns('Mozilla/5.0 (iPhone; U; ru; CPU iPhone OS 4_2_1 like Mac OS X; ru) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8C148a Safari/6533.18.5')
helper.mobile_view?.should be_false
end
end
end
end
end end

View file

@ -5,9 +5,7 @@
var $buo = function() { var $buo = function() {
var badAndroid = false, var badAndroid = false, ua = null;
haveJquery = (typeof($) !== 'undefined'),
ua = null;
// Sometimes we have to resort to parsing the user agent string. :( // Sometimes we have to resort to parsing the user agent string. :(
if (navigator && navigator.userAgent) { if (navigator && navigator.userAgent) {
@ -55,11 +53,6 @@ var $buo = function() {
// shift the body down to make room for our notification div // shift the body down to make room for our notification div
document.body.style.marginTop = (div.clientHeight) + "px"; document.body.style.marginTop = (div.clientHeight) + "px";
if (!haveJquery) {
var h = document.getElementsByTagName('html')[0];
h.className = h.className.replace(/(\s|^)css-loading(\s|$)/g, ' ');
}
}; };
$bu=$buo(); $bu=$buo();