Add updates of news items
This commit is contained in:
parent
876b268cb6
commit
d2d8118ccd
@ -17,7 +17,13 @@ interface APIResponse<T> {
|
|||||||
let newsFeed : NewsFeed | null = null;
|
let newsFeed : NewsFeed | null = null;
|
||||||
let authPage : AuthenticationPage | null = null;
|
let authPage : AuthenticationPage | null = null;
|
||||||
|
|
||||||
class ErrorAPI extends Error {}
|
class ErrorAPI extends Error {
|
||||||
|
readonly code: string;
|
||||||
|
constructor (error: ErrorDetails) {
|
||||||
|
super(error.message);
|
||||||
|
this.code = error.code;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export class ControllerAPI {
|
export class ControllerAPI {
|
||||||
protected endpoint: EndpointURL;
|
protected endpoint: EndpointURL;
|
||||||
@ -39,8 +45,8 @@ export class ControllerAPI {
|
|||||||
|
|
||||||
const response = await fetch(url, options);
|
const response = await fetch(url, options);
|
||||||
const result: APIResponse<T> = await response.json();
|
const result: APIResponse<T> = await response.json();
|
||||||
if (result.error) throw new ErrorAPI(result.error.message);
|
if (result.error) throw new ErrorAPI(result.error);
|
||||||
if (result.response === undefined) throw new ErrorAPI("Missing response from API");
|
if (result.response === undefined) throw new Error("Missing response from API");
|
||||||
return result.response;
|
return result.response;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,7 +89,11 @@ export class ControllerAPI {
|
|||||||
x.title,
|
x.title,
|
||||||
new Date(x.publishedAt),
|
new Date(x.publishedAt),
|
||||||
x.content,
|
x.content,
|
||||||
x.source
|
x.source,
|
||||||
|
x.comments,
|
||||||
|
x.likes,
|
||||||
|
x.isAdult,
|
||||||
|
x.isLiked
|
||||||
)));
|
)));
|
||||||
return newsFeed;
|
return newsFeed;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,6 +11,7 @@ export abstract class GefestElement {
|
|||||||
readonly gefestId: string;
|
readonly gefestId: string;
|
||||||
protected content: (GefestElement | string)[] | string;
|
protected content: (GefestElement | string)[] | string;
|
||||||
protected attributes: Record<string, string> = {};
|
protected attributes: Record<string, string> = {};
|
||||||
|
protected ignoreStyleClasses : Set<string> = new Set<string>;
|
||||||
protected classList: Set<string> = new Set();
|
protected classList: Set<string> = new Set();
|
||||||
protected eventHandlers: ((eventType: GefestElementEvents, eventData: unknown) => void)[] = [];
|
protected eventHandlers: ((eventType: GefestElementEvents, eventData: unknown) => void)[] = [];
|
||||||
|
|
||||||
@ -221,6 +222,9 @@ export abstract class GefestElement {
|
|||||||
* @returns The attributes string.
|
* @returns The attributes string.
|
||||||
*/
|
*/
|
||||||
protected attributesToString (): string {
|
protected attributesToString (): string {
|
||||||
|
const ignoreMap = Object.fromEntries(
|
||||||
|
Array.from(this.ignoreStyleClasses).map(x => [x, true])
|
||||||
|
);
|
||||||
let style : GefestStyle | null = null;
|
let style : GefestStyle | null = null;
|
||||||
if (typeof this.style === 'function') {
|
if (typeof this.style === 'function') {
|
||||||
style = this.style();
|
style = this.style();
|
||||||
@ -232,7 +236,9 @@ export abstract class GefestElement {
|
|||||||
if (style instanceof GefestStyle)
|
if (style instanceof GefestStyle)
|
||||||
attributes.class = [
|
attributes.class = [
|
||||||
attributes.class,
|
attributes.class,
|
||||||
style.getClasses(this).join(" ")
|
style.getClasses(this).filter(styleClass => !ignoreMap[
|
||||||
|
styleClass
|
||||||
|
]).join(" ")
|
||||||
].join(" ");
|
].join(" ");
|
||||||
|
|
||||||
return Object.entries(
|
return Object.entries(
|
||||||
|
|||||||
@ -1,6 +1,11 @@
|
|||||||
import { GefestElement } from "../modules/Gefest/element";
|
import { GefestElement } from "../modules/Gefest/element";
|
||||||
|
import { GefestA } from "../modules/Gefest/primitives/a";
|
||||||
|
import { GefestI } from "../modules/Gefest/primitives/i";
|
||||||
import { SupportedStyles } from "../styles";
|
import { SupportedStyles } from "../styles";
|
||||||
|
|
||||||
|
type FontAwesomeGroup = "fas" | "far" | "fab"; // e.g. "fas fa-home"
|
||||||
|
export type FontAwesomeIcon = `${FontAwesomeGroup} fa-${string}`;
|
||||||
|
|
||||||
export abstract class DefaultElement extends GefestElement {
|
export abstract class DefaultElement extends GefestElement {
|
||||||
style: SupportedStyles | (() => SupportedStyles) | null = null;
|
style: SupportedStyles | (() => SupportedStyles) | null = null;
|
||||||
constructor(content: (GefestElement | string)[] | string) {
|
constructor(content: (GefestElement | string)[] | string) {
|
||||||
|
|||||||
@ -36,7 +36,7 @@ function formatDate(date: Date) {
|
|||||||
}:${dateItemFormater(date.getMinutes())}`;
|
}:${dateItemFormater(date.getMinutes())}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSourceLnk (source: NewsSource, id: number) : string {
|
function getSourceLnk (source: NewsSource, id: string) : string {
|
||||||
const iconOfSource : GefestElement = !source.icon ?
|
const iconOfSource : GefestElement = !source.icon ?
|
||||||
new GefestI("") : new GefestImg("", source.icon);
|
new GefestI("") : new GefestImg("", source.icon);
|
||||||
if (iconOfSource instanceof GefestI) {
|
if (iconOfSource instanceof GefestI) {
|
||||||
@ -62,7 +62,17 @@ class CardBodyBS5 extends DefaultElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class NewsItem extends DefaultElement {
|
export class NewsItem extends DefaultElement {
|
||||||
constructor(id: number, title: string, publishDate: Date, content: string, source: NewsSource) {
|
constructor(
|
||||||
|
id: string,
|
||||||
|
title: string,
|
||||||
|
publishDate: Date,
|
||||||
|
content: string,
|
||||||
|
source: NewsSource,
|
||||||
|
commentsCount: number,
|
||||||
|
likesCount: Number,
|
||||||
|
isAdult: boolean,
|
||||||
|
isLiked: boolean,
|
||||||
|
) {
|
||||||
const header = new GefestHeader(title, 2);
|
const header = new GefestHeader(title, 2);
|
||||||
header.addClass("card-title");
|
header.addClass("card-title");
|
||||||
const published = new GefestSmall(formatDate(publishDate) + " " + getSourceLnk(source, id));
|
const published = new GefestSmall(formatDate(publishDate) + " " + getSourceLnk(source, id));
|
||||||
@ -70,11 +80,23 @@ export class NewsItem extends DefaultElement {
|
|||||||
const contentParagraph = new GefestP(content);
|
const contentParagraph = new GefestP(content);
|
||||||
contentParagraph.addClass("card-text");
|
contentParagraph.addClass("card-text");
|
||||||
|
|
||||||
|
const splitter = new (class extends DefaultElement {
|
||||||
|
constructor() {
|
||||||
|
super([]);
|
||||||
|
this.ignoreStyleClasses.add("bg-light");
|
||||||
|
this.ignoreStyleClasses.add("bg-dark");
|
||||||
|
this.ignoreStyleClasses.add("text-light");
|
||||||
|
this.ignoreStyleClasses.add("text-dark");
|
||||||
|
}
|
||||||
|
protected htmlTag: string = "hr";
|
||||||
|
});
|
||||||
|
|
||||||
super([
|
super([
|
||||||
new CardBodyBS5([
|
new CardBodyBS5([
|
||||||
header,
|
header,
|
||||||
published,
|
published,
|
||||||
contentParagraph
|
contentParagraph,
|
||||||
|
splitter,
|
||||||
])
|
])
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|||||||
@ -1,17 +1,70 @@
|
|||||||
import { NewsItem } from "./news-item";
|
import { NewsItem } from "./news-item";
|
||||||
import { DefaultElement } from "../default-element";
|
import { DefaultElement, FontAwesomeIcon } from "../default-element";
|
||||||
import { GefestHeader } from "../../modules/Gefest/primitives/header";
|
import { GefestHeader } from "../../modules/Gefest/primitives/header";
|
||||||
import { UserType } from "../../api/user-info";
|
import { UserType } from "../../api/user-info";
|
||||||
import { GefestElement } from "../../modules/Gefest/element";
|
import { GefestElement } from "../../modules/Gefest/element";
|
||||||
import { GefestI } from "../../modules/Gefest/primitives/i";
|
import { GefestI } from "../../modules/Gefest/primitives/i";
|
||||||
|
import { GefestButton } from "../../modules/Gefest/primitives/button";
|
||||||
|
|
||||||
export function isNewsUI (element : GefestElement): boolean {
|
export function isNewsUI (element : GefestElement): boolean {
|
||||||
return element instanceof NewsUserInterfaceBody ||
|
return element instanceof NewsUserInterfaceBody ||
|
||||||
element instanceof NewsUserInterfaceCard;
|
element instanceof NewsUserInterfaceCard;
|
||||||
}
|
}
|
||||||
|
class NewsUserInterfaceBtn extends DefaultElement {
|
||||||
|
constructor(icon: FontAwesomeIcon) {
|
||||||
|
const fontawesomeIcon = new (class extends GefestI {
|
||||||
|
constructor () {
|
||||||
|
super("");
|
||||||
|
this.ignoreStyleClasses.add("bg-light");
|
||||||
|
this.ignoreStyleClasses.add("bg-dark");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
fontawesomeIcon.addClass(icon);
|
||||||
|
|
||||||
|
super([fontawesomeIcon]);
|
||||||
|
this.ignoreStyleClasses.add("bg-light");
|
||||||
|
this.ignoreStyleClasses.add("bg-dark");
|
||||||
|
|
||||||
|
this.setAttribute("type", "button");
|
||||||
|
this.addClass("btn");
|
||||||
|
this.addClass("btn-outline-secondary");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected htmlTag: string = "button";
|
||||||
|
}
|
||||||
class NewsUserInterfaceBody extends DefaultElement {
|
class NewsUserInterfaceBody extends DefaultElement {
|
||||||
constructor(userType : UserType) {
|
constructor(userType : UserType) {
|
||||||
super([]);
|
const addFilter = new NewsUserInterfaceBtn("fas fa-filter");
|
||||||
|
const search = new NewsUserInterfaceBtn("fas fa-search");
|
||||||
|
//search.addClass("input-group-text");
|
||||||
|
const searchArea = new (class extends DefaultElement {
|
||||||
|
constructor () {
|
||||||
|
super([]);
|
||||||
|
this.setAttribute("type", "text");
|
||||||
|
this.addClass("form-control");
|
||||||
|
this.setAttribute("placeholder", "Type a request");
|
||||||
|
}
|
||||||
|
protected htmlTag: string = "input";
|
||||||
|
});
|
||||||
|
const panelElements: GefestElement[] = [ addFilter, search, searchArea ];
|
||||||
|
switch (userType) {
|
||||||
|
case "admin":
|
||||||
|
const addSource = new NewsUserInterfaceBtn("fas fa-plus-circle");
|
||||||
|
panelElements.push(addSource);
|
||||||
|
break;
|
||||||
|
case "user":
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
const impossible: never = userType;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
super([new (class extends DefaultElement {
|
||||||
|
constructor () {
|
||||||
|
super(panelElements);
|
||||||
|
this.addClass("btn-group");
|
||||||
|
this.setAttribute("role", "group");
|
||||||
|
}
|
||||||
|
})]);
|
||||||
this.addClass("card-body");
|
this.addClass("card-body");
|
||||||
this.addClass("central-navbar");
|
this.addClass("central-navbar");
|
||||||
}
|
}
|
||||||
@ -69,23 +122,27 @@ export class NewsUserInterface extends DefaultElement {
|
|||||||
export class NewsFeed extends DefaultElement {
|
export class NewsFeed extends DefaultElement {
|
||||||
constructor(items: NewsItem[]) {
|
constructor(items: NewsItem[]) {
|
||||||
super([
|
super([
|
||||||
new NewsUserInterface("user"),
|
new NewsUserInterface("admin"),
|
||||||
...items,
|
...items,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface NewsData {
|
|
||||||
id: number;
|
|
||||||
title : string;
|
|
||||||
content : string;
|
|
||||||
source : NewsSource;
|
|
||||||
publishedAt : string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface NewsSource {
|
export interface NewsSource {
|
||||||
name: string;
|
name: string;
|
||||||
icon?: string;
|
icon?: string;
|
||||||
sourceURL?: string;
|
sourceURL?: string;
|
||||||
itemURL?: string;
|
itemURL?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface NewsData {
|
||||||
|
id: string;
|
||||||
|
title : string;
|
||||||
|
content : string;
|
||||||
|
source : NewsSource;
|
||||||
|
publishedAt : string;
|
||||||
|
comments: number;
|
||||||
|
likes: number;
|
||||||
|
isAdult: boolean;
|
||||||
|
isLiked: boolean;
|
||||||
|
}
|
||||||
@ -9,7 +9,7 @@
|
|||||||
"main": "dist/server.js",
|
"main": "dist/server.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "echo \"Error: no test specified\" && exit 1",
|
"test": "echo \"Error: no test specified\" && exit 1",
|
||||||
"build": "npx tsc && bash build-static.sh",
|
"build": "rm -rf dist/ && npx tsc && bash build-static.sh",
|
||||||
"start": "node dist/server.js"
|
"start": "node dist/server.js"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,7 +18,7 @@ router.get('/isAuthenticated', (req, res) => {
|
|||||||
|
|
||||||
router.get('/news', authRequired, (req, res) => {
|
router.get('/news', authRequired, (req, res) => {
|
||||||
const newsList : NewsItem[] = getTestNews();
|
const newsList : NewsItem[] = getTestNews();
|
||||||
const response = new ApiResponse(newsList);
|
const response = new ApiResponse<NewsItem[]>(newsList);
|
||||||
return res.json(response.getAnswer());
|
return res.json(response.getAnswer());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -9,27 +9,32 @@ import { StringUrl } from "../../utils/requests";
|
|||||||
}*/
|
}*/
|
||||||
|
|
||||||
interface SystemNewsSource {
|
interface SystemNewsSource {
|
||||||
name: string;
|
name: string;
|
||||||
icon?: string;
|
icon?: string;
|
||||||
type: "system";
|
type: "system";
|
||||||
sourceURL?: never;
|
sourceURL?: never;
|
||||||
itemURL?: never;
|
itemURL?: never;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface OtherNewsSource {
|
interface OtherNewsSource {
|
||||||
name: string;
|
name: string;
|
||||||
icon?: string;
|
icon?: string;
|
||||||
type: "RSS";
|
type: "RSS";
|
||||||
sourceURL: StringUrl;
|
sourceURL: StringUrl;
|
||||||
itemURL?: StringUrl;
|
itemURL?: StringUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type NewsSource = SystemNewsSource | OtherNewsSource;
|
export type NewsSource = SystemNewsSource | OtherNewsSource;
|
||||||
|
|
||||||
export interface NewsItem {
|
export interface NewsItem {
|
||||||
id : number;
|
id : string;
|
||||||
title : string;
|
title : string;
|
||||||
content : string;
|
content : string;
|
||||||
source: NewsSource;
|
source: NewsSource;
|
||||||
publishedAt : Date;
|
publishedAt : Date;
|
||||||
|
comments: number;
|
||||||
|
likes: number;
|
||||||
|
|
||||||
|
isAdult: boolean;
|
||||||
|
isLiked: boolean;
|
||||||
}
|
}
|
||||||
@ -2,6 +2,7 @@ import express, { Request, Response } from 'express';
|
|||||||
import api from './api';
|
import api from './api';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { AuthenticationRulesTypes } from './api/models/AuthenticationRules';
|
import { AuthenticationRulesTypes } from './api/models/AuthenticationRules';
|
||||||
|
import env from './utils/environments';
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
const port = 3000;
|
const port = 3000;
|
||||||
@ -39,6 +40,10 @@ app.get('/home', frontend);
|
|||||||
|
|
||||||
app.use(express.static(path.join(__dirname, 'static')));
|
app.use(express.static(path.join(__dirname, 'static')));
|
||||||
|
|
||||||
app.listen(port, () => {
|
app.listen(env.SERVER_PORT, env.SERVER_ADDRESS, (err) => {
|
||||||
console.log(`Server is running at http://localhost:${port}`);
|
if (err)
|
||||||
|
throw err;
|
||||||
|
console.log(`Server is running at http://${
|
||||||
|
env.SERVER_ADDRESS
|
||||||
|
}:${env.SERVER_PORT}`);
|
||||||
});
|
});
|
||||||
@ -1,3 +0,0 @@
|
|||||||
class Database {}
|
|
||||||
|
|
||||||
export = new Database;
|
|
||||||
27
src/utils/environments.ts
Normal file
27
src/utils/environments.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
function validationError(name: string): string {
|
||||||
|
throw new Error(`There isn't environment ${name}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isEnabledSSL (ssl: string): boolean {
|
||||||
|
return ssl === "1" ||
|
||||||
|
ssl.toLowerCase() === "true" ||
|
||||||
|
ssl.toLowerCase() === "on" ||
|
||||||
|
ssl.toLowerCase() === "enable" ||
|
||||||
|
ssl.toLowerCase() === "enabled";
|
||||||
|
}
|
||||||
|
|
||||||
|
const API_ADDRESS: string = process.env.REMOTE_SERVER ?? "127.0.0.1";
|
||||||
|
let API_PORT: number = Number(process.env.API_PORT);
|
||||||
|
if (Number.isNaN(API_PORT))
|
||||||
|
API_PORT = 8080;
|
||||||
|
const API_SSL: boolean = isEnabledSSL(process.env.API_SSL ?? "");
|
||||||
|
|
||||||
|
const SERVER_ADDRESS: string = process.env.REMOTE_SERVER ?? "0.0.0.0";
|
||||||
|
let SERVER_PORT: number = Number(process.env.SERVER_PORT);
|
||||||
|
if (Number.isNaN(SERVER_PORT))
|
||||||
|
SERVER_PORT = 3000;
|
||||||
|
|
||||||
|
export default {
|
||||||
|
API_ADDRESS, API_PORT, API_SSL,
|
||||||
|
SERVER_ADDRESS, SERVER_PORT,
|
||||||
|
};
|
||||||
@ -5,134 +5,186 @@ export function getTestNews (): NewsItem[] {
|
|||||||
|
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
id: 0,
|
id: "0",
|
||||||
title: "Lorem Ispum",
|
title: "Lorem Ispum",
|
||||||
content: "Lorem Ipsum dolor sit amet, consectetur adipiscing elit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Lorem ipsum dolor sit amet,",
|
content: "Lorem Ipsum dolor sit amet, consectetur adipiscing elit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Lorem ipsum dolor sit amet,",
|
||||||
publishedAt: new Date(),
|
publishedAt: new Date(),
|
||||||
source: {
|
source: {
|
||||||
name: "Test Destination",
|
name: "Test Destination",
|
||||||
type: "system",
|
type: "system",
|
||||||
}
|
},
|
||||||
|
comments: 0,
|
||||||
|
likes: 1,
|
||||||
|
isAdult: false,
|
||||||
|
isLiked: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 1,
|
id: "1",
|
||||||
title: "News Item 1",
|
title: "News Item 1",
|
||||||
content: "This is the content for news item 1. It contains some sample text to demonstrate the news functionality.",
|
content: "This is the content for news item 1. It contains some sample text to demonstrate the news functionality.",
|
||||||
publishedAt: new Date(),
|
publishedAt: new Date(),
|
||||||
source: {
|
source: {
|
||||||
name: "Test Destination",
|
name: "Test Destination",
|
||||||
type: "system",
|
type: "system",
|
||||||
}
|
},
|
||||||
|
comments: 0,
|
||||||
|
likes: 0,
|
||||||
|
isAdult: false,
|
||||||
|
isLiked: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 2,
|
id: '2',
|
||||||
title: "News Item 2",
|
title: "News Item 2",
|
||||||
content: "This is the content for news item 2. Another piece of sample news content for testing purposes.",
|
content: "This is the content for news item 2. Another piece of sample news content for testing purposes.",
|
||||||
publishedAt: new Date(),
|
publishedAt: new Date(),
|
||||||
source: {
|
source: {
|
||||||
name: "Test Destination",
|
name: "Test Destination",
|
||||||
type: "system",
|
type: "system",
|
||||||
}
|
},
|
||||||
|
comments: 0,
|
||||||
|
likes: 0,
|
||||||
|
isAdult: false,
|
||||||
|
isLiked: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 3,
|
id: '3',
|
||||||
title: "News Item 3",
|
title: "News Item 3",
|
||||||
content: "News item 3 brings you the latest updates. Stay informed with our regular news feed.",
|
content: "News item 3 brings you the latest updates. Stay informed with our regular news feed.",
|
||||||
publishedAt: new Date(),
|
publishedAt: new Date(),
|
||||||
source: {
|
source: {
|
||||||
name: "Test Destination",
|
name: "Test Destination",
|
||||||
type: "system",
|
type: "system",
|
||||||
}
|
},
|
||||||
|
comments: 0,
|
||||||
|
likes: 0,
|
||||||
|
isAdult: false,
|
||||||
|
isLiked: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 4,
|
id: '4',
|
||||||
title: "News Item 4",
|
title: "News Item 4",
|
||||||
content: "Content for the fourth news item. This is part of the test data for the application.",
|
content: "Content for the fourth news item. This is part of the test data for the application.",
|
||||||
publishedAt: new Date(),
|
publishedAt: new Date(),
|
||||||
source: {
|
source: {
|
||||||
name: "Test Destination",
|
name: "Test Destination",
|
||||||
type: "system",
|
type: "system",
|
||||||
}
|
},
|
||||||
|
comments: 0,
|
||||||
|
likes: 0,
|
||||||
|
isAdult: false,
|
||||||
|
isLiked: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 5,
|
id: '5',
|
||||||
title: "News Item 5",
|
title: "News Item 5",
|
||||||
content: "Fifth news item in the list. More sample content to populate the news array.",
|
content: "Fifth news item in the list. More sample content to populate the news array.",
|
||||||
publishedAt: new Date(),
|
publishedAt: new Date(),
|
||||||
source: {
|
source: {
|
||||||
name: "Test Destination",
|
name: "Test Destination",
|
||||||
type: "system",
|
type: "system",
|
||||||
}
|
},
|
||||||
|
comments: 0,
|
||||||
|
likes: 0,
|
||||||
|
isAdult: false,
|
||||||
|
isLiked: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 6,
|
id: '6',
|
||||||
title: "News Item 6",
|
title: "News Item 6",
|
||||||
content: "This is news item number six. Continuing to add test data for the news feature.",
|
content: "This is news item number six. Continuing to add test data for the news feature.",
|
||||||
publishedAt: new Date(),
|
publishedAt: new Date(),
|
||||||
source: {
|
source: {
|
||||||
name: "Test Destination",
|
name: "Test Destination",
|
||||||
type: "system",
|
type: "system",
|
||||||
}
|
},
|
||||||
|
comments: 0,
|
||||||
|
likes: 0,
|
||||||
|
isAdult: false,
|
||||||
|
isLiked: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 7,
|
id: '7',
|
||||||
title: "News Item 7",
|
title: "News Item 7",
|
||||||
content: "Seventh item in the news feed. Sample text to ensure the array has sufficient data.",
|
content: "Seventh item in the news feed. Sample text to ensure the array has sufficient data.",
|
||||||
publishedAt: new Date(),
|
publishedAt: new Date(),
|
||||||
source: {
|
source: {
|
||||||
name: "Test Destination",
|
name: "Test Destination",
|
||||||
type: "system",
|
type: "system",
|
||||||
}
|
},
|
||||||
|
comments: 0,
|
||||||
|
likes: 0,
|
||||||
|
isAdult: false,
|
||||||
|
isLiked: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 8,
|
id: '8',
|
||||||
title: "News Item 8",
|
title: "News Item 8",
|
||||||
content: "News item eight provides more content. This helps in testing the display of multiple items.",
|
content: "News item eight provides more content. This helps in testing the display of multiple items.",
|
||||||
publishedAt: new Date(),
|
publishedAt: new Date(),
|
||||||
source: {
|
source: {
|
||||||
name: "Test Destination",
|
name: "Test Destination",
|
||||||
type: "system",
|
type: "system",
|
||||||
}
|
},
|
||||||
|
comments: 0,
|
||||||
|
likes: 0,
|
||||||
|
isAdult: false,
|
||||||
|
isLiked: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 9,
|
id: '9',
|
||||||
title: "News Item 9",
|
title: "News Item 9",
|
||||||
content: "The ninth news item. Almost reaching the total of 13 items including the original.",
|
content: "The ninth news item. Almost reaching the total of 13 items including the original.",
|
||||||
publishedAt: new Date(),
|
publishedAt: new Date(),
|
||||||
source: {
|
source: {
|
||||||
name: "Test Destination",
|
name: "Test Destination",
|
||||||
type: "system",
|
type: "system",
|
||||||
}
|
},
|
||||||
|
comments: 0,
|
||||||
|
likes: 0,
|
||||||
|
isAdult: false,
|
||||||
|
isLiked: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 10,
|
id: '10',
|
||||||
title: "News Item 10",
|
title: "News Item 10",
|
||||||
content: "Tenth item in the array. This completes the set of additional news items requested.",
|
content: "Tenth item in the array. This completes the set of additional news items requested.",
|
||||||
publishedAt: new Date(),
|
publishedAt: new Date(),
|
||||||
source: {
|
source: {
|
||||||
name: "Test Destination",
|
name: "Test Destination",
|
||||||
type: "system",
|
type: "system",
|
||||||
}
|
},
|
||||||
|
comments: 0,
|
||||||
|
likes: 0,
|
||||||
|
isAdult: false,
|
||||||
|
isLiked: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 11,
|
id: '11',
|
||||||
title: "News Item 11",
|
title: "News Item 11",
|
||||||
content: "Eleventh news item. Continuing to add variety to the test data.",
|
content: "Eleventh news item. Continuing to add variety to the test data.",
|
||||||
publishedAt: new Date(),
|
publishedAt: new Date(),
|
||||||
source: {
|
source: {
|
||||||
name: "Test Destination",
|
name: "Test Destination",
|
||||||
type: "system",
|
type: "system",
|
||||||
}
|
},
|
||||||
|
comments: 0,
|
||||||
|
likes: 0,
|
||||||
|
isAdult: false,
|
||||||
|
isLiked: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 12,
|
id: '12',
|
||||||
title: "News Item 12",
|
title: "News Item 12",
|
||||||
content: "The twelfth and final news item. This brings the total to 13 items in the array.",
|
content: "The twelfth and final news item. This brings the total to 13 items in the array.",
|
||||||
publishedAt: new Date(),
|
publishedAt: new Date(),
|
||||||
source: {
|
source: {
|
||||||
name: "Test Destination",
|
name: "Test Destination",
|
||||||
type: "system",
|
type: "system",
|
||||||
}
|
},
|
||||||
|
comments: 0,
|
||||||
|
likes: 0,
|
||||||
|
isAdult: false,
|
||||||
|
isLiked: false,
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@ -1,74 +0,0 @@
|
|||||||
import { NewsSource, NewsItem } from "../../api/models/news";
|
|
||||||
import requests from "../requests";
|
|
||||||
|
|
||||||
class NewsSourceTarget {
|
|
||||||
protected source : NewsSource;
|
|
||||||
protected parent : NewsParserService;
|
|
||||||
constructor(source : NewsSource, parent : NewsParserService) {
|
|
||||||
this.source = source;
|
|
||||||
this.parent = parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async readerRSS () : Promise<NewsItem[]> {
|
|
||||||
if (!this.source.sourceURL) {
|
|
||||||
throw new TypeError("RSS must have sourceURL");
|
|
||||||
}
|
|
||||||
await requests.get(this.source.sourceURL, "string");
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
async readAll() : Promise<NewsItem[]> {
|
|
||||||
switch (this.source.type) {
|
|
||||||
case "RSS":
|
|
||||||
return this.readerRSS();
|
|
||||||
case "system":
|
|
||||||
return [];
|
|
||||||
default:
|
|
||||||
const impossible: never = this.source;
|
|
||||||
return impossible;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class NewsParserService {
|
|
||||||
protected sources : Set<NewsSourceTarget> = new Set<NewsSourceTarget>;
|
|
||||||
protected sourceMap : WeakMap<NewsSource, NewsSourceTarget> = new WeakMap<NewsSource, NewsSourceTarget>;
|
|
||||||
|
|
||||||
protected newsItems : Set<NewsItem> = new Set<NewsItem>;
|
|
||||||
|
|
||||||
async setupSources(sources : NewsSource[]) {
|
|
||||||
this.sources.clear();
|
|
||||||
sources.forEach(source => this.addSource(source));
|
|
||||||
}
|
|
||||||
|
|
||||||
async addSource(source : NewsSource) {
|
|
||||||
const sourceTarget = new NewsSourceTarget(source, this);
|
|
||||||
this.sources.add(sourceTarget);
|
|
||||||
this.sourceMap.set(source, sourceTarget);
|
|
||||||
|
|
||||||
const news = await sourceTarget.readAll();
|
|
||||||
news.forEach(newsItem => new Promise<void>((resolve) => {
|
|
||||||
this.newsItems.add(newsItem);
|
|
||||||
resolve();
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
async removeSource(source : NewsSource) {
|
|
||||||
const sourceTarget = this.sourceMap.get(source);
|
|
||||||
if (sourceTarget) {
|
|
||||||
this.sources.delete(sourceTarget);
|
|
||||||
this.sourceMap.delete(source);
|
|
||||||
|
|
||||||
const forDeleting: Set<NewsItem> = new Set<NewsItem>;
|
|
||||||
for (let newsItem of this.newsItems) {
|
|
||||||
if (newsItem.source === source)
|
|
||||||
forDeleting.add(newsItem);
|
|
||||||
}
|
|
||||||
for (let deleteItem of forDeleting) {
|
|
||||||
this.newsItems.delete(deleteItem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const newsParserService = new NewsParserService;
|
|
||||||
Loading…
Reference in New Issue
Block a user