Vue PWA mylog Source Code

store/mylogs.js

그랜파 개발자 2024. 11. 11. 01:25

store/modules/mylogs.js

// src/store/modules/mylogs.js
import { v4 as uuidv4 } from 'uuid';
import { db, doc, collection, getDoc, getDocs, setDoc, addDoc,updateDoc, deleteDoc, arrayUnion, increment, where } from "@/firebase";
import { query, orderBy } from "@/firebase";

const state = {
  loading: false,
  error: null,
  mylog: null,
  mylogs: [],
  userMylogs: [],     // 회원의 마이로그
  isSubscribed:  false, // 구독 여부 - 마이로그 상세 보기에서 사용
  subscriptionId: '',   // 구독 등록 Id - 구독정보 삭제에 사용
  subscriptions: [],   // 구독 목록
  subscribedMylogs: [],  // 구독 마이로그 목록
  comments: [],       // 댓글 목록 저장
  filteredMylogs: [], // 검색된 게시물 목록
  readers: [],          // 독자들
};

const mutations = {
  setLoading(state, loading) {
    state.loading = loading;
  },
  setError(state, error) {
    state.error = error;
  },
  setMylogs(state, mylogs) {
    state.mylogs = mylogs;
  },
  setMylog(state, mylog) {
    state.mylog = mylog;
  },
  setUserMylogs(state, mylogs) {
    state.userMylogs = mylogs;
  },
  setSubscribed(state, subscribed) {  // 구독 여부
    state.isSubscribed = subscribed;
  },
  setSubscriptionId(state, subscriptionId) {  // 구독 Id
    state.subscriptionId = subscriptionId;
  },
  setSubscriptions(state, subscriptions) {  // 구독 목록
    state.subscriptions = subscriptions;
  },
  setSubscribedMylogs(state, subscribedMylogs) {  // 구독 마이로그 목록
    state.subscribedMylogs = subscribedMylogs;
  },
  setComments(state, comments) {  // 현재 댓글
    state.comments = comments;
  },
  addComment(state, comment) {    // 댓글 추가
    state.comments.push(comment);
  },
  addReply(state, { commentId, reply }) {   // 댓글에 대한 답글 추가
    const comment = state.comments.find(c => c.id === commentId);
    if (comment) {
      if (!comment.replies) {
        comment.replies = [];
      }
      comment.replies.push(reply);
    }
  },
  setFilteredMylogs(state, filteredMylogs) {  // 마이로그 찾기
    state.filteredMylogs = filteredMylogs;
  },
  setReaders(state, readers) {  // 독자들
    state.readers = readers;
  },
};

const actions = {
  // 전체 마이로그를 작성일 역순으로 로드한다.
  async fetchMylogs({ commit }) {
    commit('setLoading', true);
    try {
      const mylogs = []; 
      const mylogsRef = query(collection(db, "mylogs"), orderBy("createdAt", "desc"));
      const querySnapshot = await getDocs(mylogsRef);
      querySnapshot.forEach((doc) => {
        // doc.data() is never undefined for query doc snapshots
        mylogs.push({ id: doc.id, ...doc.data() });
      });

      // state에 저장한다.
      commit('setMylogs', mylogs);

    } catch (error) {
      console.log("error: ", error);
      commit('setError', error);
    } finally {
      commit('setLoading', false);
    }
  },
  // 전체 마이로그를 작성일 순으로 로드한다.
  async fetchMylogsAsc({ commit }) {
    commit('setLoading', true);
    try {
      const mylogs = []; 
      const mylogsRef = query(collection(db, "mylogs"), orderBy("createdAt", "asc"));
      const querySnapshot = await getDocs(mylogsRef);
      querySnapshot.forEach((doc) => {
        // doc.data() is never undefined for query doc snapshots
        mylogs.push({ id: doc.id, ...doc.data() });
      });

      // state에 저장한다.
      commit('setMylogs', mylogs);

    } catch (error) {
      commit('setError', error);
    } finally {
      commit('setLoading', false);
    }
  },
  // mylogId로 마이로그를 로드한다.
  async fetchMylog({ commit }, mylogId) {
    //console.log('mylogId:', mylogId);
    commit('setLoading', true);
    try {
      const docRef = doc(db, "mylogs", mylogId); // Reference to the specific article document
      const docSnap = await getDoc(docRef); // Fetch the document snapshot

      if (docSnap.exists()) {
        const mylog = docSnap.data(); // Set article data
        commit('setMylog', mylog);
      } else {
        console.error("mylog가 없습니다.");
      }
    } catch (error) {
      commit('setError', error);
    } finally {
      commit('setLoading', false);
    }
  },
 
  // 현재 사용자가 작성한 게시물 불러오기 - 최신순 정열
  async fetchUserMylogs({ commit }, userId) {
    commit('setLoading', true);
    if (userId) { 
      try {
        const q = query(collection(db, "mylogs"), where("userId", "==", userId), orderBy("createdAt", "desc"));
        const querySnapshot = await getDocs(q);
        const mylogs = [];
        querySnapshot.forEach((doc) => {
          mylogs.push({ id: doc.id, ...doc.data() });
        }); 
        commit('setUserMylogs', mylogs); // 상태에 저장
      } catch (error) {
        console.error("본인 글 가져오기 실패:", error);
        commit('setError', error);  
      }
    }
    commit('setLoading', false);
  },

  // 현재 사용자가 작성한 게시물 불러오기 - 오래된순 정열
  async fetchUserMylogsOrderByDateAsc({ commit }, userId) {
    commit('setLoading', true);
    if (userId) { 
      try {
        const q = query(collection(db, "mylogs"), where("userId", "==", userId), orderBy("createdAt", "asc"));
        const querySnapshot = await getDocs(q);
        const mylogs = [];
        querySnapshot.forEach((doc) => {
          mylogs.push({ id: doc.id, ...doc.data() });
        }); 
        commit('setUserMylogs', mylogs); // 상태에 저장
      } catch (error) {
        console.error("본인 글 가져오기 실패:", error);
        commit('setError', error);  
      }
    }
    commit('setLoading', false);
  },

  // 현재 사용자가 작성한 게시물 불러오기 - 조회수 역순 정열
  async fetchUserMylogsOrderByViews({ commit }, userId) {
    commit('setLoading', true);
    if (userId) { 
      try {
        const q = query(collection(db, "mylogs"), where("userId", "==", userId), orderBy("views", "desc"));
        const querySnapshot = await getDocs(q);
        const mylogs = [];
        querySnapshot.forEach((doc) => {
          mylogs.push({ id: doc.id, ...doc.data() });
        }); 
        commit('setUserMylogs', mylogs); // 상태에 저장
      } catch (error) {
        console.error("본인 글 가져오기 실패:", error);
        commit('setError', error);  
      }
    }
    commit('setLoading', false);
  },

  // 현재 사용자가 작성한 게시물 불러오기 - 조회수 역순 정열
  async fetchUserMylogsOrderByCreated({ commit }, userId) {
    commit('setLoading', true);
    if (userId) { 
      try {
        const q = query(collection(db, "mylogs"), where("userId", "==", userId), orderBy("createdAt", "asc"));
        const querySnapshot = await getDocs(q);
        const mylogs = [];
        querySnapshot.forEach((doc) => {
          mylogs.push({ id: doc.id, ...doc.data() });
        }); 
        commit('setUserMylogs', mylogs); // 상태에 저장
      } catch (error) {
        console.error("본인 글 가져오기 실패:", error);
        commit('setError', error);  
      }
    }
    commit('setLoading', false);
  },

  // 조회 기록 업데이트 함수
  async updateViewCount({ commit }, mylogId, userId = null) {

    let viewId;
    let viewedToday;

    // 비회원일 경우 로컬 스토리지 또는 쿠키를 사용하여 고유 ID 생성
    if (!userId) {
      viewId = localStorage.getItem('anonymousUserId');
      if (!viewId) {
        viewId = uuidv4();
        localStorage.setItem('anonymousUserId', viewId);
      }
    } else {
      viewId = userId; // 회원일 경우 사용자 ID
    }    

    try {     
      const today = new Date().toISOString().split('T')[0];
      const viewDocRef = doc(db, 'views', mylogId, 'users', viewId);
      
      // Firestore에서 해당 사용자의 조회 기록을 가져옵니다.
      const viewDoc = await getDoc(viewDocRef);      
      if (viewDoc.exists()) {
        // 오늘 조회 기록이 있는지 확인
        const lastViewed = viewDoc.data().lastViewed;
        viewedToday = lastViewed.some(view => view === today);
      } 
      // 조회한 내역이 없으면 조회수 추가
      if (!viewedToday) {
        // Firestore의 lastViewed 배열에 조회 시간을 추가
        await setDoc(viewDocRef, {
          lastViewed: arrayUnion(today) // 배열에 서버 시간을 추가
        }, { merge: true });

        // mylog 조회수 증가
        const mylogRef = doc(db, "mylogs", mylogId);     
        try {
          // 조회수 1 증가 또는 필드가 없을 때 1로 설정
          await updateDoc(mylogRef, {
            views: increment(1)
          });
        } catch (error) {
          alert(error.message);
        }
      }
    } catch (error) {
      alert(error.message);
    }
  },

  // 작성한 마이로그를 firestore에 저장한다.
  async saveMylog({ commit, dispatch }, { title, content, userId, userName, createdAt }) {
    commit('setLoading', true);
    try {      
      const mylog = await addDoc(collection(db, "mylogs"), {
        title: title,
        content: content,
        userId: userId,
        userName: userName,
        createdAt: createdAt
      });

      dispatch('fetchMylogs');  // 새 마이로그을 저장한 후 전체 마이로그를 새로 읽는다.  
      
    } catch (error) {
      alert(error.message);
    } finally {
      commit('setLoading', false);
    }
  },

  // 게시물 수정 액션
  async updateMylog({ commit, dispatch }, { mylogId, updatedData }) {
    
    try {
      commit('setLoading', true);
      const mylogRef = doc(db, "mylogs", mylogId);
      await updateDoc(mylogRef, {
        title: updatedData.title,
        content: updatedData.content,
        updatedAt: new Date(), // 수정한 시간 기록
      });
      commit('setLoading', false);
      alert("마이로그를 수정하였습니다."); 

      // 마이로그 수정 후 전체 다시 로드하록 한다.
      dispatch('fetchMylogs');           
    } catch (error) {
      alert("마이로그 수정 실패 : " + error.message);
    }    
  },

  // comments 가져오기
  async fetchComments({ commit, dispatch }, mylogId) {
    try {
      const q = query(collection(db, "mylogs", mylogId, "comments"), orderBy("createdAt", "asc"));
      const querySnapshot = await getDocs(q);
      const comments = [];
      querySnapshot.forEach((doc) => {
        comments.push({ id: doc.id, ...doc.data(), replies: [] });
        // 댓글에 대한 답글 로드
        dispatch('fetchReplies', { mylogId, commentId: doc.id });
      });
      commit('setComments', comments); // 댓글을 상태에 저장
    } catch (error) {
      commit("setError", error);
      console.error("댓글 가져오기 실패:", error);
    }
  },
  // comments 추가 하기
  async addComment({ commit, dispatch }, { mylogId, content, userName, userId }) {
    try {
      // db에 댓글 저장
      const commentRef = await addDoc(collection(db, "mylogs", mylogId, "comments"), {
        content: content, userName: userName, userId: userId, createdAt: new Date(),
      });

      // 저장한 댓글을 상태에 로드된 댓글에 추가
      const newComment = { id: commentRef.id, content, userName, userId, createdAt: new Date() };
      commit('addComment', newComment); // 새 댓글을 상태에 추가

      // mylog의 댓글수 증가
      const mylogRef = doc(db, "mylogs", mylogId);     
      try {
        // 조회수 1 증가 또는 필드가 없을 때 1로 설정
        await updateDoc(mylogRef, {
          commentCount: increment(1)
        });
      } catch (error) {
        commit('setError', error);
      }

      dispatch('fetchMylogs');

    } catch (error) {
      commit("setError", error);
      console.error("댓글 추가 실패:", error);
    }
  },
  // 댓글 삭제
  async deleteComment({ commit, dispatch }, { mylogId, commentId }) {
    const commentRef = doc(db, "mylogs", mylogId, "comments", commentId);
    try {
      await deleteDoc(commentRef);
      dispatch('fetchComments', mylogId); // 댓글 다시 로그

      // mylog의 댓글수 증가
      const mylogRef = doc(db, "mylogs", mylogId);     
      try {
        // 조회수 1 증가 또는 필드가 없을 때 1로 설정
        await updateDoc(mylogRef, {
          commentCount: increment(-1)
        });
      } catch (error) {
        commit('setError', error);
      }

      dispatch('fetchMylogs');
      //this.comments = this.comments.filter(c => c.id !== commentId);
      console.log("댓글이 삭제되었습니다.");
    } catch (error) {
      commit("setError", error);
      console.error("댓글 삭제 실패:", error);
    }
  },
  // 댓글에 답글 추가
  async addReply({ commit }, { mylogId, commentId, content, userName, userId  }) {
    try {
      // db에 답글 저장
      const replyRef = await addDoc(collection(db, "mylogs", mylogId, "comments", commentId, "replies"), {
        content: content, userName: userName, userId: userId, createdAt: new Date()    
      });
      // 로드된 댓글에 답글 저장
      const newReply = { id: replyRef.id, content, userName, userId, createdAt: new Date() };
      commit('addReply', { commentId, reply: newReply });
    } catch (error) {
      commit("setError", error);
      console.error("답글 추가 실패:", error);
    }
  },
  // 답글 삭제
  async deleteReply({ commit, dispatch }, { mylogId, commentId, replyId }) {
    const replyRef = doc(db, "mylogs", mylogId, "comments", commentId, "replies", replyId);
    try {
      await deleteDoc(replyRef); 
      dispatch('fetchComments', mylogId); // 댓글 다시 로그
      console.log("답글이 삭제되었습니다.");
    } catch (error) {
      commit("setError", error);
      console.error("답글 삭제 실패:", error);
    }
  },  
  // 특정 댓글의 답글 불러오기
  async fetchReplies({ commit }, { mylogId, commentId }) {
    try {
      const q = query(collection(db, "mylogs", mylogId, "comments", commentId, "replies"), orderBy("createdAt", "asc"));
      const querySnapshot = await getDocs(q);
      const replies = [];
      querySnapshot.forEach((doc) => {
        replies.push({ id: doc.id, ...doc.data() });
      });
      replies.forEach(reply => {
        commit('addReply', { commentId, reply });
      });
    } catch (error) {
      commit("setError", error);
      console.error("답글 불러오기 실패:", error);
    }
  },

  // 구독 여부 확인
  async checkSubscription({ commit }, { userId, authorId }) {
    commit('setLoading', true);
    const q = query(
      collection(db, "subscriptions"), 
      where("userId", "==", userId),
      where("authorId", "==", authorId)
    );

    const querySnapshot = await getDocs(q);
    if (!querySnapshot.empty) {
      commit("setSubscribed", true); 
      commit("setSubscriptionId", querySnapshot.docs[0].id); // Get the Firestore document ID for the subscription
    } else {
      commit("setSubscribed", false); 
      commit("setSubscriptionId", '');
    }
    commit('setLoading', false);
  },

  // 구독
  async subscribeToUser({ commit, dispatch }, {userId, authorId}) {
    //console.log('userId: ',userId ) 
    //console.log('authorId: ',authorId ) 
    commit('setLoading', true);
    try {
      const subscriptionRef = collection(db, "subscriptions");
      await addDoc(subscriptionRef, {
        userId: userId,
        authorId: authorId,
        createdAt: new Date(),
      });
      dispatch("fetchUserSubscriptions", userId);
    } catch (error) {
      commit("setError", error);
      console.error("Error subscribing:", error);
    }
    commit('setLoading', false);
  },
  // 구독 해지
  async unsubscribeFromUser({ commit, dispatch }, {userId, subscriptionId}) {
    commit('setLoading', true);
    try {
      const subscriptionDocRef = doc(db, "subscriptions", subscriptionId);
      await deleteDoc(subscriptionDocRef);
      dispatch("fetchUserSubscriptions", userId);
    } catch (error) {
      //commit("setError", error);
      alert("Error unsubscribing : ", error.message);
    }
    commit('setLoading', false);
  },
  
  // user에 대한 구독 로드
  async fetchUserSubscriptions({ commit }, userId) {
    const subscriptionRef = collection(db, "subscriptions");
    const q = query(subscriptionRef, where("userId", "==", userId));
    const querySnapshot = await getDocs(q);

    const subscriptions = [];
    querySnapshot.forEach((doc) => {
      subscriptions.push(doc.data());
    });
    commit("setSubscriptions", subscriptions);    
  },

  // 구독 중인 마이로그 로드
  async fetchSubscribedMylogs({ commit }, userId) {
    commit('setLoading', true);
    // 구독 목록
    const authodIds = [];

    const q = query(collection(db, "subscriptions"), where("userId", "==", userId));
    const querySnapshot = await getDocs(q);   
    querySnapshot.forEach((doc) => {
      authodIds.push(doc.data().authorId);
    });

    // 구독 마이로그 목록
    if(authodIds.length>0) {

      let subscribedMylogs = [];

      const mylogQ = query(collection(db, "mylogs"), where("userId", "in", authodIds));
      const mylogsSnapshot = await getDocs(mylogQ);
      subscribedMylogs = mylogsSnapshot.docs.map(doc => ({
          id: doc.id,
          ...doc.data()
        }));

      // 구독 마이로그 목록을 상태에 저장
      commit("setSubscribedMylogs", subscribedMylogs);
    }
    commit('setLoading', false);
  },

  // Firestore에서 게시물 검색
  async searchMylogs({ commit, getters }, searchTerm ) {
    // 검색어를 소문자로 
    const searchTermLower = searchTerm.toLowerCase();
    let filteredMylogs = [];    
    // 검색된 결과 필터링
    filteredMylogs = getters.mylogs.filter((mylog) => {   
      // title 또는 content에 검색어가 포함된 게시물 검색  
      return (
        mylog.title.toLowerCase().includes(searchTermLower) ||
        mylog.content.toLowerCase().includes(searchTermLower)
      );
    });

    commit('setFilteredMylogs', filteredMylogs); // 검색 결과를 상태에 저장    
  },

  // -- 독자 ------
  // 1. subscriptions 컬렉션의 문서에서 authorId가 나의 id인 userId 배열을 구한다.
  // 2. userId의 배열에 포함된 userId를 가진 모든 회원의 정보를 얻어 목록으로 나타낸다.
  async fetchReaders({ commit }, userId) {
    commit('setLoading', true);
    // 구독 목록
    const userIds = [];
    const q = query(collection(db, "subscriptions"), where("authorId", "==", userId));
    const querySnapshot = await getDocs(q);   
    querySnapshot.forEach((doc) => {       
      userIds.push(doc.data().userId);
    });

    if(userIds.length> 0)
    {
      //독자들
      let readers = [];
      for (const userId of userIds) {
        const userDocRef = doc(db, "users", userId);
        const userDoc = await getDoc(userDocRef);    
        if (userDoc.exists()) {
          readers.push({id: userId, ...userDoc.data()});
          //console.log(`${userId} =>`, userDoc.data());
        } else {
          console.log(`No such user with ID: ${userId}`);
        }
      }

      // 구독 마이로그 목록을 상태에 저장
      commit("setReaders", readers);

      commit('setLoading', false);
    }
  },
};

const getters = {
  error: state => state.error,
  loading: state => state.loading,
  mylog: state => state.mylog,
  mylogs: state => state.mylogs,
  userMylogs: state => state.userMylogs,
  isSubscribed: state => state.isSubscribed, // 구독 여부 - 마이로그 상세 보기에서 사용
  subscriptionId: state => state.subscriptionId, // 구독 등록 Id - 구독정보 삭제에 사용
  subscriptions : state => state.subscriptions,   // 구독 목록
  subscribedMylogs: state => state.subscribedMylogs,  // 구독 마이로그 목록
  comments: state => state.comments,
  filteredMylogs: state => state.filteredMylogs,  // 검색된 게시물 목록
  readers: state => state.readers, // 독자들
};

export default {
  namespaced: true,
  state,
  mutations,
  actions,
  getters
};

'Vue PWA mylog Source Code' 카테고리의 다른 글

main.js  (0) 2024.11.14
firebase-messaging-sw.js  (0) 2024.11.13
store/fcm.js  (0) 2024.11.09
store/auth.js  (0) 2024.11.09
store/index.js  (0) 2024.11.09