Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

💄 Add Notifications for Ticker #713

Merged
merged 1 commit into from
Feb 2, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading