From a9ca7d30c7683e281cc3d12c4c679f21788dc1a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20L=2E=20Redrejo=20Rodr=C3=ADguez?= Date: Fri, 1 Nov 2024 18:58:33 +0100 Subject: [PATCH 1/2] machines upgraded --- kolibri/core/package.json | 2 +- .../assets/src/machines/wizardMachine.js | 205 +++++-------- .../src/machines/changeFacilityMachine.js | 277 ++++++++++-------- yarn.lock | 36 ++- 4 files changed, 268 insertions(+), 252 deletions(-) diff --git a/kolibri/core/package.json b/kolibri/core/package.json index 099a3d5a675..3071d9f16e0 100644 --- a/kolibri/core/package.json +++ b/kolibri/core/package.json @@ -35,6 +35,6 @@ "vue-router": "^3.6.5", "vuex": "^3.6.2", "vuex-router-sync": "^5.0.0", - "xstate": "^4.38.3" + "xstate": "^5.18.2" } } diff --git a/kolibri/plugins/setup_wizard/assets/src/machines/wizardMachine.js b/kolibri/plugins/setup_wizard/assets/src/machines/wizardMachine.js index 549517da65d..79070b036b3 100644 --- a/kolibri/plugins/setup_wizard/assets/src/machines/wizardMachine.js +++ b/kolibri/plugins/setup_wizard/assets/src/machines/wizardMachine.js @@ -33,18 +33,18 @@ import { DeviceTypePresets, FacilityTypePresets, LodTypePresets, UsePresets } fr * * However, to truly emulate the machine, you will need to use the `Events` tab. If you are ever * on a node that says `DO/` then that transition expects an event to be sent with a `value` - * property. You can use the "Send Event" button to open a little dialog where you can enter + * property. You can use the "raise Event" button to open a little dialog where you can enter * JSON. One downside here is that you'll have to reference the *value* of the constants as you * cannot reference the code in this dialog. * - * You can click on events to send them, but if there is a `DO/` it implies actions with + * You can click on events to raise them, but if there is a `DO/` it implies actions with * side effects will occur. Those actions will take in the whole JSON object that you send * in the `Events` tab. Note that the use of `value` as a property is just a preference and * the only required property is the `type` property indicating what kind of event is sent. */ /* eslint-disable-next-line */ -import { assign, createMachine } from 'xstate'; +import { createMachine, assign } from 'xstate'; // NOTE: Uncomment the following function if you're using the visualizer // const checkCapability = capabilityToCheck => ["get_os_user"].includes(capabilityToCheck); @@ -77,7 +77,6 @@ export const wizardMachine = createMachine( id: 'wizard', initial: 'initializeContext', context: initialContext, - predictableActionArguments: true, on: { START_OVER: { target: 'howAreYouUsingKolibri', actions: 'resetContext' }, }, @@ -103,11 +102,11 @@ export const wizardMachine = createMachine( { // `cond` takes a function that returns a Boolean, continuing to the // `target` when it returns truthy - cond: 'isOnMyOwnOrGroup', + guard: 'isOnMyOwnOrGroup', target: 'defaultLanguage', }, { - cond: 'isGroupSetup', + guard: 'isGroupSetup', target: 'deviceName', }, // There is no fallback path here; if neither `cond` above is truthy, this will break @@ -127,7 +126,7 @@ export const wizardMachine = createMachine( on: { BACK: 'defaultLanguage' }, always: [ { - cond: 'canGetOsUser', + guard: 'canGetOsUser', target: 'finalizeSetup', }, { @@ -167,11 +166,11 @@ export const wizardMachine = createMachine( on: { BACK: 'fullOrLearnOnlyDevice' }, always: [ { - cond: 'isLodSetup', + guard: 'isLodSetup', target: 'importLodUsers', }, { - cond: 'isFullSetup', + guard: 'isFullSetup', target: 'fullDeviceNewOrImportFacility', }, ], @@ -191,11 +190,11 @@ export const wizardMachine = createMachine( on: { BACK: 'fullDeviceNewOrImportFacility' }, always: [ { - cond: 'isNewFacility', + guard: 'isNewFacility', target: 'setFacilityPermissions', }, { - cond: 'isImportFacility', + guard: 'isImportFacility', target: 'importFacility', }, ], @@ -343,7 +342,7 @@ export const wizardMachine = createMachine( lodProceedJoinOrNew: { always: [ { - cond: ctx => ctx.lodImportOrJoin === LodTypePresets.JOIN, + guard: ctx => ctx.lodImportOrJoin === LodTypePresets.JOIN, target: 'lodJoinFacility', }, { @@ -424,45 +423,33 @@ export const wizardMachine = createMachine( { actions: { // The `assign` function takes an object that maps keys that match those in the machine's - // `context`to functions that take two parameters `(context, event)` - where the context + // `context`to functions that take two parameters `({context, event})` - where the context // is the current context and event refers to the event sent to the machine to initiate a // transition. - setOnMyOwnOrGroup: assign({ - onMyOwnOrGroup: (_, event) => event.value, - }), - setDeviceName: assign({ - deviceName: (_, event) => event.value, - }), - setFullOrLOD: assign({ - fullOrLOD: (_, event) => event.value, - }), - setCanGetOsUser: assign({ - canGetOsUser: (_, event) => event.value, - }), - setFacilityNewOrImport: assign({ - facilityNewOrImport: (_, event) => { - return event.value.importOrNew; - }, - importDeviceId: (_, event) => { - return event.value.importDeviceId; - }, - }), - setSuperuser: assign({ - superuser: (_, event) => { - return event.value; - }, - }), - setSelectedImportDeviceFacility: assign({ - selectedFacility: (_, event) => { - return event.value.selectedFacility; - }, - importDevice: (_, event) => { - return event.value.importDevice; - }, - facilitiesOnDeviceCount: (_, event) => { - return event.value.facilitiesCount; - }, - }), + setOnMyOwnOrGroup: assign((_, event) => ({ + onMyOwnOrGroup: event.value, + })), + setDeviceName: assign((_, event) => ({ + deviceName: event.value, + })), + setFullOrLOD: assign((_, event) => ({ + fullOrLOD: event.value, + })), + setCanGetOsUser: assign((_, event) => ({ + canGetOsUser: event.value, + })), + setFacilityNewOrImport: assign((_, event) => ({ + facilityNewOrImport: event.value.importOrNew, + importDeviceId: event.value.importDeviceId, + })), + setSuperuser: assign((_, event) => ({ + superuser: event.value, + })), + setSelectedImportDeviceFacility: assign((_, event) => ({ + selectedFacility: event.value.selectedFacility, + importDevice: event.value.importDevice, + facilitiesOnDeviceCount: event.value.facilitiesCount, + })), clearSelectedSetupType: assign({ facilityNewOrImport: () => null, }), @@ -475,97 +462,67 @@ export const wizardMachine = createMachine( selectedFacility: () => null, importDeviceId: () => null, }), - setFacilityTypeAndName: assign({ - formalOrNonformal: (_, event) => event.value.selected, - facilityName: (_, event) => event.value.facilityName, - }), - setGuestAccess: assign({ - guestAccess: (_, event) => event.value, - }), - setLearnerCanCreateAccount: assign({ - learnerCanCreateAccount: (_, event) => event.value, - }), - setRequirePassword: assign({ - requirePassword: (_, event) => event.value, - }), - setLodType: assign({ - lodImportOrJoin: (_, event) => event.value.importOrJoin, - importDeviceId: (_, event) => event.value.importDeviceId, - }), - setLodImportDeviceId: assign({ - importDeviceId: (_, event) => event.value, - }), - addImportedUser: assign({ - importedUsers: (ctx, event) => { - const users = ctx.importedUsers; - users.push(event.value); - return uniq(users); - }, - }), - setFirstLodUser: assign({ - firstImportedLodUser: (_, event) => event.value, - }), - setLodAdmin: assign({ - // Used when setting the Admin user for multiple import - lodAdmin: (_, event) => { - return { - username: event.value.adminUsername, - password: event.value.adminPassword, - id: event.value.id, - }; + setFacilityTypeAndName: assign((_, event) => ({ + formalOrNonformal: event.value.selected, + facilityName: event.value.facilityName, + })), + setGuestAccess: assign((_, event) => ({ + guestAccess: event.value, + })), + setLearnerCanCreateAccount: assign((_, event) => ({ + learnerCanCreateAccount: event.value, + })), + setRequirePassword: assign((_, event) => ({ + requirePassword: event.value, + })), + setLodType: assign((_, event) => ({ + lodImportOrJoin: event.value.importOrJoin, + importDeviceId: event.value.importDeviceId, + })), + addImportedUser: assign((ctx, event) => ({ + importedUsers: uniq([...ctx.importedUsers, event.value]), + })), + setFirstLodUser: assign((_, event) => ({ + firstImportedLodUser: event.value, + })), + setLodAdmin: assign((_, event) => ({ + lodAdmin: { + username: event.value.adminUsername, + password: event.value.adminPassword, + id: event.value.id, }, - }), - setLodSuperAdmin: assign({ + })), + setLodSuperAdmin: assign((ctx, event) => ({ // Sets the super admin to be set as the device super admin -- the first LOD user imported - superuser: (ctx, event) => { - if (!ctx.superuser) { - return { - username: event.value.username, - password: event.value.password, - }; - } else { - return ctx.superuser; - } + superuser: ctx.superuser || { + username: event.value.username, + password: event.value.password, }, - }), - setRemoteUsers: assign({ - remoteUsers: (_, event) => event.value.users, - }), + })), + setRemoteUsers: assign((_, event) => ({ + remoteUsers: event.value.users, + })), /** * Assigns the machine to have the initial context again while maintaining the value of * canGetOsUser. * This effectively resets the machine's state */ - resetContext: assign(initialContext), + resetContext: assign(() => initialContext), setImportedFacility: assign({ - isImportedFacility: () => { - return true; - }, + isImportedFacility: () => true, }), }, guards: { // Functions used to return a true/false value. When the functions are called, they are passed // the current value of the machine's context as the only parameter - isOnMyOwnOrGroup: context => { - return context.onMyOwnOrGroup === Presets.PERSONAL; - }, - isGroupSetup: context => { - return context.onMyOwnOrGroup === UsePresets.GROUP; - }, + isOnMyOwnOrGroup: context => context.onMyOwnOrGroup === Presets.PERSONAL, + isGroupSetup: context => context.onMyOwnOrGroup === UsePresets.GROUP, canGetOsUser: () => checkCapability('get_os_user'), - isNewFacility: context => { - return context.facilityNewOrImport === FacilityTypePresets.NEW; - }, - isImportFacility: context => { - return context.facilityNewOrImport === FacilityTypePresets.IMPORT; - }, - isLodSetup: context => { - return context.fullOrLOD === DeviceTypePresets.LOD; - }, - isFullSetup: context => { - return context.fullOrLOD === DeviceTypePresets.FULL; - }, + isNewFacility: context => context.facilityNewOrImport === FacilityTypePresets.NEW, + isImportFacility: context => context.facilityNewOrImport === FacilityTypePresets.IMPORT, + isLodSetup: context => context.fullOrLOD === DeviceTypePresets.LOD, + isFullSetup: context => context.fullOrLOD === DeviceTypePresets.FULL, }, }, ); diff --git a/kolibri/plugins/user_profile/assets/src/machines/changeFacilityMachine.js b/kolibri/plugins/user_profile/assets/src/machines/changeFacilityMachine.js index 98c2846a04c..c35db97be77 100644 --- a/kolibri/plugins/user_profile/assets/src/machines/changeFacilityMachine.js +++ b/kolibri/plugins/user_profile/assets/src/machines/changeFacilityMachine.js @@ -1,4 +1,4 @@ -import { createMachine, assign, send } from 'xstate'; +import { createMachine, assign, raise, fromPromise } from 'xstate'; import { FacilityUserResource } from 'kolibri.resources'; import { default as remoteFacilityUserData, @@ -25,8 +25,10 @@ SELECTFACILITY event example: } } */ -const setInitialContext = assign((_, event) => { - return { + +/* eslint-disable no-unused-vars */ +const actions = { + setInitialContext: assign(({ _, event }) => ({ sourceFacility: event.value.facility, username: event.value.username, fullname: event.value.fullname, @@ -36,58 +38,88 @@ const setInitialContext = assign((_, event) => { username: event.value.username, password: '', }, - }; -}); - -const connectToTargetKolibri = (context, event) => { - const facility = event.value; - return remoteFacilityUsers(facility.url, facility.id, context.username).then(users => { - return { facility: facility, ...users }; - }); + })), + resetMachineContext: assign(() => generateMachineContext()), + pushHistoryItem: assign({ + history: (context, event) => { + // Push a custom value if provided in the event, otherwise push the state name + const valueToPush = event.value || context.currentState; + return [...context.history, valueToPush]; + }, + }), + removeLastHistoryItem: assign({ + history: context => context.history.slice(0, -1), + }), + checkExists: assign((context, event) => { + const filtered = event.data.users.filter(user => user.username === context.username); + const exists = filtered.length > 0; + if (!exists) { + return { accountExists: false }; + } + return { + accountExists: true, + targetFacility: event.data.facility, + targetAccount: filtered[0], + }; + }), + setMerging: assign({ + isMerging: () => true, + }), }; -const getUserWPasswordInfo = context => { - const facility = context.targetFacility; - return remoteFacilityUserData(facility.url, facility.id, context.username, null).then( - user => user, - ); +const services = { + connectToTargetKolibri: fromPromise({ + async input(args) { + return { + context: args.context, + event: args.event, + }; + }, + async run({ input }) { + const facility = input.event.value; + const users = await remoteFacilityUsers(facility.url, facility.id, input.context.username); + return { facility, ...users }; + }, + }), + getUserWPasswordInfo: fromPromise({ + async input(args) { + return { + context: args.context, + event: args.event, + }; + }, + async run({ input }) { + const { context } = input; + const facility = context.targetFacility; + const userData = await remoteFacilityUserData( + facility.url, + facility.id, + context.username, + null, + ); + return userData; + }, + }), }; -const checkExists = assign((context, event) => { - const filtered = event.data.users.filter(user => user.username === context.username); - const exists = filtered.length > 0; - if (!exists) { - return { accountExists: false }; - } - return { - accountExists: true, - targetFacility: event.data.facility, - targetAccount: filtered[0], - }; -}); - const setCurrentUserAsSuperAdmin = assign({ setAsSuperAdmin: () => true, }); const setNewSuperAdminId = assign({ - newSuperAdminId: (_, event) => event.value, + newSuperAdminId: ({ _, event }) => event.value, }); const setTargetAccount = assign({ - targetAccount: (_, event) => event.value, + targetAccount: ({ _, event }) => event.value, }); const setTargetAccountPassword = assign({ - targetAccount: (_, event) => event.value, -}); - -const setMerging = assign({ - isMerging: () => true, + targetAccount: ({ _, event }) => event.value, }); const setSourceFacilityUsers = assign({ - sourceFacilityUsers: (_, event) => event.data, + sourceFacilityUsers: ({ _, event }) => event.data, }); const clearSourceFacilityUsers = assign({ @@ -95,19 +127,12 @@ const clearSourceFacilityUsers = assign({ }); const setTaskId = assign({ - taskId: (_, event) => event.value.task_id, + taskId: ({ _, event }) => event.value.task_id, }); const resetTaskId = assign({ taskId: () => null, }); -const resetMachineContext = assign(() => { - return generateMachineContext(); -}); - -const pushHistoryItem = assign({ - history: (context, event) => [...context.history, event.value], -}); const removeLastHistoryItem = assign({ history: context => { @@ -148,7 +173,7 @@ const generateMachineContext = () => { // save rather internal transitions here. // // Each state can save itself explicitly to history by calling - // `send({ type: 'PUSH_HISTORY', value: })` action, + // `raise({ type: 'PUSH_HISTORY', value: })` action, // typically this would happen when moving forward to another // state, e.g. in `CONTINUE` transition or similar. This explicitness // is intentional to give us more control; for example, we don’t want @@ -168,7 +193,7 @@ const states = { on: { RESET: { target: 'selectFacility', - actions: [resetMachineContext], + actions: [actions.resetMachineContext], }, }, }, @@ -177,10 +202,10 @@ const states = { on: { CONTINUE: { target: 'selectFacility', - cond: context => !!context.sourceFacility && !!context.username, - actions: [send({ type: 'PUSH_HISTORY', value: 'profile' })], + guard: ({ context }) => !!context.sourceFacility && !!context.username, + actions: [raise({ type: 'PUSH_HISTORY', value: 'profile' })], }, - SETCONTEXT: { actions: setInitialContext }, + SETCONTEXT: { actions: actions.setInitialContext }, }, }, selectFacility: { @@ -188,13 +213,13 @@ const states = { on: { CONTINUE: { target: 'changeFacility', - cond: context => Object.keys(context.targetFacility).length > 0, - actions: [send({ type: 'PUSH_HISTORY', value: 'selectFacility' })], + guard: ({ context }) => Object.keys(context.targetFacility).length > 0, + actions: [raise({ type: 'PUSH_HISTORY', value: 'selectFacility' })], }, - SETCONTEXT: { actions: setInitialContext }, + SETCONTEXT: { actions: actions.setInitialContext }, SELECTFACILITY: { actions: assign({ - targetFacility: (_, event) => { + targetFacility: ({ _, event }) => { return event.value; }, }), @@ -205,10 +230,10 @@ const states = { getFacilityUsernames: { invoke: { id: 'getUserNames', - src: (context, event) => connectToTargetKolibri(context, event), + src: services.connectToTargetKolibri, onDone: { target: 'selectFacility', - actions: checkExists, + actions: actions.checkExists, }, onError: { target: 'selectFacility', @@ -220,22 +245,22 @@ const states = { on: { MERGE: { target: 'checkUsernameExists', - actions: [setMerging, send({ type: 'PUSH_HISTORY', value: 'changeFacility' })], + actions: [actions.setMerging, raise({ type: 'PUSH_HISTORY', value: 'changeFacility' })], }, CONTINUE: { target: 'checkUsernameExists', - actions: [send({ type: 'PUSH_HISTORY', value: 'changeFacility' })], + actions: [raise({ type: 'PUSH_HISTORY', value: 'changeFacility' })], }, }, }, checkUsernameExists: { always: [ { - cond: context => !!context.accountExists, + guard: ({ context }) => !!context.accountExists, target: 'usernameExists', }, { - cond: context => context.isMerging, + guard: ({ context }) => context.isMerging, target: 'mergeAccounts', }, { @@ -247,20 +272,21 @@ const states = { meta: { route: 'CONFIRM_ACCOUNT_USERNAME', path: '/change_facility' }, on: { NEW: { - cond: context => context.targetFacility && context.targetFacility.learner_can_sign_up, + guard: ({ context }) => + context.targetFacility && context.targetFacility.learner_can_sign_up, target: 'createAccount', - actions: [send({ type: 'PUSH_HISTORY', value: 'confirmAccountUsername' })], + actions: [raise({ type: 'PUSH_HISTORY', value: 'confirmAccountUsername' })], }, CONTINUE: { target: 'doesTargetRequirePassword', - actions: [send({ type: 'PUSH_HISTORY', value: 'confirmAccountUsername' })], + actions: [raise({ type: 'PUSH_HISTORY', value: 'confirmAccountUsername' })], }, }, }, doesTargetRequirePassword: { always: [ { - cond: context => context.targetFacility.learner_can_login_with_no_password === false, + guard: ({ context }) => context.targetFacility.learner_can_login_with_no_password === false, target: 'provideTargetAccountPassword', }, { @@ -275,7 +301,7 @@ const states = { target: 'isAdmin', actions: [ setTargetAccountPassword, - send({ type: 'PUSH_HISTORY', value: 'provideTargetAccountPassword' }), + raise({ type: 'PUSH_HISTORY', value: 'provideTargetAccountPassword' }), ], }, }, @@ -283,7 +309,7 @@ const states = { isAdmin: { always: [ { - cond: context => context.role === 'superuser', + guard: ({ context }) => context.role === 'superuser', target: 'fetchSourceFacilityUsers', }, { @@ -293,14 +319,12 @@ const states = { }, fetchSourceFacilityUsers: { invoke: { - src: context => { - return FacilityUserResource.fetchCollection({ + src: fromPromise(({ context }) => + FacilityUserResource.fetchCollection({ getParams: { member_of: context.sourceFacility }, force: true, - }).then(users => { - return users; - }); - }, + }), + ), onDone: { target: 'checkFacilityHasNoMoreUsers', actions: [setSourceFacilityUsers], @@ -313,7 +337,7 @@ const states = { checkFacilityHasNoMoreUsers: { always: [ { - cond: context => context.sourceFacilityUsers.length == 1, + guard: ({ context }) => context.sourceFacilityUsers.length == 1, target: 'setCurrentUserToSuperAdmin', }, { @@ -332,7 +356,7 @@ const states = { checkNeedsNewSuperAdmin: { always: [ { - cond: context => { + guard: ({ context }) => { const facilityHasAnotherSuperUser = context.sourceFacilityUsers.length > 0 && context.sourceFacilityUsers.find(u => u.id !== context.userId && u.is_superuser); @@ -350,11 +374,11 @@ const states = { on: { CONTINUE: { target: 'checkIsMerging', - cond: context => !!context.newSuperAdminId, + guard: ({ context }) => !!context.newSuperAdminId, // clear source facility users data as soon as it's not needed // anymore to prevent high memory consumption as there could // be many users - actions: [clearSourceFacilityUsers, send({ type: 'PUSH_HISTORY', value: 'chooseAdmin' })], + actions: [clearSourceFacilityUsers, raise({ type: 'PUSH_HISTORY', value: 'chooseAdmin' })], }, SELECTNEWSUPERADMIN: { actions: setNewSuperAdminId }, }, @@ -362,7 +386,7 @@ const states = { checkIsMerging: { always: [ { - cond: context => context.isMerging, + guard: ({ context }) => context.isMerging, target: 'confirmMerge', }, { @@ -375,7 +399,7 @@ const states = { on: { CONTINUE: { target: 'syncChangeFacility', - actions: [send({ type: 'PUSH_HISTORY', value: 'confirmMerge' })], + actions: [raise({ type: 'PUSH_HISTORY', value: 'confirmMerge' })], }, }, }, @@ -395,7 +419,7 @@ const states = { on: { CONTINUE: { target: 'isAdmin', - actions: [setTargetAccount, send({ type: 'PUSH_HISTORY', value: 'createAccount' })], + actions: [setTargetAccount, raise({ type: 'PUSH_HISTORY', value: 'createAccount' })], }, }, }, @@ -404,21 +428,21 @@ const states = { on: { MERGE: [ { - cond: context => + guard: ({ context }) => context.accountExists && !context.targetFacility.learner_can_login_with_no_password, target: 'requireAccountCreds', - actions: [send({ type: 'PUSH_HISTORY', value: 'usernameExists' })], + actions: [raise({ type: 'PUSH_HISTORY', value: 'usernameExists' })], }, { - cond: context => + guard: ({ context }) => context.accountExists && context.targetFacility.learner_can_login_with_no_password, target: 'getUserWithoutPasswordInfo', - actions: [send({ type: 'PUSH_HISTORY', value: 'usernameExists' })], + actions: [raise({ type: 'PUSH_HISTORY', value: 'usernameExists' })], }, ], NEW: { target: 'createAccount', - actions: [send({ type: 'PUSH_HISTORY', value: 'usernameExists' })], + actions: [raise({ type: 'PUSH_HISTORY', value: 'usernameExists' })], }, }, }, @@ -427,11 +451,11 @@ const states = { on: { CONTINUE: { target: 'confirmAccountDetails', - actions: [setTargetAccount, send({ type: 'PUSH_HISTORY', value: 'requireAccountCreds' })], + actions: [setTargetAccount, raise({ type: 'PUSH_HISTORY', value: 'requireAccountCreds' })], }, USEADMIN: { target: 'useAdminPassword', - actions: [send({ type: 'PUSH_HISTORY', value: 'requireAccountCreds' })], + actions: [raise({ type: 'PUSH_HISTORY', value: 'requireAccountCreds' })], }, }, }, @@ -440,7 +464,7 @@ const states = { on: { CONTINUE: { target: 'confirmAccountDetails', - actions: [setTargetAccount, send({ type: 'PUSH_HISTORY', value: 'useAdminPassword' })], + actions: [setTargetAccount, raise({ type: 'PUSH_HISTORY', value: 'useAdminPassword' })], }, }, }, @@ -449,7 +473,7 @@ const states = { on: { CONTINUE: { target: 'isAdmin', - actions: [send({ type: 'PUSH_HISTORY', value: 'confirmAccountDetails' })], + actions: [raise({ type: 'PUSH_HISTORY', value: 'confirmAccountDetails' })], }, }, }, @@ -458,7 +482,7 @@ const states = { on: { CONTINUE: { target: 'requireAccountCreds', - actions: [setTargetAccount, send({ type: 'PUSH_HISTORY', value: 'mergeAccounts' })], + actions: [setTargetAccount, raise({ type: 'PUSH_HISTORY', value: 'mergeAccounts' })], }, }, }, @@ -466,11 +490,11 @@ const states = { getUserWithoutPasswordInfo: { invoke: { id: 'getPasswordlessUserInfo', - src: (context, event) => getUserWPasswordInfo(context, event), + src: services.getUserWPasswordInfo, onDone: { target: 'confirmAccountDetails', actions: assign({ - targetAccount: (_, event) => { + targetAccount: ({ _, event }) => { return event.data; }, }), @@ -482,38 +506,43 @@ const states = { }, }; -export const changeFacilityMachine = createMachine({ - id: 'machine', - initial: 'selectFacility', - predictableActionArguments: true, - context: generateMachineContext(), - on: { - PUSH_HISTORY: { - actions: [pushHistoryItem], +export const changeFacilityMachine = createMachine( + { + id: 'machine', + initial: 'selectFacility', + context: generateMachineContext(), + on: { + PUSH_HISTORY: { + actions: [actions.pushHistoryItem], + }, + // Inspired by https://github.com/statelyai/xstate/discussions/1939 + // Generates + // BACK: [ + // { target: 'state-1', + // guard: context.history[context.history.length - 1] === 'state-1', + // actions: [removeLastHistoryItem] + // }, + // { target: 'state-2', + // guard: context.history[context.history.length - 1] === 'state-2', + // actions: [removeLastHistoryItem] + // }, + // ... + // ] + // to keep things DRY, however when compared to defining transitions explicitly, + // has some disadvantages, e.g. worse state visualization. + BACK: Object.keys(states).map(state => { + return { + target: state, + guard: ({ context }) => + context.history.length && context.history[context.history.length - 1] === state, + actions: [removeLastHistoryItem], + }; + }), }, - // Inspired by https://github.com/statelyai/xstate/discussions/1939 - // Generates - // BACK: [ - // { target: 'state-1', - // cond: context.history[context.history.length - 1] === 'state-1', - // actions: [removeLastHistoryItem] - // }, - // { target: 'state-2', - // cond: context.history[context.history.length - 1] === 'state-2', - // actions: [removeLastHistoryItem] - // }, - // ... - // ] - // to keep things DRY, however when compared to defining transitions explicitly, - // has some disadvantages, e.g. worse state visualization. - BACK: Object.keys(states).map(state => { - return { - target: state, - cond: context => - context.history.length && context.history[context.history.length - 1] === state, - actions: [removeLastHistoryItem], - }; - }), + states, }, - states, -}); + { + actions, + services, + }, +); diff --git a/yarn.lock b/yarn.lock index 98afecc5c23..3dff945f487 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10690,7 +10690,16 @@ string-length@^4.0.1: char-regex "^1.0.2" strip-ansi "^6.0.0" -"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0": + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -10777,7 +10786,14 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@6.0.1, strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-ansi@6.0.1, strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -12125,7 +12141,16 @@ word-wrap@^1.2.5: resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -12207,6 +12232,11 @@ xstate@4.38.3, xstate@^4.38.3: resolved "https://registry.yarnpkg.com/xstate/-/xstate-4.38.3.tgz#4e15e7ad3aa0ca1eea2010548a5379966d8f1075" integrity sha512-SH7nAaaPQx57dx6qvfcIgqKRXIh4L0A1iYEqim4s1u7c9VoCgzZc+63FY90AKU4ZzOC2cfJzTnpO4zK7fCUzzw== +xstate@^5.18.2: + version "5.18.2" + resolved "https://registry.yarnpkg.com/xstate/-/xstate-5.18.2.tgz#924122af5102f3c3f7e172ebf20a09455ddb2963" + integrity sha512-hab5VOe29D0agy8/7dH1lGw+7kilRQyXwpaChoMu4fe6rDP+nsHYhDYKfS2O4iXE7myA98TW6qMEudj/8NXEkA== + y18n@^5.0.5: version "5.0.8" resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" From 3a283cd0827019ea120bc1a17981ec5b6c009569 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20L=2E=20Redrejo=20Rodr=C3=ADguez?= Date: Mon, 4 Nov 2024 17:03:28 +0100 Subject: [PATCH 2/2] Vue components updated to xstate v5 --- .../assets/src/views/SetupWizardIndex.vue | 6 +++--- .../assets/src/views/ChangeFacility/index.vue | 20 +++++++++---------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/kolibri/plugins/setup_wizard/assets/src/views/SetupWizardIndex.vue b/kolibri/plugins/setup_wizard/assets/src/views/SetupWizardIndex.vue index 095d46e5fe8..019983b40c2 100644 --- a/kolibri/plugins/setup_wizard/assets/src/views/SetupWizardIndex.vue +++ b/kolibri/plugins/setup_wizard/assets/src/views/SetupWizardIndex.vue @@ -23,7 +23,7 @@