Actually get the feeds
This commit is contained in:
parent
3d7f0bba10
commit
8257347a09
|
@ -13,6 +13,7 @@
|
||||||
"postcss-nested": "^4.1.2",
|
"postcss-nested": "^4.1.2",
|
||||||
"postcss-smart-import": "^0.7.6",
|
"postcss-smart-import": "^0.7.6",
|
||||||
"register-service-worker": "^1.6.2",
|
"register-service-worker": "^1.6.2",
|
||||||
|
"rss-parser": "^3.7.2",
|
||||||
"tailwindcss": "^1.0.4",
|
"tailwindcss": "^1.0.4",
|
||||||
"vue": "^2.6.10",
|
"vue": "^2.6.10",
|
||||||
"vue-class-component": "^7.0.2",
|
"vue-class-component": "^7.0.2",
|
||||||
|
@ -22,6 +23,7 @@
|
||||||
"vuex-class": "^0.3.2"
|
"vuex-class": "^0.3.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@types/xml2js": "^0.4.4",
|
||||||
"@vue/cli-plugin-babel": "^3.8.0",
|
"@vue/cli-plugin-babel": "^3.8.0",
|
||||||
"@vue/cli-plugin-eslint": "^3.8.0",
|
"@vue/cli-plugin-eslint": "^3.8.0",
|
||||||
"@vue/cli-plugin-pwa": "^3.8.0",
|
"@vue/cli-plugin-pwa": "^3.8.0",
|
||||||
|
|
22
src/components/EventList/EventCard.vue
Normal file
22
src/components/EventList/EventCard.vue
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
<template>
|
||||||
|
<div class="card">{{ event.data.guid }}</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="postcss" scoped>
|
||||||
|
.card {
|
||||||
|
@apply p-2;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { Component, Vue, Prop } from "vue-property-decorator";
|
||||||
|
import { FeedEvent } from "@/store/types";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
components: {}
|
||||||
|
})
|
||||||
|
export default class EventCard extends Vue {
|
||||||
|
@Prop()
|
||||||
|
private event!: FeedEvent;
|
||||||
|
}
|
||||||
|
</script>
|
27
src/components/EventList/ListView.vue
Normal file
27
src/components/EventList/ListView.vue
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
<template>
|
||||||
|
<div class="list">
|
||||||
|
<EventCard v-for="event in events" :key="event.data.guid" :event="event" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="postcss" scoped>
|
||||||
|
.list {
|
||||||
|
@apply p-2;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { Component, Vue } from "vue-property-decorator";
|
||||||
|
import { Getter } from "vuex-class";
|
||||||
|
import EventCard from "./EventCard.vue";
|
||||||
|
import { FeedEvent } from "@/store/types";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
components: {
|
||||||
|
EventCard
|
||||||
|
}
|
||||||
|
})
|
||||||
|
export default class ListView extends Vue {
|
||||||
|
@Getter("events") private events!: FeedEvent[];
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -1,9 +1,12 @@
|
||||||
import { ActionTree } from "vuex";
|
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<AppState, AppState> = {
|
const actions: ActionTree<AppState, AppState> = {
|
||||||
// Add one or more sources
|
// Add one or more sources
|
||||||
addSources({ commit, dispatch }, sources: EventSource[]) {
|
addSources({ commit, dispatch }, sources: EventFeed[]) {
|
||||||
// Commit to state immediately
|
// Commit to state immediately
|
||||||
commit("appendSources", sources);
|
commit("appendSources", sources);
|
||||||
// Start fetching from each new source
|
// Start fetching from each new source
|
||||||
|
@ -12,7 +15,27 @@ const actions: ActionTree<AppState, AppState> = {
|
||||||
|
|
||||||
// Fetch events from a source url
|
// Fetch events from a source url
|
||||||
async fetchFromSource({ commit }, url: string) {
|
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 }
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
8
src/store/feed.ts
Normal file
8
src/store/feed.ts
Normal file
|
@ -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)
|
||||||
|
};
|
||||||
|
}
|
|
@ -1,15 +1,15 @@
|
||||||
import { GetterTree } from "vuex";
|
import { GetterTree } from "vuex";
|
||||||
import { AppState, Event, EventSource } from "./types";
|
import { AppState, FeedEvent, EventFeed } from "./types";
|
||||||
|
|
||||||
const getters: GetterTree<AppState, AppState> = {
|
const getters: GetterTree<AppState, AppState> = {
|
||||||
events(state): Event[] {
|
events(state): FeedEvent[] {
|
||||||
return Object.values(state.status)
|
return Object.values(state.status)
|
||||||
.filter(x => x.fetched && x.error == null)
|
.filter(x => x.fetched && x.error == null)
|
||||||
.map(x => x.events)
|
.map(x => x.events)
|
||||||
.flat();
|
.flat();
|
||||||
},
|
},
|
||||||
|
|
||||||
sources(state): EventSource[] {
|
sources(state): EventFeed[] {
|
||||||
return state.sources;
|
return state.sources;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { MutationTree } from "vuex";
|
import { MutationTree } from "vuex";
|
||||||
import { AppState, FetchStatus, EventSource } from "./types";
|
import { AppState, FetchStatus, EventFeed, FeedInfo } from "./types";
|
||||||
|
|
||||||
const mutations: MutationTree<AppState> = {
|
const mutations: MutationTree<AppState> = {
|
||||||
appendSources(state, payload: EventSource[]) {
|
appendSources(state, payload: EventFeed[]) {
|
||||||
// Add sources to list
|
// Add sources to list
|
||||||
state.sources = state.sources.concat(payload);
|
state.sources = state.sources.concat(payload);
|
||||||
|
|
||||||
|
@ -12,8 +12,17 @@ const mutations: MutationTree<AppState> = {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
setSourceStatus(state, payload: { url: string; status: FetchStatus }) {
|
setSourceStatus(
|
||||||
|
state,
|
||||||
|
payload: { url: string; info?: FeedInfo; status: FetchStatus }
|
||||||
|
) {
|
||||||
state.status[payload.url] = payload.status;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,21 +1,26 @@
|
||||||
import { Dict } from "@/types";
|
import { Dict } from "@/types";
|
||||||
|
|
||||||
export interface AppState {
|
export interface AppState {
|
||||||
sources: EventSource[];
|
sources: EventFeed[];
|
||||||
status: Dict<FetchStatus>;
|
status: Dict<FetchStatus>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface EventSource {
|
export interface EventFeed {
|
||||||
url: string;
|
url: string;
|
||||||
|
info?: FeedInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Event {
|
export interface FeedInfo {
|
||||||
|
title: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FeedEvent {
|
||||||
source: string; // Source URL
|
source: string; // Source URL
|
||||||
data: EventData;
|
data: FeedEventData;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface EventData {
|
export interface FeedEventData {
|
||||||
//TODO
|
guid: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FetchStatus {
|
export interface FetchStatus {
|
||||||
|
|
13
src/utils.ts
Normal file
13
src/utils.ts
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
export function must<T>(val?: T): T {
|
||||||
|
if (val) {
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
throw "must assertion failed";
|
||||||
|
}
|
||||||
|
|
||||||
|
export function mustArray<T>(val?: T[]): T[] {
|
||||||
|
if (val) {
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}
|
|
@ -1,12 +1,17 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="home"></div>
|
<div class="home">
|
||||||
|
<ListView />
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Component, Vue } from "vue-property-decorator";
|
import { Component, Vue } from "vue-property-decorator";
|
||||||
|
import ListView from "@/components/EventList/ListView.vue";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
components: {}
|
components: {
|
||||||
|
ListView
|
||||||
|
}
|
||||||
})
|
})
|
||||||
export default class Home extends Vue {}
|
export default class Home extends Vue {}
|
||||||
</script>
|
</script>
|
||||||
|
|
30
yarn.lock
30
yarn.lock
|
@ -774,6 +774,13 @@
|
||||||
resolved "https://registry.yarnpkg.com/@types/webpack-env/-/webpack-env-1.13.9.tgz#a67287861c928ebf4159a908d1fb1a2a34d4097a"
|
resolved "https://registry.yarnpkg.com/@types/webpack-env/-/webpack-env-1.13.9.tgz#a67287861c928ebf4159a908d1fb1a2a34d4097a"
|
||||||
integrity sha512-p8zp5xqkly3g4cCmo2mKOHI9+Z/kObmDj0BmjbDDJQlgDTiEGTbm17MEwTAusV6XceCy+bNw9q/ZHXHyKo3zkg==
|
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":
|
"@typescript-eslint/eslint-plugin@^1.1.0":
|
||||||
version "1.11.0"
|
version "1.11.0"
|
||||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-1.11.0.tgz#870f752c520db04db6d3668af7479026a6f2fb9a"
|
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"
|
hash-base "^3.0.0"
|
||||||
inherits "^2.0.1"
|
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:
|
run-async@^2.2.0:
|
||||||
version "2.3.0"
|
version "2.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0"
|
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"
|
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
|
||||||
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
|
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"
|
version "1.2.4"
|
||||||
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
|
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
|
||||||
integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==
|
integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==
|
||||||
|
@ -8834,6 +8849,19 @@ ws@^6.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
async-limiter "~1.0.0"
|
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:
|
xtend@^4.0.0, xtend@~4.0.1:
|
||||||
version "4.0.1"
|
version "4.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af"
|
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af"
|
||||||
|
|
Loading…
Reference in New Issue
Block a user