Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,4 @@ dist/
# environment variables
.env
dfx.json
.astro
.astro/
5 changes: 0 additions & 5 deletions src/atlas_frontend/.astro/settings.json
Comment thread
Nyaxize marked this conversation as resolved.
Outdated

This file was deleted.

1 change: 0 additions & 1 deletion src/atlas_frontend/.astro/types.d.ts

This file was deleted.

14 changes: 13 additions & 1 deletion src/atlas_frontend/src/canisters/atlasMain/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,19 @@ import type { Dispatch } from "react";
import type { UnknownAction } from "@reduxjs/toolkit";
import { setUserBlockchainData } from "../../store/slices/userSlice.js";
import { unwrapCall } from "../delegatedCall.js";
import { setConfig } from "../../store/slices/appSlice.js";
import { setConfig, setLastFetchTime } from "../../store/slices/appSlice.js";
import { setSpaces } from "../../store/slices/spacesSlice.js";
import type { ExternalLinks } from "../atlasSpace/types.js";

const shouldFetchSpaces = (lastFetchTime: string | null): boolean => {
if (!lastFetchTime) return true;
const lastFetch = new Date(lastFetchTime);
const now = new Date();
const diffMinutes = (now.getTime() - lastFetch.getTime()) / (1000 * 60);
return diffMinutes >= 5;
Comment thread
Nyaxize marked this conversation as resolved.
Outdated
};

export { shouldFetchSpaces };
interface CreateNewSpaceArgs {
authAtlasMain: ActorSubclass<_SERVICE_MAIN>;
name: string;
Expand Down Expand Up @@ -84,6 +93,9 @@ export const getAllSpaces = async ({
unAuthAtlasMain,
dispatch,
}: GetAtlasData) => {
const now = new Date().toISOString();
dispatch(setLastFetchTime(now));

let spacesCount = 0n;
let start = 0n;
const count = 200n;
Expand Down
36 changes: 19 additions & 17 deletions src/atlas_frontend/src/canisters/atlasSpace/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,24 +48,26 @@ export const getAtlasSpace = async ({
}
const externalLinksObj = Object.fromEntries(state.external_links);

dispatch(
setSpace({
spaceId,
state: {
...state,
version,
space_symbol: state.space_symbol.pop() ?? null,
space_background: state.space_background.pop() ?? null,
space_logo: state.space_logo.pop() ?? null,
external_links: {
x: externalLinksObj?.x ?? null,
telegram: externalLinksObj?.telegram ?? null,
discord: externalLinksObj?.discord ?? null,
linkedIn: externalLinksObj?.linkedIn ?? null,
},
const spaceData = {
spaceId,
state: {
...state,
version,
space_symbol: state.space_symbol.pop() ?? null,
space_background: state.space_background.pop() ?? null,
space_logo: state.space_logo.pop() ?? null,
external_links: {
x: externalLinksObj?.x ?? null,
telegram: externalLinksObj?.telegram ?? null,
discord: externalLinksObj?.discord ?? null,
linkedIn: externalLinksObj?.linkedIn ?? null,
},
})
);
},
};

dispatch(setSpace(spaceData));

return spaceData;
};

interface CreateNewSpaceTaskArgs {
Expand Down
136 changes: 103 additions & 33 deletions src/atlas_frontend/src/components/Space/SpacesList/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,58 +5,128 @@ import {
useUnAuthAgent,
useUnAuthAtlasMainActor,
} from "../../../hooks/identityKit";
import { getAllSpaces } from "../../../canisters/atlasMain/api";
import { deserialize, type RootState } from "../../../store/store";
import {
getAllSpaces,
shouldFetchSpaces,
} from "../../../canisters/atlasMain/api";
import {
customSerify,
deserialize,
type RootState,
} from "../../../store/store";
import { Principal } from "@dfinity/principal";
import { getAtlasSpace } from "../../../canisters/atlasSpace/api";
import SpaceItem from "./SpaceItem";
import { useNavigate } from "react-router-dom";
import type { Spaces } from "../../../store/slices/spacesSlice";
import LocalBlurOverlay from "../../Shared/LocalBlurOverlay";
import { serify } from "@karmaniverous/serify-deserify";
import { setSpaces } from "../../../store/slices/spacesSlice";
import type { StorableState } from "../../../canisters/atlasSpace/types";

const syncSpacesWithLocalStorage = (reduxSpaces: Spaces) => {
try {
const savedData = localStorage.getItem("atlasSpaces");
const localSpaces = savedData ? JSON.parse(savedData) : {};
const reduxKeys = Object.keys(reduxSpaces || {});
const localKeys = Object.keys(localSpaces || {});

if (reduxKeys.length !== localKeys.length) {
const serializedData = JSON.stringify(
serify(reduxSpaces, customSerify)
);
localStorage.setItem("atlasSpaces", serializedData);
}
} catch (err) {
console.error("Failed to sync Redux spaces with localStorage:", err);
}
};
Comment thread
Nyaxize marked this conversation as resolved.
Outdated

const SpacesList = () => {
const dispatch = useDispatch();
const unAuthAtlasMain = useUnAuthAtlasMainActor();
const spaces = deserialize<Spaces>(useSelector((state: RootState) => state.spaces.spaces));
const [fetchedSpacesData, setFetchedSpacesData] = useState(false);
const [fetchingInProgress, setFetchingInProgress] = useState(true);
const spaces = deserialize<Spaces>(
useSelector((state: RootState) => state.spaces.spaces)
);
const lastFetchTime = useSelector((state: RootState) => state.app.lastFetchTime);

const [, setFetchingInProgress] = useState(true);
const agent = useUnAuthAgent();
const navigate = useNavigate();

useEffect(() => {
const fetchSpaces = async () => {
if (unAuthAtlasMain && !spaces) {
await getAllSpaces({
const loadAndFetchSpaces = async () => {
if (spaces && Object.keys(spaces).length > 1) {
setFetchingInProgress(false);
return;
}

setFetchingInProgress(true);
try {
const savedData = localStorage.getItem("atlasSpaces");
if (savedData) {
const loadedSpaces = JSON.parse(savedData);
if (loadedSpaces && Object.keys(loadedSpaces).length > 0) {
dispatch(setSpaces(loadedSpaces));
}
}
} catch (error) {
console.error("Failed to load spaces data from localStorage:", error);
}
Comment thread
Nyaxize marked this conversation as resolved.
Outdated
if (unAuthAtlasMain && agent && shouldFetchSpaces(lastFetchTime)) {
const spacesIDs = await getAllSpaces({
dispatch,
unAuthAtlasMain,
});
setFetchingInProgress(false);
const updatedSpacesData: {
[key: string]: { state: StorableState | null; tasks: null };
} = {};
const spaceIds = Object.keys(spacesIDs);

for (const spaceId of spaceIds) {
const spacePrincipal = Principal.from(spaceId);
const unAuthAtlasSpace = getUnAuthAtlasSpaceActor(
agent,
spacePrincipal
);
if (!unAuthAtlasSpace) continue;

const spaceData = await getAtlasSpace({
spaceId,
unAuthAtlasSpace,
dispatch,
});

if (spaceData) {
updatedSpacesData[spaceId] = {
state: spaceData.state,
tasks: null,
};
}
}

if (Object.keys(updatedSpacesData).length > 0) {
dispatch(setSpaces(updatedSpacesData));
try {
const serializedData = JSON.stringify(
serify(updatedSpacesData, customSerify)
);
localStorage.setItem("atlasSpaces", serializedData);
} catch (error) {
console.error("Failed to save spaces data to localStorage:", error);
}
}
}
Comment thread
Nyaxize marked this conversation as resolved.
Outdated
setFetchingInProgress(false);
};
fetchSpaces();
}, [dispatch, unAuthAtlasMain]);

useEffect(() => {
if (!spaces || fetchedSpacesData || !agent) return;
Object.keys(spaces).map(async (spaceId) => {
const spacePrincipal = Principal.from(spaceId);
getUnAuthAtlasSpaceActor(agent, spacePrincipal);
const unAuthAtlasSpace = getUnAuthAtlasSpaceActor(agent, spacePrincipal);
if (!unAuthAtlasSpace) return;
await getAtlasSpace({
spaceId,
unAuthAtlasSpace,
dispatch,
});
});
setFetchedSpacesData(true);
}, [dispatch, spaces, fetchedSpacesData]);
loadAndFetchSpaces();

if (spaces && Object.keys(spaces).length > 0) {
syncSpacesWithLocalStorage(spaces);
}
}, [dispatch, unAuthAtlasMain, agent, lastFetchTime]);

if (!spaces) {
if (!fetchingInProgress) navigate("/");
return (
<LocalBlurOverlay isLoading={true} />
)
return <LocalBlurOverlay isLoading={true} />;
}

const spacesEntries = Object.entries(spaces);
Expand Down Expand Up @@ -89,4 +159,4 @@ const SpacesList = () => {
}
};

export default SpacesList;
export default SpacesList;
7 changes: 6 additions & 1 deletion src/atlas_frontend/src/store/slices/appSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@ interface AppState {
blockchainConfig: null | StorableConfig;
isScreenBlur: boolean;
isLoading: boolean;
lastFetchTime: string | null;
}

const initialState: AppState = {
blockchainConfig: null,
isScreenBlur: false,
isLoading: false,
lastFetchTime: null,
};

export const appSlice = createSlice({
Expand All @@ -31,6 +33,9 @@ export const appSlice = createSlice({
setConfig: (state, action: PayloadAction<StorableConfig>) => {
state.blockchainConfig = action.payload;
},
setLastFetchTime: (state, action: PayloadAction<string>) => {
state.lastFetchTime = action.payload;
},
},
selectors: {
selectBlockchainConfig: (state: AppState) => {
Expand All @@ -40,7 +45,7 @@ export const appSlice = createSlice({
}
});

export const { setScreenBlur, setConfig, setLoading } =
export const { setScreenBlur, setConfig, setLoading, setLastFetchTime } =
appSlice.actions;
export const { selectBlockchainConfig } = appSlice.selectors;

Expand Down