diff --git a/frontend/src/stores/useFeeds.ts b/frontend/src/stores/useFeeds.ts
index ee028e1..8e34100 100644
--- a/frontend/src/stores/useFeeds.ts
+++ b/frontend/src/stores/useFeeds.ts
@@ -16,6 +16,17 @@ export const useFeeds = defineStore('feeds', {
async remove(url: string) {
await fetch(`/feeds?url=${encodeURIComponent(url)}`, {method: 'DELETE'});
await this.fetch();
+ },
+ async loadFeeds() {
+ try {
+ const response = await fetch('/feeds');
+ if (response.ok) {
+ const data = await response.json();
+ this.list = data;
+ }
+ } catch (error) {
+ console.error('Failed to load feeds:', error);
+ }
}
}
});
diff --git a/frontend/src/stores/useNews.ts b/frontend/src/stores/useNews.ts
index 7a22982..c6b9ae2 100644
--- a/frontend/src/stores/useNews.ts
+++ b/frontend/src/stores/useNews.ts
@@ -5,12 +5,14 @@ export const useNews = defineStore('news', {
articles: [] as {
id: number,
title: string,
- description: string,
+ summary: string,
url: string,
published: number,
country: string,
created_at: number
- }[], lastSync: 0
+ }[],
+ lastSync: 0,
+ clientTimezone: Intl.DateTimeFormat().resolvedOptions().timeZone
}),
actions: {
async loadLastSync() {
@@ -20,13 +22,18 @@ export const useNews = defineStore('news', {
canManualSync() {
return Date.now() - this.lastSync > 30 * 60 * 1000; // 30‑min guard
},
- async sync(filters: Record) {
+ async sync(filters: Record = {}) {
if (!this.canManualSync()) {
console.log('Too soon to sync again');
return;
}
- const q = new URLSearchParams(filters).toString();
+ // Include timezone in the request
+ const filtersWithTz = {
+ ...filters,
+ timezone: this.clientTimezone
+ };
+ const q = new URLSearchParams(filtersWithTz as Record).toString();
const res = await fetch(`/news?${q}`);
if (res.ok) {
@@ -35,17 +42,45 @@ export const useNews = defineStore('news', {
this.lastSync = Date.now();
}
},
- async getNews(filters: Record = {}) {
- const q = new URLSearchParams(filters).toString();
- const res = await fetch(`/news?${q}`);
+ async getNews(filters: Record = {}) {
+ try {
+ const filtersWithTz = {
+ ...filters,
+ timezone: this.clientTimezone
+ };
+ const q = new URLSearchParams(filtersWithTz as Record).toString();
+ const res = await fetch(`/news?${q}`);
+
+ if (!res.ok) {
+ throw new Error(`HTTP ${res.status}: ${res.statusText}`);
+ }
+
+ const contentType = res.headers.get('content-type');
+ if (!contentType || !contentType.includes('application/json')) {
+ throw new Error('Response is not JSON');
+ }
- if (res.ok) {
const data = await res.json();
this.articles = data;
return data;
+ } catch (error) {
+ console.error('Failed to get news:', error);
+ return [];
}
+ },
- return [];
+ // New convenience methods
+ async getAllNews() {
+ return this.getNews({ all_countries: 'true', all_dates: 'true' });
+ },
+
+ async getNewsForCountries(countries: string[], from?: string, to?: string) {
+ const filters: Record = {
+ country: countries.join(',')
+ };
+ if (from) filters.from_ = from;
+ if (to) filters.to_ = to;
+ return this.getNews(filters);
}
}
});
diff --git a/frontend/src/style.css b/frontend/src/style.css
index f1d8c73..c170fc5 100644
--- a/frontend/src/style.css
+++ b/frontend/src/style.css
@@ -1 +1,115 @@
@import "tailwindcss";
+
+@import "assets/main.css";
+
+/* Custom scrollbar for webkit browsers */
+::-webkit-scrollbar {
+ width: 6px;
+ height: 6px;
+}
+
+::-webkit-scrollbar-track {
+ background: #f1f5f9;
+ border-radius: 3px;
+}
+
+::-webkit-scrollbar-thumb {
+ background: #cbd5e1;
+ border-radius: 3px;
+}
+
+::-webkit-scrollbar-thumb:hover {
+ background: #94a3b8;
+}
+
+/* Smooth transitions for better mobile experience */
+* {
+ -webkit-tap-highlight-color: transparent;
+}
+
+/* Focus styles for better accessibility */
+button:focus,
+input:focus,
+select:focus,
+textarea:focus {
+ outline: 2px solid #10b981;
+ outline-offset: 2px;
+}
+
+/* Loading animation */
+@keyframes spin {
+ to {
+ transform: rotate(360deg);
+ }
+}
+
+.animate-spin {
+ animation: spin 1s linear infinite;
+}
+
+/* Enhanced responsive utilities */
+@media (max-width: 640px) {
+ .container {
+ padding-left: 1rem;
+ padding-right: 1rem;
+ }
+}
+
+/* Custom line clamp utilities */
+.line-clamp-1 {
+ display: -webkit-box;
+ -webkit-line-clamp: 1;
+ -webkit-box-orient: vertical;
+ overflow: hidden;
+}
+
+.line-clamp-2 {
+ display: -webkit-box;
+ -webkit-line-clamp: 2;
+ -webkit-box-orient: vertical;
+ overflow: hidden;
+}
+
+.line-clamp-3 {
+ display: -webkit-box;
+ -webkit-line-clamp: 3;
+ -webkit-box-orient: vertical;
+ overflow: hidden;
+}
+
+/* Mobile-first button styles */
+button {
+ min-height: 44px; /* iOS recommended touch target */
+}
+
+@media (min-width: 1024px) {
+ button {
+ min-height: auto;
+ }
+}
+
+/* Enhanced mobile card styles */
+@media (max-width: 768px) {
+ .card-mobile {
+ margin-left: -1rem;
+ margin-right: -1rem;
+ border-radius: 0;
+ border-left: none;
+ border-right: none;
+ }
+}
+
+/* Reset any conflicting styles specifically for the app */
+#app {
+ display: block !important;
+ grid-template-columns: none !important;
+ padding: 0 !important;
+ margin: 0 !important;
+ max-width: none !important;
+}
+
+/* Ensure body doesn't interfere with the layout */
+body {
+ display: block !important;
+ place-items: unset !important;
+}