Skip to content

Commit

Permalink
💄 Add Notifications for Ticker
Browse files Browse the repository at this point in the history
  • Loading branch information
0x46616c6b committed Feb 2, 2025
1 parent 8a8a036 commit 6587c57
Show file tree
Hide file tree
Showing 24 changed files with 248 additions and 35 deletions.
11 changes: 7 additions & 4 deletions src/components/ticker/BlueskyCard.test.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import sign from 'jwt-encode'
import BlueskyCard from './BlueskyCard'
import { MemoryRouter } from 'react-router'
import { AuthProvider } from '../../contexts/AuthContext'
import { Ticker } from '../../api/Ticker'
import userEvent from '@testing-library/user-event'
import { AuthProvider } from '../../contexts/AuthContext'
import { NotificationProvider } from '../../contexts/NotificationContext'
import BlueskyCard from './BlueskyCard'

const token = sign({ id: 1, email: '[email protected]', roles: ['user'], exp: new Date().getTime() / 1000 + 600 }, 'secret')

Expand Down Expand Up @@ -42,7 +43,9 @@ describe('BlueSkyCard', () => {
<QueryClientProvider client={client}>
<MemoryRouter>
<AuthProvider>
<BlueskyCard ticker={ticker} />
<NotificationProvider>
<BlueskyCard ticker={ticker} />
</NotificationProvider>
</AuthProvider>
</MemoryRouter>
</QueryClientProvider>
Expand Down
11 changes: 6 additions & 5 deletions src/components/ticker/BlueskyForm.test.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import sign from 'jwt-encode'
import { Ticker } from '../../api/Ticker'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import sign from 'jwt-encode'
import { MemoryRouter } from 'react-router'
import { Ticker } from '../../api/Ticker'
import { AuthProvider } from '../../contexts/AuthContext'
import { NotificationProvider } from '../../contexts/NotificationContext'
import BlueskyForm from './BlueskyForm'
import userEvent from '@testing-library/user-event'

const token = sign({ id: 1, email: '[email protected]', roles: ['user'], exp: new Date().getTime() / 1000 + 600 }, 'secret')

Expand Down Expand Up @@ -44,10 +45,10 @@ describe('BlueskyForm', () => {
<QueryClientProvider client={client}>
<MemoryRouter>
<AuthProvider>
<div>
<NotificationProvider>
<BlueskyForm callback={callback} ticker={ticker} />
<input name="Submit" type="submit" value="Submit" form="configureBluesky" />
</div>
</NotificationProvider>
</AuthProvider>
</MemoryRouter>
</QueryClientProvider>
Expand Down
4 changes: 4 additions & 0 deletions src/components/ticker/BlueskyForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@ import { FC } from 'react'
import { useForm } from 'react-hook-form'
import { Ticker, TickerBlueskyFormData, putTickerBlueskyApi } from '../../api/Ticker'
import useAuth from '../../contexts/useAuth'
import useNotification from '../../contexts/useNotification'

interface Props {
callback: () => void
ticker: Ticker
}

const BlueskyForm: FC<Props> = ({ callback, ticker }) => {
const { createNotification } = useNotification()
const bluesky = ticker.bluesky
const { token } = useAuth()
const {
Expand All @@ -32,8 +34,10 @@ const BlueskyForm: FC<Props> = ({ callback, ticker }) => {
putTickerBlueskyApi(token, data, ticker).then(response => {
if (response.status == 'error') {
setError('root.authenticationFailed', { message: 'Authentication failed' })
createNotification({ content: 'Bluesky integration failed to update', severity: 'error' })
} else {
queryClient.invalidateQueries({ queryKey: ['ticker', ticker.id] })
createNotification({ content: 'Bluesky integration was successfully updated', severity: 'success' })
callback()
}
})
Expand Down
5 changes: 4 additions & 1 deletion src/components/ticker/MastodonCard.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import sign from 'jwt-encode'
import { MemoryRouter } from 'react-router'
import { Ticker } from '../../api/Ticker'
import { AuthProvider } from '../../contexts/AuthContext'
import { NotificationProvider } from '../../contexts/NotificationContext'
import MastodonCard from './MastodonCard'

const token = sign({ id: 1, email: '[email protected]', roles: ['user'], exp: new Date().getTime() / 1000 + 600 }, 'secret')
Expand Down Expand Up @@ -42,7 +43,9 @@ describe('MastodonCard', () => {
<QueryClientProvider client={client}>
<MemoryRouter>
<AuthProvider>
<MastodonCard ticker={ticker} />
<NotificationProvider>
<MastodonCard ticker={ticker} />
</NotificationProvider>
</AuthProvider>
</MemoryRouter>
</QueryClientProvider>
Expand Down
5 changes: 3 additions & 2 deletions src/components/ticker/MastodonForm.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import sign from 'jwt-encode'
import { MemoryRouter } from 'react-router'
import { Ticker } from '../../api/Ticker'
import { AuthProvider } from '../../contexts/AuthContext'
import { NotificationProvider } from '../../contexts/NotificationContext'
import MastodonForm from './MastodonForm'

const token = sign({ id: 1, email: '[email protected]', roles: ['user'], exp: new Date().getTime() / 1000 + 600 }, 'secret')
Expand Down Expand Up @@ -44,10 +45,10 @@ describe('MastodonForm', () => {
<QueryClientProvider client={client}>
<MemoryRouter>
<AuthProvider>
<div>
<NotificationProvider>
<MastodonForm callback={callback} ticker={ticker} />
<input name="Submit" type="submit" value="Submit" form="configureMastodon" />
</div>
</NotificationProvider>
</AuthProvider>
</MemoryRouter>
</QueryClientProvider>
Expand Down
3 changes: 3 additions & 0 deletions src/components/ticker/MastodonForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@ import { FC } from 'react'
import { SubmitHandler, useForm } from 'react-hook-form'
import { Ticker, TickerMastodonFormData, putTickerMastodonApi } from '../../api/Ticker'
import useAuth from '../../contexts/useAuth'
import useNotification from '../../contexts/useNotification'

interface Props {
callback: () => void
ticker: Ticker
}

const MastodonForm: FC<Props> = ({ callback, ticker }) => {
const { createNotification } = useNotification()
const mastodon = ticker.mastodon
const { token } = useAuth()
const { handleSubmit, register } = useForm<TickerMastodonFormData>({
Expand All @@ -25,6 +27,7 @@ const MastodonForm: FC<Props> = ({ callback, ticker }) => {
const onSubmit: SubmitHandler<TickerMastodonFormData> = data => {
putTickerMastodonApi(token, data, ticker).finally(() => {
queryClient.invalidateQueries({ queryKey: ['ticker', ticker.id] })
createNotification({ content: 'Mastodon integration was successfully updated', severity: 'success' })
callback()
})
}
Expand Down
4 changes: 2 additions & 2 deletions src/components/ticker/SignalGroupCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ const SignalGroupCard: FC<Props> = ({ ticker }) => {
putTickerSignalGroupApi(token, { active: true }, ticker)
.finally(() => {
queryClient.invalidateQueries({ queryKey: ['ticker', ticker.id] })
createNotification({ content: 'Signal group successfully configured', severity: 'success' })
createNotification({ content: 'Signal Group enabled successfully', severity: 'success' })
setSubmittingAdd(false)
})
.catch(() => {
Expand All @@ -68,7 +68,7 @@ const SignalGroupCard: FC<Props> = ({ ticker }) => {
deleteTickerSignalGroupApi(token, ticker)
.finally(() => {
queryClient.invalidateQueries({ queryKey: ['ticker', ticker.id] })
createNotification({ content: 'Signal group deleted successfully', severity: 'success' })
createNotification({ content: 'Signal Group deleted successfully', severity: 'success' })
})
.catch(() => {
createNotification({ content: 'Failed to delete Signal group', severity: 'error' })
Expand Down
5 changes: 4 additions & 1 deletion src/components/ticker/TelegramCard.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import sign from 'jwt-encode'
import { MemoryRouter } from 'react-router'
import { Ticker } from '../../api/Ticker'
import { AuthProvider } from '../../contexts/AuthContext'
import { NotificationProvider } from '../../contexts/NotificationContext'
import TelegramCard from './TelegramCard'

const token = sign({ id: 1, email: '[email protected]', roles: ['user'], exp: new Date().getTime() / 1000 + 600 }, 'secret')
Expand Down Expand Up @@ -42,7 +43,9 @@ describe('TelegramCard', () => {
<QueryClientProvider client={client}>
<MemoryRouter>
<AuthProvider>
<TelegramCard ticker={ticker} />
<NotificationProvider>
<TelegramCard ticker={ticker} />
</NotificationProvider>
</AuthProvider>
</MemoryRouter>
</QueryClientProvider>
Expand Down
12 changes: 9 additions & 3 deletions src/components/ticker/TelegramCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,34 @@ import { useQueryClient } from '@tanstack/react-query'
import { FC, useCallback, useState } from 'react'
import { Ticker, deleteTickerTelegramApi, putTickerTelegramApi } from '../../api/Ticker'
import useAuth from '../../contexts/useAuth'
import useNotification from '../../contexts/useNotification'
import TelegramModalForm from './TelegramModalForm'

interface Props {
ticker: Ticker
}

const TelegramCard: FC<Props> = ({ ticker }) => {
const { createNotification } = useNotification()
const { token } = useAuth()
const [open, setOpen] = useState<boolean>(false)
const queryClient = useQueryClient()

const telegram = ticker.telegram

const handleToggle = useCallback(() => {
putTickerTelegramApi(token, { active: !telegram.active }, ticker).finally(() => queryClient.invalidateQueries({ queryKey: ['ticker', ticker.id] }))
}, [token, queryClient, telegram.active, ticker])
putTickerTelegramApi(token, { active: !telegram.active }, ticker).finally(() => {
queryClient.invalidateQueries({ queryKey: ['ticker', ticker.id] })
createNotification({ content: `Telegram integration ${telegram.active ? 'disabled' : 'enabled'} successfully`, severity: 'success' })
})
}, [token, telegram.active, ticker, queryClient, createNotification])

const handleDelete = useCallback(() => {
deleteTickerTelegramApi(token, ticker).finally(() => {
queryClient.invalidateQueries({ queryKey: ['ticker', ticker.id] })
createNotification({ content: 'Telegram integration successfully deleted', severity: 'success' })
})
}, [token, queryClient, ticker])
}, [token, ticker, queryClient, createNotification])

const channelLink = (
<Link href={`https://t.me/${telegram.channelName}`} rel="noreferrer" target="_blank">
Expand Down
5 changes: 3 additions & 2 deletions src/components/ticker/TelegramForm.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import sign from 'jwt-encode'
import { MemoryRouter } from 'react-router'
import { Ticker } from '../../api/Ticker'
import { AuthProvider } from '../../contexts/AuthContext'
import { NotificationProvider } from '../../contexts/NotificationContext'
import TelegramForm from './TelegramForm'

const token = sign({ id: 1, email: '[email protected]', roles: ['user'], exp: new Date().getTime() / 1000 + 600 }, 'secret')
Expand Down Expand Up @@ -44,10 +45,10 @@ describe('TelegramForm', () => {
<QueryClientProvider client={client}>
<MemoryRouter>
<AuthProvider>
<div>
<NotificationProvider>
<TelegramForm callback={callback} ticker={ticker} />
<input name="Submit" type="submit" value="Submit" form="configureTelegram" />
</div>
</NotificationProvider>
</AuthProvider>
</MemoryRouter>
</QueryClientProvider>
Expand Down
3 changes: 3 additions & 0 deletions src/components/ticker/TelegramForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { FC } from 'react'
import { SubmitHandler, useForm } from 'react-hook-form'
import { Ticker, putTickerTelegramApi } from '../../api/Ticker'
import useAuth from '../../contexts/useAuth'
import useNotification from '../../contexts/useNotification'

interface Props {
callback: () => void
Expand All @@ -17,6 +18,7 @@ interface FormValues {
}

const TelegramForm: FC<Props> = ({ callback, ticker }) => {
const { createNotification } = useNotification()
const telegram = ticker.telegram
const { token } = useAuth()
const { handleSubmit, register } = useForm<FormValues>({
Expand All @@ -30,6 +32,7 @@ const TelegramForm: FC<Props> = ({ callback, ticker }) => {
const onSubmit: SubmitHandler<FormValues> = data => {
putTickerTelegramApi(token, data, ticker).finally(() => {
queryClient.invalidateQueries({ queryKey: ['ticker', ticker.id] })
createNotification({ content: 'Telegram integration was successfully updated', severity: 'success' })
callback()
})
}
Expand Down
5 changes: 4 additions & 1 deletion src/components/ticker/TickerListItems.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import sign from 'jwt-encode'
import { MemoryRouter } from 'react-router'
import { GetTickersQueryParams } from '../../api/Ticker'
import { AuthProvider } from '../../contexts/AuthContext'
import { NotificationProvider } from '../../contexts/NotificationContext'
import TickerListItems from './TickerListItems'

describe('TickerListItems', function () {
Expand Down Expand Up @@ -35,7 +36,9 @@ describe('TickerListItems', function () {
<QueryClientProvider client={client}>
<MemoryRouter>
<AuthProvider>
<TickerListItems params={params} token={jwt('admin')} />
<NotificationProvider>
<TickerListItems params={params} token={jwt('admin')} />
</NotificationProvider>
</AuthProvider>
</MemoryRouter>
</QueryClientProvider>
Expand Down
5 changes: 4 additions & 1 deletion src/components/ticker/TickerModalDelete.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { useQueryClient } from '@tanstack/react-query'
import { FC, useCallback } from 'react'
import { Ticker, deleteTickerApi } from '../../api/Ticker'
import useAuth from '../../contexts/useAuth'
import useNotification from '../../contexts/useNotification'
import Modal from '../common/Modal'

interface Props {
Expand All @@ -11,14 +12,16 @@ interface Props {
}

const TickerModalDelete: FC<Props> = ({ open, onClose, ticker }) => {
const { createNotification } = useNotification()
const { token } = useAuth()
const queryClient = useQueryClient()

const handleDelete = useCallback(() => {
deleteTickerApi(token, ticker).finally(() => {
queryClient.invalidateQueries({ queryKey: ['tickers'] })
createNotification({ content: 'Ticker was successfully deleted', severity: 'success' })
})
}, [token, ticker, queryClient])
}, [token, ticker, queryClient, createNotification])

return (
<Modal dangerActionButtonText="Delete" onClose={onClose} onDangerAction={handleDelete} open={open} title="Delete Ticker">
Expand Down
86 changes: 86 additions & 0 deletions src/components/ticker/TickerResetModal.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import sign from 'jwt-encode'
import { MemoryRouter } from 'react-router'
import { Ticker } from '../../api/Ticker'
import { AuthProvider } from '../../contexts/AuthContext'
import { NotificationProvider } from '../../contexts/NotificationContext'
import TickerResetModal from './TickerResetModal'

const token = sign({ id: 1, email: '[email protected]', roles: ['user'], exp: new Date().getTime() / 1000 + 600 }, 'secret')

describe('TickerResetModal', () => {
beforeAll(() => {
localStorage.setItem('token', token)
})

beforeEach(() => {
fetchMock.resetMocks()
onClose.mockClear()
})

const onClose = vi.fn()

function setup(ticker: Ticker) {
const client = new QueryClient({
defaultOptions: {
queries: {
retry: false,
},
},
})
return render(
<QueryClientProvider client={client}>
<MemoryRouter>
<AuthProvider>
<NotificationProvider>
<TickerResetModal open={true} onClose={onClose} ticker={ticker} />
</NotificationProvider>
</AuthProvider>
</MemoryRouter>
</QueryClientProvider>
)
}

it('should render the component', async () => {
const ticker = {
id: 1,
title: 'Ticker 1',
} as Ticker
setup(ticker)

expect(screen.getByRole('button', { name: 'Reset' })).toBeInTheDocument()
expect(screen.getByRole('button', { name: 'Close' })).toBeInTheDocument()

fetchMock.mockResponseOnce(JSON.stringify({ status: 'success' }))

await userEvent.click(screen.getByRole('button', { name: 'Reset' }))

expect(onClose).toHaveBeenCalledTimes(1)
expect(fetchMock).toHaveBeenCalledTimes(1)
expect(fetchMock).toHaveBeenCalledWith('http://localhost:8080/v1/admin/tickers/1/reset', {
method: 'put',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`,
},
})
})

it('should render the component and close the modal', async () => {
const ticker = {
id: 1,
title: 'Ticker 1',
} as Ticker
setup(ticker)

fetchMock.mockResponseOnce(JSON.stringify({ status: 'success' }))

await userEvent.click(screen.getByRole('button', { name: 'Close' }))

expect(onClose).toHaveBeenCalledTimes(1)
expect(fetchMock).toHaveBeenCalledTimes(0)
})
})
Loading

0 comments on commit 6587c57

Please sign in to comment.