Skip to content

Commit

Permalink
Merge pull request #2328 from daostack/feature/CW-2288-faster-feed-lo…
Browse files Browse the repository at this point in the history
…ading

Use caching to load inbox and feed faster - Make feed loading faster #2288
  • Loading branch information
andreymikhadyuk authored Nov 21, 2023
2 parents 35ecf67 + a30f2a4 commit ea37688
Show file tree
Hide file tree
Showing 6 changed files with 75 additions and 25 deletions.
11 changes: 6 additions & 5 deletions src/pages/commonFeed/hooks/useCommonData/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,13 @@ export const useCommonData = (userId?: string): Return => {
(async () => {
try {
const [common, governance, sharedFeedItem] = await Promise.all([
CommonService.getCommonById(commonId),
GovernanceService.getGovernanceByCommonId(commonId),
CommonService.getCommonById(commonId, true),
GovernanceService.getGovernanceByCommonId(commonId, true),
sharedFeedItemId
? CommonFeedService.getCommonFeedItemById(
commonId,
sharedFeedItemId,
true,
)
: null,
]);
Expand All @@ -69,10 +70,10 @@ export const useCommonData = (userId?: string): Return => {
const { rootCommonId } = common;
const [parentCommons, subCommons, rootCommonGovernance] =
await Promise.all([
CommonService.getAllParentCommonsForCommon(common),
CommonService.getCommonsByDirectParentIds([common.id]),
CommonService.getAllParentCommonsForCommon(common, true),
CommonService.getCommonsByDirectParentId(common.id, true),
rootCommonId
? GovernanceService.getGovernanceByCommonId(rootCommonId)
? GovernanceService.getGovernanceByCommonId(rootCommonId, true)
: null,
]);
const rootCommon = await getRootCommon(
Expand Down
45 changes: 37 additions & 8 deletions src/services/Common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import {
SubCollections,
} from "@/shared/models";
import {
convertObjectDatesToFirestoreTimestamps,
emptyFunction,
firestoreDataConverter,
transformFirebaseDataList,
Expand All @@ -30,16 +29,25 @@ const converter = firestoreDataConverter<Common>();
const commonMemberConverter = firestoreDataConverter<CommonMember>();

class CommonService {
public getCommonById = async (commonId: string): Promise<Common | null> => {
const common = await firebase
public getCommonById = async (
commonId: string,
cached = false,
): Promise<Common | null> => {
const snapshot = await firebase
.firestore()
.collection(Collection.Daos)
.where("id", "==", commonId)
.where("state", "==", CommonState.ACTIVE)
.get();
const data = transformFirebaseDataList<Common>(common);
.withConverter(converter)
.get({ source: cached ? "cache" : "default" });
const commons = snapshot.docs.map((doc) => doc.data());
const common = commons[0] || null;

if (cached && !common) {
return this.getCommonById(commonId);
}

return data[0] ? convertObjectDatesToFirestoreTimestamps(data[0]) : null;
return common;
};

public getCachedCommonById = async (
Expand Down Expand Up @@ -93,6 +101,26 @@ class CommonService {
.reduce((acc, items) => [...acc, ...items], []);
};

public getCommonsByDirectParentId = async (
parentCommonId: string,
cached = false,
): Promise<Common[]> => {
const snapshot = await firebase
.firestore()
.collection(Collection.Daos)
.where("state", "==", CommonState.ACTIVE)
.where("directParent.commonId", "==", parentCommonId)
.withConverter(converter)
.get({ source: cached ? "cache" : "default" });
const commons = snapshot.docs.map((doc) => doc.data());

if (cached && commons.length === 0) {
return this.getCommonsByDirectParentId(parentCommonId);
}

return commons;
};

public getCommonsByDirectParentIds = async (
ids: string[],
): Promise<Common[]> => {
Expand Down Expand Up @@ -249,10 +277,11 @@ class CommonService {
// Fetch all parent commons. Order: from root parent common to lowest ones
public getAllParentCommonsForCommon = async (
commonToCheck: Pick<Common, "directParent"> | string,
cached = false,
): Promise<Common[]> => {
const common =
typeof commonToCheck === "string"
? await this.getCommonById(commonToCheck)
? await this.getCommonById(commonToCheck, cached)
: commonToCheck;

if (!common || common.directParent === null) {
Expand All @@ -263,7 +292,7 @@ class CommonService {
let nextCommonId = common.directParent.commonId;

while (nextCommonId) {
const common = await this.getCommonById(nextCommonId);
const common = await this.getCommonById(nextCommonId, cached);

if (common) {
finalCommons = [common, ...finalCommons];
Expand Down
19 changes: 14 additions & 5 deletions src/services/CommonFeed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import {
convertToTimestamp,
firestoreDataConverter,
} from "@/shared/utils";
import firebase from "@/shared/utils/firebase";
import firebase, { isFirestoreCacheError } from "@/shared/utils/firebase";
import Api, { CancelToken } from "./Api";

const converter = firestoreDataConverter<CommonFeed>();
Expand All @@ -42,12 +42,21 @@ class CommonFeedService {
public getCommonFeedItemById = async (
commonId: string,
commonFeedId: string,
cached = false,
): Promise<CommonFeed | null> => {
const snapshot = await this.getCommonFeedSubCollection(commonId)
.doc(commonFeedId)
.get();
try {
const snapshot = await this.getCommonFeedSubCollection(commonId)
.doc(commonFeedId)
.get({ source: cached ? "cache" : "default" });

return snapshot?.data() || null;
return snapshot?.data() || null;
} catch (error) {
if (cached && isFirestoreCacheError(error)) {
return this.getCommonFeedItemById(commonId, commonFeedId);
} else {
throw error;
}
}
};

public getCommonFeedItemWithSnapshot = async (
Expand Down
14 changes: 11 additions & 3 deletions src/services/Governance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,20 @@ const converter = firestoreDataConverter<Governance>();
class GovernanceService {
public getGovernanceByCommonId = async (
commonId: string,
cached = false,
): Promise<Governance | null> => {
const governanceList = await governanceCollection
const snapshot = await governanceCollection
.where("commonId", "==", commonId)
.get();
.withConverter(converter)
.get({ source: cached ? "cache" : "default" });
const governanceList = snapshot.docs.map((doc) => doc.data());
const governance = governanceList[0] || null;

return transformFirebaseDataList<Governance>(governanceList)[0] || null;
if (cached && !governance) {
return this.getGovernanceByCommonId(commonId);
}

return governance;
};

public getGovernanceListByCommonIds = async (
Expand Down
2 changes: 1 addition & 1 deletion src/shared/hooks/useCases/useFullCommonData/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export const getRootCommon = async (
return initialRootCommon;
}

return CommonService.getCommonById(rootCommonId);
return CommonService.getCommonById(rootCommonId, true);
};

export const useFullCommonData = (): Return => {
Expand Down
9 changes: 6 additions & 3 deletions src/shared/utils/firebase.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,11 @@ if (REACT_APP_ENV === Environment.Local) {
});
}

export const isFirebaseError = (error: any): error is FirebaseError => {
return error && error.code && error.code.startsWith("auth/");
};
export const isFirebaseError = (error: any): error is FirebaseError =>
(error && error.code && error.code.startsWith("auth/")) ||
error.name === "FirebaseError";

export const isFirestoreCacheError = (error: any): boolean =>
isFirebaseError(error) && error.code === "unavailable";

export default firebase;

0 comments on commit ea37688

Please sign in to comment.