Skip to content

Commit

Permalink
Add possibility to add, disabled/enable profiles. State can now handl…
Browse files Browse the repository at this point in the history
…e any amount of profiles
  • Loading branch information
Pelsin committed Sep 26, 2024
1 parent 8d85ff6 commit 569ac89
Show file tree
Hide file tree
Showing 4 changed files with 120 additions and 66 deletions.
1 change: 0 additions & 1 deletion www/server/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -390,7 +390,6 @@ app.get('/api/getProfileOptions', (req, res) => {
alternativePinMappings: [
createPinMappings({ profileLabel: 'Profile 2' }),
createPinMappings({ profileLabel: 'Profile 3' }),
createPinMappings({ profileLabel: 'Profile 4' }),
],
});
});
Expand Down
4 changes: 4 additions & 0 deletions www/src/Locales/en/PinMapping.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ export default {
'Max 16 characters. Letters, numbers, and spaces allowed.',
'profile-pin-mapping-title': '{{profileLabel}} - Pin Mapping',
'profile-label-default': 'Profile {{profileNumber}}',
'profile-add-button': '+ Add Profile',
'profile-disabled': 'Disabled',
'profile-enabled-tooltip':
'Disabled profiles will not be available when using hotkeys to change profile.',
'profile-pins-warning':
'Try to avoid changing the buttons and/or directions used for the switch profile hotkeys. Otherwise, it will be difficult to understand what profile is being selected!',
'profile-copy-base': 'Copy base profile',
Expand Down
100 changes: 83 additions & 17 deletions www/src/Pages/PinMapping.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,24 @@ import React, {
} from 'react';
import { NavLink } from 'react-router-dom';
import { useShallow } from 'zustand/react/shallow';
import { Alert, Button, Col, Form, Nav, Row, Tab } from 'react-bootstrap';
import {
Alert,
Button,
Col,
Form,
FormCheck,
Nav,
OverlayTrigger,
Row,
Tab,
Tooltip,
} from 'react-bootstrap';
import { Trans, useTranslation } from 'react-i18next';
import invert from 'lodash/invert';
import omit from 'lodash/omit';

import { AppContext } from '../Contexts/AppContext';
import useProfilesStore from '../Store/useProfilesStore';
import useProfilesStore, { MAX_PROFILES } from '../Store/useProfilesStore';

import Section from '../Components/Section';
import CustomSelect from '../Components/CustomSelect';
Expand All @@ -23,6 +34,7 @@ import { BUTTON_MASKS, DPAD_MASKS, getButtonLabels } from '../Data/Buttons';
import { BUTTON_ACTIONS, PinActionValues } from '../Data/Pins';
import './PinMapping.scss';
import { MultiValue, SingleValue } from 'react-select';
import InfoCircle from '../Icons/InfoCircle';

type OptionType = {
label: string;
Expand Down Expand Up @@ -141,8 +153,11 @@ const PinSelectList = memo(function PinSelectList({
profileIndex: number;
}) {
const setProfilePin = useProfilesStore((state) => state.setProfilePin);

const pins = useProfilesStore(
useShallow((state) => omit(state.profiles[profileIndex], 'profileLabel')),
useShallow((state) =>
omit(state.profiles[profileIndex], ['profileLabel', 'enabled']),
),
);
const { t } = useTranslation('');
const { buttonLabels } = useContext(AppContext);
Expand Down Expand Up @@ -242,11 +257,18 @@ const PinSection = memo(function PinSection({
const copyBaseProfile = useProfilesStore((state) => state.copyBaseProfile);
const setProfilePin = useProfilesStore((state) => state.setProfilePin);
const saveProfiles = useProfilesStore((state) => state.saveProfiles);
const toggleProfileEnabled = useProfilesStore(
(state) => state.toggleProfileEnabled,
);
const enabled = useProfilesStore(
(state) => state.profiles[profileIndex].enabled,
);
const profileLabel =
useProfilesStore((state) => state.profiles[profileIndex].profileLabel) ||
t('PinMapping:profile-label-default', {
profileNumber: profileIndex + 1,
});

const { updateUsedPins, buttonLabels } = useContext(AppContext);
const { buttonLabelType, swapTpShareLabels } = buttonLabels;
const CURRENT_BUTTONS = getButtonLabels(buttonLabelType, swapTpShareLabels);
Expand Down Expand Up @@ -285,7 +307,36 @@ const PinSection = memo(function PinSection({
})}
>
<Form onSubmit={handleSubmit}>
<ProfileLabel profileIndex={profileIndex} />
<div className="d-flex justify-content-between">
<ProfileLabel profileIndex={profileIndex} />
{profileIndex > 0 && (
<div className="d-flex">
<FormCheck
size={3}
label={
<OverlayTrigger
overlay={
<Tooltip>
{t('PinMapping:profile-enabled-tooltip')}
</Tooltip>
}
>
<div className="d-flex gap-1">
<label>{t('Common:switch-enabled')} </label>
<InfoCircle />
</div>
</OverlayTrigger>
}
type="switch"
reverse
checked={!enabled}
onChange={() => {
toggleProfileEnabled(profileIndex);
}}
/>
</div>
)}
</div>
<hr />
<div className="pin-grid gap-3 mt-3">
<PinSelectList profileIndex={profileIndex} />
Expand Down Expand Up @@ -328,7 +379,10 @@ const PinSection = memo(function PinSection({

export default function PinMapping() {
const fetchProfiles = useProfilesStore((state) => state.fetchProfiles);
const addProfile = useProfilesStore((state) => state.addProfile);
const profiles = useProfilesStore((state) => state.profiles);
const loadingProfiles = useProfilesStore((state) => state.loadingProfiles);

const [pressedPin, setPressedPin] = useState<number | null>(null);
const { t } = useTranslation('');

Expand All @@ -340,17 +394,36 @@ export default function PinMapping() {
<Tab.Container defaultActiveKey="profile-0">
<Row>
<Col sm={2}>
{loadingProfiles && (
<div className="d-flex justify-content-center">
<span className="spinner-border" />
</div>
)}
<Nav variant="pills" className="flex-column">
{profiles.map(({ profileLabel }, index) => (
{profiles.map(({ profileLabel, enabled }, index) => (
<Nav.Item key={`profile-${index}`}>
<Nav.Link eventKey={`profile-${index}`}>
{profileLabel ||
t('PinMapping:profile-label-default', {
profileNumber: index + 1,
})}

{enabled && (
<span>{` - (${t('PinMapping:profile-disabled')}`}</span>
)}
</Nav.Link>
</Nav.Item>
))}
{profiles.length !== MAX_PROFILES && (
<Button
type="button"
className="mt-1"
variant="outline"
onClick={addProfile}
>
{t('PinMapping:profile-add-button')}
</Button>
)}
</Nav>
<hr />
<p className="text-center">{t('PinMapping:sub-header-text')}</p>
Expand All @@ -369,18 +442,11 @@ export default function PinMapping() {
</Col>
<Col sm={10}>
<Tab.Content>
<Tab.Pane eventKey="profile-0">
<PinSection profileIndex={0} />
</Tab.Pane>
<Tab.Pane eventKey="profile-1">
<PinSection profileIndex={1} />
</Tab.Pane>
<Tab.Pane eventKey="profile-2">
<PinSection profileIndex={2} />
</Tab.Pane>
<Tab.Pane eventKey="profile-3">
<PinSection profileIndex={3} />
</Tab.Pane>
{profiles.map((_, index) => (
<Tab.Pane key={`profile-${index}`} eventKey={`profile-${index}`}>
<PinSection profileIndex={index} />
</Tab.Pane>
))}
</Tab.Content>
</Col>
</Row>
Expand Down
81 changes: 33 additions & 48 deletions www/src/Store/useProfilesStore.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { create } from 'zustand';
import WebApi from '../Services/WebApi';
import { BUTTON_ACTIONS, PinActionValues } from '../Data/Pins';
import { PinActionValues } from '../Data/Pins';

// Max number of profiles that can be created, including the base profile
export const MAX_PROFILES = 4;

type CustomMasks = {
customButtonMask: number;
Expand Down Expand Up @@ -43,6 +46,7 @@ export type PinsType = {
pin28: MaskPayload;
pin29: MaskPayload;
profileLabel: string;
enabled: boolean;
};

type State = {
Expand All @@ -57,65 +61,37 @@ export type SetProfilePinType = (
) => void;

type Actions = {
fetchProfiles: () => void;
setProfilePin: SetProfilePinType;
addProfile: () => void;
copyBaseProfile: (profileIndex: number) => void;
setProfileLabel: (profileIndex: number, profileLabel: string) => void;
fetchProfiles: () => void;
saveProfiles: () => Promise<object>;
};

const DEFAULT_PIN_STATE = {
action: BUTTON_ACTIONS.NONE,
customButtonMask: 0,
customDpadMask: 0,
};

const defaultProfilePins: PinsType = {
pin00: DEFAULT_PIN_STATE,
pin01: DEFAULT_PIN_STATE,
pin02: DEFAULT_PIN_STATE,
pin03: DEFAULT_PIN_STATE,
pin04: DEFAULT_PIN_STATE,
pin05: DEFAULT_PIN_STATE,
pin06: DEFAULT_PIN_STATE,
pin07: DEFAULT_PIN_STATE,
pin08: DEFAULT_PIN_STATE,
pin09: DEFAULT_PIN_STATE,
pin10: DEFAULT_PIN_STATE,
pin11: DEFAULT_PIN_STATE,
pin12: DEFAULT_PIN_STATE,
pin13: DEFAULT_PIN_STATE,
pin14: DEFAULT_PIN_STATE,
pin15: DEFAULT_PIN_STATE,
pin16: DEFAULT_PIN_STATE,
pin17: DEFAULT_PIN_STATE,
pin18: DEFAULT_PIN_STATE,
pin19: DEFAULT_PIN_STATE,
pin20: DEFAULT_PIN_STATE,
pin21: DEFAULT_PIN_STATE,
pin22: DEFAULT_PIN_STATE,
pin23: DEFAULT_PIN_STATE,
pin24: DEFAULT_PIN_STATE,
pin25: DEFAULT_PIN_STATE,
pin26: DEFAULT_PIN_STATE,
pin27: DEFAULT_PIN_STATE,
pin28: DEFAULT_PIN_STATE,
pin29: DEFAULT_PIN_STATE,
profileLabel: '',
setProfileLabel: (profileIndex: number, profileLabel: string) => void;
setProfilePin: SetProfilePinType;
toggleProfileEnabled: (profileIndex: number) => void;
};

const INITIAL_STATE: State = {
profiles: [
defaultProfilePins,
defaultProfilePins,
defaultProfilePins,
defaultProfilePins,
// Profiles will be populated dynamically
],
loadingProfiles: false,
};

const useProfilesStore = create<State & Actions>()((set, get) => ({
...INITIAL_STATE,
addProfile: () => {
if (get().profiles.length < MAX_PROFILES) {
set((state) => ({
profiles: [
...state.profiles,
{
...state.profiles[0],
profileLabel: `Profile ${state.profiles.length + 1}`,
},
],
}));
}
},
fetchProfiles: async () => {
set({ loadingProfiles: true });

Expand Down Expand Up @@ -172,6 +148,15 @@ const useProfilesStore = create<State & Actions>()((set, get) => ({
WebApi.setProfileOptions(profiles),
]);
},
toggleProfileEnabled: (profileIndex) =>
set((state) => {
const profiles = [...state.profiles];
profiles[profileIndex] = {
...profiles[profileIndex],
enabled: !profiles[profileIndex].enabled,
};
return { ...state, profiles };
}),
}));

export default useProfilesStore;

0 comments on commit 569ac89

Please sign in to comment.