Skip to content
Open
Show file tree
Hide file tree
Changes from 13 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
147 changes: 109 additions & 38 deletions apps/antalmanac/src/actions/AppStoreActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@ import { TRPCError } from '@trpc/server';
import { SnackbarOrigin, VariantType } from 'notistack';
import { PostHog } from 'posthog-js/react';

import analyticsEnum, { logAnalytics, courseNumAsDecimal } from '$lib/analytics/analytics';
import analyticsEnum, { analyticsIdentifyUser, logAnalytics } from '$lib/analytics/analytics';
import trpc from '$lib/api/trpc';
import { warnMultipleTerms } from '$lib/helpers';
import { setLocalStorageUserId, setLocalStorageDataCache } from '$lib/localStorage';
import { getErrorMessage } from '$lib/utils';
import AppStore from '$stores/AppStore';
import { scheduleComponentsToggleStore } from '$stores/ScheduleComponentsToggleStore';
import { useSessionStore } from '$stores/SessionStore';
Expand All @@ -41,8 +42,10 @@ export const addCourse = (
logAnalytics(postHog, {
category: analyticsEnum.classSearch,
action: analyticsEnum.classSearch.actions.ADD_COURSE,
label: courseDetails.deptCode,
value: courseNumAsDecimal(courseDetails.courseNumber),
customProps: {
courseDept: courseDetails.deptCode,
courseNumber: courseDetails.courseNumber,
},
});
const terms = AppStore.termsInSchedule(term);

Expand Down Expand Up @@ -103,13 +106,6 @@ export const saveSchedule = async (
userInfo?: { email?: string | null; name?: string | null; avatar?: string | null },
postHog?: PostHog
) => {
logAnalytics(postHog, {
category: analyticsEnum.nav,
action: analyticsEnum.nav.actions.SAVE_SCHEDULE,
label: providerId,
value: rememberMe ? 1 : 0,
});

if (providerId != null) {
providerId = providerId.replace(/\s+/g, '');

Expand Down Expand Up @@ -146,29 +142,39 @@ export const saveSchedule = async (
);
}
deleteTempSaveData();
logAnalytics(postHog, {
category: analyticsEnum.auth,
action: analyticsEnum.auth.actions.SAVE_SCHEDULE,
customProps: {
autoSave: false,
},
});
AppStore.saveSchedule();
} catch (e) {
if (e instanceof TRPCError) {
if (useSessionStore.getState().sessionIsValid) {
openSnackbar('error', `Schedule could not be saved`);
} else {
openSnackbar('error', `Schedule could not be saved under username "${providerId}`);
openSnackbar('error', `Schedule could not be saved under username "${providerId}"`);
}
} else {
openSnackbar('error', 'Network error or server is down.');
}
logAnalytics(postHog, {
category: analyticsEnum.auth,
action: analyticsEnum.auth.actions.SAVE_SCHEDULE_FAIL,
error: getErrorMessage(e),
customProps: {
autoSave: false,
},
});
}
}
}
};

export async function autoSaveSchedule(providerID: string, options: AutoSaveScheduleOptions) {
const { userInfo, postHog } = options;
logAnalytics(postHog, {
category: analyticsEnum.nav,
action: analyticsEnum.nav.actions.SAVE_SCHEDULE,
label: providerID,
});
if (providerID == null) return;
providerID = providerID.replace(/\s+/g, '');
if (providerID.length < 0) return;
Expand All @@ -185,15 +191,31 @@ export async function autoSaveSchedule(providerID: string, options: AutoSaveSche
userData: scheduleSaveState,
},
});

deleteTempSaveData();
AppStore.saveSchedule();
logAnalytics(postHog, {
category: analyticsEnum.auth,
action: analyticsEnum.auth.actions.SAVE_SCHEDULE,
customProps: {
providerID,
autoSave: true,
},
});
} catch (e) {
if (e instanceof TRPCError) {
openSnackbar('error', `Schedule could not be auto-saved under username "${providerID}`);
openSnackbar('error', `Schedule could not be auto-saved under username "${providerID}"`);
} else {
openSnackbar('error', 'Network error or server is down.');
}
logAnalytics(postHog, {
category: analyticsEnum.auth,
action: analyticsEnum.auth.actions.SAVE_SCHEDULE_FAIL,
error: getErrorMessage(e),
customProps: {
providerID,
autoSave: true,
},
});
}
}

Expand All @@ -216,7 +238,7 @@ export const mergeShortCourseSchedules = (
currentSchedules.push(...cacheSchedule);
};

const handleScheduleImport = async (username: string, skipImportedCheck = false) => {
const handleScheduleImport = async (username: string, skipImportedCheck = false, postHog?: PostHog) => {
const session = useSessionStore.getState();
if (!session.sessionIsValid) {
throw new Error("Invalid session: User isn't logged in.");
Expand Down Expand Up @@ -252,11 +274,16 @@ const handleScheduleImport = async (username: string, skipImportedCheck = false)

const isScheduleLoaded = await AppStore.loadSchedule(currentSchedules);
if (isScheduleLoaded) {
logAnalytics(postHog, {
category: analyticsEnum.auth,
action: analyticsEnum.auth.actions.IMPORT_LEGACY,
});

openSnackbar('success', `Schedule with name "${username}" imported successfully!`);

scheduleComponentsToggleStore.setState({ openScheduleSelect: true, openLoadingSchedule: false });

await saveSchedule(accounts.providerAccountId, true, users);
await saveSchedule(accounts.providerAccountId, true, users, postHog);

await trpc.userData.flagImportedSchedule.mutate({
providerId: username,
Expand All @@ -267,17 +294,17 @@ const handleScheduleImport = async (username: string, skipImportedCheck = false)
return { imported: false, error: null };
};

export const importValidatedSchedule = async (username: string) => {
export const importValidatedSchedule = async (username: string, postHog?: PostHog) => {
try {
return await handleScheduleImport(username, true);
return await handleScheduleImport(username, true, postHog);
} catch (e) {
return { imported: false, error: e };
}
};

export const importScheduleWithUsername = async (username: string) => {
export const importScheduleWithUsername = async (username: string, postHog?: PostHog) => {
try {
return await handleScheduleImport(username, false);
return await handleScheduleImport(username, false, postHog);
} catch (e) {
return { imported: false, error: e };
}
Expand All @@ -289,12 +316,6 @@ export const loadSchedule = async (
accountType: 'OIDC' | 'GOOGLE' | 'GUEST',
postHog?: PostHog
) => {
logAnalytics(postHog, {
category: analyticsEnum.nav,
action: analyticsEnum.nav.actions.LOAD_SCHEDULE,
label: providerId,
value: rememberMe ? 1 : 0,
});
if (
providerId != null &&
(!AppStore.hasUnsavedChanges() ||
Expand Down Expand Up @@ -329,13 +350,36 @@ export const loadSchedule = async (
}

if (error) {
logAnalytics(postHog, {
category: analyticsEnum.auth,
action:
accountType === 'GOOGLE'
? analyticsEnum.auth.actions.LOAD_SCHEDULE_FAIL
: analyticsEnum.auth.actions.LOAD_SCHEDULE_LEGACY_FAIL,
error: 'Load schedule error',
...(accountType !== 'GOOGLE' && { customProps: { rememberMe } }),
});
openSnackbar(
'error',
`Network error loading course information for "${providerId}".
If this continues to happen, please submit a feedback form.`
);
} else {
logAnalytics(postHog, {
category: analyticsEnum.auth,
action: analyticsEnum.auth.actions.LOAD_SCHEDULE,
});
}
} catch (e) {
logAnalytics(postHog, {
category: analyticsEnum.auth,
action:
accountType === 'GOOGLE'
? analyticsEnum.auth.actions.LOAD_SCHEDULE_FAIL
: analyticsEnum.auth.actions.LOAD_SCHEDULE_LEGACY_FAIL,
error: getErrorMessage(e),
...(accountType !== 'GOOGLE' && { customProps: { rememberMe } }),
});
if (e instanceof TRPCClientError) {
if (e.data.httpStatus === 404) {
openSnackbar('error', e.message);
Expand All @@ -351,37 +395,55 @@ export const loadSchedule = async (
}
};

export const loadScheduleWithSessionToken = async () => {
// logAnalytics({
// category: analyticsEnum.nav.title,
// action: analyticsEnum.nav.actions.LOAD_SCHEDULE,
// label: providerId,
// value: rememberMe ? 1 : 0,
// });
export const loadScheduleWithSessionToken = async (postHog?: PostHog) => {
try {
let analyticsErrorMessage = '';
const userDataResponse = await trpc.userData.getUserDataWithSession.query({
refreshToken: useSessionStore.getState().session ?? '',
});
const scheduleSaveState = userDataResponse?.userData;
const userId = userDataResponse?.id;

if (scheduleSaveState !== undefined && isEmptySchedule(scheduleSaveState.schedules)) {
analyticsIdentifyUser(postHog, userId);
logAnalytics(postHog, {
category: analyticsEnum.auth,
action: analyticsEnum.auth.actions.LOAD_SCHEDULE,
});
return true;
}

if (scheduleSaveState === undefined) {
openSnackbar('error', `Couldn't find schedules for this account`);
} else if (await AppStore.loadSchedule(scheduleSaveState)) {
analyticsIdentifyUser(postHog, userId);
openSnackbar('success', `Schedule loaded.`);
logAnalytics(postHog, {
category: analyticsEnum.auth,
action: analyticsEnum.auth.actions.LOAD_SCHEDULE,
});
return true;
} else {
analyticsErrorMessage = 'Network error';
AppStore.loadSkeletonSchedule(scheduleSaveState);
openSnackbar(
'error',
`Network error loading course information".
If this continues to happen, please submit a feedback form.`
);
}
logAnalytics(postHog, {
category: analyticsEnum.auth,
action: analyticsEnum.auth.actions.LOAD_SCHEDULE_FAIL,
error: analyticsErrorMessage,
});
return false;
} catch (e) {
logAnalytics(postHog, {
category: analyticsEnum.auth,
action: analyticsEnum.auth.actions.LOAD_SCHEDULE_FAIL,
error: getErrorMessage(e),
});
console.error('Error in loadScheduleWithSessionToken:', e);
openSnackbar('error', `Failed to load schedules. If this continues to happen, please submit a feedback form.`);
return false;
Expand All @@ -395,14 +457,23 @@ const cacheSchedule = () => {
}
};

export const loginUser = async () => {
export const loginUser = async (postHog?: PostHog) => {
try {
const authUrl = await trpc.userData.getGoogleAuthUrl.query();
if (authUrl) {
cacheSchedule();
window.location.href = authUrl.toString();
}
logAnalytics(postHog, {
category: analyticsEnum.auth,
action: analyticsEnum.auth.actions.SIGN_IN,
});
} catch (error) {
logAnalytics(postHog, {
category: analyticsEnum.auth,
action: analyticsEnum.auth.actions.SIGN_IN_FAIL,
error: getErrorMessage(error),
});
console.error('Error during login initiation', error);
openSnackbar('error', 'Error during login initiation. Please Try Again.');
}
Expand Down
37 changes: 27 additions & 10 deletions apps/antalmanac/src/components/Header/Import.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ export function Import() {
for (const event of zotcourseImport.customEvents) {
addCustomEvent(event, [currentSchedule]);
}
uploadSectionCodes(sectionCodes, term, currentSchedule);
uploadSectionCodes(sectionCodes, term, currentSchedule, ImportSource.ZOT_COURSE_IMPORT);
} catch (e) {
if (e instanceof QueryZotcourseError) {
openSnackbar('error', e.message);
Expand All @@ -111,10 +111,10 @@ export function Import() {
sectionCodes = studyListText.match(/\d{5}/g);

if (!sectionCodes || sectionCodes.length === 0) break;
uploadSectionCodes(sectionCodes, term, currentSchedule);
uploadSectionCodes(sectionCodes, term, currentSchedule, ImportSource.STUDY_LIST_IMPORT);
break;
case ImportSource.AA_USERNAME_IMPORT: {
const importStatus = await importScheduleWithUsername(aaUsername);
const importStatus = await importScheduleWithUsername(aaUsername, postHog);
if (importStatus.error) {
setAlertDialog(true);
setAlertDialogSeverity('error');
Expand All @@ -140,7 +140,7 @@ export function Import() {
openSnackbar(
'error',
`Cannot import an empty ${
importSource === ImportSource.ZOT_COURSE_IMPORT ? 'Zotcourse' : 'Study List'
importSource === ImportSource.ZOT_COURSE_IMPORT ? 'Zotcourse Schedule' : 'Study List'
}.`
);
handleClose();
Expand All @@ -156,12 +156,17 @@ export function Import() {
};

const handleImportAnyways = () => {
importValidatedSchedule(aaUsername);
importValidatedSchedule(aaUsername, postHog);
setOpenImportDialog(false);
setAlertDialog(false);
};

const uploadSectionCodes = async (sectionCodes: string[], term: string, currentSchedule: number) => {
const uploadSectionCodes = async (
sectionCodes: string[],
term: string,
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.
Outdated
currentSchedule: number,
importSource: ImportSource.STUDY_LIST_IMPORT | ImportSource.ZOT_COURSE_IMPORT
) => {
try {
const term = RightPaneStore.getFormData().term;

Expand All @@ -176,8 +181,13 @@ export function Import() {

logAnalytics(postHog, {
category: analyticsEnum.nav,
action: analyticsEnum.nav.actions.IMPORT_STUDY_LIST,
value: sectionsAdded / (sectionCodes.length || 1),
action:
importSource === ImportSource.STUDY_LIST_IMPORT
? analyticsEnum.nav.actions.IMPORT_STUDY_LIST
: analyticsEnum.nav.actions.IMPORT_ZOTCOURSE,
customProps: {
percentImported: sectionsAdded / (sectionCodes.length || 1),
},
});

if (sectionsAdded === sectionCodes.length) {
Expand All @@ -191,11 +201,18 @@ export function Import() {
} else {
openSnackbar(
'error',
'Failed to import any classes! Please make sure that you pasted the correct Study List.'
`Failed to import any classes! Please make sure that you pasted the correct ${
importSource === ImportSource.STUDY_LIST_IMPORT ? 'Study List' : 'Zotcourse Schedule'
}.`
);
}
} catch (e) {
openSnackbar('error', 'An error occurred while trying to import the Study List.');
openSnackbar(
'error',
`An error occurred while trying to import the ${
importSource === ImportSource.STUDY_LIST_IMPORT ? 'Study List' : 'Zotcourse Schedule'
}.`
);
console.error(e);
}
};
Expand Down
Loading
Loading