diff --git a/controller/Notebooks.py b/controller/Notebooks.py index 1a709df..a87591f 100644 --- a/controller/Notebooks.py +++ b/controller/Notebooks.py @@ -128,6 +128,11 @@ class Notebooks( object ): if revision: result[ "note_read_write" ] = False + notebook = self.__database.load( Notebook, notebook_id ) + if not notebook: + raise Access_error() + result[ "recent_notes" ] = self.__database.select_many( Note, notebook.sql_load_recent_notes( start = 0, count = 10 ) ) + # if the user doesn't have any storage bytes yet, they're a new user, so see what type of # conversion this is (demo or signup) if result[ "user" ].storage_bytes == 0: diff --git a/static/css/style.css b/static/css/style.css index b2e15de..097c1eb 100644 --- a/static/css/style.css +++ b/static/css/style.css @@ -270,6 +270,11 @@ img { font-size: 90%; } +#recent_notes_table { + margin-bottom: 1em; + font-size: 90%; +} + .note_tree_table { border-collapse: collapse; } @@ -299,6 +304,11 @@ img { padding-left: 19px; } +.recent_note_link { + background: url(/static/images/note_icon.png) left center no-repeat; + padding-left: 19px; +} + .note_tree_loading { background: url(/static/images/loading.gif) left center no-repeat; padding-left: 20px; @@ -582,6 +592,10 @@ img { padding: 0.25em 0.25em 0.25em 0; } +.recent_note_item { + padding: 0.25em 0.25em 0.25em 0; +} + .tree_expander { float: left; width: 11px; diff --git a/static/js/Wiki.js b/static/js/Wiki.js index b8b1e10..69f4b7e 100644 --- a/static/js/Wiki.js +++ b/static/js/Wiki.js @@ -19,6 +19,7 @@ function Wiki( invoker ) { this.signup_plan = getElement( "signup_plan" ).value; this.font_size = null; this.note_tree = new Note_tree( this, this.notebook_id, this.invoker ); + this.recent_notes = new Recent_notes( this, this.notebook_id ); var total_notes_count_node = getElement( "total_notes_count" ); if ( total_notes_count_node ) @@ -2578,7 +2579,7 @@ function Note_tree( wiki, notebook_id, invoker ) { var self = this; function connect_expander( note_id ) { - connect( "note_tree_expander_" + note_id, "onclick", function ( event ) { self.expand_link( event, note_id ); } ); + connect( "note_tree_expander_" + note_id, "onclick", function ( event ) { self.expand_collapse_link( event, note_id ); } ); } for ( var i in links ) { @@ -2643,7 +2644,7 @@ Note_tree.prototype.add_root_link = function ( editor ) { ) ); var self = this; - connect( expander, "onclick", function ( event ) { self.expand_link( event, editor.id ); } ); + connect( expander, "onclick", function ( event ) { self.expand_collapse_link( event, editor.id ); } ); connect( link, "onclick", function ( event ) { self.link_clicked( event ); } ); var instructions = getElement( "note_tree_instructions" ); @@ -2667,11 +2668,8 @@ Note_tree.prototype.remove_link = function ( note_id ) { Note_tree.prototype.rename_link = function ( editor, new_title ) { var link = getElement( "note_tree_link_" + editor.id ); - - if ( !link ) - return; - - replaceChildNodes( link, new_title || "untitled note" ); + if ( link ) + replaceChildNodes( link, new_title || "untitled note" ); } Note_tree.prototype.update_link = function ( editor ) { @@ -2711,7 +2709,7 @@ Note_tree.prototype.update_link = function ( editor ) { } } -Note_tree.prototype.expand_link = function ( event, note_id ) { +Note_tree.prototype.expand_collapse_link = function ( event, note_id ) { var expander = event.target(); if ( !expander || hasElementClass( expander, "tree_expander_empty" ) ) @@ -2765,14 +2763,11 @@ Note_tree.prototype.expand_link = function ( event, note_id ) { } } -Note_tree.prototype.collapse_link = function ( event, note_id ) { -} - Note_tree.prototype.display_child_links = function ( result, link, children_area ) { var self = this; function connect_expander( expander, note_id ) { - connect( expander, "onclick", function ( event ) { self.expand_link( event, note_id ); } ); + connect( expander, "onclick", function ( event ) { self.expand_collapse_link( event, note_id ); } ); } var span = createDOM( "span" ); @@ -2827,3 +2822,99 @@ Note_tree.prototype.display_child_links = function ( result, link, children_area var note_id = parse_query( link )[ "note_id" ]; connect_expander( expander, note_id ); } + +function Recent_notes( wiki, notebook_id ) { + this.wiki = wiki; + this.notebook_id = notebook_id; + + // if there's no recent notes table, there's nothing to do with recent notes! + if ( !getElement( "recent_notes_table" ) ) + return; + + // add onclick handlers to the recent note links as well + var self = this; + var recent_links = getElementsByTagAndClassName( "a", "recent_note_link", "note_tree_area" ); + + for ( var i in recent_links ) { + var link = recent_links[ i ]; + var query = parse_query( link ); + var note_id = query[ "note_id" ]; + + connect( link, "onclick", function ( event ) { self.link_clicked( event ); } ); + } + + // connect to the wiki note events + connect( wiki, "note_added", function ( editor ) { self.add_link( editor ); } ); + connect( wiki, "note_removed", function ( id ) { self.remove_link( id ); } ); + connect( wiki, "note_saved", function ( editor ) { self.update_link( editor ); } ); +} + +Recent_notes.prototype.link_clicked = function ( event ) { + var link = event.target(); + var query = parse_query( link ); + var note_id = query[ "note_id" ]; + var title = query[ "title" ]; + + if ( !note_id ) + return; + + this.wiki.load_editor( title, note_id ); + event.stop(); +} + +Recent_notes.prototype.add_link = function ( editor ) { + // if the link is already present in the recent notes list, bail + var item = getElement( "recent_note_item_" + editor.id ) + if ( item ) return; + + MAX_RECENT_NOTES_COUNT = 10; + + // if there will be too many recent notes listed once another is added, then remove the last one + var recent_items = getElementsByTagAndClassName( "tr", "recent_note_item", "recent_notes_table" ); + if ( recent_items && recent_items.length >= MAX_RECENT_NOTES_COUNT ) { + var last_item = recent_items[ recent_items.length - 1 ]; + removeElement( last_item ); + } + + // add a new recent note link at the top of the list + var expander = createDOM( "td", { "class": "tree_expander_empty", "id": "recent_note_expander_" + editor.id } ); + + var link = createDOM( "a", { + "href": "/notebooks/" + this.notebook_id + "?note_id=" + editor.id, + "id": "recent_note_link_" + editor.id, + "class": "recent_note_link" + }, editor.title || "untitled note" ); + + insertSiblingNodesAfter( "recent_notes_top", createDOM( + "tr", + { "id": "recent_note_item_" + editor.id, "class": "recent_note_item" }, + expander, + createDOM( "td", {}, link ) + ) ); + + var self = this; + connect( link, "onclick", function ( event ) { self.link_clicked( event ); } ); +} + +Recent_notes.prototype.remove_link = function ( note_id ) { + var item = getElement( "recent_note_item_" + note_id ); + if ( !item ) return; + + removeElement( item ); +} + +Recent_notes.prototype.update_link = function ( editor ) { + var item = getElement( "recent_note_item_" + editor.id ); + var link = getElement( "recent_note_link_" + editor.id ); + + // the link isn't in the recent notes list, so add it + if ( !item || !link ) { + this.add_link( editor ); + return; + } + + // the link is already in the recent notes list, so just move it to the top of the list + removeElement( item ); + replaceChildNodes( link, editor.title ); + insertSiblingNodesAfter( "recent_notes_top", item ); +} diff --git a/view/Main_page.py b/view/Main_page.py index efc17f2..e30cc2c 100644 --- a/view/Main_page.py +++ b/view/Main_page.py @@ -33,6 +33,7 @@ class Main_page( Page ): invite_id = None, after_login = None, signup_plan = None, + recent_notes = None, ): startup_note_ids = [ startup_note.object_id for startup_note in startup_notes ] @@ -141,6 +142,7 @@ class Main_page( Page ): ), notebook, startup_notes + ( notes and notes or [] ), + recent_notes, total_notes_count, ), id = u"left_area", diff --git a/view/Note_tree_area.py b/view/Note_tree_area.py index 69432d1..03ddde0 100644 --- a/view/Note_tree_area.py +++ b/view/Note_tree_area.py @@ -5,7 +5,7 @@ from Tags import Div, Span, H4, A, Table, Tr, Td class Note_tree_area( Div ): LINK_PATTERN = re.compile( u']+\s)?href="[^"]+"[^>]*>', re.IGNORECASE ) - def __init__( self, toolbar, notebook, root_notes, total_notes_count ): + def __init__( self, toolbar, notebook, root_notes, recent_notes, total_notes_count ): Div.__init__( self, toolbar, @@ -32,20 +32,24 @@ class Note_tree_area( Div ): ) or None, tree_id = "note_tree_root_table", ), - ( notebook.name != u"trash" ) and Span( + ( recent_notes and notebook.name != u"trash" ) and Span( H4( u"recent notes", id = u"recent_notes_area_title", ), + self.make_tree( + Tr( id = "recent_notes_top" ), + [ self.make_item( + title = note.title, + link_attributes = u'href="/notebooks/%s?note_id=%s"' % ( notebook.object_id, note.object_id ), + link_class = u"recent_note_link", + has_children = False, + root_note_id = note.object_id, + base_name = u"recent_note", + ) for note in recent_notes ], + tree_id = "recent_notes_table", + ), ) or None, - self.make_tree( - [ self.make_item( - title = note.title, - link_attributes = u'href="/notebooks/%s?note_id=%s"' % ( notebook.object_id, note.object_id ), - link_class = u"note_tree_link", - has_children = False, - ) for note in []],#recent_notes ], - ), - id = u"recent_notes_area_holder", + id = u"note_tree_area_holder", ), Span( id = "tree_arrow_hover_preload" ), Span( id = "tree_arrow_down_preload" ), @@ -54,22 +58,25 @@ class Note_tree_area( Div ): ) @staticmethod - def make_item( title, link_attributes, link_class, has_children = False, root_note_id = None, target = None ): + def make_item( title, link_attributes, link_class, has_children = False, root_note_id = None, target = None, base_name = None ): + if base_name is None: + base_name = u"note_tree" + return Tr( has_children and \ - Td( id = root_note_id and u"note_tree_expander_" + root_note_id or None, class_ = u"tree_expander" ) or - Td( id = root_note_id and u"note_tree_expander_" + root_note_id or None, class_ = u"tree_expander_empty" ), + Td( id = root_note_id and u"%s_expander_%s" % ( base_name, root_note_id ) or None, class_ = u"tree_expander" ) or + Td( id = root_note_id and u"%s_expander_%s" % ( base_name, root_note_id ) or None, class_ = u"tree_expander_empty" ), Td( u"%s" % ( link_attributes, - root_note_id and u' id="note_tree_link_%s"' % root_note_id or "", + root_note_id and u' id="%s_link_%s"' % ( base_name, root_note_id ) or "", target and u' target="%s"' % target or "", link_class, title or u"untitled note", ), ), - id = root_note_id and u"note_tree_item_" + root_note_id or None, - class_ = u"note_tree_item", + id = root_note_id and u"%s_item_%s" % ( base_name, root_note_id ) or None, + class_ = u"%s_item" % base_name, ) @staticmethod