Ignore:
Timestamp:
03/24/26 22:13:36 (3 months ago)
Author:
kikisrbinoska <srbinoskakristina07@…>
Branches:
main
Children:
7fbb91c
Parents:
acf690c
Message:

Fixed reading lists,comments and likes

Location:
chapterx-frontend/src/store
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • chapterx-frontend/src/store/notificationStore.ts

    racf690c r73b69b2  
    11import { create } from 'zustand'
     2import axios from 'axios'
    23import { Notification } from '../types'
    3 import { mockNotifications } from '../data/mockData'
     4
     5const API = 'https://localhost:7125/api'
     6
     7function getAuthHeaders() {
     8  try {
     9    const token = JSON.parse(localStorage.getItem('chapterx-auth') || '{}')?.state?.token
     10    return token ? { Authorization: `Bearer ${token}` } : {}
     11  } catch {
     12    return {}
     13  }
     14}
    415
    516interface NotificationStore {
    617  notifications: Notification[]
    7   addNotification: (n: Omit<Notification, 'notification_id' | 'created_at' | 'is_read'>) => void
    8   markAllRead: () => void
    9   markRead: (id: number) => void
     18  fetchUserNotifications: (userId: number) => Promise<void>
     19  addNotification: (n: { recipientUserId: number; type: string; content: string; link?: string }) => Promise<void>
     20  markAllRead: () => Promise<void>
     21  markRead: (id: number) => Promise<void>
    1022  getUnreadCount: () => number
    1123}
    1224
    1325export const useNotificationStore = create<NotificationStore>((set, get) => ({
    14   notifications: [...mockNotifications],
     26  notifications: [],
    1527
    16   addNotification: (n) =>
    17     set(state => ({
    18       notifications: [
    19         {
    20           ...n,
    21           notification_id: Date.now(),
    22           created_at: new Date().toISOString(),
    23           is_read: false,
    24         },
    25         ...state.notifications,
    26       ],
    27     })),
     28  fetchUserNotifications: async (userId) => {
     29    try {
     30      const res = await axios.get(`${API}/notifications/user/${userId}`, { headers: getAuthHeaders() })
     31      const data: any[] = res.data ?? []
     32      const notifications: Notification[] = data.map(n => ({
     33        notification_id: n.id,
     34        user_id: userId,
     35        type: n.type ?? 'info',
     36        title: n.type ?? 'Notification',
     37        message: n.content,
     38        link: n.link,
     39        is_read: n.isRead,
     40        created_at: n.createdAt,
     41      }))
     42      set({ notifications })
     43    } catch {
     44      // keep existing
     45    }
     46  },
    2847
    29   markAllRead: () =>
    30     set(state => ({
    31       notifications: state.notifications.map(n => ({ ...n, is_read: true })),
    32     })),
     48  addNotification: async ({ recipientUserId, type, content, link }) => {
     49    try {
     50      await axios.post(`${API}/notifications`, {
     51        content,
     52        recipientUserId,
     53        type,
     54        link,
     55      }, { headers: getAuthHeaders() })
     56    } catch {
     57      // silent — notification is for recipient, not current user
     58    }
     59  },
    3360
    34   markRead: (id) =>
     61  markAllRead: async () => {
     62    const unread = get().notifications.filter(n => !n.is_read)
     63    set(state => ({ notifications: state.notifications.map(n => ({ ...n, is_read: true })) }))
     64    try {
     65      await Promise.all(unread.map(n =>
     66        axios.put(`${API}/notifications/${n.notification_id}/read`, {}, { headers: getAuthHeaders() })
     67      ))
     68    } catch {
     69      // keep optimistic
     70    }
     71  },
     72
     73  markRead: async (id) => {
    3574    set(state => ({
    3675      notifications: state.notifications.map(n =>
    3776        n.notification_id === id ? { ...n, is_read: true } : n
    3877      ),
    39     })),
     78    }))
     79    try {
     80      await axios.put(`${API}/notifications/${id}/read`, {}, { headers: getAuthHeaders() })
     81    } catch {
     82      // keep optimistic
     83    }
     84  },
    4085
    4186  getUnreadCount: () => get().notifications.filter(n => !n.is_read).length,
  • chapterx-frontend/src/store/storyStore.ts

    racf690c r73b69b2  
    2525const API = 'https://localhost:7125/api'
    2626
     27function mapReadingList(l: any): ReadingList {
     28  return {
     29    list_id: l.id,
     30    user_id: l.userId,
     31    username: l.username ?? '',
     32    name: l.name,
     33    description: l.content ?? '',
     34    is_public: l.isPublic,
     35    created_at: l.createdAt,
     36    stories: (l.readingListItems ?? []).map((i: any) => ({
     37      item_id: i.listId ?? 0,
     38      list_id: l.id,
     39      story_id: i.storyId,
     40      story_title: i.storyTitle ?? `Story #${i.storyId}`,
     41      author_username: i.authorUsername ?? '',
     42      added_at: i.addedAt ?? new Date().toISOString(),
     43      genres: i.genres ?? [],
     44    })),
     45  }
     46}
     47
    2748function getAuthHeaders() {
    2849  try {
     
    5374  fetchChapters: () => Promise<void>
    5475  fetchReadingLists: () => Promise<void>
     76  fetchUserReadingLists: (userId: number) => Promise<void>
     77  fetchGenres: () => Promise<void>
    5578
    5679  // Story actions
     
    86109
    87110  // Genre actions
    88   addGenre: (genre: Genre) => void
    89   deleteGenre: (id: number) => void
     111  addGenre: (name: string) => Promise<void>
     112  deleteGenre: (id: number) => Promise<void>
    90113
    91114  // Reading list actions
     
    103126  aiSuggestions: [...mockAISuggestions],
    104127  genres: [...mockGenres],
    105   readingLists: [...mockReadingLists],
     128  readingLists: [],
    106129  likedStories: [],
    107130
     
    118141        mature_content: s.matureContent,
    119142        status: 'published' as StoryStatus,
    120         author_username: '',
     143        author_username: s.writer?.user?.username ?? '',
    121144        created_at: s.createdAt,
    122145        updated_at: s.updatedAt,
     
    125148        total_chapters: 0,
    126149        total_views: 0,
    127         genres: [],
     150        genres: (s.hasGenres ?? []).map((hg: any) => hg.genre?.name ?? hg.name).filter(Boolean),
    128151      }))
    129152      if (stories.length > 0) set({ stories })
     
    159182    const res = await axios.post(`${API}/stories`, {
    160183      matureContent: story.mature_content,
    161       shortDescription: story.title || story.short_description,
     184      shortDescription: story.short_description || story.title,
    162185      image: null,
    163186      content: story.content,
    164187      userId: story.user_id,
     188      genres: story.genres ?? [],
    165189    }, { headers: getAuthHeaders() })
    166190    const backendId = res.data?.id ?? res.data
     
    443467  },
    444468
    445   addGenre: (genre) =>
    446     set(state => ({ genres: [...state.genres, genre] })),
    447 
    448   deleteGenre: (id) =>
    449     set(state => ({ genres: state.genres.filter(g => g.genre_id !== id) })),
     469  fetchGenres: async () => {
     470    try {
     471      const res = await axios.get(`${API}/genres`)
     472      const data: any[] = res.data?.genres ?? res.data ?? []
     473      const genres: Genre[] = data.map((g: any) => ({ genre_id: g.id, name: g.name }))
     474      if (genres.length > 0) set({ genres })
     475    } catch {
     476      // keep mock data on failure
     477    }
     478  },
     479
     480  addGenre: async (name) => {
     481    const res = await axios.post(`${API}/genres`, { name }, { headers: getAuthHeaders() })
     482    const id = res.data?.id ?? res.data
     483    set(state => ({ genres: [...state.genres, { genre_id: id, name }] }))
     484  },
     485
     486  deleteGenre: async (id) => {
     487    set(state => ({ genres: state.genres.filter(g => g.genre_id !== id) }))
     488    try {
     489      await axios.delete(`${API}/genres/${id}`, { headers: getAuthHeaders() })
     490    } catch {
     491      // optimistic delete already applied
     492    }
     493  },
    450494
    451495  fetchReadingLists: async () => {
    452496    try {
    453       const [listsRes, storiesRes] = await Promise.all([
    454         axios.get(`${API}/readinglists`),
    455         axios.get(`${API}/stories`),
    456       ])
    457       const data: any[] = listsRes.data?.readingLists ?? listsRes.data ?? []
    458       const storiesData: any[] = storiesRes.data?.stories ?? storiesRes.data ?? []
    459       const lists: ReadingList[] = data.map((l: any) => ({
    460         list_id: l.id,
    461         user_id: l.userId,
    462         username: '',
    463         name: l.name,
    464         description: l.content ?? '',
    465         is_public: l.isPublic,
    466         created_at: l.createdAt,
    467         stories: (l.readingListItems ?? []).map((i: any) => {
    468           const story = storiesData.find((s: any) => s.id === i.storyId)
    469           return {
    470             item_id: i.id ?? 0,
    471             list_id: l.id,
    472             story_id: i.storyId,
    473             story_title: story?.title ?? story?.shortDescription ?? `Story #${i.storyId}`,
    474             author_username: story?.authorUsername ?? '',
    475             added_at: i.addedAt ?? new Date().toISOString(),
    476             genres: story?.genres ?? [],
    477           }
    478         }),
    479       }))
    480       if (lists.length > 0) set({ readingLists: lists })
    481     } catch {
    482       // keep mock data on failure
     497      const res = await axios.get(`${API}/readinglists`)
     498      const data: any[] = res.data ?? []
     499      const lists: ReadingList[] = data.map(mapReadingList)
     500      set({ readingLists: lists })
     501    } catch {
     502      // keep existing data on failure
     503    }
     504  },
     505
     506  fetchUserReadingLists: async (userId) => {
     507    try {
     508      const res = await axios.get(`${API}/readinglists/user/${userId}`, { headers: getAuthHeaders() })
     509      const data: any[] = res.data ?? []
     510      const lists: ReadingList[] = data.map(mapReadingList)
     511      set(state => ({
     512        readingLists: [
     513          ...state.readingLists.filter(l => l.user_id !== userId),
     514          ...lists,
     515        ]
     516      }))
     517    } catch (err) {
     518      console.error('fetchUserReadingLists failed:', err)
    483519    }
    484520  },
     
    525561    }))
    526562    try {
    527       // find the item id from backend list items to delete
    528       const res = await axios.get(`${API}/readinglistitems`)
    529       const items: any[] = res.data?.readingListItems ?? res.data ?? []
    530       const item = items.find((i: any) => i.readingListId === listId && i.storyId === storyId)
    531       if (item) await axios.delete(`${API}/readinglistitems/${item.id}`, { headers: getAuthHeaders() })
     563      await axios.delete(`${API}/readinglistitems/${listId}/story/${storyId}`, { headers: getAuthHeaders() })
    532564    } catch {
    533565      // optimistic update already applied
Note: See TracChangeset for help on using the changeset viewer.