mirror of
https://github.com/TracksApp/tracks.git
synced 2025-12-16 23:30:12 +01:00
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:
parent
da22d34962
commit
2ca153a771
16 changed files with 300 additions and 33 deletions
85
tracks/app/controllers/mobile_controller.rb
Normal file
85
tracks/app/controllers/mobile_controller.rb
Normal 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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
2
tracks/app/helpers/mobile_helper.rb
Normal file
2
tracks/app/helpers/mobile_helper.rb
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
module MobileHelper
|
||||
end
|
||||
|
|
@ -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
|
||||
#
|
||||
|
|
|
|||
15
tracks/app/views/layouts/mobile.rhtml
Normal file
15
tracks/app/views/layouts/mobile.rhtml
Normal 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>
|
||||
14
tracks/app/views/mobile/_add_new_action_form.rhtml
Normal file
14
tracks/app/views/mobile/_add_new_action_form.rhtml
Normal 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>
|
||||
27
tracks/app/views/mobile/_mobile_actions.rhtml
Normal file
27
tracks/app/views/mobile/_mobile_actions.rhtml
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
<ul>
|
||||
<% for todo in @todos -%>
|
||||
<li>
|
||||
<%= link_to "»", :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 %>
|
||||
14
tracks/app/views/mobile/_mobile_edit.rhtml
Normal file
14
tracks/app/views/mobile/_mobile_edit.rhtml
Normal 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>
|
||||
4
tracks/app/views/mobile/detail.rhtml
Normal file
4
tracks/app/views/mobile/detail.rhtml
Normal 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' %>
|
||||
5
tracks/app/views/mobile/filter.rhtml
Normal file
5
tracks/app/views/mobile/filter.rhtml
Normal 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' %>
|
||||
4
tracks/app/views/mobile/list.rhtml
Normal file
4
tracks/app/views/mobile/list.rhtml
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
<h1><%= @desc %>
|
||||
<%= link_to "+", :controller => 'mobile', :action => 'show_add_form' %></h1>
|
||||
<hr />
|
||||
<%= render :partial => 'mobile_actions' %>
|
||||
4
tracks/app/views/mobile/show_add_form.rhtml
Normal file
4
tracks/app/views/mobile/show_add_form.rhtml
Normal 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' %>
|
||||
|
|
@ -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'
|
||||
|
|
|
|||
42
tracks/public/stylesheets/mobile.css
Normal file
42
tracks/public/stylesheets/mobile.css
Normal 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;
|
||||
}
|
||||
18
tracks/test/functional/mobile_controller_test.rb
Normal file
18
tracks/test/functional/mobile_controller_test.rb
Normal 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
|
||||
Loading…
Add table
Add a link
Reference in a new issue