Applied Luke Melia's patch to update the todo toggling to use RJS templates (#190).

I also added the following:

* Expanded Luke's patch so that toggling also works on Context and Project pages.
* The 'empty' messages for the uncompleted and completed actions divs now appear and disappear automatically on the context and project pages as you toggle, untoggle, add and delete actions
* At some point, hiding of contexts on the front page broke. It seems that recent updates to Rails changed the way that it interprets tinyint fields: these can now only be tested with true or false, not 0 and 1, and that was why it broke. Also the code for selecting only unhidden contexts on the front page used .hidden? for some reason and not .hide. Fixed now.

A remaining issue is that on the home page, if you add an action to (or uncheck an action to) a context that is not currently shown (because it is hidden, or it has been empty), the record will be changed, but nothing will appear to happen until you refresh. I'd like to test for this situation and put a message up assuring the user that things worked and that they need to refresh.



git-svn-id: http://www.rousette.org.uk/svn/tracks-repos/trunk@175 a4c988fc-2ded-0310-b66e-134b36920a42
This commit is contained in:
bsag 2006-01-10 06:15:48 +00:00
parent 7bc8783d03
commit 825bad76a7
14 changed files with 163 additions and 117 deletions

View file

@ -59,7 +59,9 @@ class ContextController < ApplicationController
@saved = @item.save
@on_page = "context"
@up_count = Todo.find(:all, :conditions => ["todos.user_id = ? and todos.done = 0 and todos.context_id IN (?)", @user.id, @item.context_id]).size.to_s
if @saved
@up_count = Todo.find(:all, :conditions => ["todos.user_id = ? and todos.done = 0 and todos.context_id IN (?)", @user.id, @item.context_id]).size.to_s
end
return if request.xhr?
@ -88,7 +90,9 @@ class ContextController < ApplicationController
@item = check_user_return_item
@saved = @item.destroy
@down_count = Todo.find(:all, :conditions => ["todos.user_id = ? and todos.done = 0 and todos.context_id IN (?)", @user.id, @item.context_id]).size.to_s
if @saved
@down_count = Todo.find(:all, :conditions => ["todos.user_id = ? and todos.done = 0 and todos.context_id IN (?)", @user.id, @item.context_id]).size.to_s
end
return if request.xhr?
@ -108,6 +112,29 @@ class ContextController < ApplicationController
render :controller => 'todo', :action => 'list'
end
end
# Toggles the 'done' status of the action
#
def toggle_check
self.init
@item = check_user_return_item
@item.toggle!('done')
@item.completed = Time.now() # For some reason, the before_save in todo.rb stopped working
@saved = @item.save
if @saved
@down_count = Todo.find(:all, :conditions => ["todos.user_id = ? and todos.done = 0 and todos.context_id IN (?)", @user.id, @item.context_id]).size.to_s
@done_count = Todo.find(:all, :conditions => ["todos.user_id = ? and todos.done = 1 and todos.context_id IN (?)", @user.id, @item.context_id]).size.to_s
end
return if request.xhr?
if @saved
flash['notice'] = "The action <strong>'#{@item.description}'</strong> was marked as <strong>#{@item.done? ? 'complete' : 'incomplete' }</strong>"
else
flash['notice'] = "The action <strong>'#{@item.description}'</strong> was NOT marked as <strong>#{@item.done? ? 'complete' : 'incomplete' } due to an error on the server.</strong>"
end
redirect_to :action => "list"
end
# Edit the details of the context
#

View file

@ -78,7 +78,9 @@ class ProjectController < ApplicationController
@saved = @item.save
@on_page = "project"
@up_count = Todo.find(:all, :conditions => ["todos.user_id = ? and todos.done = 0 and todos.project_id IN (?)", @user.id, @item.project_id]).size.to_s
if @saved
@up_count = Todo.find(:all, :conditions => ["todos.user_id = ? and todos.done = 0 and todos.project_id IN (?)", @user.id, @item.project_id]).size.to_s
end
return if request.xhr?
@ -107,7 +109,10 @@ class ProjectController < ApplicationController
@item = check_user_return_item
@saved = @item.destroy
@down_count = Todo.find(:all, :conditions => ["todos.user_id = ? and todos.done = 0 and todos.project_id IN (?)", @user.id, @item.project_id]).size.to_s
@on_page = "project"
if @saved
@down_count = Todo.find(:all, :conditions => ["todos.user_id = ? and todos.done = 0 and todos.project_id IN (?)", @user.id, @item.project_id]).size.to_s
end
return if request.xhr?
@ -128,6 +133,30 @@ class ProjectController < ApplicationController
end
end
# Toggles the 'done' status of the action
#
def toggle_check
self.init
@item = check_user_return_item
@item.toggle!('done')
@item.completed = Time.now() # For some reason, the before_save in todo.rb stopped working
@saved = @item.save
@on_page = "project"
if @saved
@down_count = Todo.find(:all, :conditions => ["todos.user_id = ? and todos.done = 0 and todos.project_id IN (?)", @user.id, @item.project_id]).size.to_s
@done_count = Todo.find(:all, :conditions => ["todos.user_id = ? and todos.done = 1 and todos.project_id IN (?)", @user.id, @item.project_id]).size.to_s
end
return if request.xhr?
if @saved
flash['notice'] = "The action <strong>'#{@item.description}'</strong> was marked as <strong>#{@item.done? ? 'complete' : 'incomplete' }</strong>"
else
flash['notice'] = "The action <strong>'#{@item.description}'</strong> was NOT marked as <strong>#{@item.done? ? 'complete' : 'incomplete' } due to an error on the server.</strong>"
end
redirect_to :action => "list"
end
# Edit the details of the project
#
def update

View file

@ -26,10 +26,10 @@ class TodoController < ApplicationController
@done = @done[0..(NO_OF_ACTIONS-1)]
@contexts_to_show = @contexts.clone
@contexts_to_show = @contexts_to_show.collect {|x| (!x.hidden? and !x.find_not_done_todos.empty?) ? x:nil }.compact
@contexts_to_show = @contexts_to_show.collect {|x| (!x.hide? and !x.find_not_done_todos.empty?) ? x:nil }.compact
# Set count badge to number of not-done, not hidden context items
@count = @todos.collect { |x| ( !x.done? and !x.context.hidden? ) ? x:nil }.compact.size
@count = @todos.collect { |x| ( !x.done? and !x.context.hide? ) ? x:nil }.compact.size
end
def update_element
@ -51,8 +51,9 @@ class TodoController < ApplicationController
@saved = @item.save
@on_page = "home"
@up_count = Todo.find(:all, :conditions => ["todos.user_id = ? and todos.done = 0", @user.id]).size.to_s
if @saved
@up_count = @todos.collect { |x| ( !x.done? and !x.context.hide? ) ? x:nil }.compact.size.to_s
end
return if request.xhr?
# fallback for standard requests
@ -75,8 +76,8 @@ class TodoController < ApplicationController
def edit_action
self.init
item = check_user_return_item
render :partial => 'action_edit_form', :object => item
end
@ -85,17 +86,22 @@ class TodoController < ApplicationController
def toggle_check
self.init
item = check_user_return_item
item.toggle!('done')
item.completed = Time.now() # For some reason, the before_save in todo.rb stopped working
if item.save
if request.xhr?
render :partial => 'item', :object => item
else
flash['notice'] = "The item <strong>'#{item.description}'</strong> was marked as <strong>#{item.done? ? 'complete' : 'incomplete' }</strong>"
redirect_to :action => "list"
end
@item = check_user_return_item
@item.toggle!('done')
@item.completed = Time.now() # For some reason, the before_save in todo.rb stopped working
@saved = @item.save
@on_page = "home"
if @saved
@down_count = @todos.collect { |x| ( !x.done? and !x.context.hide? ) ? x:nil }.compact.size.to_s
end
return if request.xhr?
if @saved
flash['notice'] = "The action <strong>'#{@item.description}'</strong> was marked as <strong>#{@item.done? ? 'complete' : 'incomplete' }</strong>"
else
flash['notice'] = "The action <strong>'#{@item.description}'</strong> was NOT marked as <strong>#{@item.done? ? 'complete' : 'incomplete' } due to an error on the server.</strong>"
end
redirect_to :action => "list"
end
# Edit the details of an action
@ -129,7 +135,10 @@ class TodoController < ApplicationController
@item = check_user_return_item
@saved = @item.destroy
@down_count = Todo.find(:all, :conditions => ["todos.user_id = ? and todos.done = 0", @user.id]).size.to_s
@on_page = "home"
if @saved
@down_count = @todos.collect { |x| ( !x.done? and !x.context.hide? ) ? x:nil }.compact.size.to_s
end
return if request.xhr?

View file

@ -6,30 +6,6 @@ module TodoHelper
count = Todo.find_all("done=0 AND context_id=#{context.id}").length
end
def form_remote_tag_toggle_todo( item )
target_div = item.done? ? "c#{item.context_id}" : "completed"
target_position = item.done? ? "bottom" : "top"
form_id = "checkbox-#{item.id}-form"
item_container_id = "item-#{item.id}-container"
loading_javascript = "Form.disable('#{form_id}');"
success_javascript = " $('#{item_container_id}').setAttribute('id','#{item_container_id}-fading');"
success_javascript << visual_effect( :fade, "#{item_container_id}-fading",
{
:duration => 0.4,
:afterFinish => "function(effect) { Element.remove('#{item_container_id}-fading'); }"
})
form_remote_tag( :url => url_for( :controller => "todo", :action => "toggle_check", :id => item.id ),
:html => { :id=> "#{form_id}", :class => "inline-form item-checkmark-form" },
:update => target_div,
:position => target_position,
:loading => loading_javascript,
:success => success_javascript,
:complete => visual_effect( :highlight, item_container_id))
end
def form_remote_tag_edit_todo( item )
form_remote_tag( :url => { :controller => 'todo', :action => 'update_action', :id => item.id },
:html => { :id => "form-action-#{item.id}", :class => "inline-form" },

View file

@ -11,7 +11,7 @@
</div>
<div class="buttons">
<% if context.hide == 1 %>
<% if context.hide? %>
<span class="grey">HIDDEN</span>
<% else %>
<span class="grey">VISIBLE</span>

View file

@ -0,0 +1,23 @@
if @saved
page.call "fadeAndRemoveItem", "item-#{@item.id}-container"
if @item.done?
page.insert_html :top, "completed", :partial => 'todo/item'
page.visual_effect :highlight, "item-#{@item.id}", {'startcolor' => "'#99ff99'"}
if @down_count == '0'
page.show "empty-nd"
end
page.hide "empty-d" # If we've checked something as done, completed items can't be empty
else
page.call "ensureVisibleWithEffectAppear", "c#{@item.context_id}"
page.insert_html :bottom, "c#{@item.context_id}", :partial => 'todo/item'
page.visual_effect :highlight, "item-#{@item.id}", {'startcolor' => "'#99ff99'"}
if @done_count == '0'
page.show "empty-d"
end
page.hide "empty-nd" # If we've checked something as undone, uncompleted items can't be empty
end
page.hide "status"
page.replace_html "badge_count", @down_count
else
page.replace_html "status", content_tag("div", content_tag("h2", "#{pluralize(@item.errors.count, "error")} prohibited this record from being saved") + content_tag("p", "There were problems with the following fields:") + content_tag("ul", @item.errors.each_full { |msg| content_tag("li", msg) }), "id" => "ErrorExplanation", "class" => "ErrorExplanation")
end

View file

@ -0,0 +1,23 @@
if @saved
page.call "fadeAndRemoveItem", "item-#{@item.id}-container"
if @item.done?
page.insert_html :top, "completed", :partial => 'todo/item'
page.visual_effect :highlight, "item-#{@item.id}", {'startcolor' => "'#99ff99'"}
if @down_count == '0'
page.show "empty-nd"
end
page.hide "empty-d" # If we've checked something as done, completed items can't be empty
else
page.call "ensureVisibleWithEffectAppear", "p#{@item.project_id}"
page.insert_html :bottom, "p#{@item.project_id}", :partial => 'todo/item'
page.visual_effect :highlight, "item-#{@item.id}", {'startcolor' => "'#99ff99'"}
if @done_count == '0'
page.show "empty-d"
end
page.hide "empty-nd" # If we've checked something as undone, uncompleted items can't be empty
end
page.hide "status"
page.replace_html "badge_count", @down_count
else
page.replace_html "status", content_tag("div", content_tag("h2", "#{pluralize(@item.errors.count, "error")} prohibited this record from being saved") + content_tag("p", "There were problems with the following fields:") + content_tag("ul", @item.errors.each_full { |msg| content_tag("li", msg) }), "id" => "ErrorExplanation", "class" => "ErrorExplanation")
end

View file

@ -16,7 +16,7 @@
<h3>Active Contexts:</h3>
<ul>
<% for context in @contexts.collect { |x| x.hidden? ? nil:x }.compact -%>
<% for context in @contexts.collect { |x| x.hide? ? nil:x }.compact -%>
<li><%= link_to( sanitize(context.name), { :controller => "context", :action => "show",
:name => urlize(context.name) } ) + " (" + context.count_undone_todos("actions") + ")" %></li>
<% end -%>
@ -24,7 +24,7 @@
<h3>Hidden Contexts:</h3>
<ul>
<% for context in @contexts.collect { |x| x.hidden? ? x:nil }.compact -%>
<% for context in @contexts.collect { |x| x.hide? ? x:nil }.compact -%>
<li><%= link_to( sanitize(context.name), { :controller => "context", :action => "show",
:name => urlize(context.name) } ) + " (" + context.count_undone_todos("actions") + ")" %></li>
<% end -%>

View file

@ -9,8 +9,7 @@
<div id="completed" class="items toggle_target">
<div id="empty-d" style="display:<%= @done.empty? ? 'block' : 'none' %>">
<%= render :partial => "shared/empty",
:locals => { :message => "Currently there are no completed actions " + suffix } %>
<div class="message"><p>Currently there are no completed actions in this context</p></div>
</div>
<%= render :partial => "todo/item", :collection => done %>

View file

@ -1,11 +1,10 @@
<div id="item-<%= item.id %>-container" class="item-container">
<div id="item-<%= item.id %>">
<%= form_remote_tag_toggle_todo( item ) %>
<%= form_tag( { :controller => "todo", :action => "toggle_check", :id => item.id },
{ :class => "inline-form" }) %>
<%= link_to_remote_todo( item, controller.controller_name ) %>
<input type="checkbox" class="item-checkbox" name="item_id" value="<%= item.id %>"<% if item.done? %> checked="checked" <% end %> />
<%= link_to_remote_todo( item, controller.controller_name ) %>
<input type="checkbox" class="item-checkbox"
onclick="new Ajax.Request('/<%= controller.controller_name %>/toggle_check/<%= item.id %>', {asynchronous:true, evalScripts:true});"
name="item_id" value="<%= item.id %>"<% if item.done? %> checked="checked" <% end %> />
<div class="description<%= staleness_class( item ) %>"><% # start of div which has a class 'description', and possibly 'stale_11', 'stale_12', 'stale_13' etc %>
<% if item.done? -%>
<span class="grey"><%= format_date( item.completed ) %></span>
@ -22,12 +21,10 @@
<%= toggle_show_notes( item ) %>
<% end -%>
</div>
<%= end_form_tag %>
</div><!-- [end:item-item.id] -->
<div id="action-<%= item.id %>-edit-form" class="edit-form" style="display:none;">
<%= form_remote_tag_edit_todo( item ) -%>
<% #note: edit form will load here remotely -%>
<%= end_form_tag -%>
</div><!-- [end:action-item.id-edit-form] -->
</div><!-- [end:item-item.id-container] -->
</div><!-- [end:item-item.id-container] -->

View file

@ -1,11 +1,11 @@
if @saved
page.insert_html :bottom, "c#{@item.context_id}", :partial => 'todo/item'
page.hide "status"
page.replace_html "status", content_tag("div", "Added new next action", "class" => "confirmation")
page.visual_effect :appear, 'status', :duration => 0.5
page.replace_html "badge_count", @up_count
page.visual_effect :highlight, "item-#{@item.id}-container", :duration => 3
page.send :record, "Form.reset('todo-form-new-action');Form.focusFirstElement('todo-form-new-action')"
page.insert_html :bottom, "c#{@item.context_id}", :partial => 'todo/item'
else
page.hide "status"
page.replace_html "status", content_tag("div", content_tag("h2", "#{pluralize(@item.errors.count, "error")} prohibited this record from being saved") + content_tag("p", "There were problems with the following fields:") + content_tag("ul", @item.errors.each_full { |msg| content_tag("li", msg) }), "id" => "ErrorExplanation", "class" => "ErrorExplanation")

View file

@ -0,0 +1,15 @@
if @saved
page.call "fadeAndRemoveItem", "item-#{@item.id}-container"
if @item.done?
page.insert_html :top, "completed", :partial => 'todo/item'
page.visual_effect :highlight, "item-#{@item.id}", {'startcolor' => "'#99ff99'"}
else
page.call "ensureVisibleWithEffectAppear", "c#{@item.context_id}"
page.insert_html :bottom, "c#{@item.context_id}", :partial => 'todo/item'
page.visual_effect :highlight, "item-#{@item.id}", {'startcolor' => "'#99ff99'"}
end
page.hide "status"
page.replace_html "badge_count", @down_count
else
page.replace_html "status", content_tag("div", content_tag("h2", "#{pluralize(@item.errors.count, "error")} prohibited this record from being saved") + content_tag("p", "There were problems with the following fields:") + content_tag("ul", @item.errors.each_full { |msg| content_tag("li", msg) }), "id" => "ErrorExplanation", "class" => "ErrorExplanation")
end

View file

@ -8,7 +8,6 @@
*/
addEvent(window, "load", addNextActionListingToggles);
addEvent(window, "load", addAjaxToDoItemCheckmarkHandling);
function addNextActionListingToggles()
{
@ -19,25 +18,6 @@ function addNextActionListingToggles()
}
}
function addAjaxToDoItemCheckmarkHandling()
{
var itemCheckboxes = document.getElementsByClassName('item-checkbox');
for(var i = 0; i < itemCheckboxes.length; i++)
{
addEvent(itemCheckboxes[i], "click", toggleTodoItemChecked);
}
}
function addOneAjaxToDoItemCheckmarkHandling(elem)
{
addEvent(document.getElementsByClassName('item-checkbox',elem)[0], "click", toggleTodoItemChecked);
}
function getMarkUndoneTargetElem()
{
return document.getElementsByClassName('container')[0];
}
function ensureVisibleWithEffectAppear(elemId)
{
if ($(elemId).style.display == 'none')
@ -46,28 +26,6 @@ function ensureVisibleWithEffectAppear(elemId)
}
}
function toggleTodoItemChecked()
{
var itemContainerElem = findNearestParentByClassName(this, 'item-container');
var itemContainerElemId = itemContainerElem.getAttribute('id');
var checkboxForm = this.form;
var markingAsDone = this.checked;
var targetElemId = markingAsDone ? 'completed' : getMarkUndoneTargetElem().getAttribute('id');
new Ajax.Updater(
targetElemId,
checkboxForm.action,
{
asynchronous:true,
evalScripts:true,
insertion:markingAsDone ? Insertion.Top : Insertion.Bottom,
onLoading:function(request){ Form.disable(checkboxForm); removeFlashNotice(); ensureVisibleWithEffectAppear(targetElemId); },
onSuccess:function(request){ fadeAndRemoveItem(itemContainerElemId); },
onComplete:function(request){ new Effect.Highlight(itemContainerElemId,{}); addOneAjaxToDoItemCheckmarkHandling($(itemContainerElemId)); },
parameters:Form.serialize(checkboxForm)
});
return false;
}
function fadeAndRemoveItem(itemContainerElemId)
{
var fadingElemId = itemContainerElemId + '-fading';
@ -76,15 +34,6 @@ function fadeAndRemoveItem(itemContainerElemId)
new Effect.Fade(fadingElemId,{afterFinish:function(effect) { Element.remove(fadingElemId); }, duration:0.4});
}
function removeFlashNotice()
{
var flashNotice = document.getElementById("notice");
if (flashNotice)
{
new Effect.Fade("notice",{afterFinish:function(effect) { Element.remove("notice"); }, duration:0.4});
}
}
function toggleNextActionListing()
{
var itemsElem = findItemsElem(this);

View file

@ -161,8 +161,12 @@ h2 a:hover {
width: 20px;
}
.item-container {
padding:2px;
}
.container a.icon {
float: left;
float: left;
vertical-align: middle;
background-color: transparent;
}
@ -173,11 +177,6 @@ input.item-checkbox {
vertical-align: middle;
}
.checkbox form {
display: inline;
margin: 0;
}
.description {
margin-left: 70px;
margin-right: 10px;
@ -461,7 +460,7 @@ form {
width: 313px;
}
.inline-form, .item-checkmark-form {
.inline-form {
border: none;
padding: 3px;
width: 100%;
@ -527,7 +526,7 @@ div.message {
#ErrorExplanation {
width: 400px;
border: 2px solid #red;
border: 2px solid red;
padding: 7px;
padding-bottom: 12px;
margin-bottom: 20px;