Skip to content

Commit 2e0ea07

Browse files
dem4roniHiD
andauthored
Add JustConnectedModal (exercism#7896)
* Add JustConnectedModal * Remove unused imports * Handle all json-less error responses * Restore accidentally deleted component * Make internal green again * Fix delete --------- Co-authored-by: Jeremy Walker <[email protected]>
1 parent d36bca8 commit 2e0ea07

15 files changed

+199
-81
lines changed

app/controllers/settings/github_syncer_controller.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
class Settings::GithubSyncerController < ApplicationController
2-
skip_before_action :verify_authenticity_token, only: %i[update sync_everything sync_track sync_solution sync_iteration]
2+
skip_before_action :verify_authenticity_token, only: %i[update sync_everything sync_track sync_solution sync_iteration destroy]
33

44
def show
55
@just_connected = !!params[:connected]

app/javascript/components/modals/welcome-modal/WelcomeModal.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,11 @@ export const WelcomeModalContext =
5252
setOpen: () => null,
5353
currentView: 'initial',
5454
setCurrentView: () => null,
55-
links: { hideModalEndpoint: '', apiUserEndpoint: '', codingFundamentalsCourse: '' },
55+
links: {
56+
hideModalEndpoint: '',
57+
apiUserEndpoint: '',
58+
codingFundamentalsCourse: '',
59+
},
5660
})
5761

5862
export default function WelcomeModal({
@@ -63,7 +67,7 @@ export default function WelcomeModal({
6367
links: {
6468
hideModalEndpoint: string
6569
apiUserEndpoint: string
66-
bootcampUrl: string
70+
codingFundamentalsCourse: string
6771
}
6872
numTracks: number
6973
}): JSX.Element {

app/javascript/components/settings/BootcampFreeCouponForm.tsx

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,15 @@ type Links = {
66
bootcampFreeCouponCode: string
77
}
88

9+
export type BootcampFreeCouponFormProps = {
10+
bootcampFreeCouponCode: string
11+
links: Links
12+
}
13+
914
export default function BootcampFreeCouponForm({
1015
bootcampFreeCouponCode,
1116
links,
12-
}: {
13-
bootcampFreeCouponCode: string
14-
links: Links
15-
}): JSX.Element {
17+
}: BootcampFreeCouponFormProps): JSX.Element {
1618
const [couponCode, setCouponCode] = useState(bootcampFreeCouponCode)
1719
const [error, setError] = useState<string | null>(null)
1820
const [loading, setLoading] = useState(false)

app/javascript/components/settings/github-syncer/common/SectionHeader.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
1-
import React, { useContext } from 'react'
1+
import React from 'react'
22
import { InsiderBubble } from './InsiderBubble'
3-
import { GitHubSyncerContext } from '../GitHubSyncerForm'
43

54
export function SectionHeader({ title }: { title: string }) {
6-
const { isUserInsider } = useContext(GitHubSyncerContext)
75
return (
86
<div className="!mb-6 flex justify-start items-center gap-12">
97
<h2 className="!mb-0">{title}</h2>

app/javascript/components/settings/github-syncer/fetchWithParams.tsx

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import toast from 'react-hot-toast'
2+
13
export function fetchWithParams({
24
url,
35
params,
@@ -13,8 +15,26 @@ export function fetchWithParams({
1315
'Content-Type': 'application/json',
1416
Accept: 'application/json',
1517
},
16-
body: JSON.stringify({
17-
github_solution_syncer: params,
18-
}),
18+
body: params
19+
? JSON.stringify({
20+
github_solution_syncer: params,
21+
})
22+
: null,
1923
})
2024
}
25+
26+
export async function handleJsonErrorResponse(
27+
response: Response,
28+
fallbackMessage: string
29+
) {
30+
const text = await response.text()
31+
32+
try {
33+
const data = JSON.parse(text)
34+
const message = data?.error?.message || fallbackMessage
35+
toast.error(message)
36+
} catch {
37+
console.error('Expected JSON, but received:', text)
38+
toast.error(fallbackMessage)
39+
}
40+
}

app/javascript/components/settings/github-syncer/sections/ConnectedSection/CommitMessageTemplateSection.tsx

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React, { useCallback, useState } from 'react'
22
import toast from 'react-hot-toast'
33
import { ConfirmationModal } from '../../common/ConfirmationModal'
4-
import { fetchWithParams } from '../../fetchWithParams'
4+
import { fetchWithParams, handleJsonErrorResponse } from '../../fetchWithParams'
55
import { GitHubSyncerContext } from '../../GitHubSyncerForm'
66
import { SectionHeader } from '../../common/SectionHeader'
77
import { flushSync } from 'react-dom'
@@ -32,10 +32,7 @@ export function CommitMessageTemplateSection() {
3232
if (response.ok) {
3333
toast.success('Saved changes successfully!')
3434
} else {
35-
const data = await response.json()
36-
toast.error(
37-
'Failed to save changes: ' + data.error.message || 'Unknown error'
38-
)
35+
await handleJsonErrorResponse(response, 'Failed to save changes.')
3936
}
4037
})
4138
.catch((error) => {

app/javascript/components/settings/github-syncer/sections/ConnectedSection/DangerZoneSection.tsx

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -51,15 +51,24 @@ export function DangerZoneSection() {
5151
toast.success('GitHub sync deleted successfully')
5252
setDeleteConfirmationModalOpen(false)
5353
} else {
54-
const data = await response.json()
55-
toast.error(
56-
`Failed to delete GitHub sync: ${
57-
data.error.message || 'Unknown error'
58-
}`
59-
)
54+
const text = await response.text()
55+
try {
56+
const data = JSON.parse(text)
57+
toast.error(
58+
`Failed to delete GitHub sync: ${
59+
data.error?.message || 'Unknown error'
60+
}`
61+
)
62+
} catch {
63+
console.error('Expected JSON, but received:', text)
64+
toast.error('Failed to delete GitHub sync.')
65+
}
6066
}
6167
})
6268
.catch((error) => {
69+
toast.error(
70+
`Oops! We received an unexpected error while deleting the GitHub sync.`
71+
)
6372
console.error('Error:', error)
6473
})
6574
}, [links.settings])

app/javascript/components/settings/github-syncer/sections/ConnectedSection/FileStructureSection.tsx

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import React, { useCallback, useState } from 'react'
22
import { flushSync } from 'react-dom'
33
import toast from 'react-hot-toast'
44
import { ConfirmationModal } from '../../common/ConfirmationModal'
5-
import { fetchWithParams } from '../../fetchWithParams'
5+
import { fetchWithParams, handleJsonErrorResponse } from '../../fetchWithParams'
66
import { GitHubSyncerContext } from '../../GitHubSyncerForm'
77
import { SectionHeader } from '../../common/SectionHeader'
88
import { assembleClassNames } from '@/utils/assemble-classnames'
@@ -40,10 +40,7 @@ export function FileStructureSection() {
4040
if (response.ok) {
4141
toast.success('Saved changes successfully!')
4242
} else {
43-
const data = await response.json()
44-
toast.error(
45-
'Failed to save changes: ' + data.error.message || 'Unknown error'
46-
)
43+
await handleJsonErrorResponse(response, 'Failed to save changes.')
4744
}
4845
})
4946
.catch((error) => {

app/javascript/components/settings/github-syncer/sections/ConnectedSection/IterationFilesSection.tsx

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import React, { useState, useCallback } from 'react'
22
import toast from 'react-hot-toast'
33
import { assembleClassNames } from '@/utils/assemble-classnames'
44
import { GitHubSyncerContext } from '../../GitHubSyncerForm'
5-
import { fetchWithParams } from '../../fetchWithParams'
5+
import { fetchWithParams, handleJsonErrorResponse } from '../../fetchWithParams'
66
import { SectionHeader } from '../../common/SectionHeader'
77
import { GraphicalIcon } from '@/components/common'
88

@@ -23,10 +23,7 @@ export function IterationFilesSection() {
2323
if (response.ok) {
2424
toast.success('Saved changes successfully!')
2525
} else {
26-
const data = await response.json()
27-
toast.error(
28-
'Failed to save changes: ' + data.error.message || 'Unknown error'
29-
)
26+
await handleJsonErrorResponse(response, 'Failed to save changes.')
3027
}
3128
})
3229
.catch((error) => {
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import React from 'react'
2+
import Modal from '@/components/modals/Modal'
3+
import { GitHubSyncerContext } from '../../GitHubSyncerForm'
4+
import { Icon } from '@/components/common'
5+
import { handleSyncEverything } from './ManualSyncSection'
6+
7+
export function JustConnectedModal(): JSX.Element {
8+
const { links } = React.useContext(GitHubSyncerContext)
9+
const [isModalOpen, setIsModalOpen] = React.useState(false)
10+
11+
React.useEffect(() => {
12+
const searchParams = new URLSearchParams(window.location.search)
13+
if (searchParams.get('connected') === 'true') {
14+
setIsModalOpen(true)
15+
}
16+
}, [])
17+
18+
const handleRemoveParam = React.useCallback(() => {
19+
const url = new URL(window.location.href)
20+
url.searchParams.delete('connected')
21+
window.history.replaceState({}, '', url.toString())
22+
setIsModalOpen(false)
23+
}, [])
24+
25+
const handleSync = React.useCallback(() => {
26+
handleSyncEverything({ syncEverythingEndpoint: links.syncEverything })
27+
handleRemoveParam()
28+
}, [links.syncEverything])
29+
30+
const handleCloseModal = React.useCallback(() => {
31+
handleRemoveParam()
32+
setIsModalOpen(false)
33+
}, [])
34+
35+
return (
36+
<Modal
37+
className="m-generic-confirmation"
38+
onClose={handleCloseModal}
39+
open={isModalOpen}
40+
>
41+
<div className="flex flex-col items-start max-w-[540px] mx-auto pb-1">
42+
<div className="flex gap-20 items-center mb-8">
43+
<Icon
44+
icon="exercism-face"
45+
category="icons"
46+
alt="Exercism"
47+
className="mb-16 h-[64px]"
48+
/>
49+
<Icon
50+
icon="sync"
51+
category="graphics"
52+
alt="Sync with"
53+
className="mb-16 h-[64px]"
54+
/>
55+
<Icon
56+
icon="external-site-github"
57+
category="icons"
58+
alt="Github"
59+
className="mb-16 h-[64px]"
60+
/>
61+
</div>
62+
<h3 className="!text-[24px] !mb-8">
63+
Repository connected successfully!
64+
</h3>
65+
<p className="!text-18 leading-140 mb-8">
66+
We've connected your Exercism account to your chosen repository.
67+
</p>
68+
<p className="!text-18 leading-140 mb-12">
69+
If you're happy with the defaults, you can back everything up now. Or
70+
you can tweak your settings, then use the button at the bottom of the
71+
settings page to back up later. Do you want to backup everything now?
72+
</p>
73+
</div>
74+
75+
<div className="flex gap-8 items-center">
76+
<button className="btn btn-l btn-primary w-fit" onClick={handleSync}>
77+
Back up everything now
78+
</button>
79+
<button className="btn btn-default btn-l" onClick={handleCloseModal}>
80+
Back up later
81+
</button>
82+
</div>
83+
</Modal>
84+
)
85+
}

0 commit comments

Comments
 (0)