feat(gui): Add a Introduction for new users (#287)
* feat(gui): add IntroductionModal component * feat(gui): add interactivity to IntroductionModal * feat(gui): create SlideTemplate component for IntroductionModal Slides * feat(gui): add generic slides to IntroductionModal with images and content * feat(gui): add Slide with SwapStatusAlert to IntroductionModal * feat(gui): show the introduction only on the first app start * feat(gui): make external links functional * fix(gui): update github link to link to active repo * feat(gui): replace old images with new mockups and update Slide05 content * feat(gui): add CardSelectionGroup and CardSelectionOption components for improved card selection UI * feat(gui): add FiatPricePreference slide to IntroductionModal * feat(gui): save user preference regarding fiat prices I set the initial store configuration for fetching fiat prices to false to avoid any calls to coingecko without user consent * refactor(gui): remove old Slide05 component for improved codebase maintenance * fix(gui): add UnstoppableSwap logo to FiatPricePreference slide * refactor(gui): update image imports and improve slide content for introduction modal * fix(gui): introduce ExternalLink component and update Slide05 to use it for external navigation * fix(gui): replace webp images for introduction with svg mockups for improved quality * fix(gui): change order of introduction slides, to asking for fiat price preference at the end * refactor(gui): implement CardSelectionContext for managing card selection state * refactor: texts in intro modakl * fix(gui): update currency fetching SVG for improved design and clarity * feat(gui): added changelog entry for introduction --------- Co-authored-by: Binarybaron <binarybaron@protonmail.com>
44
src-gui/src/assets/currencyFetching.svg
Normal file
After Width: | Height: | Size: 9.1 KiB |
BIN
src-gui/src/assets/groupWithChatbubbles.png
Normal file
After Width: | Height: | Size: 1.6 MiB |
73
src-gui/src/assets/mockConfigureSwap.svg
Normal file
After Width: | Height: | Size: 57 KiB |
51
src-gui/src/assets/mockHistoryPage.svg
Normal file
After Width: | Height: | Size: 46 KiB |
93
src-gui/src/assets/mockMakerSelection.svg
Normal file
After Width: | Height: | Size: 54 KiB |
75
src-gui/src/assets/simpleSwapFlowDiagram.svg
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
src-gui/src/assets/walletWithBitcoinAndMonero.png
Normal file
After Width: | Height: | Size: 2.2 MiB |
|
@ -15,6 +15,7 @@ import { useEffect } from "react";
|
|||
import { setupBackgroundTasks } from "renderer/background";
|
||||
import "@fontsource/roboto";
|
||||
import FeedbackPage from "./pages/feedback/FeedbackPage";
|
||||
import IntroductionModal from "./modal/introduction/IntroductionModal";
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
innerContent: {
|
||||
|
@ -36,6 +37,7 @@ export default function App() {
|
|||
<ThemeProvider theme={themes[theme]}>
|
||||
<GlobalSnackbarProvider>
|
||||
<CssBaseline />
|
||||
<IntroductionModal/>
|
||||
<Router>
|
||||
<Navigation />
|
||||
<InnerContent />
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
import { createContext, useContext, useState, ReactNode } from 'react'
|
||||
|
||||
interface CardSelectionContextType {
|
||||
selectedValue: string
|
||||
setSelectedValue: (value: string) => void
|
||||
}
|
||||
|
||||
const CardSelectionContext = createContext<CardSelectionContextType | undefined>(undefined)
|
||||
|
||||
export function CardSelectionProvider({
|
||||
children,
|
||||
initialValue,
|
||||
onChange
|
||||
}: {
|
||||
children: ReactNode
|
||||
initialValue: string
|
||||
onChange?: (value: string) => void
|
||||
}) {
|
||||
const [selectedValue, setSelectedValue] = useState(initialValue)
|
||||
|
||||
const handleValueChange = (value: string) => {
|
||||
setSelectedValue(value)
|
||||
onChange?.(value)
|
||||
}
|
||||
|
||||
return (
|
||||
<CardSelectionContext.Provider value={{ selectedValue, setSelectedValue: handleValueChange }}>
|
||||
{children}
|
||||
</CardSelectionContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
export function useCardSelection() {
|
||||
const context = useContext(CardSelectionContext)
|
||||
if (context === undefined) {
|
||||
throw new Error('useCardSelection must be used within a CardSelectionProvider')
|
||||
}
|
||||
return context
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
import { Box } from '@material-ui/core'
|
||||
import CheckIcon from '@material-ui/icons/Check'
|
||||
import { CardSelectionProvider } from './CardSelectionContext'
|
||||
|
||||
interface CardSelectionGroupProps {
|
||||
children: React.ReactElement<{ value: string }>[]
|
||||
value: string
|
||||
onChange: (value: string) => void
|
||||
}
|
||||
|
||||
export default function CardSelectionGroup({
|
||||
children,
|
||||
value,
|
||||
onChange,
|
||||
}: CardSelectionGroupProps) {
|
||||
return (
|
||||
<CardSelectionProvider initialValue={value} onChange={onChange}>
|
||||
<Box style={{ display: 'flex', flexDirection: 'column', gap: 12, marginTop: 12 }}>
|
||||
{children}
|
||||
</Box>
|
||||
</CardSelectionProvider>
|
||||
)
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
import { Box } from "@material-ui/core";
|
||||
import CheckIcon from '@material-ui/icons/Check'
|
||||
import { useCardSelection } from './CardSelectionContext'
|
||||
|
||||
// The value prop is used by the parent CardSelectionGroup to determine which option is selected
|
||||
export default function CardSelectionOption({children, value}: {children: React.ReactNode, value: string}) {
|
||||
const { selectedValue, setSelectedValue } = useCardSelection()
|
||||
const selected = value === selectedValue
|
||||
|
||||
return (
|
||||
<Box
|
||||
onClick={() => setSelectedValue(value)}
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'flex-start',
|
||||
gap: 16,
|
||||
border: selected ? '2px solid #FF5C1B' : '2px solid #555',
|
||||
borderRadius: 16,
|
||||
padding: '1em',
|
||||
cursor: 'pointer',
|
||||
transition: 'all 0.2s ease-in-out',
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
style={{
|
||||
border: selected ? '2px solid #FF5C1B' : '2px solid #555',
|
||||
borderRadius: 99999,
|
||||
width: 28,
|
||||
height: 28,
|
||||
background: selected ? '#FF5C1B' : 'transparent',
|
||||
overflow: 'hidden',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
transition: 'all 0.2s ease-in-out',
|
||||
transform: selected ? 'scale(1.1)' : 'scale(1)',
|
||||
flexShrink: 0,
|
||||
}}
|
||||
>
|
||||
{selected ? (
|
||||
<CheckIcon
|
||||
style={{
|
||||
transition: 'all 0.2s ease-in-out',
|
||||
transform: 'scale(1)',
|
||||
animation: 'checkIn 0.2s ease-in-out'
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
</Box>
|
||||
<Box pt={0.5}>{children}</Box>
|
||||
</Box>
|
||||
)
|
||||
}
|
|
@ -0,0 +1,114 @@
|
|||
import { makeStyles, Modal } from '@material-ui/core'
|
||||
import { useState } from 'react'
|
||||
import Slide01_GettingStarted from './slides/Slide01_GettingStarted'
|
||||
import Slide02_ChooseAMaker from './slides/Slide02_ChooseAMaker'
|
||||
import Slide03_PrepareSwap from './slides/Slide03_PrepareSwap'
|
||||
import Slide04_ExecuteSwap from './slides/Slide04_ExecuteSwap'
|
||||
import Slide05_KeepAnEyeOnYourSwaps from './slides/Slide05_KeepAnEyeOnYourSwaps'
|
||||
import Slide06_FiatPricePreference from './slides/Slide06_FiatPricePreference'
|
||||
import Slide07_ReachOut from './slides/Slide07_ReachOut'
|
||||
import {
|
||||
setFetchFiatPrices,
|
||||
setUserHasSeenIntroduction,
|
||||
} from 'store/features/settingsSlice'
|
||||
import { useAppDispatch, useSettings } from 'store/hooks'
|
||||
|
||||
const useStyles = makeStyles({
|
||||
modal: {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
paper: {
|
||||
width: '80%',
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
},
|
||||
})
|
||||
|
||||
export default function IntroductionModal() {
|
||||
const userHasSeenIntroduction = useSettings(
|
||||
(s) => s.userHasSeenIntroduction
|
||||
)
|
||||
|
||||
const dispatch = useAppDispatch()
|
||||
|
||||
// Handle Display State
|
||||
const [open, setOpen] = useState<boolean>(!userHasSeenIntroduction)
|
||||
const [showFiat, setShowFiat] = useState<boolean>(true)
|
||||
const handleClose = () => {
|
||||
setOpen(false)
|
||||
}
|
||||
|
||||
// Handle Slide Index
|
||||
const [currentSlideIndex, setCurrentSlideIndex] = useState(0)
|
||||
|
||||
const handleContinue = () => {
|
||||
if (currentSlideIndex == slideComponents.length - 1) {
|
||||
handleClose()
|
||||
dispatch(setUserHasSeenIntroduction(true))
|
||||
dispatch(setFetchFiatPrices(showFiat))
|
||||
return
|
||||
}
|
||||
|
||||
setCurrentSlideIndex((i) => i + 1)
|
||||
}
|
||||
|
||||
const handlePrevious = () => {
|
||||
if (currentSlideIndex == 0) {
|
||||
return
|
||||
}
|
||||
|
||||
setCurrentSlideIndex((i) => i - 1)
|
||||
}
|
||||
|
||||
const slideComponents = [
|
||||
<Slide01_GettingStarted
|
||||
handleContinue={handleContinue}
|
||||
handlePrevious={handlePrevious}
|
||||
hidePreviousButton
|
||||
/>,
|
||||
<Slide02_ChooseAMaker
|
||||
handleContinue={handleContinue}
|
||||
handlePrevious={handlePrevious}
|
||||
/>,
|
||||
<Slide03_PrepareSwap
|
||||
handleContinue={handleContinue}
|
||||
handlePrevious={handlePrevious}
|
||||
/>,
|
||||
<Slide04_ExecuteSwap
|
||||
handleContinue={handleContinue}
|
||||
handlePrevious={handlePrevious}
|
||||
/>,
|
||||
<Slide05_KeepAnEyeOnYourSwaps
|
||||
handleContinue={handleContinue}
|
||||
handlePrevious={handlePrevious}
|
||||
/>,
|
||||
<Slide06_FiatPricePreference
|
||||
handleContinue={handleContinue}
|
||||
handlePrevious={handlePrevious}
|
||||
showFiat={showFiat}
|
||||
onChange={(showFiatSetting: string) =>
|
||||
setShowFiat(showFiatSetting === 'show')
|
||||
}
|
||||
/>,
|
||||
<Slide07_ReachOut
|
||||
handleContinue={handleContinue}
|
||||
handlePrevious={handlePrevious}
|
||||
/>,
|
||||
]
|
||||
|
||||
const classes = useStyles()
|
||||
|
||||
return (
|
||||
<Modal
|
||||
open={open}
|
||||
onClose={handleClose}
|
||||
className={classes.modal}
|
||||
disableAutoFocus
|
||||
closeAfterTransition
|
||||
>
|
||||
{slideComponents[currentSlideIndex]}
|
||||
</Modal>
|
||||
)
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
import { Typography } from '@material-ui/core'
|
||||
import SlideTemplate from './SlideTemplate'
|
||||
import imagePath from 'assets/walletWithBitcoinAndMonero.png'
|
||||
|
||||
export default function Slide01_GettingStarted(props: slideProps) {
|
||||
return (
|
||||
<SlideTemplate
|
||||
title="Getting Started"
|
||||
{...props}
|
||||
imagePath={imagePath}
|
||||
>
|
||||
<Typography variant="subtitle1">
|
||||
To start swapping, you'll need:
|
||||
</Typography>
|
||||
<Typography>
|
||||
<ul>
|
||||
<li>A Bitcoin wallet with funds to swap</li>
|
||||
<li>A Monero wallet to receive your Monero</li>
|
||||
</ul>
|
||||
</Typography>
|
||||
</SlideTemplate>
|
||||
)
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
import { Typography } from '@material-ui/core'
|
||||
import SlideTemplate from './SlideTemplate'
|
||||
import imagePath from 'assets/mockMakerSelection.svg'
|
||||
|
||||
export default function Slide02_ChooseAMaker(props: slideProps) {
|
||||
return (
|
||||
<SlideTemplate
|
||||
title="Choose a Maker"
|
||||
stepLabel="Step 1"
|
||||
{...props}
|
||||
imagePath={imagePath}
|
||||
>
|
||||
<Typography variant="subtitle1">
|
||||
To start a swap, choose a maker. Each maker offers different exchange rates and limits.
|
||||
</Typography>
|
||||
</SlideTemplate>
|
||||
)
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
import { Typography } from '@material-ui/core'
|
||||
import SlideTemplate from './SlideTemplate'
|
||||
import imagePath from 'assets/mockConfigureSwap.svg'
|
||||
|
||||
export default function Slide02_ChooseAMaker(props: slideProps) {
|
||||
return (
|
||||
<SlideTemplate title="Prepare Swap" stepLabel="Step 2" {...props} imagePath={imagePath}>
|
||||
<Typography variant="subtitle1">
|
||||
To initiate a swap, provide a Monero address and optionally a Bitcoin refund address.
|
||||
</Typography>
|
||||
</SlideTemplate>
|
||||
)
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
import { Typography } from '@material-ui/core'
|
||||
import SlideTemplate from './SlideTemplate'
|
||||
import imagePath from 'assets/simpleSwapFlowDiagram.svg'
|
||||
|
||||
export default function Slide02_ChooseAMaker(props: slideProps) {
|
||||
return (
|
||||
<SlideTemplate
|
||||
title="Execute Swap"
|
||||
stepLabel="Step 3"
|
||||
{...props}
|
||||
imagePath={imagePath}
|
||||
>
|
||||
<Typography variant="subtitle1">
|
||||
After confirming:
|
||||
</Typography>
|
||||
<Typography>
|
||||
<ol>
|
||||
<li>Your Bitcoin are locked</li>
|
||||
<li>Maker locks the Monero</li>
|
||||
<li>Maker reedems the Bitcoin</li>
|
||||
<li>Monero is sent to your address</li>
|
||||
</ol>
|
||||
</Typography>
|
||||
</SlideTemplate>
|
||||
)
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
import { Link, Typography } from '@material-ui/core'
|
||||
import SlideTemplate from './SlideTemplate'
|
||||
import imagePath from 'assets/mockHistoryPage.svg'
|
||||
import ExternalLink from 'renderer/components/other/ExternalLink'
|
||||
|
||||
export default function Slide05_KeepAnEyeOnYourSwaps(props: slideProps) {
|
||||
return (
|
||||
<SlideTemplate
|
||||
title="Monitor Your Swaps"
|
||||
stepLabel="Step 3"
|
||||
{...props}
|
||||
imagePath={imagePath}
|
||||
>
|
||||
<Typography>
|
||||
Monitor active swaps to ensure everything proceeds smoothly.
|
||||
</Typography>
|
||||
<Typography>
|
||||
<ExternalLink href='https://docs.unstoppableswap.net/usage/first_swap'>
|
||||
Learn more about atomic swaps
|
||||
</ExternalLink>
|
||||
</Typography>
|
||||
</SlideTemplate>
|
||||
)
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
import { Box, Typography, Paper, Button, Slide } from '@material-ui/core'
|
||||
import CardSelectionGroup from 'renderer/components/inputs/CardSelection/CardSelectionGroup'
|
||||
import CardSelectionOption from 'renderer/components/inputs/CardSelection/CardSelectionOption'
|
||||
import SlideTemplate from './SlideTemplate'
|
||||
import imagePath from 'assets/currencyFetching.svg'
|
||||
|
||||
const FiatPricePreferenceSlide = ({
|
||||
handleContinue,
|
||||
handlePrevious,
|
||||
showFiat,
|
||||
onChange,
|
||||
}: slideProps & {
|
||||
showFiat: boolean
|
||||
onChange: (value: string) => void
|
||||
}) => {
|
||||
return (
|
||||
<SlideTemplate handleContinue={handleContinue} handlePrevious={handlePrevious} title="Fiat Prices" imagePath={imagePath}>
|
||||
<Typography variant="subtitle1" color="textSecondary">
|
||||
Do you want to show fiat prices?
|
||||
</Typography>
|
||||
<CardSelectionGroup
|
||||
value={showFiat ? 'show' : 'hide'}
|
||||
onChange={onChange}
|
||||
>
|
||||
<CardSelectionOption value="show">
|
||||
<Typography>Show fiat prices</Typography>
|
||||
<Typography
|
||||
variant="caption"
|
||||
color="textSecondary"
|
||||
paragraph
|
||||
style={{ marginBottom: 4 }}
|
||||
>
|
||||
We connect to CoinGecko to provide realtime currency
|
||||
prices.
|
||||
</Typography>
|
||||
</CardSelectionOption>
|
||||
<CardSelectionOption value="hide">
|
||||
<Typography>Don't show fiat prices</Typography>
|
||||
</CardSelectionOption>
|
||||
</CardSelectionGroup>
|
||||
<Box style={{ marginTop: "0.5rem" }}>
|
||||
<Typography
|
||||
variant="caption"
|
||||
color="textSecondary"
|
||||
>
|
||||
You can change your preference later in the settings
|
||||
</Typography>
|
||||
</Box>
|
||||
</SlideTemplate>
|
||||
)
|
||||
}
|
||||
|
||||
export default FiatPricePreferenceSlide
|
|
@ -0,0 +1,25 @@
|
|||
import { Box, Typography } from '@material-ui/core'
|
||||
import SlideTemplate from './SlideTemplate'
|
||||
import imagePath from 'assets/groupWithChatbubbles.png'
|
||||
import GitHubIcon from "@material-ui/icons/GitHub"
|
||||
import MatrixIcon from 'renderer/components/icons/MatrixIcon'
|
||||
import LinkIconButton from 'renderer/components/icons/LinkIconButton'
|
||||
|
||||
export default function Slide02_ChooseAMaker(props: slideProps) {
|
||||
return (
|
||||
<SlideTemplate title="Reach out" {...props} imagePath={imagePath} customContinueButtonText="Get Started">
|
||||
<Typography variant="subtitle1">
|
||||
We would love to hear about your experience with Unstoppable
|
||||
Swap and invite you to join our community.
|
||||
</Typography>
|
||||
<Box mt={3}>
|
||||
<LinkIconButton url="https://github.com/UnstoppableSwap/core">
|
||||
<GitHubIcon/>
|
||||
</LinkIconButton>
|
||||
<LinkIconButton url="https://matrix.to/#/#unstoppableswap:matrix.org">
|
||||
<MatrixIcon/>
|
||||
</LinkIconButton>
|
||||
</Box>
|
||||
</SlideTemplate>
|
||||
)
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
import { makeStyles, Paper, Box, Typography, Button } from '@material-ui/core'
|
||||
|
||||
type slideTemplateProps = {
|
||||
handleContinue: () => void
|
||||
handlePrevious: () => void
|
||||
hidePreviousButton?: boolean
|
||||
stepLabel?: String
|
||||
title: String
|
||||
children?: React.ReactNode
|
||||
imagePath?: string
|
||||
imagePadded?: boolean
|
||||
customContinueButtonText?: String
|
||||
}
|
||||
|
||||
const useStyles = makeStyles({
|
||||
paper: {
|
||||
height: "80%",
|
||||
width: "80%",
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
},
|
||||
stepLabel: {
|
||||
textTransform: 'uppercase',
|
||||
},
|
||||
splitImage: {
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
objectFit: 'contain'
|
||||
}
|
||||
})
|
||||
|
||||
export default function SlideTemplate({
|
||||
handleContinue,
|
||||
handlePrevious,
|
||||
hidePreviousButton,
|
||||
stepLabel,
|
||||
title,
|
||||
children,
|
||||
imagePath,
|
||||
imagePadded,
|
||||
customContinueButtonText
|
||||
}: slideTemplateProps) {
|
||||
const classes = useStyles()
|
||||
|
||||
return (
|
||||
<Paper className={classes.paper}>
|
||||
<Box m={3} flex alignContent="center" position="relative" width="50%" flexGrow={1}>
|
||||
<Box>
|
||||
{stepLabel && (
|
||||
<Typography
|
||||
variant="overline"
|
||||
className={classes.stepLabel}
|
||||
>
|
||||
{stepLabel}
|
||||
</Typography>
|
||||
)}
|
||||
<Typography variant="h4" style={{ marginBottom: 16 }}>{title}</Typography>
|
||||
{children}
|
||||
</Box>
|
||||
<Box
|
||||
position="absolute"
|
||||
bottom={0}
|
||||
width="100%"
|
||||
display="flex"
|
||||
justifyContent={
|
||||
hidePreviousButton ? 'flex-end' : 'space-between'
|
||||
}
|
||||
>
|
||||
{!hidePreviousButton && (
|
||||
<Button onClick={handlePrevious}>Back</Button>
|
||||
)}
|
||||
<Button
|
||||
onClick={handleContinue}
|
||||
variant="contained"
|
||||
color="primary"
|
||||
>
|
||||
{customContinueButtonText ? customContinueButtonText : 'Next' }
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
{imagePath && (
|
||||
<Box
|
||||
bgcolor="#212121"
|
||||
width="50%"
|
||||
display="flex"
|
||||
justifyContent="center"
|
||||
p={imagePadded ? "1.5em" : 0}
|
||||
>
|
||||
<img src={imagePath} className={classes.splitImage} />
|
||||
</Box>
|
||||
)}
|
||||
</Paper>
|
||||
)
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
type slideProps = {
|
||||
handleContinue: () => void
|
||||
handlePrevious: () => void
|
||||
hidePreviousButton?: boolean
|
||||
}
|
|
@ -35,7 +35,7 @@ export default function NavigationFooter() {
|
|||
<Box className={classes.linksOuter}>
|
||||
<Tooltip title="Check out the GitHub repository">
|
||||
<span>
|
||||
<LinkIconButton url="https://github.com/UnstoppableSwap/unstoppableswap-gui">
|
||||
<LinkIconButton url="https://github.com/UnstoppableSwap/core">
|
||||
<GitHubIcon />
|
||||
</LinkIconButton>
|
||||
</span>
|
||||
|
|
10
src-gui/src/renderer/components/other/ExternalLink.tsx
Normal file
|
@ -0,0 +1,10 @@
|
|||
import Link from "@material-ui/core/Link";
|
||||
import { open } from "@tauri-apps/plugin-shell";
|
||||
|
||||
export default function ExternalLink({children, href}: {children: React.ReactNode, href: string}) {
|
||||
return (
|
||||
<Link style={{cursor: 'pointer'}} onClick={() => open(href)}>
|
||||
{children}
|
||||
</Link>
|
||||
)
|
||||
}
|
|
@ -11,6 +11,7 @@ export interface SettingsState {
|
|||
fiatCurrency: FiatCurrency;
|
||||
/// Whether to enable Tor for p2p connections
|
||||
enableTor: boolean
|
||||
userHasSeenIntroduction: boolean;
|
||||
}
|
||||
|
||||
export enum FiatCurrency {
|
||||
|
@ -98,9 +99,10 @@ const initialState: SettingsState = {
|
|||
}
|
||||
},
|
||||
theme: Theme.Darker,
|
||||
fetchFiatPrices: true,
|
||||
fetchFiatPrices: false,
|
||||
fiatCurrency: FiatCurrency.Usd,
|
||||
enableTor: true
|
||||
enableTor: true,
|
||||
userHasSeenIntroduction: false
|
||||
};
|
||||
|
||||
const alertsSlice = createSlice({
|
||||
|
@ -135,6 +137,9 @@ const alertsSlice = createSlice({
|
|||
removeNode(slice, action: PayloadAction<{ network: Network, type: Blockchain, node: string }>) {
|
||||
slice.nodes[action.payload.network][action.payload.type] = slice.nodes[action.payload.network][action.payload.type].filter(node => node !== action.payload.node);
|
||||
},
|
||||
setUserHasSeenIntroduction(slice, action: PayloadAction<boolean>) {
|
||||
slice.userHasSeenIntroduction = action.payload
|
||||
},
|
||||
resetSettings(_) {
|
||||
return initialState;
|
||||
},
|
||||
|
@ -153,6 +158,7 @@ export const {
|
|||
setFetchFiatPrices,
|
||||
setFiatCurrency,
|
||||
setTorEnabled,
|
||||
setUserHasSeenIntroduction,
|
||||
} = alertsSlice.actions;
|
||||
|
||||
export default alertsSlice.reducer;
|
||||
|
|