Implemented a proper mobile view, designed for viewing on a mobile phone browser (so far only tested on 'small screen' view of Opera, which mimics Opera mini). To get the mobile view, enter the URL http://yoururl.com/mobile/

That will take you to the login page, and then to the mobile view. I've tried to make the interface as functional as possible, while still fitting neatly on a small screen, being very lightweight in terms of page size (those data plans are expensive!), and not requiring too much messing about with a phone keyboard. 

The main screen lists all uncompleted next actions, 6 per page. If you select the double right arrow link, you'll be taken to a detail view which doubles as a place to view all the details, or an editing page (hit the back button to get back if you're just viewing, update to commit your edits).

At the bottom of each page there are two select boxes which allow you to filter the view to a particular context or project.

It needs a little more work, but it's quite functional right now.



git-svn-id: http://www.rousette.org.uk/svn/tracks-repos/trunk@263 a4c988fc-2ded-0310-b66e-134b36920a42
This commit is contained in:
bsag 2006-06-18 17:17:34 +00:00
parent da22d34962
commit 2ca153a771
16 changed files with 300 additions and 33 deletions

View file

@ -11,7 +11,7 @@ class ApplicationController < ActionController::Base
helper :application
include LoginSystem
before_filter :set_session_expiration
before_filter :get_current_user

View file

@ -0,0 +1,85 @@
class MobileController < ApplicationController
model :user
model :project
model :context
model :todo
layout 'mobile'
prepend_before_filter :login_required
# Plain list of all next actions, paginated 6 per page
# Sorted by due date, then creation date
#
def list
self.init
@page_title = @desc = "All actions"
@todos_pages, @todos = paginate( :todos, :order => 'due IS NULL, due ASC, created_at ASC',
:conditions => ['user_id = ? and type = ? and done = ?', @user.id, "Immediate", false],
:per_page => 6 )
end
def detail
self.init
@item = check_user_return_item
@place = @item.context.id
end
def update_action
if params[:id]
@item = check_user_return_item
else
@item = @user.todos.build
end
@item.attributes = params[:item]
if @item.save
redirect_to :action => 'list'
else
flash["warning"] = "Action could not be saved"
redirect_to :action => 'list'
end
end
def show_add_form
self.init
end
def filter
self.init
case params[:type]
when 'context'
@context = Context.find( params[:context][:id] )
@page_title = @desc = "#{@context.name}"
@todos_pages, @todos = paginate( :todos, :order => 'due IS NULL, due ASC, created_at ASC',
:conditions => ['user_id = ? and type = ? and done = ? and context_id = ?', @user.id, "Immediate", false, @context.id], :per_page => 6 )
when 'project'
@project = Project.find( params[:project][:id] )
@page_title = @desc = "#{@project.name}"
@todos_pages, @todos = paginate( :todos, :order => 'due IS NULL, due ASC, created_at ASC',
:conditions => ['user_id = ? and type = ? and done = ? and project_id = ?', @user.id, "Immediate", false, @project.id], :per_page => 6 )
end
end
protected
def check_user_return_item
item = Todo.find( params['id'] )
if @user == item.user
return item
else
flash["warning"] = "Item and session user mis-match: #{item.user.name} and #{@user.name}!"
render_text ""
end
end
def init
@contexts = Context.find :all, :order => 'position ASC',
:conditions => ['user_id = ?', @user.id]
@projects = Project.find :all, :order => 'position ASC',
:conditions => ['user_id = ? and done = ?', @user.id, false]
end
end

View file

@ -50,4 +50,65 @@ module ApplicationHelper
"<a href=\"#{url}\"#{tag_options}#{id_tag}>#{name || url}</a>"
end
# Check due date in comparison to today's date
# Flag up date appropriately with a 'traffic light' colour code
#
def due_date(due)
if due == nil
return ""
end
@now = Date.today
@days = due-@now
case @days
# overdue or due very soon! sound the alarm!
when -1000..-1
"<a title='" + format_date(due) + "'><span class=\"red\">Overdue by " + (@days * -1).to_s + " days</span></a> "
when 0
"<a title='" + format_date(due) + "'><span class=\"amber\">Due Today</span></a> "
when 1
"<a title='" + format_date(due) + "'><span class=\"amber\">Due Tomorrow</span></a> "
# due 2-7 days away
when 2..7
if @user.preferences["due_style"] == "1"
"<a title='" + format_date(due) + "'><span class=\"orange\">Due on " + due.strftime("%A") + "</span></a> "
else
"<a title='" + format_date(due) + "'><span class=\"orange\">Due in " + @days.to_s + " days</span></a> "
end
# more than a week away - relax
else
"<a title='" + format_date(due) + "'><span class=\"green\">Due in " + @days.to_s + " days</span></a> "
end
end
# Check due date in comparison to today's date
# Flag up date appropriately with a 'traffic light' colour code
# Modified method for mobile screen
#
def due_date_mobile(due)
if due == nil
return ""
end
@now = Date.today
@days = due-@now
case @days
# overdue or due very soon! sound the alarm!
when -1000..-1
"<span class=\"red\">" + format_date(due) +"</span>"
when 0
"<span class=\"amber\">"+ format_date(due) + "</span>"
when 1
"<span class=\"amber\">" + format_date(due) + "</span>"
# due 2-7 days away
when 2..7
"<span class=\"orange\">" + format_date(due) + "</span>"
# more than a week away - relax
else
"<span class=\"green\">" + format_date(due) + "</span>"
end
end
end

View file

@ -0,0 +1,2 @@
module MobileHelper
end

View file

@ -63,38 +63,6 @@ module TodoHelper
end
end
# Check due date in comparison to today's date
# Flag up date appropriately with a 'traffic light' colour code
#
def due_date(due)
if due == nil
return ""
end
@now = Date.today
@days = due-@now
case @days
# overdue or due very soon! sound the alarm!
when -1000..-1
"<a title='" + format_date(due) + "'><span class=\"red\">Overdue by " + (@days * -1).to_s + " days</span></a> "
when 0
"<a title='" + format_date(due) + "'><span class=\"amber\">Due Today</span></a> "
when 1
"<a title='" + format_date(due) + "'><span class=\"amber\">Due Tomorrow</span></a> "
# due 2-7 days away
when 2..7
if @user.preferences["due_style"] == "1"
"<a title='" + format_date(due) + "'><span class=\"orange\">Due on " + due.strftime("%A") + "</span></a> "
else
"<a title='" + format_date(due) + "'><span class=\"orange\">Due in " + @days.to_s + " days</span></a> "
end
# more than a week away - relax
else
"<a title='" + format_date(due) + "'><span class=\"green\">Due in " + @days.to_s + " days</span></a> "
end
end
# Check show_from date in comparison to today's date
# Flag up date appropriately with a 'traffic light' colour code
#

View file

@ -0,0 +1,15 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<%= stylesheet_link_tag "mobile" %>
<title><%= @page_title %></title>
</head>
<body>
<%= yield %>
</body>
</html>

View file

@ -0,0 +1,14 @@
<p><label for="item_description">Description</label></p>
<p><%= text_field( "item", "description", "size" => 25, "tabindex" => 1) %></p>
<p><label for="item_notes">Notes</label></p>
<p><%= text_area( "item", "notes", "cols" => 25, "rows" => 10, "tabindex" => 2) %></p>
<p><label for="item_context_id">Context</label></p>
<p><%= collection_select( "item", "context_id", @contexts, "id", "name",
{}, {"tabindex" => 3}) %></p>
<p><label for="item_project_id">Project</label></p>
<p><%= collection_select( "item", "project_id", @projects, "id", "name",
{ :include_blank => true }, {"tabindex" => 4}) %></p>
<p><label for="item_due">Due</label></p>
<p><%= text_field("item", "due", "size" => 10, "tabindex" => 5, "autocomplete" => "off") %></p>
<%= hidden_field("type", "new") %>
<p><input type="submit" value="Add action" tabindex="6"></p>

View file

@ -0,0 +1,27 @@
<ul>
<% for todo in @todos -%>
<li>
<%= link_to "&raquo;", :controller => 'mobile', :action => 'detail', :id => todo.id %>
<% if todo.due? -%>
<%= due_date_mobile(todo.due) %>
<% end -%>
<%= todo.description %>
(<em><%= todo.context.name %></em>)
<% end -%>
</li>
<ul>
<hr />
Pages: <%= pagination_links( @todos_pages, :always_show_anchors => true ) %>
<hr />
<%= form_tag( { :action => "filter", :type => "context" } ) %>
<%= collection_select( "context", "id", @contexts, "id", "name",
{ :include_blank => true } ) %>
<%= submit_tag( value = "Go" ) %>
<%= end_form_tag %>
<%= form_tag( {:action => "filter", :type => "project" }) %>
<%= collection_select( "project", "id", @projects, "id", "name",
{ :include_blank => true } ) %>
<%= submit_tag( value = "Go" ) %>
<%= end_form_tag %>

View file

@ -0,0 +1,14 @@
<%= error_messages_for("item") %>
<p><label for="item_done">Done?</label></p>
<p><%= check_box("item", "done", "tabindex" => 1) %></p>
<p><label for="item_description">Next action</label></p>
<p><%= text_field( "item", "description", "tabindex" => 2) %></p>
<p><label for="item_notes">Notes</label></p>
<p><%= text_area( "item", "notes", "cols" => 30, "rows" => 5, "tabindex" => 3) %></p>
<p><label for="item_context_id">Context</label></p>
<p><%= collection_select( "item", "context_id", @contexts, "id", "name", {"tabindex" => 4} ) %></p> <p><label for="item_project_id">Project</label></p>
<p><%= collection_select( "item", "project_id", @projects, "id", "name",
{:include_blank => true}, {"tabindex" => 5} ) %></p>
<p><label for="item_due">Due</label></p>
<p><%= date_select("item", "due", :order => [:day, :month, :year], :include_blank => true) %></p>
<p><input type="submit" value="Update" tabindex="6" /></p>

View file

@ -0,0 +1,4 @@
<%= form_tag :action => 'update_action', :id => @item.id %>
<%= render :partial => 'mobile_edit', :locals => {:type => "updated"} %>
<%= end_form_tag %>
<%= button_to "Back", :controller => 'mobile', :action => 'list' %>

View file

@ -0,0 +1,5 @@
<h1><%= @desc %>
<%= link_to "+", :controller => 'mobile', :action => 'add_action' %></h1>
<hr />
<%= render :partial => 'mobile_actions' %>
<%= link_to "View All", :controller => 'mobile', :action => 'list' %>

View file

@ -0,0 +1,4 @@
<h1><%= @desc %>
<%= link_to "+", :controller => 'mobile', :action => 'show_add_form' %></h1>
<hr />
<%= render :partial => 'mobile_actions' %>

View file

@ -0,0 +1,4 @@
<%= form_tag :action => 'update_action' %>
<%= render :partial => 'mobile_edit', :locals => {:type => "new"} %>
<%= end_form_tag %>
<%= button_to "Back", :controller => 'mobile', :action => 'list' %>

View file

@ -17,6 +17,10 @@ ActionController::Routing::Routes.draw do |map|
# Index Route
map.connect '', :controller => 'todo', :action => 'list'
# Mobile/lite version
map.connect 'mobile', :controller => 'mobile', :action => 'list'
map.connect 'mobile/add_action', :controller => 'mobile', :action => 'show_add_form'
# Login Routes
map.connect 'login', :controller => 'login', :action => 'login'
map.connect 'logout', :controller => 'login', :action => 'logout'

View file

@ -0,0 +1,42 @@
a {text-decoration: none;}
h1 {color: #f00;}
ul {list-style-type: none;}
/* Draw attention to some text
Same format as traffic lights */
.red {
color: #fff;
background: #f00;
padding: 1px;
font-size: 10px;
}
.amber {
color: #fff;
background: #ff6600;
padding: 1px;
font-size: 10px;
}
.orange {
color: #fff;
background: #FFA500;
padding: 1px;
font-size: 10px;
}
.green {
color: #fff;
background: #33cc00;
padding: 1px;
font-size: 10px;
}
.grey {
color: #fff;
background: #999;
padding: 1px;
font-size: 10px;
}

View file

@ -0,0 +1,18 @@
require File.dirname(__FILE__) + '/../test_helper'
require 'mobile_controller'
# Re-raise errors caught by the controller.
class MobileController; def rescue_action(e) raise e end; end
class MobileControllerTest < Test::Unit::TestCase
def setup
@controller = MobileController.new
@request = ActionController::TestRequest.new
@response = ActionController::TestResponse.new
end
# Replace this with your real tests.
def test_truth
assert true
end
end