Improved a news items

This commit is contained in:
FullGreaM 2026-05-03 03:05:40 +03:00
parent 0c9249a4c8
commit 9bfb0f7a98
9 changed files with 188 additions and 51 deletions

View File

@ -1,3 +1,4 @@
import { server } from "typescript";
import { AuthenticationPage, AuthenticationRules } from "../pages/auth/auth";
import { NewsData, NewsFeed } from "../pages/feed/news";
import { NewsItem } from "../pages/feed/news-item";
@ -69,7 +70,7 @@ export class ControllerAPI {
const result = await this.request<boolean>('/isAuthenticated', {
method: 'GET',
credentials: 'include' // Include cookies for authentication
credentials: 'include'
});
return result;
}
@ -79,33 +80,37 @@ export class ControllerAPI {
const result = await this.request<NewsData[]>('/news', {
method: 'GET',
credentials: 'include' // Include cookies for authentication
credentials: 'include'
});
if (!returnPage)
return result;
if (!newsFeed)
newsFeed = new NewsFeed(result.map(x => new NewsItem(
x.id,
x.title,
new Date(x.publishedAt),
x.content,
x.source,
x.comments,
x.likes,
x.isAdult,
x.isLiked
)));
newsFeed = new NewsFeed(result.map(feed => new NewsItem(feed)));
return newsFeed;
}
async getAuthRules(returnPage: boolean = false) : Promise<AuthenticationRules | AuthenticationPage> {
const result = await this.request<AuthenticationRules>('/authenticationRules', {
method: 'GET',
credentials: 'include' // Include cookies for authentication
credentials: 'include'
});
if (!returnPage) return result;
if (!authPage)
authPage = new AuthenticationPage(result);
return authPage;
}
async setLike(post: string | NewsData) : Promise<{ status: boolean, likes: number }> {
if (typeof post === "object")
post = post.id;
return await this.request<{ status: boolean, likes: number }>('/setLike', {
method: 'POST',
credentials: 'include',
body: JSON.stringify({ post }),
headers: {
"Content-Type": "application/json"
}
});
}
}

View File

@ -6,11 +6,11 @@ import { NewsFeed } from "./pages/feed/news";
import { AuthenticationPage } from "./pages/auth/auth";
import { DefaultElement } from "./pages/default-element";
/*declare global {
declare global {
interface Window {
selectedStyle: SupportedStyles;
api: ControllerAPI;
}
}*/
}
const styleLinker : {
selectedStyle: SupportedStyles
} = {
@ -28,16 +28,16 @@ const currentStyle: () => SupportedStyles = () => styleLinker.selectedStyle;
}
}
const api : ControllerAPI = new ControllerAPI();
window.api = new ControllerAPI();
async function authInit() {
console.log("User is authenticated");
return await api.getNews(true) as NewsFeed;
return await window.api.getNews(true) as NewsFeed;
}
async function logInInit () {
console.log("User isn't authenticated");
return await api.getAuthRules(true) as AuthenticationPage;
return await window.api.getAuthRules(true) as AuthenticationPage;
}
async function changeStyleLogic (activeElement: DefaultElement, navbar: DefaultElement) {
@ -67,7 +67,7 @@ async function main() {
navbar.style = currentStyle;
let activeElement: DefaultElement;
if (await api.isAuthenticated()) {
if (await window.api.isAuthenticated()) {
activeElement = await authInit();
} else {
activeElement = await logInInit();

View File

@ -4,7 +4,7 @@ import { GefestI } from "../modules/Gefest/primitives/i";
import { SupportedStyles } from "../styles";
type FontAwesomeGroup = "fas" | "far" | "fab"; // e.g. "fas fa-home"
export type FontAwesomeIcon = `${FontAwesomeGroup} fa-${string}`;
export type FontAwesomeIconType = `${FontAwesomeGroup} fa-${string}`;
export abstract class DefaultElement extends GefestElement {
style: SupportedStyles | (() => SupportedStyles) | null = null;
@ -26,4 +26,21 @@ export class DefaultRoundedButton extends DefaultElement {
this.addClass("rounded-circle");
this.addClass("border");
}
}
export class FontAwesomeIcon extends DefaultElement {
constructor(icon: FontAwesomeIconType) {
super("");
this.ignoreStyleClasses.add("bg-light");
this.ignoreStyleClasses.add("bg-dark");
this.addClass(icon);
}
}
export class DefaultButton extends DefaultElement {
constructor(content: (GefestElement | string)[] | string) {
super(content);
this.addClass("btn");
this.addClass("btn-outline-secondary");
}
}

View File

@ -1,12 +1,13 @@
import { DefaultElement } from "../default-element";
import { DefaultButton, DefaultElement, FontAwesomeIcon, FontAwesomeIconType } from "../default-element";
import { GefestHeader } from "../../modules/Gefest/primitives/header";
import { GefestSmall } from "../../modules/Gefest/primitives/small";
import { GefestElement } from "../../modules/Gefest/element";
import { GefestP } from "../../modules/Gefest/primitives/p";
import { NewsSource } from "./news";
import { NewsData, NewsSource } from "./news";
import { GefestA } from "../../modules/Gefest/primitives/a";
import { GefestI } from "../../modules/Gefest/primitives/i";
import { GefestImg } from "../../modules/Gefest/primitives/img";
import { GefestEngine } from "../../modules/Gefest/engine";
function dateItemFormater (value: number | string, isMonth = false) {
const MONTHS = [
@ -63,21 +64,13 @@ class CardBodyBS5 extends DefaultElement {
export class NewsItem extends DefaultElement {
constructor(
id: string,
title: string,
publishDate: Date,
content: string,
source: NewsSource,
commentsCount: number,
likesCount: Number,
isAdult: boolean,
isLiked: boolean,
newsData: NewsData
) {
const header = new GefestHeader(title, 2);
const header = new GefestHeader(newsData.title, 2);
header.addClass("card-title");
const published = new GefestSmall(formatDate(publishDate) + " " + getSourceLnk(source, id));
const published = new GefestSmall(formatDate(new Date(newsData.publishedAt)) + " " + getSourceLnk(newsData.source, newsData.id));
published.addClass("published-text");
const contentParagraph = new GefestP(content);
const contentParagraph = new GefestP(newsData.content);
contentParagraph.addClass("card-text");
const splitter = new (class extends DefaultElement {
@ -91,12 +84,115 @@ export class NewsItem extends DefaultElement {
protected htmlTag: string = "hr";
});
const actions = new (class extends DefaultElement {
constructor() {
const commentsCounter = new (class extends GefestElement {
comments: number = 0;
constructor() {
super([]);
}
protected wrapHTML(): string {
return ` ${this.comments}`;
}
});
commentsCounter.comments = newsData.comments;
const comments = new DefaultButton([
new FontAwesomeIcon("fas fa-comment"),
commentsCounter
]);
const likeBtn = new (class extends DefaultButton {
private status: boolean;
private likes: number;
constructor() {
const likedIco = new FontAwesomeIcon("fas fa-heart");
const unlikedIco = new FontAwesomeIcon("far fa-heart");
const likesCounter = new (class extends GefestElement {
likes: number = 0;
constructor() {
super([]);
}
protected wrapHTML(): string {
return ` ${this.likes}`;
}
});
super([
likedIco, unlikedIco,
likesCounter
]);
this.likes = newsData.likes;
this.status = newsData.isLiked;
likesCounter.likes = newsData.likes;
const setStatus = () => {
likedIco.isHidden = !this.status;
unlikedIco.isHidden = this.status;
this.update();
};
setStatus();
this.onClick = async () => {
likesCounter.likes = this.status ? --this.likes : ++this.likes;
// TODO: Dirty code, fix it
this.status = !this.status;
setStatus();
const result = await window.api.setLike(newsData);
this.status = result.status;
likesCounter.likes = result.likes;
this.likes = result.likes;
setStatus();
};
}
});
super([
comments, likeBtn
]);
this.addClass("btn-group");
}
});
const views = new (class extends DefaultElement {
constructor() {
const lookedShow = new (class extends GefestElement {
views: number = 0;
constructor() {
super([]);
}
protected wrapHTML(content: string): string {
return `${this.views} `;
}
});
lookedShow.views = newsData.looked;
super([
lookedShow,
new FontAwesomeIcon("fas fa-eye"),
]);
}
});
super([
new CardBodyBS5([
header,
published,
contentParagraph,
splitter,
new (class extends DefaultElement {
constructor() {
super([actions, views]);
[
"d-flex",
"justify-content-between",
"align-items-center"
].forEach(c => this.addClass(c));
}
}),
])
]);

View File

@ -1,5 +1,5 @@
import { NewsItem } from "./news-item";
import { DefaultElement, FontAwesomeIcon } from "../default-element";
import { DefaultElement, FontAwesomeIcon, FontAwesomeIconType } from "../default-element";
import { GefestHeader } from "../../modules/Gefest/primitives/header";
import { UserType } from "../../api/user-info";
import { GefestElement } from "../../modules/Gefest/element";
@ -11,15 +11,8 @@ export function isNewsUI (element : GefestElement): boolean {
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);
constructor(icon: FontAwesomeIconType) {
const fontawesomeIcon = new FontAwesomeIcon(icon);
super([fontawesomeIcon]);
this.ignoreStyleClasses.add("bg-light");
@ -143,6 +136,7 @@ export interface NewsData {
publishedAt : string;
comments: number;
likes: number;
looked: number;
isAdult: boolean;
isLiked: boolean;
}

View File

@ -1,12 +1,11 @@
import { GefestA } from "../modules/Gefest/primitives/a";
import { GefestI } from "../modules/Gefest/primitives/i";
import { DefaultElement, FontAwesomeIcon } from "./default-element";
import { DefaultElement, FontAwesomeIcon, FontAwesomeIconType } from "./default-element";
class NavbarButton extends DefaultElement {
constructor(icon: FontAwesomeIcon) {
const iconElement = new GefestI("");
iconElement.addClass(icon);
constructor(icon: FontAwesomeIconType) {
const iconElement = new FontAwesomeIcon(icon);
const elementA = new GefestA([
iconElement
]);

View File

@ -18,8 +18,20 @@ router.get('/isAuthenticated', (req, res) => {
router.get('/news', authRequired, (req, res) => {
const newsList : NewsItem[] = getTestNews();
const response = new ApiResponse<NewsItem[]>(newsList);
const response = new ApiResponse(newsList);
return res.json(response.getAnswer());
});
router.post('/setLike', authRequired, (req, res) => {
if (req.body?.post || typeof req.body.post !== "string") {
// TODO: Some actions with API
const testResponse = new ApiResponse({ status: true, likes: 1 });
return res.json(testResponse.getAnswer());
}
return res.status(400).json((new ApiResponse({
code: "MISSING_REQUIRED",
message: "Missed required param: post(string) at JSON body",
})).getAnswer());
});
export default router;

View File

@ -32,6 +32,7 @@ export interface NewsItem {
content : string;
source: NewsSource;
publishedAt : Date;
looked: number;
comments: number;
likes: number;

View File

@ -13,6 +13,7 @@ export function getTestNews (): NewsItem[] {
name: "Test Destination",
type: "system",
},
looked: 1,
comments: 0,
likes: 1,
isAdult: false,
@ -27,6 +28,7 @@ export function getTestNews (): NewsItem[] {
name: "Test Destination",
type: "system",
},
looked: 1,
comments: 0,
likes: 0,
isAdult: false,
@ -41,6 +43,7 @@ export function getTestNews (): NewsItem[] {
name: "Test Destination",
type: "system",
},
looked: 1,
comments: 0,
likes: 0,
isAdult: false,
@ -55,6 +58,7 @@ export function getTestNews (): NewsItem[] {
name: "Test Destination",
type: "system",
},
looked: 1,
comments: 0,
likes: 0,
isAdult: false,
@ -69,6 +73,7 @@ export function getTestNews (): NewsItem[] {
name: "Test Destination",
type: "system",
},
looked: 1,
comments: 0,
likes: 0,
isAdult: false,
@ -83,6 +88,7 @@ export function getTestNews (): NewsItem[] {
name: "Test Destination",
type: "system",
},
looked: 1,
comments: 0,
likes: 0,
isAdult: false,
@ -97,6 +103,7 @@ export function getTestNews (): NewsItem[] {
name: "Test Destination",
type: "system",
},
looked: 1,
comments: 0,
likes: 0,
isAdult: false,
@ -111,6 +118,7 @@ export function getTestNews (): NewsItem[] {
name: "Test Destination",
type: "system",
},
looked: 1,
comments: 0,
likes: 0,
isAdult: false,
@ -125,6 +133,7 @@ export function getTestNews (): NewsItem[] {
name: "Test Destination",
type: "system",
},
looked: 1,
comments: 0,
likes: 0,
isAdult: false,
@ -139,6 +148,7 @@ export function getTestNews (): NewsItem[] {
name: "Test Destination",
type: "system",
},
looked: 1,
comments: 0,
likes: 0,
isAdult: false,
@ -153,6 +163,7 @@ export function getTestNews (): NewsItem[] {
name: "Test Destination",
type: "system",
},
looked: 1,
comments: 0,
likes: 0,
isAdult: false,
@ -167,6 +178,7 @@ export function getTestNews (): NewsItem[] {
name: "Test Destination",
type: "system",
},
looked: 1,
comments: 0,
likes: 0,
isAdult: false,
@ -181,6 +193,7 @@ export function getTestNews (): NewsItem[] {
name: "Test Destination",
type: "system",
},
looked: 1,
comments: 0,
likes: 0,
isAdult: false,