Skip to content
Closed
Show file tree
Hide file tree
Changes from all 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
8 changes: 1 addition & 7 deletions src/commons/controlBar/ControlBarChapterSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Button, Menu, MenuItem } from '@blueprintjs/core';
import { IconNames } from '@blueprintjs/icons';
import { ItemListRenderer, ItemRenderer, Select } from '@blueprintjs/select';
import { Chapter, Variant } from 'js-slang/dist/types';
import React, { useEffect } from 'react';
import React from 'react';
import { useDispatch } from 'react-redux';

import { flagLanguageDirectoryEnable } from '../../features/languageDirectory/flagLanguageDirectory';
Expand Down Expand Up @@ -39,12 +39,6 @@ export const ControlBarChapterSelect: React.FC<ControlBarChapterSelectProps> = (
const selectedEvaluatorId = useTypedSelector(s => s.languageDirectory.selectedEvaluatorId);
const dirLanguages = useTypedSelector(s => s.languageDirectory.languages);

useEffect(() => {
if (directoryEnabled && dirLanguages.length === 0) {
dispatch(LanguageDirectoryActions.fetchLanguages());
}
}, [directoryEnabled, dirLanguages.length, dispatch]);

if (!directoryEnabled) {
return (
<LegacyControlBarChapterSelect
Expand Down
2 changes: 1 addition & 1 deletion src/commons/featureFlags/featureSelector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ import { OverallState } from '../application/ApplicationTypes';
import { FeatureFlag } from './FeatureFlag';

export const featureSelector = (featureFlag: FeatureFlag<any>) => (state: OverallState) =>
state.featureFlags.modifiedFlags[featureFlag.flagName];
state.featureFlags.modifiedFlags[featureFlag.flagName] ?? featureFlag.defaultValue;
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Position } from '@blueprintjs/core';
import { useEffect, useState } from 'react';
import { useState } from 'react';
import { useDispatch } from 'react-redux';
import { useFeature } from 'src/commons/featureFlags/useFeature';
import SimpleDropdown from 'src/commons/SimpleDropdown';
Expand All @@ -21,14 +21,8 @@ const NavigationBarLangSelectButton = () => {

const dispatch = useDispatch();
const dirOptions = useDirectoryOptions();
const languagesLoaded = useTypedSelector(s => s.languageDirectory.languages.length > 0);
useEffect(() => {
if (!languagesLoaded) {
dispatch(LanguageDirectoryActions.fetchLanguages());
}
}, [languagesLoaded, dispatch]);

const directoryEnabled = useFeature(flagLanguageDirectoryEnable);

if (!directoryEnabled) {
return <LegacyNavigationBarLangSelectButton />;
}
Expand Down
57 changes: 45 additions & 12 deletions src/commons/sagas/LanguageDirectorySaga.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { call, put, select } from 'redux-saga/effects';
import type { OverallState } from 'src/commons/application/ApplicationTypes';
import { flagConductorEnable } from 'src/features/conductor/flagConductorEnable';
import { flagConductorEvaluatorUrl } from 'src/features/conductor/flagConductorEvaluatorUrl';
import { flagLanguageDirectoryEnable } from 'src/features/languageDirectory/flagLanguageDirectory';
import { selectLanguageDirectoryEnable } from 'src/features/languageDirectory/flagLanguageDirectory';
import { staticLanguageDirectoryProvider } from 'src/features/languageDirectory/LanguageDirectoryTypes';

import LanguageDirectoryActions from '../../features/languageDirectory/LanguageDirectoryActions';
Expand All @@ -19,30 +21,32 @@ const LanguageDirectorySaga = combineSagaHandlers({
}
},
[LanguageDirectoryActions.fetchLanguages.type]: function* () {
const directoryEnabled = yield select(selectLanguageDirectoryEnable);
if (!directoryEnabled) {
return;
}
const langs = yield call(
staticLanguageDirectoryProvider.getLanguages.bind(staticLanguageDirectoryProvider)
);
yield put(actions.setLanguages(langs));
yield put(LanguageDirectoryActions.setLanguages(langs));
},
[LanguageDirectoryActions.setSelectedLanguage.type]: function* (action) {
const {
payload: { languageId, evaluatorId }
} = action;
if (evaluatorId) return; // already explicitly set
const language = yield call(
staticLanguageDirectoryProvider.getLanguageById.bind(staticLanguageDirectoryProvider),
languageId
);
if (!language) return;
const defaultEvaluatorId: string | null =
language.evaluators.length > 0 ? language.evaluators[0].id : null;
if (!defaultEvaluatorId) return;
// If state still matches the same language, set evaluator
const currentLanguageId: string | null = yield select(
(s: OverallState) => s.languageDirectory.selectedLanguageId
);
if (currentLanguageId !== languageId) return;
yield put(actions.setSelectedEvaluator(defaultEvaluatorId));
console.log('A LanguageDirectorySaga: language', language);
// If evaluatorId is explicitly provided, use it; otherwise use the first available
const targetEvaluatorId =
evaluatorId || (language.evaluators.length > 0 ? language.evaluators[0].id : null);
console.log('B LanguageDirectorySaga: targetEvaluatorId', targetEvaluatorId);
if (!targetEvaluatorId) return;

yield put(actions.setSelectedEvaluator(targetEvaluatorId));
},
[LanguageDirectoryActions.setSelectedEvaluator.type]: function* (action) {
const {
Expand All @@ -51,15 +55,44 @@ const LanguageDirectorySaga = combineSagaHandlers({
const selectedLanguageId: string | null = yield select(
(s: OverallState) => s.languageDirectory.selectedLanguageId
);
if (!selectedLanguageId) return;
console.log('C LanguageDirectorySaga: selectedLanguageId', selectedLanguageId);
// Only proceed if we have both a language and evaluator ID
if (!selectedLanguageId || !evaluatorId) return;
const evaluator = yield call(
staticLanguageDirectoryProvider.getEvaluatorDefinition.bind(staticLanguageDirectoryProvider),
selectedLanguageId,
evaluatorId
);
console.log('D LanguageDirectorySaga: evaluator', evaluator);
if (!evaluator) return;
// Set conductor enable and evaluator url to true and the evaluator path
yield put(actions.setFlag({ featureFlag: flagConductorEnable, value: true }));
yield put(actions.setFlag({ featureFlag: flagConductorEvaluatorUrl, value: evaluator.path }));
},
[LanguageDirectoryActions.resetConductor.type]: function* () {
// Reset languageDirectory in state

// Reset conductor flags to their default values
yield put(actions.resetFlag({ featureFlag: flagConductorEnable }));
yield put(actions.resetFlag({ featureFlag: flagConductorEvaluatorUrl }));
},
[actions.setFlag.type]: {
takeEvery: function* (action) {
const {
payload: { featureFlag, value }
} = action as any;
if (featureFlag.flagName !== flagLanguageDirectoryEnable.flagName) return;
if (value) {
// Auto-fetch languages when enabling the language directory
const currentLanguages = yield select((s: OverallState) => s.languageDirectory.languages);
if (currentLanguages.length === 0) {
yield put(LanguageDirectoryActions.fetchLanguages());
}
} else {
// Reset conductor settings when disabling the language directory
yield put(LanguageDirectoryActions.resetConductor());
}
}
}
});

Expand Down
3 changes: 3 additions & 0 deletions src/features/conductor/flagConductorEvaluatorUrl.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { createFeatureFlag } from '../../commons/featureFlags';
import { featureSelector } from '../../commons/featureFlags/featureSelector';

export const flagConductorEvaluatorUrl = createFeatureFlag(
'conductor.evaluator.url',
'https://fyp.tsammeow.dev/evaluator/0.2.1/worker.js',
'The URL where Conductor may find the Runner to be used for running user programs.'
);

export const selectConductorEvaluatorUrl = featureSelector(flagConductorEvaluatorUrl);
4 changes: 3 additions & 1 deletion src/features/languageDirectory/LanguageDirectoryActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ const LanguageDirectoryActions = createActions('conductor/languageDirectory', {
/** Set selected language; evaluatorId optional (defaults to first available) */
setSelectedLanguage: (languageId: string, evaluatorId?: string) => ({ languageId, evaluatorId }),
/** Set selected evaluator explicitly */
setSelectedEvaluator: (evaluatorId: string) => ({ evaluatorId })
setSelectedEvaluator: (evaluatorId: string) => ({ evaluatorId }),
/** Reset conductor enable and evaluator url */
resetConductor: null
});

export default LanguageDirectoryActions;
5 changes: 5 additions & 0 deletions src/features/languageDirectory/LanguageDirectoryReducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,10 @@ export const LanguageDirectoryReducer: Reducer<LanguageDirectoryState, SourceAct
})
.addCase(Actions.setSelectedEvaluator, (state, action) => {
state.selectedEvaluatorId = action.payload.evaluatorId;
})
.addCase(Actions.resetConductor, (state, action) => {
state.languages = [];
state.selectedLanguageId = null;
state.selectedEvaluatorId = null;
});
});
Loading