Compare commits

...

11 Commits

27 changed files with 8625 additions and 60 deletions

1
go.mod
View File

@@ -3,6 +3,7 @@ module github.com/averagemarcus/gopherss
go 1.15 go 1.15
require ( require (
github.com/PuerkitoBio/goquery v1.5.1
github.com/dustin/go-humanize v1.0.0 github.com/dustin/go-humanize v1.0.0
github.com/gofiber/fiber/v2 v2.0.6 github.com/gofiber/fiber/v2 v2.0.6
github.com/gofiber/template v1.6.3 github.com/gofiber/template v1.6.3

9
go.sum
View File

@@ -140,6 +140,7 @@ github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/gookit/color v1.2.4/go.mod h1:AhIE+pS6D4Ql0SQWbBeXPHw7gY0/sjHoA4s/n1KB7xg= github.com/gookit/color v1.2.4/go.mod h1:AhIE+pS6D4Ql0SQWbBeXPHw7gY0/sjHoA4s/n1KB7xg=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
@@ -185,6 +186,7 @@ github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCV
github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68= github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
@@ -201,6 +203,7 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kyoh86/exportloopref v0.1.4/go.mod h1:h1rDl2Kdj97+Kwh4gdz3ujE7XHmH51Q0lUiZ1z4NLj8= github.com/kyoh86/exportloopref v0.1.4/go.mod h1:h1rDl2Kdj97+Kwh4gdz3ujE7XHmH51Q0lUiZ1z4NLj8=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
@@ -252,6 +255,7 @@ github.com/mozilla/tls-observatory v0.0.0-20200317151703-4fa42e1c2dee/go.mod h1:
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/nakabonne/nestif v0.3.0/go.mod h1:dI314BppzXjJ4HsCnbo7XzrJHPszZsjnk5wEBSYHI2c= github.com/nakabonne/nestif v0.3.0/go.mod h1:dI314BppzXjJ4HsCnbo7XzrJHPszZsjnk5wEBSYHI2c=
github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU= github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/nishanths/exhaustive v0.0.0-20200525081945-8e46705b6132/go.mod h1:wBEpHwM2OdmeNpdCvRPUlkEbBuaFmcK4Wv8Q7FuGW3c= github.com/nishanths/exhaustive v0.0.0-20200525081945-8e46705b6132/go.mod h1:wBEpHwM2OdmeNpdCvRPUlkEbBuaFmcK4Wv8Q7FuGW3c=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
@@ -298,7 +302,9 @@ github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOms
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/sonatard/noctx v0.0.1/go.mod h1:9D2D/EoULe8Yy2joDHJj7bv3sZoq9AaSb8B4lqBjiZI= github.com/sonatard/noctx v0.0.1/go.mod h1:9D2D/EoULe8Yy2joDHJj7bv3sZoq9AaSb8B4lqBjiZI=
@@ -327,6 +333,7 @@ github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
@@ -522,6 +529,7 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U=
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
@@ -537,6 +545,7 @@ gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/driver/sqlite v1.1.3 h1:BYfdVuZB5He/u9dt4qDpZqiqDJ6KhPqs5QUqsr/Eeuc= gorm.io/driver/sqlite v1.1.3 h1:BYfdVuZB5He/u9dt4qDpZqiqDJ6KhPqs5QUqsr/Eeuc=
gorm.io/driver/sqlite v1.1.3/go.mod h1:AKDgRWk8lcSQSw+9kxCJnX/yySj8G3rdwYlU57cB45c= gorm.io/driver/sqlite v1.1.3/go.mod h1:AKDgRWk8lcSQSw+9kxCJnX/yySj8G3rdwYlU57cB45c=

View File

@@ -3,9 +3,12 @@ package feeds
import ( import (
"encoding/base64" "encoding/base64"
"fmt" "fmt"
"net/http"
"net/url"
"strings" "strings"
"time" "time"
"github.com/PuerkitoBio/goquery"
"github.com/mmcdole/gofeed" "github.com/mmcdole/gofeed"
"github.com/spf13/viper" "github.com/spf13/viper"
) )
@@ -31,12 +34,17 @@ func Refresh() error {
} }
} }
func RefreshFeed(url string) Feed { func RefreshFeed(feedUrl string) Feed {
fmt.Printf("Refreshing %s\n", url) fmt.Printf("Refreshing %s\n", feedUrl)
var feed Feed var feed Feed
f, err := fp.ParseURL(url) f, err := fp.ParseURL(feedUrl)
if err != nil { if err != nil && err == gofeed.ErrFeedTypeNotDetected {
fmt.Printf("Failed to refresh %s\n", url) foundFeed := loadFeedFromWebpage(feedUrl)
if foundFeed != nil {
feed = *foundFeed
}
} else if err != nil {
fmt.Printf("Failed to refresh %s - %v\n", feedUrl, err)
} else { } else {
imageURL := "" imageURL := ""
if f.Image != nil { if f.Image != nil {
@@ -44,11 +52,11 @@ func RefreshFeed(url string) Feed {
} }
feed = Feed{ feed = Feed{
ID: strings.ReplaceAll(base64.StdEncoding.EncodeToString([]byte(url)), "/", ""), ID: strings.ReplaceAll(base64.StdEncoding.EncodeToString([]byte(feedUrl)), "/", ""),
Title: f.Title, Title: f.Title,
Description: f.Description, Description: f.Description,
HomepageURL: f.Link, HomepageURL: f.Link,
FeedURL: url, FeedURL: feedUrl,
ImageURL: imageURL, ImageURL: imageURL,
LastUpdated: f.UpdatedParsed, LastUpdated: f.UpdatedParsed,
Items: []Item{}, Items: []Item{},
@@ -84,3 +92,35 @@ func RefreshFeed(url string) Feed {
return feed return feed
} }
func loadFeedFromWebpage(webpageUrl string) *Feed {
res, err := http.Get(webpageUrl)
if err != nil {
fmt.Println(err)
return nil
}
defer res.Body.Close()
if res.StatusCode != 200 {
fmt.Printf("status code error: %d %s", res.StatusCode, res.Status)
return nil
}
doc, err := goquery.NewDocumentFromReader(res.Body)
if err != nil {
fmt.Println(err)
return nil
}
feedUrl, ok := doc.Find(`[rel="alternate"][type="application/rss+xml"]`).First().Attr("href")
if ok {
if !strings.HasPrefix(feedUrl, "http") {
parsedUrl, _ := url.Parse(webpageUrl)
feedUrl = fmt.Sprintf("%s://%s%s", parsedUrl.Scheme, parsedUrl.Host, feedUrl)
}
feed := RefreshFeed(feedUrl)
return &feed
}
return nil
}

View File

@@ -2,10 +2,7 @@ package server
import ( import (
"fmt" "fmt"
"html/template"
"time"
"github.com/dustin/go-humanize"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
"github.com/gofiber/template/html" "github.com/gofiber/template/html"
@@ -24,21 +21,6 @@ func Start(port string) error {
engine := html.New("./views", ".html") engine := html.New("./views", ".html")
engine.Reload(true) engine.Reload(true)
engine.AddFunc("htmlSafe", func(html string) template.HTML {
return template.HTML(html)
})
engine.AddFunc("humanDate", func(date time.Time) template.HTML {
return template.HTML(humanize.Time(date))
})
engine.AddFunc("coalesce", func(args ...*string) string {
for _, s := range args {
if s != nil && *s != "" {
return *s
}
}
return ""
})
app := fiber.New(fiber.Config{ app := fiber.New(fiber.Config{
Views: engine, Views: engine,
}) })

View File

@@ -16,10 +16,11 @@
} }
</script> </script>
<script src="/static/feed-item.js" defer></script> <script src="/static/feed-item.js" defer></script>
<script src="/static/favicon.js"></script> <script src="/static/favicon.js"></script>
<script src="https://unpkg.com/vue@2.5.17/dist/vue.min.js"></script> <script src="https://unpkg.com/vue@2.5.17/dist/vue.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue-progressbar@0.7.5/dist/vue-progressbar.min.js"></script>
<script src="https://unpkg.com/dayjs@1.8.21/dayjs.min.js"></script> <script src="https://unpkg.com/dayjs@1.8.21/dayjs.min.js"></script>
<script src="https://unpkg.com/dayjs@1.9.5/plugin/relativeTime.js"></script> <script src="https://unpkg.com/dayjs@1.9.5/plugin/relativeTime.js"></script>
<script>dayjs.extend(window.dayjs_plugin_relativeTime)</script> <script>dayjs.extend(window.dayjs_plugin_relativeTime)</script>
@@ -90,7 +91,6 @@
<fieldset class="form-group"> <fieldset class="form-group">
<label for="url">URL:</label> <label for="url">URL:</label>
<input id="url" type="text" placeholder="" class="form-control" v-model="newSiteURL"> <input id="url" type="text" placeholder="" class="form-control" v-model="newSiteURL">
<div class="help-block">Enter the direct URL to the feed</div>
</fieldset> </fieldset>
<div class="form-actions"> <div class="form-actions">
<button type="button" class="btn btn-primary btn-block" v-on:click="addSite(newSiteURL)" :disabled="isBusy">Add</button> <button type="button" class="btn btn-primary btn-block" v-on:click="addSite(newSiteURL)" :disabled="isBusy">Add</button>
@@ -119,7 +119,7 @@
<div class="card-content"> <div class="card-content">
<div class="menu"> <div class="menu">
<button title="Show IFrame" v-on:click="showIframe(item)" :disabled="isBusy"> <button title="Show IFrame" v-on:click="showIframe(item)" :disabled="isBusy">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"width="24" height="24" viewBox="0 0 426 426" enable-background="new 0 0 426.001 426.001"><g><path d="M406.8 54.2H19.2A19.2 19.2 0 000 73.4v279.2c0 10.6 8.6 19.2 19.2 19.2h387.6c10.6 0 19.2-8.6 19.2-19.2V73.4c0-10.6-8.6-19.2-19.2-19.2zM368.4 82a17.8 17.8 0 110 35.7 17.8 17.8 0 010-35.7zm-48 0a17.8 17.8 0 110 35.7 17.8 17.8 0 010-35.7zm-48 0a17.8 17.8 0 110 35.7 17.8 17.8 0 010-35.7zm115.2 251.5H38.4V141.6h349.2v191.8z"/></g><g/><g/><g/><g/><g/><g/><g/><g/><g/><g/><g/><g/><g/><g/><g/></svg> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 426 426" ><path d="M406.8 54.2H19.2A19.2 19.2 0 000 73.4v279.2c0 10.6 8.6 19.2 19.2 19.2h387.6c10.6 0 19.2-8.6 19.2-19.2V73.4c0-10.6-8.6-19.2-19.2-19.2zM368.4 82a17.8 17.8 0 110 35.7 17.8 17.8 0 010-35.7zm-48 0a17.8 17.8 0 110 35.7 17.8 17.8 0 010-35.7zm-48 0a17.8 17.8 0 110 35.7 17.8 17.8 0 010-35.7zm115.2 251.5H38.4V141.6h349.2v191.8z" :style="{'fill': item.IframeVisible ? '#ff2e88' : '' }" fill-rule="nonzero"/></svg>
</button> </button>
<button title="Save" v-on:click="saveItem(item)" :disabled="isBusy"> <button title="Save" v-on:click="saveItem(item)" :disabled="isBusy">
<svg width="24" height="24" xmlns="http://www.w3.org/2000/svg"><path d="M12.8 5.6l-.8.8-.8-.8a5.4 5.4 0 00-7.6 7.6l7.9 7.9c.3.3.7.3 1 0l8-8a5.4 5.4 0 10-7.7-7.5z" :style="{'fill': item.Save ? '#ff2e88' : '' }" fill-rule="nonzero"/></svg> <svg width="24" height="24" xmlns="http://www.w3.org/2000/svg"><path d="M12.8 5.6l-.8.8-.8-.8a5.4 5.4 0 00-7.6 7.6l7.9 7.9c.3.3.7.3 1 0l8-8a5.4 5.4 0 10-7.7-7.5z" :style="{'fill': item.Save ? '#ff2e88' : '' }" fill-rule="nonzero"/></svg>
@@ -137,10 +137,34 @@
</div> </div>
</div> </div>
<vue-progress-bar></vue-progress-bar>
</div> </div>
<script> <script>
function isInViewport(element) {
const rect = element.getBoundingClientRect();
return (
rect.top >= 0 &&
rect.left >= 0 &&
rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
rect.right <= (window.innerWidth || document.documentElement.clientWidth)
);
}
Vue.use(VueProgressBar, {
color: '#00bcd4',
failedColor: '#ff2e88',
thickness: '5px',
transition: {
speed: '1s',
opacity: '0.8s',
termination: 100
},
autoRevert: true,
location: 'top',
inverse: false
});
const vm = new Vue({ const vm = new Vue({
el: '#app', el: '#app',
data: { data: {
@@ -189,6 +213,11 @@
setFavicon(this.unread); setFavicon(this.unread);
}, },
setBusy(isBusy) { setBusy(isBusy) {
if (isBusy) {
this.$Progress.start();
} else {
this.$Progress.finish();
}
this.isBusy = isBusy; this.isBusy = isBusy;
document.body.style.cursor = isBusy ? "wait" : ""; document.body.style.cursor = isBusy ? "wait" : "";
this.setPageTitle(); this.setPageTitle();
@@ -200,7 +229,10 @@
}, },
loadFeed(feed) { loadFeed(feed) {
this.selectedItem = undefined; this.selectedItem = undefined;
this.items.forEach(item => item.Read = item.Read || item.PendingRead); this.items.forEach(item => {
item.Read = item.Read || item.PendingRead;
item.IframeVisible = false;
});
this.selectedFeed = feed; this.selectedFeed = feed;
window.location.hash = feed; window.location.hash = feed;
}, },
@@ -210,7 +242,9 @@
this.selectedItem = undefined; this.selectedItem = undefined;
} else { } else {
this.selectedItem = item.ID; this.selectedItem = item.ID;
if (!isInViewport(document.getElementById(this.selectedItem))) {
document.getElementById(this.selectedItem).scrollIntoView(); document.getElementById(this.selectedItem).scrollIntoView();
}
item.PendingRead = true; item.PendingRead = true;
fetch(`/api/read/${item.ID}`, {method: "POST"}) fetch(`/api/read/${item.ID}`, {method: "POST"})
} }
@@ -231,6 +265,7 @@
}) })
}, },
showIframe(item) { showIframe(item) {
item.IframeVisible = !item.IframeVisible;
document.querySelector(`feed-item[item-id='${item.ID}'`).showIframe(); document.querySelector(`feed-item[item-id='${item.ID}'`).showIframe();
}, },
deleteFeed(feed) { deleteFeed(feed) {
@@ -306,10 +341,12 @@
}) })
.then(() => { .then(() => {
this.setBusy(false); this.setBusy(false);
this.newSiteURL = '';
}) })
.catch(err => { .catch(err => {
console.error(err); console.error(err);
this.setBusy(false); this.setBusy(false);
this.newSiteURL = '';
}); });
this.showAddModal = false; this.showAddModal = false;
}, },
@@ -376,7 +413,7 @@
this.setBusy(true); this.setBusy(true);
Promise.all([ Promise.all([
fetch(`/api/feeds`).then(res => res.json()).then(feeds => this.feeds = feeds), fetch(`/api/feeds`).then(res => res.json()).then(feeds => this.feeds = feeds),
fetch(`/api/unread`).then(res => res.json()).then(items => this.items = items.map(item => {item.PendingRead = false; return item;})), fetch(`/api/unread`).then(res => res.json()).then(items => this.items = items.map(item => {item.PendingRead = false; item.IframeVisible = false; return item;})),
fetch(`/api/saved`).then(res => res.json()).then(items => this.savedItems = items) fetch(`/api/saved`).then(res => res.json()).then(items => this.savedItems = items)
]) ])
.then(() => { .then(() => {
@@ -413,18 +450,39 @@
} }
}; };
let inView = true;
window.onfocus = window.onblur = window.onpageshow = window.onpagehide = function (e) {
if ({focus:1, pageshow:1}[e.type]) {
if (inView) return;
inView = true;
} else if (inView) {
inView = false;
}
};
// Fetch updates every 5 minutes // Fetch updates every 5 minutes
setInterval(() => { setInterval(() => {
if (!inView) {
fetch(`/api/unread`) fetch(`/api/unread`)
.then(res => res.json()) .then(res => res.json())
.then(items => { .then(items => {
if (!this.showRead) {
if (this.selectedItem && !items.some(i => i.ID == this.selectedItem)) {
items.unshift(this.items.find(i => i.ID == this.selectedItem));
}
this.items = items;
} else {
for (let item of items) { for (let item of items) {
if (!this.items.some(i => i.ID == item.ID)) { if (!this.items.some(i => i.ID == item.ID)) {
this.items.unshift(item); this.items.unshift(item);
}
}
}
this.setPageTitle(); this.setPageTitle();
});
} }
}
})
}, 5 * 60 * 1000); }, 5 * 60 * 1000);
document.addEventListener('keydown', this._keyListener.bind(this)); document.addEventListener('keydown', this._keyListener.bind(this));

View File

@@ -9,13 +9,6 @@ class FeedItem extends HTMLElement {
const template = document.createElement('template'); const template = document.createElement('template');
template.innerHTML = ` template.innerHTML = `
<style> <style>
@font-face {
font-family: "charter";
src: url("https://glyph.medium.com/font/be78681/0-3j_4g_6bu_6c4_6c8_6c9_6cc_6cd_6ci_6cm/charter-400-normal.woff") format("woff");
font-style: normal;
font-weight: 400;
unicode-range: U+0-7F, U+A0, U+200A, U+2014, U+2018, U+2019, U+201C, U+201D, U+2022, U+2026;
}
:host { :host {
width: 100% !important; width: 100% !important;
@@ -36,7 +29,9 @@ class FeedItem extends HTMLElement {
margin: auto auto !important; margin: auto auto !important;
} }
h1, h2, h3, h4 { h1, h2, h3, h4 {
font-family: "Atkinson Hyperlegible Bold";
margin-top: 1.3em; margin-top: 1.3em;
line-height: 1em;
} }
:root > h1 { :root > h1 {
margin-top: 0; margin-top: 0;
@@ -44,18 +39,34 @@ class FeedItem extends HTMLElement {
p, a { p, a {
line-height: 1.2em; line-height: 1.2em;
} }
p { p, li, div {
font-family: charter, Georgia, "Times New Roman", Times, serif; font-family: "Atkinson Hyperlegible Regular";
font-style: normal; font-style: normal;
font-weight: 400; font-weight: 400;
letter-spacing: -0.063px; letter-spacing: 0.05em
}
em {
font-family: "Atkinson Hyperlegible Italic";
font-style: normal;
}
strong {
font-weight: 500;
font-family: "Atkinson Hyperlegible Bold";
}
em strong, strong em {
font-family: "Atkinson Hyperlegible BoldItalic";
}
li {
margin: 0.6em 0;
} }
a { a {
color: #333; color: #333;
font-weight: bold; font-family: "Atkinson Hyperlegible Bold";
font-weight: 500;
letter-spacing: 0.05em
} }
:host(.dark) a { :host(.dark) a {
color: #ccc; color: #eee;
} }
a:hover, :host(.dark) a:hover { a:hover, :host(.dark) a:hover {
color: #ff2e88; color: #ff2e88;
@@ -117,9 +128,14 @@ class FeedItem extends HTMLElement {
} }
showIframe() { showIframe() {
if (this.shadowRoot.querySelector(".feedContent").style.display != "none") {
this.shadowRoot.querySelector(".feedContent").style.display = "none"; this.shadowRoot.querySelector(".feedContent").style.display = "none";
this.shadowRoot.querySelector("iframe").src = this.shadowRoot.querySelector("iframe").dataset.src; this.shadowRoot.querySelector("iframe").src = this.shadowRoot.querySelector("iframe").dataset.src;
this.shadowRoot.querySelector("iframe").style.display = "block"; this.shadowRoot.querySelector("iframe").style.display = "block";
} else {
this.shadowRoot.querySelector(".feedContent").style.display = "block";
this.shadowRoot.querySelector("iframe").style.display = "none";
}
} }
} }
customElements.define('feed-item', FeedItem); customElements.define('feed-item', FeedItem);

Binary file not shown.

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 169 KiB

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 172 KiB

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 138 KiB

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 136 KiB

View File

@@ -1,3 +1,48 @@
@font-face {
font-family: "Atkinson Hyperlegible Bold";
font-style: normal;
font-weight: normal;
font-display: block;
src: url("/static/fonts/Atkinson-Hyperlegible-Bold-102a.woff2") format("woff2"),
url("/static/fonts/Atkinson-Hyperlegible-Bold-102a.woff") format("woff"),
url("/static/fonts/Atkinson-Hyperlegible-Bold-102a.ttf") format("ttf"),
url("/static/fonts/Atkinson-Hyperlegible-Bold-102a.eot") format("eot"),
url("/static/fonts/Atkinson-Hyperlegible-Bold-102a.svg") format("svg");
}
@font-face {
font-family: "Atkinson Hyperlegible Regular";
font-style: normal;
font-weight: normal;
font-display: block;
src: url("/static/fonts/Atkinson-Hyperlegible-Regular-102a.woff2") format("woff2"),
url("/static/fonts/Atkinson-Hyperlegible-Regular-102a.woff") format("woff"),
url("/static/fonts/Atkinson-Hyperlegible-Regular-102a.ttf") format("ttf"),
url("/static/fonts/Atkinson-Hyperlegible-Regular-102a.eot") format("eot"),
url("/static/fonts/Atkinson-Hyperlegible-Regular-102a.svg") format("svg");
}
@font-face {
font-family: "Atkinson Hyperlegible Italic";
font-style: normal;
font-weight: normal;
font-display: block;
src: url("/static/fonts/Atkinson-Hyperlegible-Italic-102a.woff2") format("woff2"),
url("/static/fonts/Atkinson-Hyperlegible-Italic-102a.woff") format("woff"),
url("/static/fonts/Atkinson-Hyperlegible-Italic-102a.ttf") format("ttf"),
url("/static/fonts/Atkinson-Hyperlegible-Italic-102a.eot") format("eot"),
url("/static/fonts/Atkinson-Hyperlegible-Italic-102a.svg") format("svg");
}
@font-face {
font-family: "Atkinson Hyperlegible BoldItalic";
font-style: normal;
font-weight: normal;
font-display: block;
src: url("/static/fonts/Atkinson-Hyperlegible-BoldItalic-102a.woff2") format("woff2"),
url("/static/fonts/Atkinson-Hyperlegible-BoldItalic-102a.woff") format("woff"),
url("/static/fonts/Atkinson-Hyperlegible-BoldItalic-102a.ttf") format("ttf"),
url("/static/fonts/Atkinson-Hyperlegible-BoldItalic-102a.eot") format("eot"),
url("/static/fonts/Atkinson-Hyperlegible-BoldItalic-102a.svg") format("svg");
}
body { body {
padding-bottom: 20px; padding-bottom: 20px;
} }
@@ -171,7 +216,7 @@ button:not(:disabled):hover svg path {
} }
.item-content .card-content p { .item-content .card-content p {
font-family: 'Roboto', sans-serif; font-family: "Atkinson Hyperlegible Regular"; /*'Roboto', sans-serif;*/
font-size: 14px; font-size: 14px;
line-height: 20px; line-height: 20px;
letter-spacing: 0em; letter-spacing: 0em;
@@ -182,6 +227,10 @@ button:not(:disabled):hover svg path {
color: #333; color: #333;
font-weight: bold; font-weight: bold;
} }
.dark .item-content .card-content a { .dark {
color: #ccc; color: #eee;
}
.card-header {
padding: 5px !important;
} }