mirror of
https://github.com/ajnart/homarr.git
synced 2025-11-09 23:15:46 +01:00
🐛 Fix issue with creation of app / widget
This commit is contained in:
@@ -14,6 +14,7 @@ import { useState } from 'react';
|
||||
import { useConfigContext } from '../../../../config/provider';
|
||||
import { useConfigStore } from '../../../../config/store';
|
||||
import { AppType } from '../../../../types/app';
|
||||
import { useEditModeStore } from '../../Views/useEditModeStore';
|
||||
import { AppearanceTab } from './Tabs/AppereanceTab/AppereanceTab';
|
||||
import { BehaviourTab } from './Tabs/BehaviourTab/BehaviourTab';
|
||||
import { GeneralTab } from './Tabs/GeneralTab/GeneralTab';
|
||||
@@ -33,6 +34,7 @@ export const EditAppModal = ({
|
||||
const { t } = useTranslation(['layout/modals/add-app', 'common']);
|
||||
const { name: configName, config } = useConfigContext();
|
||||
const updateConfig = useConfigStore((store) => store.updateConfig);
|
||||
const { enabled: isEditMode } = useEditModeStore();
|
||||
const [allowAppNamePropagation, setAllowAppNamePropagation] = useState<boolean>(
|
||||
innerProps.allowAppNamePropagation
|
||||
);
|
||||
@@ -87,9 +89,15 @@ export const EditAppModal = ({
|
||||
configName,
|
||||
(previousConfig) => ({
|
||||
...previousConfig,
|
||||
apps: [...previousConfig.apps.filter((x) => x.id !== form.values.id), form.values],
|
||||
apps: [
|
||||
...previousConfig.apps.filter((x) => x.id !== values.id),
|
||||
{
|
||||
...values,
|
||||
},
|
||||
],
|
||||
}),
|
||||
true
|
||||
true,
|
||||
!isEditMode
|
||||
);
|
||||
|
||||
// also close the parent modal
|
||||
|
||||
@@ -3,8 +3,10 @@ import { IconBox, IconStack } from '@tabler/icons';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { ReactNode } from 'react';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { useConfigContext } from '../../../../../../config/provider';
|
||||
import { openContextModalGeneric } from '../../../../../../tools/mantineModalManagerExtensions';
|
||||
import { AppType } from '../../../../../../types/app';
|
||||
import { appTileDefinition } from '../../../../Tiles/Apps/AppTile';
|
||||
import { useStyles } from '../Shared/styles';
|
||||
|
||||
interface AvailableElementTypesProps {
|
||||
@@ -17,6 +19,8 @@ export const AvailableElementTypes = ({
|
||||
onOpenStaticElements,
|
||||
}: AvailableElementTypesProps) => {
|
||||
const { t } = useTranslation('layout/element-selector/selector');
|
||||
const { config } = useConfigContext();
|
||||
const getLowestWrapper = () => config?.wrappers.sort((a, b) => a.position - b.position)[0];
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -45,10 +49,11 @@ export const AvailableElementTypes = ({
|
||||
isOpeningNewTab: true,
|
||||
externalUrl: '',
|
||||
},
|
||||
|
||||
area: {
|
||||
type: 'sidebar',
|
||||
type: 'wrapper',
|
||||
properties: {
|
||||
location: 'right',
|
||||
id: getLowestWrapper()?.id ?? '',
|
||||
},
|
||||
},
|
||||
shape: {
|
||||
@@ -57,8 +62,8 @@ export const AvailableElementTypes = ({
|
||||
y: 0,
|
||||
},
|
||||
size: {
|
||||
height: 1,
|
||||
width: 1,
|
||||
width: appTileDefinition.minWidth,
|
||||
height: appTileDefinition.minHeight,
|
||||
},
|
||||
},
|
||||
integration: {
|
||||
|
||||
@@ -4,6 +4,7 @@ import { useTranslation } from 'next-i18next';
|
||||
import { useConfigContext } from '../../../../../../config/provider';
|
||||
import { useConfigStore } from '../../../../../../config/store';
|
||||
import { IWidget, IWidgetDefinition } from '../../../../../../widgets/widgets';
|
||||
import { useEditModeStore } from '../../../../Views/useEditModeStore';
|
||||
import { GenericAvailableElementType } from '../Shared/GenericElementType';
|
||||
|
||||
interface WidgetElementTypeProps {
|
||||
@@ -18,6 +19,7 @@ export const WidgetElementType = ({ id, image, disabled, widget }: WidgetElement
|
||||
const { t } = useTranslation(`modules/${id}`);
|
||||
const { name: configName, config } = useConfigContext();
|
||||
const updateConfig = useConfigStore((x) => x.updateConfig);
|
||||
const isEditMode = useEditModeStore((x) => x.enabled);
|
||||
|
||||
if (!configName) return null;
|
||||
|
||||
@@ -56,9 +58,10 @@ export const WidgetElementType = ({ id, image, disabled, widget }: WidgetElement
|
||||
},
|
||||
],
|
||||
}),
|
||||
true
|
||||
true,
|
||||
!isEditMode
|
||||
);
|
||||
// TODO: safe to file system
|
||||
|
||||
closeModal('selectElement');
|
||||
};
|
||||
|
||||
|
||||
@@ -88,3 +88,11 @@ const useStyles = createStyles((theme, _params, getRef) => ({
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
export const appTileDefinition = {
|
||||
component: AppTile,
|
||||
minWidth: 2,
|
||||
maxWidth: 12,
|
||||
minHeight: 2,
|
||||
maxHeight: 12,
|
||||
};
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
import calendarDefinition from '../../../widgets/calendar/CalendarTile';
|
||||
import clockDefinition from '../../../widgets/date/DateTile';
|
||||
import dashDotDefinition from '../../../widgets/dashDot/DashDotTile';
|
||||
import useNetDefinition from '../../../widgets/useNet/UseNetTile';
|
||||
import weatherDefinition from '../../../widgets/weather/WeatherTile';
|
||||
import { EmptyTile } from './EmptyTile';
|
||||
import { AppTile } from './Apps/AppTile';
|
||||
|
||||
// TODO: just remove and use app (later app) directly. For widgets the the definition should contain min/max width/height
|
||||
type TileDefinitionProps = {
|
||||
[key in keyof any | 'app']: {
|
||||
minWidth?: number;
|
||||
minHeight?: number;
|
||||
maxWidth?: number;
|
||||
maxHeight?: number;
|
||||
component: React.ElementType;
|
||||
};
|
||||
};
|
||||
|
||||
export const Tiles: TileDefinitionProps = {
|
||||
app: {
|
||||
component: AppTile,
|
||||
minWidth: 2,
|
||||
maxWidth: 12,
|
||||
minHeight: 2,
|
||||
maxHeight: 12,
|
||||
},
|
||||
bitTorrent: {
|
||||
component: EmptyTile,
|
||||
minWidth: 4,
|
||||
maxWidth: 12,
|
||||
minHeight: 5,
|
||||
maxHeight: 12,
|
||||
},
|
||||
calendar: {
|
||||
component: calendarDefinition.component,
|
||||
minWidth: 4,
|
||||
maxWidth: 12,
|
||||
minHeight: 5,
|
||||
maxHeight: 12,
|
||||
},
|
||||
clock: {
|
||||
component: clockDefinition.component,
|
||||
minWidth: 4,
|
||||
maxWidth: 12,
|
||||
minHeight: 2,
|
||||
maxHeight: 12,
|
||||
},
|
||||
dashDot: {
|
||||
component: dashDotDefinition.component,
|
||||
minWidth: 4,
|
||||
maxWidth: 9,
|
||||
minHeight: 5,
|
||||
maxHeight: 14,
|
||||
},
|
||||
torrentNetworkTraffic: {
|
||||
component: EmptyTile,
|
||||
minWidth: 4,
|
||||
maxWidth: 12,
|
||||
minHeight: 5,
|
||||
maxHeight: 12,
|
||||
},
|
||||
useNet: {
|
||||
component: useNetDefinition.component,
|
||||
minWidth: 4,
|
||||
maxWidth: 12,
|
||||
minHeight: 5,
|
||||
maxHeight: 12,
|
||||
},
|
||||
weather: {
|
||||
component: weatherDefinition.component,
|
||||
minWidth: 4,
|
||||
maxWidth: 12,
|
||||
minHeight: 2,
|
||||
maxHeight: 12,
|
||||
},
|
||||
};
|
||||
@@ -4,7 +4,7 @@ import { AppType } from '../../../types/app';
|
||||
import Widgets from '../../../widgets';
|
||||
import { IWidget, IWidgetDefinition } from '../../../widgets/widgets';
|
||||
import { WidgetWrapper } from '../../../widgets/WidgetWrapper';
|
||||
import { Tiles } from '../Tiles/tilesDefinitions';
|
||||
import { appTileDefinition } from '../Tiles/Apps/AppTile';
|
||||
import { GridstackTileWrapper } from '../Tiles/TileWrapper';
|
||||
|
||||
interface WrapperContentProps {
|
||||
@@ -21,7 +21,7 @@ export function WrapperContent({ apps, refs, widgets }: WrapperContentProps) {
|
||||
return (
|
||||
<>
|
||||
{apps?.map((app) => {
|
||||
const { component: TileComponent, ...tile } = Tiles.app;
|
||||
const { component: TileComponent, ...tile } = appTileDefinition;
|
||||
return (
|
||||
<GridstackTileWrapper
|
||||
id={app.id}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import axios from 'axios';
|
||||
import create from 'zustand';
|
||||
import { ConfigType } from '../types/config';
|
||||
|
||||
@@ -15,7 +16,8 @@ export const useConfigStore = create<UseConfigStoreType>((set, get) => ({
|
||||
updateConfig: async (
|
||||
name,
|
||||
updateCallback: (previous: ConfigType) => ConfigType,
|
||||
shouldRegenerateGridstack = false
|
||||
shouldRegenerateGridstack = false,
|
||||
shouldSaveConfigToFileSystem = false
|
||||
) => {
|
||||
const { configs } = get();
|
||||
const currentConfig = configs.find((x) => x.value.configProperties.name === name);
|
||||
@@ -23,7 +25,6 @@ export const useConfigStore = create<UseConfigStoreType>((set, get) => ({
|
||||
// copies the value of currentConfig and creates a non reference object named previousConfig
|
||||
const previousConfig: ConfigType = JSON.parse(JSON.stringify(currentConfig.value));
|
||||
|
||||
// TODO: update config on server
|
||||
const updatedConfig = updateCallback(currentConfig.value);
|
||||
set((old) => ({
|
||||
...old,
|
||||
@@ -40,6 +41,10 @@ export const useConfigStore = create<UseConfigStoreType>((set, get) => ({
|
||||
) {
|
||||
currentConfig.increaseVersion();
|
||||
}
|
||||
|
||||
if (shouldSaveConfigToFileSystem) {
|
||||
axios.put(`/api/configs/${name}`, { ...updatedConfig });
|
||||
}
|
||||
},
|
||||
}));
|
||||
|
||||
@@ -51,6 +56,7 @@ interface UseConfigStoreType {
|
||||
updateCallback: (previous: ConfigType) => ConfigType,
|
||||
shouldRegenerateGridstace?:
|
||||
| boolean
|
||||
| ((previousConfig: ConfigType, currentConfig: ConfigType) => boolean)
|
||||
| ((previousConfig: ConfigType, currentConfig: ConfigType) => boolean),
|
||||
shouldSaveConfigToFileSystem?: boolean
|
||||
) => Promise<void>;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"forceConsistentCasingInFileNames": false,
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
|
||||
Reference in New Issue
Block a user