Merge pull request #57 from maddentim/mobile

Mobile interface updates
This commit is contained in:
Matt Rogers 2011-12-30 20:20:40 -08:00
commit ff16cb3051
19 changed files with 228 additions and 105 deletions

View file

@ -361,6 +361,49 @@ class TodosController < ApplicationController
end
end
def mobile_done
# copied from toggle_check, left out other formats as they shouldn't come here
# ultimately would like to just use toggle_check
@todo = current_user.todos.find(params['id'])
@source_view = params['_source_view'] || 'todo'
@original_item_due = @todo.due
@original_item_was_deferred = @todo.deferred?
@original_item_was_pending = @todo.pending?
@original_item_was_hidden = @todo.hidden?
@original_item_context_id = @todo.context_id
@original_item_project_id = @todo.project_id
@todo_was_completed_from_deferred_or_blocked_state = @original_item_was_deferred || @original_item_was_pending
@saved = @todo.toggle_completion!
@todo_was_blocked_from_completed_state = @todo.pending? # since we toggled_completion the previous state was completed
# check if this todo has a related recurring_todo. If so, create next todo
@new_recurring_todo = check_for_next_todo(@todo) if @saved
@predecessors = @todo.uncompleted_predecessors
if @saved
if @todo.completed?
@pending_to_activate = @todo.activate_pending_todos
else
@active_to_block = @todo.block_successors
end
end
if @saved
if cookies[:mobile_url]
old_path = cookies[:mobile_url]
cookies[:mobile_url] = {:value => nil, :secure => SITE_CONFIG['secure_cookies']}
notify(:notice, t("todos.action_marked_complete", :description => @todo.description, :completed => @todo.completed? ? 'complete' : 'incomplete'))
redirect_to old_path
else
notify(:notice, t("todos.action_marked_complete", :description => @todo.description, :completed => @todo.completed? ? 'complete' : 'incomplete'))
redirect_to todos_path(:format => 'm')
end
else
render :action => "edit", :format => :m
end
end
def toggle_star
@todo = current_user.todos.find(params['id'])
@todo.toggle_star!
@ -1176,6 +1219,10 @@ class TodosController < ApplicationController
lambda do
@page_title = t('todos.mobile_todos_page_title')
@home = true
max_completed = current_user.prefs.show_number_completed
@done = current_user.todos.completed.find(:all, :limit => max_completed, :include => Todo::DEFAULT_INCLUDES) unless max_completed == 0
cookies[:mobile_url]= { :value => request.request_uri, :secure => SITE_CONFIG['secure_cookies']}
determine_down_count

View file

@ -89,6 +89,12 @@ module TodosHelper
:title => todo.pending? ? t('todos.blocked_by', :predecessors => todo.uncompleted_predecessors.map(&:description).join(', ')) : "", :readonly => todo.pending?)
end
def remote_mobile_checkbox(todo=@todo)
form_tag mobile_done_todo_path(@todo, :format => 'm'), :method => :put, :class => "mobile-done", :name => "mobile_complete_#{@todo.id}" do
check_box_tag('_source_view', 'todo', @todo && @todo.completed?, "onClick" => "document.mobile_complete_#{@todo.id}.submit()")
end
end
def date_span(todo=@todo)
if todo.completed?
content_tag(:span, {:class => :grey}) { format_date( todo.completed_at ) }

View file

@ -7,10 +7,8 @@ if not @not_done.empty?
%>
<h2><%=@context.name%></h2>
<ul class="c">
<table cellpadding="0" cellspacing="0" border="0" class="c">
<%= render :partial => "todos/mobile_todo",
:collection => @not_done,
:locals => { :parent_container_type => "context" }-%>
</table>
</ul>
<% end -%>

View file

@ -7,13 +7,12 @@
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="viewport" content="initial-scale = 1.0" />
<meta name="viewport" content="width=device-width, user-scalable=no">
<%= stylesheet_link_tag "mobile", :media => 'handheld,all' %>
<title><%= @page_title %></title>
</head><body>
<% if !(@new_mobile || @edit_mobile)
if current_user && !current_user.prefs.nil? -%>
<h1><span class="count" id="badge_count"><%= @down_count %></span> <%=
<% if current_user && !current_user.prefs.nil? -%>
<div id="topbar"><h1><% if @down_count -%><span class="count" id="badge_count"><%= @down_count %></span><% end -%> <%=
l(Date.today, :format => current_user.prefs.title_date_format) -%></h1>
<div class="nav">
<%= (link_to(t('layouts.mobile_navigation.new_action'), new_todo_path(new_todo_params))+" | ") unless @new_mobile -%>
@ -21,10 +20,9 @@
<%= (link_to(t('layouts.mobile_navigation.contexts'), contexts_path(:format => 'm'))+" | ") -%>
<%= (link_to(t('layouts.mobile_navigation.projects'), projects_path(:format => 'm'))+" | ") -%>
<%= (link_to(t('layouts.mobile_navigation.starred'), {:action => "tag", :controller => "todos", :id => "starred.m"})) -%>
<% end
end -%><%= render_flash -%>
</div>
<%= yield -%>
<% end -%>
</div></div>
<div id="content"><%= render_flash -%><%= yield -%></div>
<hr/><% if current_user && !current_user.prefs.nil? -%>
<div class="nav">
<%= (link_to(t('layouts.mobile_navigation.logout'), logout_path(:format => 'm')) +" | ") -%>

View file

@ -4,24 +4,34 @@
<% unless @project.description.blank? -%>
<div class="project_description"><%= sanitize(@project.description) %></div>
<% end -%>
<%= render :partial => "todos/mobile_todo", :collection => @not_done, :locals => { :parent_container_type => "project" }%>
<ul class="c">
<%= render :partial => "todos/mobile_todo",
:collection => @not_done,
:locals => { :parent_container_type => "project" }%>
</ul>
<h2><%= t('projects.deferred_actions')%></h2>
<% if @deferred.empty? -%>
<%= t('projects.deferred_actions_empty') %>
<% else -%>
<%= render :partial => "todos/mobile_todo", :collection => @deferred, :locals => { :parent_container_type => "project" }%>
<% end
<ul class="c">
<%= render :partial => "todos/mobile_todo",
:collection => @deferred,
:locals => { :parent_container_type => "project" }%>
</ul><% end
-%>
<h2><%= t('projects.completed_actions')%></h2>
<% if @done.empty? -%>
<%= t('projects.completed_actions_empty') %>
<% else -%>
<%= render :partial => "todos/mobile_todo", :collection => @done, :locals => { :parent_container_type => "project" }%>
<% end %>
<ul class="c">
<%= render :partial => "todos/mobile_todo",
:collection => @done,
:locals => { :parent_container_type => "project" }%>
</ul><% end %>
<h2><%= t('projects.notes') %></h2>
<% if @project.notes.empty? -%>
<%= t('projects.notes_empty') %>
<% else -%><%= render :partial => "notes/mobile_notes_summary", :collection => @project.notes %>
<% end -%>
<h2><%= t('projects.settings') %></h2>
<%= t('projects.state', :state => project.aasm_current_state.to_s) %>. <%= @project_default_context %>
<%= t('projects.state', :state => project.aasm_current_state.to_s) %>. <%= @project_default_context %>

View file

@ -4,37 +4,34 @@
<%= error_messages_for("todo") %>
</span>
<% this_year = current_user.time.to_date.strftime("%Y").to_i -%>
<% if parent_container_type == 'show_mobile' -%>
<p><label for="todo_done"><%= t('todos.done') %></label>&nbsp;<%= check_box_tag("done", 1, @todo && @todo.completed?, "tabindex" => 1, "onClick" => "document.mobileEdit.submit()") %></p>
<% end -%>
<h2><label for="todo_description"><%= t('common.description') %></label></h2>
<%= text_field( "todo", "description", "tabindex" => 2, "maxlength" => 100, "size" => 50) %>
<h2><label for="todo_notes"><%= t('common.notes') %></label></h2>
<%= text_area( "todo", "notes", "cols" => 40, "rows" => 3, "tabindex" => 3) %>
<%= text_field( "todo", "description", "tabindex" => 1, "maxlength" => 100, "size" => 50) %>
<h2><label for="tag_list"><%= t('todos.tags') %></label></h2>
<%= text_field_tag "tag_list", @tag_list_text, :size => 50, :tabindex => 2 %>
<h2><label for="todo_context_id"><%= t('common.context') %></label></h2>
<%= unless @mobile_from_context
collection_select( "todo", "context_id", @contexts, "id", "name", {}, {"tabindex" => 4} )
collection_select( "todo", "context_id", @contexts, "id", "name", {}, {"tabindex" => 3} )
else
select_tag("todo[context_id]", options_from_collection_for_select(
@contexts, "id", "name", @mobile_from_context.id),
{"id" => :todo_context_id, :tabindex => 4} )
{"id" => :todo_context_id, :tabindex => 3} )
end %>
<h2><label for="todo_project_id"><%= t('common.project') %></label></h2>
<%= unless @mobile_from_project
collection_select( "todo", "project_id", @projects, "id", "name",
{:include_blank => t('todos.no_project')}, {"tabindex" => 5} )
{:include_blank => t('todos.no_project')}, {"tabindex" => 4} )
else
# manually add blank option since :include_blank does not work
# with options_from_collection_for_select
select_tag("todo[project_id]", "<option value=\"\"></option>"+options_from_collection_for_select(
@projects, "id", "name", @mobile_from_project.id),
{"id" => :todo_project_id, :tabindex => 5} )
{"id" => :todo_project_id, :tabindex => 4} )
end %>
<h2><label for="tag_list"><%= t('todos.tags') %></label></h2>
<%= text_field_tag "tag_list", @tag_list_text, :size => 50, :tabindex => 6 %>
<h2><label for="todo_notes"><%= t('common.notes') %></label></h2>
<%= text_area( "todo", "notes", "cols" => 40, "rows" => 3, "tabindex" => 5) %>
<h2><label for="todo_due"><%= t('todos.due') %></label></h2>
<%= date_select("todo", "due", {:order => [:day, :month, :year],
:start_year => this_year, :include_blank => '--'}, :tabindex => 7) %>
:start_year => this_year, :include_blank => '--'}, :tabindex => 6) %>
<h2><label for="todo_show_from"><%= t('todos.show_from') %></label></h2>
<%= date_select("todo", "show_from", {:order => [:day, :month, :year],
:start_year => this_year, :include_blank => true}, :tabindex => 8) %>
:start_year => this_year, :include_blank => true}, :tabindex => 7) %>

View file

@ -2,4 +2,12 @@
<p><%= t('todos.no_incomplete_actions') %></p>
<% else -%>
<%= render :partial => "contexts/mobile_context", :collection => @contexts_to_show -%>
<% end -%>
<% end -%>
<% unless @done.nil? -%>
<div id="completed_container">
<h2><%= t('todos.completed_actions') %></h2>
<ul class="c">
<%= render :partial => "todos/mobile_todo", :collection => @done %>
</ul>
</div>
<% end %>

View file

@ -1,21 +1,13 @@
<% @todo = mobile_todo
if mobile_todo.starred?
bullet = "<span class=\"star\">"+image_tag("menustar_small.gif")+"</span>"
li_class = " class=\"star\""
else
bullet = "<span class=\"r\">&raquo;&nbsp;</span>"
li_class = ""
end -%>
<li id="<%= dom_id(mobile_todo) %>" <%= li_class %>><%= bullet %><%
if mobile_todo.completed?
-%><span class="m_t_d">
<% else
-%><span class="m_t">
<% end -%>
<% @todo = mobile_todo -%>
<li id="<%= dom_id(mobile_todo) %>" >
<% remote_mobile_checkbox(mobile_todo) %>
<%= date_span -%> <%= link_to mobile_todo.description, todo_path(mobile_todo, :format => 'm') -%>
<% unless mobile_todo.notes.blank? %>
<%= link_to(image_tag("mobile_notes.png", :border => "0"), mobile_todo_show_notes_path(mobile_todo, :format => 'm')) -%>
<% end %>
<% if mobile_todo.starred? %>
<%= image_tag("menustar_small.gif", :border => "0") -%>
<% end %>
<% if parent_container_type == 'context' or parent_container_type == 'tag' -%>
<%= "<span class=\"prj\"> (" +
link_to(mobile_todo.project.name, project_path(mobile_todo.project, :format => 'm')) +

View file

@ -2,4 +2,4 @@
<%= link_to @todo.description, todo_path(@todo, :format => 'm') -%>
<h2><%= t('todos.notes') %></h2>
<%= format_note(@todo.notes) %>
<%= link_to t('common.back'), @return_path %>
<%= link_to t('common.back'), @return_path %>

View file

@ -8,18 +8,18 @@
<%= render :partial => "contexts/mobile_context", :collection => @contexts_to_show -%>
<h2><%= t('todos.deferred_actions_with', :tag_name=> @tag_title) %></h2>
<% unless (@deferred.nil? or @deferred.size == 0) -%>
<table cellpadding="0" cellspacing="0" border="0">
<ul class="c">
<%= render :partial => "todos/mobile_todo", :collection => @deferred, :locals => { :parent_container_type => "tag" } -%>
</table>
</ul>
<% else -%>
<%= t('todos.no_deferred_actions_with', :tag_name => @tag_title) %>
<% end -%>
<h2><%= t('todos.completed_actions_with', :tag_name => @tag_title) %></h2>
<% unless (@done.nil? or @done.size == 0) -%>
<table cellpadding="0" cellspacing="0" border="0">
<ul class="c">
<%= render :partial => "todos/mobile_todo", :collection => @done, :locals => { :parent_container_type => "tag" } %>
</table>
</ul>
<% else -%>
<%= t('todos.no_completed_actions_with', :tag_name => @tag_title) %>
<% end -%>
</div>
</div>

View file

@ -99,12 +99,12 @@ de:
mobile_navigation:
logout: Abmelden
feeds: Feeds
new_action: 0-Neue Aufgabe
starred: 4-Markiert
projects: 3-Projekte
new_action: Neue Aufgabe
starred: Markiert
projects: Projekte
tickler: Notizbuch
contexts: 2-Kontexte
home: 1-Home
contexts: Kontexte
home: Home
navigation:
api_docs: REST API Docs
recurring_todos: Sich wiederholende To-Dos

View file

@ -7,14 +7,14 @@ en:
next_actions_rss_feed: RSS feed of next actions
toggle_notes_title: Toggle all notes
mobile_navigation:
new_action: 0-New action
new_action: New
logout: Logout
feeds: Feeds
starred: 4-Starred
projects: 3-Projects
starred: Starred
projects: Projects
tickler: Tickler
contexts: 2-Contexts
home: 1-Home
contexts: Contexts
home: Home
navigation:
manage_users_title: Add or delete users
recurring_todos: Repeating todos

View file

@ -88,12 +88,12 @@ es:
mobile_navigation:
logout: "Cerrar sesi\xC3\xB3n"
feeds: Feeds
new_action: 0-Nueva tarea
starred: 4-Favoritos
projects: 3-Proyectos
new_action: Nueva tarea
starred: avoritos
projects: Proyectos
tickler: Tickler
contexts: 2-Contextos
home: 1-Inicio
contexts: Contextos
home: Inicio
navigation:
api_docs: REST API Docs
recurring_todos: Tareas repetitivas

View file

@ -99,11 +99,11 @@ fr:
logout: "D\xC3\xA9connexion"
feeds: Flux
new_action: 0-Nouvelle action
starred: "4-Marqu\xC3\xA9"
projects: 3-Projets
starred: "Marqu\xC3\xA9"
projects: Projets
tickler: Reporteur
contexts: 2-Contextes
home: 1-Accueil
contexts: Contextes
home: Accueil
navigation:
api_docs: Doc REST API
recurring_todos: "T\xC3\xA2ches (todos) r\xC3\xA9p\xC3\xA9titives"

View file

@ -86,12 +86,12 @@ nl:
mobile_navigation:
logout: Afmelden
feeds: Feeds
new_action: 0-Nieuwe actie
starred: 4-Ster
projects: 3-Projecten
new_action: Nieuwe actie
starred: Ster
projects: Projecten
tickler: Tickler
contexts: 2-Contexten
home: 1-Start
contexts: Contexten
home: Start
navigation:
api_docs: REST API Docs
recurring_todos: Terugkerende acties

View file

@ -25,7 +25,7 @@ ActionController::Routing::Routes.draw do |map|
map.resources :notes
map.resources :todos,
:member => {:toggle_check => :put, :toggle_star => :put, :defer => :put},
:member => {:toggle_check => :put, :toggle_star => :put, :defer => :put, :mobile_done => :put},
:collection => {:check_deferred => :post, :filter_to_context => :post, :filter_to_project => :post, :done => :get, :all_done => :get
}

View file

@ -14,7 +14,7 @@ Feature: Add new next action from mobile page
Scenario Outline: The new action form is prefilled with context and project
Given I am on the <page>
When I follow "0-New action"
When I follow "New"
Then the selected project should be "<project>"
And the selected context should be "<context>"
@ -29,7 +29,7 @@ Feature: Add new next action from mobile page
Scenario: I can add a new todo using the mobile interface
Given I am on the home page
Then the badge should show 0
When I follow "0-New action"
When I follow "New"
And I fill in "Description" with "test me"
And I press "Create"
Then I should see "test me"

View file

@ -14,6 +14,7 @@ Feature: Edit a next action from the mobile view
| context | description |
| @mobile | test action |
@selenium
Scenario: I can edit an action on the mobile page
When I am on the home page
Then the badge should show 1
@ -27,10 +28,8 @@ Feature: Edit a next action from the mobile view
Then I should see "changed action"
And I should not see "test action"
When I follow "changed action"
And I press "Edit this action"
And I check "done"
And I press "Update"
Then I should not see "changed action"
And I press "Mark complete"
Then I should see "changed action" in the completed container
Scenario: Navigate from home page
move this to separate features when other scenarios are created for these features

View file

@ -3,36 +3,41 @@ body {
font-size: smaller;
}
#content {
margin-top: 50px;
}
div.footer {
font-size: XX-small;
color: #999999;
text-align: center;
}
a, a:link, a:active, a:visited {
color: #CC3334;
padding-left: 1px;
padding-right: 1px;
text-decoration: none;
}
a:hover {
background-color: #CC3334;
color: #FFFFFF;
}
div.footer a {
text-decoration: underline;
color: #999999;
}
.m_t_d a {
text-decoration: none;
color: #000000;
}
.m_t_d a:hover {
text-decoration: underline;
color: #0000FF;
}
.m_t_d .red, .m_t_d .amber, .m_t_d .orange, .m_t_d .green{
background-color: #999999;
}
h1 {
color: #f00;
color: #fff;
font-size: small;
margin-top:.3em;
margin-bottom:.3em;
padding-top: 0.2em;
padding-bottom: 0.2em;
padding-left:8px;
margin-top:0;
margin-bottom:0;
}
h2 {
@ -43,6 +48,17 @@ h2 {
border-top: 1px solid #777777;
}
h2 a, h2 a:link, h2 a:active, h2 a:visited {
color: #666666;
text-decoration: none;
}
h2 a:hover {
background-color: transparent;
color: #CC3334;
text-decoration: none;
}
h4.alert {
border: 1px solid #666666;
text-align: center;
@ -79,6 +95,15 @@ span.r {
span.prj, span.ctx{
font-size: X-small;
}
#ctx, #pjr {
margin: 0.5em 0;
}
#ctx a, #pjr a {
padding: 0.1em 0;
}
/* Draw attention to some text
Same format as traffic lights */
.red {
@ -118,22 +143,22 @@ span.prj, span.ctx{
.count {
color: #fff;
background: #000;
font-size: medium;
background: #f00;
padding: 0.2em;
}
.errors {
background: #FFC2C2;
}
ul.c li.star {
list-style-type: circle;
ul.c li {
list-style-type: none;
}
ul.c {
padding: 0;
margin: 0;
padding-left: 1.1em;
padding-left: 0.1em;
}
ul.c li {
@ -149,8 +174,44 @@ span.r {
display:none;
}
#topbar {
background-color: #000000;
clear: both;
color: #EEEEEE;
height: 45px;
left: 0;
margin-bottom: 5px;
position: fixed;
top: 0;
width: 100%;
z-index: 501;
}
.nav {
font-size: x-small;
color: #fff;
background: #000;
padding-top: 0.2em;
padding-bottom: 0.2em;
}
#topbar .nav {
padding-left:8px;
margin-bottom:0.3em;
}
.nav a, .nav a:link, .nav a:active, .nav a:visited {
color: #fff;
padding-top: 1.0em;
padding-bottom: 0.5em;
}
.nav a:focus, .nav a:hover, .nav a:active {
background: transparent;
text-decoration: underline;
}
.nav li:hover, .nav a:focus, .nav a:hover, .nav a:active {
color: #CCCCCC;
}
#database_auth_form table td {
@ -160,3 +221,10 @@ span.r {
table.c {
margin-left: 5px;
}
.mobile-done {
display:inline;
}
input#todo_description, input#tag_list, textarea#todo_notes, select#todo_project_id, select#todo_context_id {
width: 95%;
}