aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--init.sql2
-rw-r--r--models/feed/feed.go54
-rw-r--r--models/item/item.go22
-rw-r--r--reset.sql2
-rw-r--r--static/ui.html11
-rw-r--r--static/ui.js27
-rw-r--r--web/web.go26
7 files changed, 108 insertions, 36 deletions
diff --git a/init.sql b/init.sql
index 51938ee..05fc263 100644
--- a/init.sql
+++ b/init.sql
@@ -6,6 +6,7 @@ CREATE TABLE feed (
url VARCHAR(255) NOT NULL,
web_url VARCHAR(255) NOT NULL DEFAULT "",
title VARCHAR(255) NOT NULL DEFAULT "",
+ category VARCHAR(255) NOT NULL DEFAULT "",
last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY (url),
PRIMARY KEY (id)
@@ -27,4 +28,3 @@ CREATE TABLE item (
INDEX (publish_date),
PRIMARY KEY (id)
);
-
diff --git a/models/feed/feed.go b/models/feed/feed.go
index 3d2473b..17f344f 100644
--- a/models/feed/feed.go
+++ b/models/feed/feed.go
@@ -8,16 +8,21 @@ import (
)
type Feed struct {
- Id int64 `json:"_id" xml:"-"`
- Url string `json:"url" xml:"xmlUrl,attr"`
- WebUrl string `json:"web_url" xml:"htmlUrl,attr"`
- Title string `json:"title" xml:"text,attr"`
+ Id int64 `json:"_id" xml:"-"`
+ Url string `json:"url" xml:"xmlUrl,attr"`
+ WebUrl string `json:"web_url" xml:"htmlUrl,attr"`
+ Title string `json:"title" xml:"text,attr"`
+ Category string `json:"category"`
// for OPML output purposes
XMLName string `json:"-" xml:"outline"`
Type string `json:"-" xml:"type,attr"`
}
+type Category struct {
+ Title string `json:"title"`
+}
+
func NewFeed(url string) error {
url = ResolveFeedURL(url)
stmt, err := models.DB.Prepare("INSERT INTO feed(url) VALUES(?)")
@@ -37,7 +42,8 @@ func All() ([]*Feed, error) {
func filter(where string) ([]*Feed, error) {
// todo: add back in title
- rows, err := models.DB.Query(`SELECT id, url, web_url, title
+ rows, err := models.DB.Query(`SELECT
+ id, url, web_url, title, category
FROM feed ` + where)
if err != nil {
return nil, err
@@ -47,7 +53,7 @@ func filter(where string) ([]*Feed, error) {
feeds := make([]*Feed, 0)
for rows.Next() {
f := new(Feed)
- err := rows.Scan(&f.Id, &f.Url, &f.WebUrl, &f.Title)
+ err := rows.Scan(&f.Id, &f.Url, &f.WebUrl, &f.Title, &f.Category)
f.Type = "rss"
if err != nil {
return nil, err
@@ -74,23 +80,23 @@ func (f *Feed) Update() {
}
models.DB.Query(`UPDATE feed
- SET title=?, url=?, web_url=?
- WHERE id=?`, f.Title, f.Url, f.WebUrl, f.Id)
+ SET title=?, url=?, web_url=?, category=?
+ WHERE id=?`, f.Title, f.Url, f.WebUrl, f.Category, f.Id)
}
func (f *Feed) Delete() {
log.Println("lets delete some shiteeee")
_, err := models.DB.Exec(`DELETE FROM feed
- WHERE id=?`, f.Id)
+ WHERE id=?`, f.Id)
if err != nil {
log.Println(err)
}
}
func (f *Feed) ByUrl(url string) error {
- err := models.DB.QueryRow(`SELECT id, url, title
+ err := models.DB.QueryRow(`SELECT id, url, title, category
FROM feed
- WHERE url = ?`, url).Scan(&f.Id, &f.Url, &f.Title)
+ WHERE url = ?`, url).Scan(&f.Id, &f.Url, &f.Title, &f.Category)
if err != nil {
return err
}
@@ -167,3 +173,29 @@ func ResolveFeedURL(url string) string {
}
return f
}
+
+func Categories() ([]*Category, error) {
+ rows, err := models.DB.Query(`SELECT
+ DISTINCT category
+ FROM feed
+ WHERE category!=""
+ ORDER BY lower(category) ASC`)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+
+ categories := make([]*Category, 0)
+ for rows.Next() {
+ c := new(Category)
+ err := rows.Scan(&c.Title)
+ if err != nil {
+ return nil, err
+ }
+ categories = append(categories, c)
+ }
+ if err = rows.Err(); err != nil {
+ return nil, err
+ }
+ return categories, nil
+}
diff --git a/models/item/item.go b/models/item/item.go
index c5ffff9..b3dc5eb 100644
--- a/models/item/item.go
+++ b/models/item/item.go
@@ -19,9 +19,10 @@ type Item struct {
Description string `json:"description"`
PublishDate string `json:"publish_date"`
- FeedId int64
- FeedTitle string `json:"feed_title"`
- FeedUrl string `json:"feed_url"`
+ FeedId int64
+ FeedTitle string `json:"feed_title"`
+ FeedUrl string `json:"feed_url"`
+ FeedCategory string `json:"feed_category"`
ReadState bool `json:"read"`
Starred bool `json:"starred"`
@@ -112,16 +113,16 @@ func (i *Item) GetFullContent() {
}
}
-func Filter(max_id int64, feed_id int64, unread_only bool, starred_only bool) ([]*Item, error) {
+func Filter(max_id int64, feed_id int64, category string, unread_only bool, starred_only bool) ([]*Item, error) {
var args []interface{}
query := `SELECT item.id, item.title, item.url, item.description,
item.read_state, item.starred, item.publish_date,
item.full_content, item.header_image,
- feed.url, feed.title
- FROM item,feed
- WHERE item.feed_id=feed.id `
+ feed.url, feed.title, feed.category
+ FROM item,feed
+ WHERE item.feed_id=feed.id `
if max_id != 0 {
query = query + "AND item.id < ? "
@@ -133,6 +134,11 @@ func Filter(max_id int64, feed_id int64, unread_only bool, starred_only bool) ([
args = append(args, feed_id)
}
+ if category != "" {
+ query = query + " AND feed.category=? "
+ args = append(args, category)
+ }
+
if unread_only {
query = query + " AND item.read_state=0 "
}
@@ -157,7 +163,7 @@ func Filter(max_id int64, feed_id int64, unread_only bool, starred_only bool) ([
items := make([]*Item, 0)
for rows.Next() {
i := new(Item)
- err := rows.Scan(&i.Id, &i.Title, &i.Url, &i.Description, &i.ReadState, &i.Starred, &i.PublishDate, &i.FullContent, &i.HeaderImage, &i.FeedUrl, &i.FeedTitle)
+ err := rows.Scan(&i.Id, &i.Title, &i.Url, &i.Description, &i.ReadState, &i.Starred, &i.PublishDate, &i.FullContent, &i.HeaderImage, &i.FeedUrl, &i.FeedTitle, &i.FeedCategory)
if err != nil {
log.Println(err)
return nil, err
diff --git a/reset.sql b/reset.sql
index 6bf30e1..ebb1e89 100644
--- a/reset.sql
+++ b/reset.sql
@@ -1,2 +1,2 @@
-DROP TABLE feed;
DROP TABLE item;
+DROP TABLE feed;
diff --git a/static/ui.html b/static/ui.html
index 2e002b9..a7bd3a4 100644
--- a/static/ui.html
+++ b/static/ui.html
@@ -20,6 +20,10 @@
<div id="controls"></div>
+ <h4 onclick="$('#tags').toggle();">Tags</h4>
+ <ul id="tags" style="display: none;">
+ </ul>
+
<h4 onclick="$('#feeds').toggle();">Feeds</h4>
<ul id="feeds" style="display: none;">
<li><a href="/import/">import</a></li>
@@ -39,6 +43,7 @@
</h2>
<p class="dateline" style="clear: both;">
<a href="${item.feed_url}">${item.feed_title}</a> | <a href="${item.url}">${item.p_url}</a>
+ | ${item.feed_category}
</p>
{{if item.header_image}}
<div class="img"><img src="${item.header_image}" /></div>
@@ -53,7 +58,9 @@
</script>
<script id="tag_template" type="text/jqtmp">
- ${tag.name} ${tag.unread}
+ {{if tag.selected}}[{{/if}}
+ ${tag.title}
+ {{if tag.selected}}]{{/if}}
</script>
<script id="feed_template" type="text/jqtmp">
@@ -69,8 +76,6 @@
</script>
<script id="controls_template" type="text/jqtmp">
-
-
<ul>
<li>
<a {{if app.unreadFilter}}style="font-weight: bold;"{{/if}}
diff --git a/static/ui.js b/static/ui.js
index d76602e..49f6ad0 100644
--- a/static/ui.js
+++ b/static/ui.js
@@ -21,7 +21,7 @@ var AppModel = Backbone.Model.extend({
boot: function() {
this.items.boot();
-// this.tags.boot();
+ this.tags.boot();
this.feeds.fetch({set: true, remove: false})
window.setInterval(function() { App.update_read_status() }, 5000);
},
@@ -239,9 +239,6 @@ var ItemCollection = Backbone.Collection.extend({
App.loading = true;
url = '/stream/';
- if(App.tag != undefined) {
- url = url + 'tag/' + App.tag + '/';
- }
url=url+'?foo=bar'
if(App.get('searchFilter')) {
url = url + '&q=' + App.get('searchFilter');
@@ -251,7 +248,10 @@ var ItemCollection = Backbone.Collection.extend({
}
if(App.get('starredFilter')) {
url = url + '&starred=1';
- }
+ }
+ if(App.tag != undefined) {
+ url = url + '&tag=' + App.tag;
+ }
if(App.items.last()) {
url = url + '&max_id=' + App.items.last().get('_id');
}
@@ -375,7 +375,7 @@ var TagCollection = Backbone.Collection.extend({
boot: function() {
var t = this;
- $.getJSON('/tags/', function(data) {
+ $.getJSON('/tag/', function(data) {
$.each(data, function(i,v) {
var tag = new Tag(v);
t.add(tag);
@@ -399,13 +399,20 @@ var TagView = Backbone.View.extend({
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.tag = null;
+ if (this.model.get('selected')) {
+ this.model.set('selected', false);
+ }
+ else {
+ App.tags.models.forEach ( function (tag) {
+ tag.set('selected', false);
+ });
+ this.model.set('selected', true);
+ App.tag = this.model.get('title');
+ }
App.items.reboot();
}
});
diff --git a/web/web.go b/web/web.go
index f2f2f8e..0dd0025 100644
--- a/web/web.go
+++ b/web/web.go
@@ -2,9 +2,9 @@ package web
import (
"adammathes.com/neko/config"
+ "adammathes.com/neko/crawler"
"adammathes.com/neko/models/feed"
"adammathes.com/neko/models/item"
- "adammathes.com/neko/crawler"
"encoding/json"
"fmt"
"golang.org/x/crypto/bcrypt"
@@ -33,6 +33,11 @@ func streamHandler(w http.ResponseWriter, r *http.Request) {
feed_id = f.Id
}
+ category := ""
+ if r.FormValue("tag") != "" {
+ category = r.FormValue("tag")
+ }
+
unread_only := true
if r.FormValue("read_filter") != "" {
unread_only = false
@@ -44,7 +49,7 @@ func streamHandler(w http.ResponseWriter, r *http.Request) {
}
var items []*item.Item
- items, err := item.Filter(int64(max_id), feed_id, unread_only, starred_only)
+ items, err := item.Filter(int64(max_id), feed_id, category, unread_only, starred_only)
if err != nil {
log.Println(err)
}
@@ -107,6 +112,22 @@ func feedHandler(w http.ResponseWriter, r *http.Request) {
}
}
+func categoryHandler(w http.ResponseWriter, r *http.Request) {
+ if r.Method == "GET" {
+ categories, err := feed.Categories()
+ if err != nil {
+ log.Println(err)
+ }
+
+ js, err := json.Marshal(categories)
+ if err != nil {
+ log.Println(err)
+ }
+ w.Write(js)
+ return
+ }
+}
+
var AuthCookie = "auth"
var SecondsInAYear = 60 * 60 * 24 * 365
@@ -164,6 +185,7 @@ func Serve() {
http.HandleFunc("/stream/", AuthWrap(streamHandler))
http.HandleFunc("/item/", AuthWrap(itemHandler))
http.HandleFunc("/feed/", AuthWrap(feedHandler))
+ http.HandleFunc("/tag/", AuthWrap(categoryHandler))
http.HandleFunc("/login/", loginHandler)
http.HandleFunc("/logout/", logoutHandler)