From 93d6d36eb697cd9452eb4aab446151a1a33ed245 Mon Sep 17 00:00:00 2001 From: Adam Mathes Date: Mon, 23 Jan 2017 20:04:03 -0800 Subject: neko v2 initial commit --- static/ui.js | 573 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 573 insertions(+) create mode 100644 static/ui.js (limited to 'static/ui.js') diff --git a/static/ui.js b/static/ui.js new file mode 100644 index 0000000..0c95e69 --- /dev/null +++ b/static/ui.js @@ -0,0 +1,573 @@ +var templates = {}; + +$(document).ready(function() { + boot(); +}); + +var AppModel = Backbone.Model.extend({ + defaults: { + 'selectedIndex': 0, + 'starredFilter': false, + 'allFilter': false, + 'unreadFilter': true, + 'feedFilter': undefined, + 'searchFilter': undefined + }, + + initialize: function() { + this.bind('change:selectedIndex', this.scroll_to_selected); +// this.bind('change:selectedIndex', this.scroll_to_selected) + }, + + boot: function() { + this.items.boot(); +// this.tags.boot(); + this.feeds.fetch({set: true, remove: false}) + window.setInterval(function() { App.update_read_status() }, 5000); + + }, + + filterToFeed: function(feed) { + this.set('feedFilter', feed); + this.items.reboot(); + }, + + filterToStarred: function() { + this.set('starredFilter', true); + this.set('allFilter', false); + this.set('unreadFilter', false); + this.items.reboot(); + }, + + filterToAll: function() { + this.set('starredFilter', false); + this.set('allFilter', true); + this.set('unreadFilter', false); + this.items.reboot(); + }, + + filterToUnread: function() { + this.set('starredFilter', false); + this.set('allFilter', false); + this.set('unreadFilter', true); + this.items.reboot(); + }, + + filterToSearch: function() { + this.set('searchFilter', $('#search').val()); + // this.set('starredFilter', false); + // this.set('allFilter', true); + // this.set('unreadFilter', false); + this.items.reboot(); + }, + + update_read_status: function() { + if(PUBLIC_VERSION) { + return; + } + + var screen_top = $(window).scrollTop(); + var screen_bottom = $(window).scrollTop() + $(window).height(); + + // // mark all visible items as read + $.each($('.item'), function(i,v) { + var item_top = $(v).offset().top; + // console.log("i ", i, "item_top ", item_top, "screen_top ", screen_top, "screen_bottom ", screen_bottom); + + if( (item_top < screen_top)) { + App.items.at(i).markRead(); + // console.log('marking as read: ', i); + } + }); +// window.setTimeout(App.update_read_status, 5000); + }, + + scroll_to_selected: function() { + var item = $('.item').eq(this.get('selectedIndex')); + if(item.offset()) { + var item_top = item.offset().top; + $('.item').removeClass('selected'); + item.addClass('selected'); + $(window).scrollTop(item_top); + } + App.items.at(this.get('selectedIndex')).markRead(); + if(App.items.models.length>1) { + if(this.get('selected')>=App.items.models.length-1) { + App.items.boot(); + } + } + }, + + next: function() { + if(this.get('selectedIndex') < this.items.models.length-1) { + this.set('selectedIndex', this.get('selectedIndex')+1); + } + if(this.get('selectedIndex') == this.items.models.length-1) { + App.items.boot(); + } + }, + + previous: function() { + if(this.get('selectedIndex') > 0) { + this.set('selectedIndex', this.get('selectedIndex')-1); + } + }, + + star: function() { + if(this.get('selectedIndex') > 0) { + App.items.at(this.get('selectedIndex')).toggleStar(); + } + } +}); +var App = new AppModel(); + +var ControlsView = Backbone.View.extend({ + className: 'controls', + + events: { + 'click .starred_filter': 'filterToStarred', + 'click .all_filter': 'filterToAll', + 'click .unread_filter': 'filterToUnread', + 'click .new_feed': 'newFeed', + 'click .search_go': 'filterToSearch', + }, + + initialize: function() { + _.bindAll(this, 'render'); + this.model.bind('change', this.render); + }, + + filterToStarred: function() { + App.filterToStarred(); + }, + + filterToAll: function() { + App.filterToAll(); + }, + + filterToUnread: function() { + App.filterToUnread(); + }, + + filterToSearch: function() { + App.filterToSearch(); + }, + + newFeed: function() { + var feed_url = prompt('New url to subscribe to'); + var feed = new Feed({'url': feed_url}); + App.feeds.add(feed); + feed.save(); + }, + + render: function() { + var h = $.tmpl(templates.controls_template, { 'app': this.model.toJSON() }); + $(this.el).html(h); + return this; + }, + +}); + + + +var Item = Backbone.Model.extend({ + idAttribute: "_id", + url: '/item/', + + initialize: function() { + this.bind('change', this.maybeSave); + }, + + maybeSave: function() { + if(PUBLIC_VERSION) { + return; + } + + if(this.hasChanged()) { + this.save(); + } + }, + + markRead: function() { + // recover if not tag + if(this.get('read')) { + return; + } + + // var t = this.get('feed').tag; + // var tag = App.tags.find(function(x){ return x.get('name') == t }); + this.set('read', true); + // if(tag) { + // tag.set('unread', tag.get('unread')-1); + // } + }, + + toggleStar: function() { + this.set({'starred': !(this.get('starred'))} ); + }, + + star: function() { + this.set({'starred': true}); + }, + + unstar: function() { + this.set({'starred': false}); + } + +}); + + +var ItemCollection = Backbone.Collection.extend({ + model: Item, + initialize: function() { + _.bindAll(this, 'boot', 'reboot'); + }, + + boot: function() { + if(App.loading) { + return; + } + if(App.noMore) { + return; + } + + App.loading = true; + url = '/stream/'; + if(App.get('starredFilter')) { + url = url + 'starred/'; + } + if(App.tag != undefined) { + url = url + 'tag/' + App.tag + '/'; + } + url=url+'?foo=bar' + if(App.get('searchFilter')) { + url = url + '&q=' + App.get('searchFilter'); + } + + if(App.get('feedFilter')) { + url = url + '&feed_url=' + App.get('feedFilter').get('url'); + } + + if(App.items.last()) { + url = url + '&max_id=' + App.items.last().get('_id'); + } + + if(App.get('allFilter') || App.get('starredFilter')) { + url = url + '&read_filter=all'; + } + + if(PUBLIC_VERSION) { + url = url + '&read_filter=all'; + } + + console.log('fetching from ', url); + + var t = this; + $.getJSON(url, function(data) { + var items = []; + $.each(data, function(i,v) { + var item = new Item(v); + t.add(item); + items.push(item); + if(t.models.length==1){ + App.set('selectedIndex', 0); + } + }); + if(data.length == 0) { + App.nomore = true; + } + App.loading = false; + + // we wait and add them all at once for performance on mobile + App.itemListView.addAll(items); + + }); + }, + + reboot: function() { + App.noMore = false; + this.reset(); + this.boot(); + }, + + +}); +App.items = new ItemCollection(); + + +var ItemView = Backbone.View.extend({ + tagName: "div", + className: "item", + template: templates.item_template, + events: { + "click .star": "star", + "click .unstar": "unstar", + }, + + initialize: function() { + _.bindAll(this, 'render', 'star'); + this.model.bind('change', this.render); + }, + + star: function() { + this.model.star(); + this.render(); + }, + + unstar: function() { + this.model.unstar(); + this.render(); + }, + + render: function() { + var h = $.tmpl(templates.item_template, { 'item': this.model.toJSON() }); + $(this.el).html(h); + return this; + }, +}); + +var ItemListView = Backbone.View.extend( { + initialize: function() { + _.bindAll(this, 'addOne', 'addAll', 'change', 'render', 'reset'); + // App.items.bind('add', this.addOne); + App.items.bind('reset', this.reset); + }, + addOne: function(item) { + var view = new ItemView({'model': item}); + this.$el.append(view.render().el); + }, + addAll: function(items) { + // Posts.each(this.addOne); + for(i in items) { + item = items[i]; + var view = new ItemView({'model': item}); + this.$el.append(view.render().el); + }; + }, + change: function() { + }, + render: function() { + }, + reset: function() { + this.$el.children().remove(); + } +}); + + +var Tag = Backbone.Model.extend({ +}); + +var TagCollection = Backbone.Collection.extend({ + model: Tag, + initialize: function() { + _.bindAll(this, 'boot'); + }, + + boot: function() { + var t = this; + $.getJSON('/tags/', function(data) { + $.each(data, function(i,v) { + var tag = new Tag(v); + t.add(tag); + }); + }); + } +}); +App.tags = new TagCollection(); + + +var TagView = Backbone.View.extend({ + tagName: "li", + className: "tag", + events: { + "click": "filterTo", + }, + initialize: function() { + _.bindAll(this, 'render', 'filterTo'); + this.model.bind('change', this.render); + }, + render: function() { + var h = $.tmpl(templates.tag_template, { 'tag': this.model.toJSON() }); + $(this.el).html(h); + if(this.model.get('unread')) { + $(this.el).addClass('hasunread'); + } + return this; + }, + filterTo: function() { + App.tag = this.model.get('name'); + App.items.reboot(); + } +}); + + +var TagListView = Backbone.View.extend( { + + initialize: function() { + _.bindAll(this, 'addOne', 'addAll', 'change', 'render'); + App.tags.bind('add', this.addOne); + App.tags.bind('refresh', this.addAll); + App.tags.bind('change', this.render); + }, + addOne: function(tag) { + var view = new TagView({'model': tag}); + this.$el.append(view.render().el); + }, + addAll: function() { + App.tags.each(this.addOne); + }, + change: function() { + }, + render: function() { + }, +}); + +App.tag = undefined; +App.page = 0; +App.read_filter = 'unread'; + + +var Feed = Backbone.Model.extend({ + idAttribute: "_id", +}); + +var FeedCollection = Backbone.Collection.extend({ + model: Feed, + url: '/feed/', + + initialize: function() { + /// _.bindAll(this, 'boot'); + console.log('initialized'); + }, +}); +App.feeds = new FeedCollection(); + +var FeedView = Backbone.View.extend({ + tagName: "li", + className: "feed", + events: { + "click .txt": "filterTo", + "click .delete": "del", + }, + initialize: function() { + _.bindAll(this, 'render', 'filterTo', "del"); + this.model.bind('change', this.render); + }, + render: function() { + var h = $.tmpl(templates.feed_template, { 'feed': this.model.toJSON() }); + $(this.el).html(h); + return this; + }, + filterTo: function() { + // console.log('filtering to feed ', this.model); + App.filterToFeed(this.model); + }, + del: function() { + this.model.destroy(); + this.$el.remove(); + }, +}); + + +var FeedListView = Backbone.View.extend( { + initialize: function() { + _.bindAll(this, 'addOne', 'addAll', 'change', 'render'); + App.feeds.bind('add', this.addOne); + App.feeds.bind('refresh', this.addAll); + App.feeds.bind('change', this.render); + }, + addOne: function(feed) { + console.log('adding a feed...', feed); + var view = new FeedView({'model': feed}); + this.$el.append(view.render().el); + }, + addAll: function() { + console.log('feed add all...'); + App.feeds.each(this.addOne); + }, + change: function() { + console.log('feeds changed add all...'); + }, + render: function() { + }, +}); + + +// var page = 0; +// var read_filter = 'unread'; + +var selected_item = 0; + +function boot() { + templates['item_template'] = $('#item_template').html(); + templates['tag_template'] = $('#tag_template').html(); + templates['feed_template'] = $('#feed_template').html(); + templates['controls_template'] = $('#controls_template').html(); + + App.itemListView = new ItemListView(); + App.itemListView.setElement($('#items')); + App.tagListView = new TagListView(); + App.tagListView.setElement($('#tags')); + App.feedListView = new FeedListView(); + App.feedListView.setElement($('#feeds')); + App.controlsView = new ControlsView({model: App}); + App.controlsView.setElement($('#controls')); + App.controlsView.render(); + + infini_scroll(); + + $('#unread_filter').on('click', function() { + App.read_filter = 'unread'; + App.items.reboot(); + }); + + $('#all_filter').on('click', function() { + App.read_filter = 'all'; + App.items.reboot(); + }); + + $('.logo').on('click', function() { + App.set('feedFilter', undefined); + App.items.reboot(); + }); + + // keyboard shortcuts + $('body').keydown(function(event) { + if (event.which == 74) { + event.preventDefault(); + App.next(); + } + if (event.which == 75) { + event.preventDefault(); + App.previous(); + } + if (event.which == 83) { + event.preventDefault(); + App.star(); + } + + }); + + App.boot(); +} + + +// // this is legacy code + +function infini_scroll() { + if(App.loading) { + } + else { + var dh = $('#items').height() - $(window).height(); + var st = $(window).scrollTop(); + if ( (dh-st) < 100 ){ + App.items.boot(); + } + } + window.setTimeout(infini_scroll, 1000); +} + + +var ItemSelector = { + selected_index: 0, +} -- cgit v1.2.3