From 9c11490d261dd33d37b8b65c1877648b5d14571c Mon Sep 17 00:00:00 2001 From: William Chong Date: Thu, 21 Nov 2024 23:46:35 +0800 Subject: [PATCH 1/5] =?UTF-8?q?=E2=9C=A8=20Store=20cart=20to=20user=20db?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/server/api/routes/users/v2/auth.js | 4 ++ src/server/api/routes/users/v2/cart.js | 72 +++++++++++++++++++++++++ src/server/api/routes/users/v2/index.js | 2 + src/store/modules/cart.js | 28 +++++++--- src/store/modules/wallet.js | 5 +- src/util/api/index.js | 4 ++ 6 files changed, 108 insertions(+), 7 deletions(-) create mode 100644 src/server/api/routes/users/v2/cart.js diff --git a/src/server/api/routes/users/v2/auth.js b/src/server/api/routes/users/v2/auth.js index 07a381ea8..6680dad6c 100644 --- a/src/server/api/routes/users/v2/auth.js +++ b/src/server/api/routes/users/v2/auth.js @@ -32,6 +32,7 @@ router.get('/self', authenticateV2Login, async (req, res, next) => { eventLastSeenTs, lastLoginMethod: loginMethod, locale = DEFAULT_LOCALE, + cart, } = userDoc.data(); if (email) { try { @@ -53,6 +54,7 @@ router.get('/self', authenticateV2Login, async (req, res, next) => { eventLastSeenTs: eventLastSeenTs ? eventLastSeenTs.toMillis() : 1000, locale, crispToken: email ? getCrispUserHash(email) : undefined, + cart, }); } catch (err) { if (req.session) req.session = null; @@ -71,6 +73,7 @@ router.post('/login', async (req, res, next) => { loginMethod = '', gaClientId, userIdHash, + cart, } = req.body; try { if (!inputWallet || !signature || !publicKey || !message) { @@ -158,6 +161,7 @@ router.post('/login', async (req, res, next) => { displayName, isNew, crispToken: email ? getCrispUserHash(email) : undefined, + cart, }; res.json(payload); return; diff --git a/src/server/api/routes/users/v2/cart.js b/src/server/api/routes/users/v2/cart.js new file mode 100644 index 000000000..6717cec4e --- /dev/null +++ b/src/server/api/routes/users/v2/cart.js @@ -0,0 +1,72 @@ +const { Router } = require('express'); + +const { authenticateV2Login } = require('../../../middleware/auth'); +const { + FieldValue, + walletUserCollection, +} = require('../../../../modules/firebase'); + +const router = Router(); + +router.get('/cart', authenticateV2Login, async (req, res) => { + const { user } = req.session; + const cartDoc = await walletUserCollection.doc(user).get(); + const { cart } = cartDoc.data(); + res.json({ cart: cart || [] }); +}); + +router.post('/cart', authenticateV2Login, async (req, res) => { + const { user } = req.session; + const { cart: inputCart } = req.body; + if (!Array.isArray(inputCart)) { + res.status(400).send('INVALID_CART'); + return; + } + const cart = Object.values( + inputCart.reduce((acc, item) => { + acc[item.productId] = item; + return acc; + }, {}) + ).map(item => { + const { + productId, + collectionId, + classId, + coupon, + customPriceInDecimal, + from, + timestamp, + quantity, + priceIndex, + } = item; + return JSON.parse( + JSON.stringify({ + productId, + collectionId, + classId, + coupon, + customPriceInDecimal, + from, + timestamp, + quantity, + priceIndex, + }) + ); + }); + await walletUserCollection.doc(user).update({ + cart: cart?.length ? cart : FieldValue.delete(), + cartUpdateTimestamp: FieldValue.serverTimestamp(), + }); + res.sendStatus(200); +}); + +router.delete('/cart', authenticateV2Login, async (req, res) => { + const { user } = req.session; + await walletUserCollection.doc(user).update({ + cart: FieldValue.delete(), + cartUpdateTimestamp: FieldValue.serverTimestamp(), + }); + res.sendStatus(200); +}); + +module.exports = router; diff --git a/src/server/api/routes/users/v2/index.js b/src/server/api/routes/users/v2/index.js index 8615cba03..73643dcca 100644 --- a/src/server/api/routes/users/v2/index.js +++ b/src/server/api/routes/users/v2/index.js @@ -1,6 +1,7 @@ const { Router } = require('express'); const auth = require('./auth'); +const cart = require('./cart'); const email = require('./email'); const event = require('./event'); const follow = require('./follow'); @@ -12,6 +13,7 @@ const wallet = require('./wallet'); const router = Router(); router.use(auth); +router.use(cart); router.use(email); router.use(event); router.use(follow); diff --git a/src/store/modules/cart.js b/src/store/modules/cart.js index dc75891d5..d144fa0d4 100644 --- a/src/store/modules/cart.js +++ b/src/store/modules/cart.js @@ -5,6 +5,7 @@ import { saveShoppingCartToStorage, } from '~/util/shopping-cart'; import { BATCH_COLLECT_MAX } from '~/constant'; +import { postShoppingCart, deleteShoppingCart } from '~/util/api'; import * as TYPES from '../mutation-types'; const state = () => ({ @@ -169,14 +170,29 @@ const actions = { commit(TYPES.SHOPPING_CART_REPLACE_ALL_BOOK_PRODUCT, {}); dispatch('saveBookProductShoppingCart'); }, - saveBookProductShoppingCart({ state }) { + async saveBookProductShoppingCart({ state, getters }) { saveShoppingCartToStorage(state.shoppingCartBookProductByIdMap, 'book'); + if (getters.loginAddress) { + const cart = Object.values(state.shoppingCartBookProductByIdMap); + try { + if (cart.length) { + await this.$api.$post(postShoppingCart(), { cart }); + } else { + await this.$api.$post(postShoppingCart(), { cart }); + } + } catch (e) { + // eslint-disable-next-line no-console + console.error(e); + } + } }, - loadBookProductShoppingCart({ commit }) { - commit( - TYPES.SHOPPING_CART_REPLACE_ALL_BOOK_PRODUCT, - loadShoppingCartFromStorage('book') - ); + loadBookProductShoppingCart({ commit, getters }) { + if (!getters.shoppingCartBookItems.length) { + commit( + TYPES.SHOPPING_CART_REPLACE_ALL_BOOK_PRODUCT, + loadShoppingCartFromStorage('book') + ); + } }, }; diff --git a/src/store/modules/wallet.js b/src/store/modules/wallet.js index 7e5bbc3e8..886d69b6d 100644 --- a/src/store/modules/wallet.js +++ b/src/store/modules/wallet.js @@ -840,13 +840,16 @@ const actions = { if (!checkIsLikeCoinAppInAppBrowser(this.$router.app.$route)) { await dispatch('setLocale', userInfo.locale); } - const { displayName, email, crispToken } = userInfo; + const { displayName, email, crispToken, cart } = userInfo; updateLoggerUserInfo(this, { email, displayName, wallet: state.address, crispToken, }); + if (cart?.length) { + dispatch('addBookProductsToShoppingCart', cart); + } return userInfo; }, async walletFetchSessionUserData( diff --git a/src/util/api/index.js b/src/util/api/index.js index 3302cfeb1..3ae23719e 100644 --- a/src/util/api/index.js +++ b/src/util/api/index.js @@ -454,6 +454,10 @@ export const getUserV2Self = () => '/api/v2/users/self'; export const postUserV2Login = () => '/api/v2/users/login'; export const postUserV2Logout = () => '/api/v2/users/logout'; +export const getShoppingCart = () => '/api/v2/users/cart'; +export const postShoppingCart = () => '/api/v2/users/cart'; +export const deleteShoppingCart = () => '/api/v2/users/cart'; + export const postUserV2WalletEmail = ({ email, followee, From 641e316516686b44bd4dddddab5881dfc467b727 Mon Sep 17 00:00:00 2001 From: William Chong Date: Fri, 22 Nov 2024 03:45:08 +0800 Subject: [PATCH 2/5] =?UTF-8?q?=F0=9F=90=9B=20Dont=20clear=20cart=20on=20a?= =?UTF-8?q?dd=20cart=20items?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/store/modules/cart.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/store/modules/cart.js b/src/store/modules/cart.js index d144fa0d4..e54b64ee6 100644 --- a/src/store/modules/cart.js +++ b/src/store/modules/cart.js @@ -156,7 +156,6 @@ const actions = { dispatch('saveBookProductShoppingCart'); }, addBookProductsToShoppingCart({ commit, dispatch }, items) { - commit(TYPES.SHOPPING_CART_REPLACE_ALL_BOOK_PRODUCT, {}); items.slice(0, BATCH_COLLECT_MAX).forEach(item => { commit(TYPES.SHOPPING_CART_ADD_BOOK_PRODUCT, item); }); From 3286f76fcd2797dee80421111c0bf31ad38c12a1 Mon Sep 17 00:00:00 2001 From: William Chong Date: Thu, 28 Nov 2024 01:25:04 +0800 Subject: [PATCH 3/5] =?UTF-8?q?=F0=9F=90=9B=20Overwrite=20cart=20item=20on?= =?UTF-8?q?=20recover?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/store/modules/cart.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/store/modules/cart.js b/src/store/modules/cart.js index e54b64ee6..751870ba0 100644 --- a/src/store/modules/cart.js +++ b/src/store/modules/cart.js @@ -35,7 +35,10 @@ const mutations = { [TYPES.SHOPPING_CART_REPLACE_ALL_NFT_CLASS](state, map) { state.shoppingCartNFTClassByIdMap = map; }, - [TYPES.SHOPPING_CART_ADD_BOOK_PRODUCT](state, newItem) { + [TYPES.SHOPPING_CART_ADD_BOOK_PRODUCT]( + state, + { item: newItem, overwrite = false } + ) { const { collectionId, classId, @@ -58,7 +61,7 @@ const mutations = { quantity: 1, priceIndex: priceIndex ?? (classId ? 0 : undefined), }; - } else { + } else if (!overwrite) { item = { ...item, quantity: item.quantity + 1, @@ -152,12 +155,12 @@ const actions = { if (getters.shoppingCartBookProductList.length >= BATCH_COLLECT_MAX) { return; } - commit(TYPES.SHOPPING_CART_ADD_BOOK_PRODUCT, item); + commit(TYPES.SHOPPING_CART_ADD_BOOK_PRODUCT, { item }); dispatch('saveBookProductShoppingCart'); }, addBookProductsToShoppingCart({ commit, dispatch }, items) { items.slice(0, BATCH_COLLECT_MAX).forEach(item => { - commit(TYPES.SHOPPING_CART_ADD_BOOK_PRODUCT, item); + commit(TYPES.SHOPPING_CART_ADD_BOOK_PRODUCT, { item, overwrite: true }); }); dispatch('saveBookProductShoppingCart'); }, From 31a443019f69b7a577b24242a636fed34892da26 Mon Sep 17 00:00:00 2001 From: William Chong Date: Mon, 2 Dec 2024 18:23:59 +0800 Subject: [PATCH 4/5] =?UTF-8?q?=F0=9F=9A=B8=20Clear=20local=20cart=20on=20?= =?UTF-8?q?account=20restore?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/store/modules/cart.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/store/modules/cart.js b/src/store/modules/cart.js index 751870ba0..2af58bfc6 100644 --- a/src/store/modules/cart.js +++ b/src/store/modules/cart.js @@ -173,19 +173,22 @@ const actions = { dispatch('saveBookProductShoppingCart'); }, async saveBookProductShoppingCart({ state, getters }) { - saveShoppingCartToStorage(state.shoppingCartBookProductByIdMap, 'book'); if (getters.loginAddress) { const cart = Object.values(state.shoppingCartBookProductByIdMap); try { if (cart.length) { await this.$api.$post(postShoppingCart(), { cart }); } else { - await this.$api.$post(postShoppingCart(), { cart }); + await this.$api.$delete(postShoppingCart()); } + saveShoppingCartToStorage({}, 'book'); } catch (e) { // eslint-disable-next-line no-console console.error(e); + saveShoppingCartToStorage(state.shoppingCartBookProductByIdMap, 'book'); } + } else { + saveShoppingCartToStorage(state.shoppingCartBookProductByIdMap, 'book'); } }, loadBookProductShoppingCart({ commit, getters }) { From d9d0217dc0d8dc7113a4be78b7da769c7f23900b Mon Sep 17 00:00:00 2001 From: William Chong Date: Tue, 3 Dec 2024 12:16:30 +0800 Subject: [PATCH 5/5] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Combine=20cart=20API?= =?UTF-8?q?=20path?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/util/api/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/util/api/index.js b/src/util/api/index.js index 3ae23719e..942eb92a1 100644 --- a/src/util/api/index.js +++ b/src/util/api/index.js @@ -455,8 +455,8 @@ export const postUserV2Login = () => '/api/v2/users/login'; export const postUserV2Logout = () => '/api/v2/users/logout'; export const getShoppingCart = () => '/api/v2/users/cart'; -export const postShoppingCart = () => '/api/v2/users/cart'; -export const deleteShoppingCart = () => '/api/v2/users/cart'; +export const postShoppingCart = getShoppingCart; +export const deleteShoppingCart = getShoppingCart; export const postUserV2WalletEmail = ({ email,