diff --git a/package.json b/package.json
index a03ce6e..4f907cd 100644
--- a/package.json
+++ b/package.json
@@ -13,6 +13,7 @@
"postcss-nested": "^4.1.2",
"postcss-smart-import": "^0.7.6",
"register-service-worker": "^1.6.2",
+ "rss-parser": "^3.7.2",
"tailwindcss": "^1.0.4",
"vue": "^2.6.10",
"vue-class-component": "^7.0.2",
@@ -22,6 +23,7 @@
"vuex-class": "^0.3.2"
},
"devDependencies": {
+ "@types/xml2js": "^0.4.4",
"@vue/cli-plugin-babel": "^3.8.0",
"@vue/cli-plugin-eslint": "^3.8.0",
"@vue/cli-plugin-pwa": "^3.8.0",
diff --git a/src/components/EventList/EventCard.vue b/src/components/EventList/EventCard.vue
new file mode 100644
index 0000000..70a74c3
--- /dev/null
+++ b/src/components/EventList/EventCard.vue
@@ -0,0 +1,22 @@
+
+ {{ event.data.guid }}
+
+
+
+
+
diff --git a/src/components/EventList/ListView.vue b/src/components/EventList/ListView.vue
new file mode 100644
index 0000000..5623a8a
--- /dev/null
+++ b/src/components/EventList/ListView.vue
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/store/actions.ts b/src/store/actions.ts
index 949171a..bd8ca70 100644
--- a/src/store/actions.ts
+++ b/src/store/actions.ts
@@ -1,9 +1,12 @@
import { ActionTree } from "vuex";
-import { AppState } from "./types";
+import { AppState, EventFeed, FeedEvent } from "./types";
+import { toEvent } from "./feed";
+import Parser from "rss-parser";
+import { mustArray } from "@/utils";
const actions: ActionTree = {
// Add one or more sources
- addSources({ commit, dispatch }, sources: EventSource[]) {
+ addSources({ commit, dispatch }, sources: EventFeed[]) {
// Commit to state immediately
commit("appendSources", sources);
// Start fetching from each new source
@@ -12,7 +15,27 @@ const actions: ActionTree = {
// Fetch events from a source url
async fetchFromSource({ commit }, url: string) {
- //TODO
+ try {
+ const parser = new Parser();
+ const feed = await parser.parseURL(url);
+ // Parse events
+ const events: FeedEvent[] = mustArray(feed.items)
+ .map(toEvent)
+ .map(x => ({
+ source: url,
+ data: x
+ }));
+ commit("setSourceStatus", {
+ url,
+ info: { title: feed.title, description: feed.description },
+ status: { fetched: true, events }
+ });
+ } catch (e) {
+ commit("setSourceStatus", {
+ url,
+ status: { fetched: true, error: e }
+ });
+ }
}
};
diff --git a/src/store/feed.ts b/src/store/feed.ts
new file mode 100644
index 0000000..6185885
--- /dev/null
+++ b/src/store/feed.ts
@@ -0,0 +1,8 @@
+import { FeedEvent, FeedEventData } from "./types";
+import { Item } from "rss-parser";
+
+export function toEvent(item: Item): FeedEventData {
+ return {
+ guid: item.guid || item.link || item.title || Math.random().toString(32)
+ };
+}
diff --git a/src/store/getters.ts b/src/store/getters.ts
index 3ca1892..15dd778 100644
--- a/src/store/getters.ts
+++ b/src/store/getters.ts
@@ -1,15 +1,15 @@
import { GetterTree } from "vuex";
-import { AppState, Event, EventSource } from "./types";
+import { AppState, FeedEvent, EventFeed } from "./types";
const getters: GetterTree = {
- events(state): Event[] {
+ events(state): FeedEvent[] {
return Object.values(state.status)
.filter(x => x.fetched && x.error == null)
.map(x => x.events)
.flat();
},
- sources(state): EventSource[] {
+ sources(state): EventFeed[] {
return state.sources;
}
};
diff --git a/src/store/mutations.ts b/src/store/mutations.ts
index 51affd6..cc1015f 100644
--- a/src/store/mutations.ts
+++ b/src/store/mutations.ts
@@ -1,8 +1,8 @@
import { MutationTree } from "vuex";
-import { AppState, FetchStatus, EventSource } from "./types";
+import { AppState, FetchStatus, EventFeed, FeedInfo } from "./types";
const mutations: MutationTree = {
- appendSources(state, payload: EventSource[]) {
+ appendSources(state, payload: EventFeed[]) {
// Add sources to list
state.sources = state.sources.concat(payload);
@@ -12,8 +12,17 @@ const mutations: MutationTree = {
});
},
- setSourceStatus(state, payload: { url: string; status: FetchStatus }) {
+ setSourceStatus(
+ state,
+ payload: { url: string; info?: FeedInfo; status: FetchStatus }
+ ) {
state.status[payload.url] = payload.status;
+ if (payload.info) {
+ let source = state.sources.find(x => x.url == payload.url);
+ if (source) {
+ source.info = payload.info;
+ }
+ }
}
};
diff --git a/src/store/types.ts b/src/store/types.ts
index cf304e5..5da4b36 100644
--- a/src/store/types.ts
+++ b/src/store/types.ts
@@ -1,21 +1,26 @@
import { Dict } from "@/types";
export interface AppState {
- sources: EventSource[];
+ sources: EventFeed[];
status: Dict;
}
-export interface EventSource {
+export interface EventFeed {
url: string;
+ info?: FeedInfo;
}
-export interface Event {
+export interface FeedInfo {
+ title: string;
+}
+
+export interface FeedEvent {
source: string; // Source URL
- data: EventData;
+ data: FeedEventData;
}
-export interface EventData {
- //TODO
+export interface FeedEventData {
+ guid: string;
}
export interface FetchStatus {
diff --git a/src/utils.ts b/src/utils.ts
new file mode 100644
index 0000000..a268b91
--- /dev/null
+++ b/src/utils.ts
@@ -0,0 +1,13 @@
+export function must(val?: T): T {
+ if (val) {
+ return val;
+ }
+ throw "must assertion failed";
+}
+
+export function mustArray(val?: T[]): T[] {
+ if (val) {
+ return val;
+ }
+ return [];
+}
diff --git a/src/views/Home.vue b/src/views/Home.vue
index 22b3c6e..2a24091 100644
--- a/src/views/Home.vue
+++ b/src/views/Home.vue
@@ -1,12 +1,17 @@
-
+
+
+
diff --git a/yarn.lock b/yarn.lock
index 1ebd4dd..502ccaf 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -774,6 +774,13 @@
resolved "https://registry.yarnpkg.com/@types/webpack-env/-/webpack-env-1.13.9.tgz#a67287861c928ebf4159a908d1fb1a2a34d4097a"
integrity sha512-p8zp5xqkly3g4cCmo2mKOHI9+Z/kObmDj0BmjbDDJQlgDTiEGTbm17MEwTAusV6XceCy+bNw9q/ZHXHyKo3zkg==
+"@types/xml2js@^0.4.4":
+ version "0.4.4"
+ resolved "https://registry.yarnpkg.com/@types/xml2js/-/xml2js-0.4.4.tgz#2093d94359a201806d997dccefc80153db311c66"
+ integrity sha512-O6Xgai01b9PB3IGA0lRIp1Ex3JBcxGDhdO0n3NIIpCyDOAjxcIGQFmkvgJpP8anTrthxOUQjBfLdRRi0Zn/TXA==
+ dependencies:
+ "@types/node" "*"
+
"@typescript-eslint/eslint-plugin@^1.1.0":
version "1.11.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-1.11.0.tgz#870f752c520db04db6d3668af7479026a6f2fb9a"
@@ -7149,6 +7156,14 @@ ripemd160@^2.0.0, ripemd160@^2.0.1:
hash-base "^3.0.0"
inherits "^2.0.1"
+rss-parser@^3.7.2:
+ version "3.7.2"
+ resolved "https://registry.yarnpkg.com/rss-parser/-/rss-parser-3.7.2.tgz#9f5b7d4944d4f7a190b469e31a8353aedb17c052"
+ integrity sha512-kx0VIFelgwBk5qA4n32U6cx40anAU7TwlRXjyxLDFgMlg8/UcJ64x+Hj5oRX1Kjos+OeFGOmnd5YXH5ES+bmzg==
+ dependencies:
+ entities "^1.1.1"
+ xml2js "^0.4.19"
+
run-async@^2.2.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0"
@@ -7199,7 +7214,7 @@ safe-regex@^1.1.0:
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
-sax@^1.2.4, sax@~1.2.4:
+sax@>=0.6.0, sax@^1.2.4, sax@~1.2.4:
version "1.2.4"
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==
@@ -8834,6 +8849,19 @@ ws@^6.0.0:
dependencies:
async-limiter "~1.0.0"
+xml2js@^0.4.19:
+ version "0.4.19"
+ resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.19.tgz#686c20f213209e94abf0d1bcf1efaa291c7827a7"
+ integrity sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==
+ dependencies:
+ sax ">=0.6.0"
+ xmlbuilder "~9.0.1"
+
+xmlbuilder@~9.0.1:
+ version "9.0.7"
+ resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-9.0.7.tgz#132ee63d2ec5565c557e20f4c22df9aca686b10d"
+ integrity sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=
+
xtend@^4.0.0, xtend@~4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af"