Compare commits

...

3 Commits

Author SHA1 Message Date
Mary Hipp
f8ccf78af4 lint 2024-09-19 15:35:12 -04:00
Mary Hipp
b91738596a add translations 2024-09-19 15:33:15 -04:00
Mary Hipp
4f592ca79f feat(ui): whats new lightning bulb in side nav with indicator badge 2024-09-19 15:30:28 -04:00
6 changed files with 125 additions and 0 deletions

View File

@@ -2073,5 +2073,16 @@
"metadata": "Metadata"
},
"showSendingToAlerts": "Alert When Sending to Different View"
},
"whatsNew": {
"whatsNewInInvoke": "What's New in Invoke",
"canvasV2Announcement": {
"newCanvas": "A powerful new control canvas",
"newLayerTypes": "New layer types for even more control",
"fluxSupport": "Support for the Flux family of models",
"readReleaseNotes": "Read Release Notes",
"watchReleaseVideo": "Watch Release Video",
"watchUiUpdatesOverview": "Watch UI Updates Overview"
}
}
}

View File

@@ -0,0 +1,36 @@
import { Flex, Icon, ListItem, Text, UnorderedList } from '@invoke-ai/ui-library';
import { useTranslation } from 'react-i18next';
import { PiArrowSquareOutBold } from 'react-icons/pi';
export const CanvasV2Announcement = () => {
const { t } = useTranslation();
return (
<Flex gap={4} flexDir="column">
<UnorderedList fontSize="sm">
<ListItem>{t('whatsNew.canvasV2Announcement.newCanvas')}</ListItem>
<ListItem>{t('whatsNew.canvasV2Announcement.newLayerTypes')}</ListItem>
<ListItem>{t('whatsNew.canvasV2Announcement.fluxSupport')}</ListItem>
</UnorderedList>
<Flex flexDir="column" gap={1}>
<Flex gap={2}>
<Text as="a" target="_blank" href="https://github.com/invoke-ai/InvokeAI/releases" fontWeight="semibold">
{t('whatsNew.canvasV2Announcement.readReleaseNotes')}
</Text>
<Icon as={PiArrowSquareOutBold} />
</Flex>
<Flex gap={2}>
<Text as="a" target="_blank" href="https://www.youtube.com/@invokeai/videos" fontWeight="semibold">
{t('whatsNew.canvasV2Announcement.watchReleaseVideo')}
</Text>
<Icon as={PiArrowSquareOutBold} />
</Flex>
<Flex gap={2}>
<Text as="a" target="_blank" href="https://www.youtube.com/@invokeai/videos" fontWeight="semibold">
{t('whatsNew.canvasV2Announcement.watchUiUpdatesOverview')}
</Text>
<Icon as={PiArrowSquareOutBold} />
</Flex>
</Flex>
</Flex>
);
};

View File

@@ -0,0 +1,67 @@
import {
Box,
Flex,
IconButton,
Image,
Popover,
PopoverBody,
PopoverCloseButton,
PopoverContent,
PopoverHeader,
PopoverTrigger,
} from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { shouldShowNotificationIndicatorChanged } from 'features/ui/store/uiSlice';
import InvokeSymbol from 'public/assets/images/invoke-favicon.png';
import { useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { PiLightbulbFilamentBold } from 'react-icons/pi';
import { CanvasV2Announcement } from './CanvasV2Announcement';
export const Notifications = () => {
const { t } = useTranslation();
const dispatch = useAppDispatch();
const shouldShowNotificationIndicator = useAppSelector((s) => s.ui.shouldShowNotificationIndicator);
const resetIndicator = useCallback(() => {
dispatch(shouldShowNotificationIndicatorChanged(false));
}, [dispatch]);
return (
<Popover onOpen={resetIndicator} placement="top-start">
<PopoverTrigger>
<Flex pos="relative">
<IconButton
aria-label="Notifications"
variant="link"
icon={<PiLightbulbFilamentBold fontSize={20} />}
boxSize={8}
/>
{shouldShowNotificationIndicator && (
<Box
pos="absolute"
top={0}
right="2px"
w={2}
h={2}
backgroundColor="invokeYellow.500"
borderRadius="100%"
/>
)}
</Flex>
</PopoverTrigger>
<PopoverContent>
<PopoverCloseButton />
<PopoverHeader fontSize="md" fontWeight="semibold">
<Flex alignItems="center" gap={3}>
<Image src={InvokeSymbol} boxSize={6} />
{t('whatsNew.whatsNewInInvoke')}
</Flex>
</PopoverHeader>
<PopoverBody p={2}>
<CanvasV2Announcement />
</PopoverBody>
</PopoverContent>
</Popover>
);
};

View File

@@ -9,6 +9,7 @@ import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import { PiBoundingBoxBold, PiCubeBold, PiFlowArrowBold, PiFrameCornersBold, PiQueueBold } from 'react-icons/pi';
import { Notifications } from './Notifications';
import { TabButton } from './TabButton';
export const VerticalNavBar = memo(() => {
@@ -37,6 +38,7 @@ export const VerticalNavBar = memo(() => {
</Flex>
<Spacer />
<StatusIndicator />
<Notifications />
{customNavComponent ? customNavComponent : <SettingsMenu />}
</Flex>
);

View File

@@ -13,6 +13,7 @@ const initialUIState: UIState = {
shouldShowProgressInViewer: true,
accordions: {},
expanders: {},
shouldShowNotificationIndicator: true,
};
export const uiSlice = createSlice({
@@ -36,6 +37,9 @@ export const uiSlice = createSlice({
const { id, isOpen } = action.payload;
state.expanders[id] = isOpen;
},
shouldShowNotificationIndicatorChanged: (state, action: PayloadAction<boolean>) => {
state.shouldShowNotificationIndicator = action.payload;
},
},
extraReducers(builder) {
builder.addCase(workflowLoadRequested, (state) => {
@@ -50,6 +54,7 @@ export const {
setShouldShowProgressInViewer,
accordionStateChanged,
expanderStateChanged,
shouldShowNotificationIndicatorChanged,
} = uiSlice.actions;
export const selectUiSlice = (state: RootState) => state.ui;

View File

@@ -25,4 +25,8 @@ export interface UIState {
* The state of expanders. The key is the id of the expander, and the value is a boolean representing the open state.
*/
expanders: Record<string, boolean>;
/**
* Whether or not to show the user an indicator on notifications icon.
*/
shouldShowNotificationIndicator: boolean;
}