Skip to content

Commit 976916c

Browse files
committed
fix: download links and base64 links in ios instead of copying
1 parent 7840753 commit 976916c

File tree

6 files changed

+50
-23
lines changed

6 files changed

+50
-23
lines changed

dashboard/src/components/ActionButtons.tsx

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -235,9 +235,21 @@ const ActionButtons: FC<ActionButtonsProps> = ({ user }) => {
235235

236236
const handleLinksCopy = async (subLink: SubscribeLink) => {
237237
try {
238-
const content = await fetchContent(subLink.link)
239-
copy(content)
240-
toast.success(t('usersTable.copied', { defaultValue: 'Copied to clipboard' }))
238+
if (isIOS()) {
239+
// iOS: redirect/open in new tab instead of copying
240+
const newWindow = window.open(subLink.link, '_blank')
241+
if (!newWindow) {
242+
const content = await fetchContent(subLink.link)
243+
showManualCopyAlert(content, 'url')
244+
} else {
245+
toast.success(t('downloadSuccess', { defaultValue: 'Configuration opened in new tab' }))
246+
}
247+
} else {
248+
// Non-iOS: copy content as before
249+
const content = await fetchContent(subLink.link)
250+
await copy(content)
251+
toast.success(t('usersTable.copied', { defaultValue: 'Copied to clipboard' }))
252+
}
241253
} catch (error) {
242254
console.error('Failed to fetch and copy content:', error)
243255
// Fallback: copy the URL instead
@@ -247,7 +259,7 @@ const ActionButtons: FC<ActionButtonsProps> = ({ user }) => {
247259

248260
const handleUrlCopy = async (url: string) => {
249261
try {
250-
copy(url)
262+
await copy(url)
251263
toast.success(t('usersTable.copied', { defaultValue: 'URL copied to clipboard' }))
252264
} catch (error) {
253265
toast.error(t('copyFailed', { defaultValue: 'Failed to copy content' }))

dashboard/src/components/CopyButton.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,10 @@ export function CopyButton({ value, className, copiedMessage = 'Copied!', defaul
1919

2020
const { copy, copied } = useClipboard({ timeout: 1500 })
2121
const handleCopy = useCallback(
22-
(e: React.MouseEvent) => {
22+
async (e: React.MouseEvent) => {
2323
e.preventDefault()
2424
e.stopPropagation()
25-
copy(value)
25+
await copy(value)
2626
onClick?.(e)
2727
},
2828
[copy, value, onClick],

dashboard/src/components/dialogs/UserAllIPsModal.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ const NodeIPCard = React.memo(({ nodeId, nodeName, ips }: NodeIPCardProps) => {
3232
const { copy } = useClipboard()
3333

3434
const handleCopyIP = useCallback(
35-
(ip: string) => {
36-
copy(ip)
35+
async (ip: string) => {
36+
await copy(ip)
3737
toast.success(t('userAllIPs.ipCopied', { defaultValue: 'IP address copied to clipboard' }))
3838
},
3939
[copy, t],

dashboard/src/components/ui/variables-popover.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ export function VariablesPopover({ includeProtocolTransport = false, side = 'rig
2020
const { t } = useTranslation()
2121
const { copy } = useClipboard()
2222

23-
const handleCopy = (text: string) => {
24-
copy(text)
23+
const handleCopy = async (text: string) => {
24+
await copy(text)
2525
toast.success(t('usersTable.copied'))
2626
}
2727

@@ -82,8 +82,8 @@ export function VariablesList({ includeProtocolTransport = false }: { includePro
8282
const { t } = useTranslation()
8383
const { copy } = useClipboard()
8484

85-
const handleCopy = (text: string) => {
86-
copy(text)
85+
const handleCopy = async (text: string) => {
86+
await copy(text)
8787
toast.success(t('usersTable.copied'))
8888
}
8989

dashboard/src/hooks/use-clipboard.ts

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,23 @@
11
import { useState, useCallback } from 'react'
22

3-
function copyToClipboard(text: string): boolean {
3+
async function copyToClipboard(text: string): Promise<boolean> {
4+
// Try modern clipboard API first (required for iOS)
45
if (navigator.clipboard && window.isSecureContext) {
5-
navigator.clipboard.writeText(text)
6-
return true
6+
try {
7+
await navigator.clipboard.writeText(text)
8+
return true
9+
} catch (err) {
10+
console.error('Clipboard API failed:', err)
11+
// Fall through to fallback method
12+
}
713
}
814

15+
// Fallback: use execCommand for older browsers
916
const input = document.createElement('input')
1017
input.value = text
1118
input.style.position = 'fixed'
1219
input.style.left = '-9999px'
20+
input.style.top = '-9999px'
1321
document.body.appendChild(input)
1422
input.focus()
1523
input.select()
@@ -36,12 +44,19 @@ export function useClipboard({ timeout = 1500 } = {}) {
3644
}
3745

3846
const copy = useCallback(
39-
(text: string) => {
40-
const success = copyToClipboard(text)
41-
if (success) {
42-
handleCopyResult(true)
43-
} else {
44-
setError(new Error('useClipboard: copyToClipboard failed'))
47+
async (text: string) => {
48+
try {
49+
const success = await copyToClipboard(text)
50+
if (success) {
51+
handleCopyResult(true)
52+
setError(null)
53+
} else {
54+
setError(new Error('useClipboard: copyToClipboard failed'))
55+
handleCopyResult(false)
56+
}
57+
} catch (err) {
58+
setError(err instanceof Error ? err : new Error('useClipboard: copyToClipboard failed'))
59+
handleCopyResult(false)
4560
}
4661
},
4762
[timeout],

dashboard/src/pages/_dashboard._index.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -243,9 +243,9 @@ const Dashboard = () => {
243243
queryClient.invalidateQueries({ queryKey: ['/api/users/'] })
244244
}
245245

246-
const handleCreateUserSuccess = (user: UserResponse) => {
246+
const handleCreateUserSuccess = async (user: UserResponse) => {
247247
if (user.subscription_url) {
248-
copy(user.subscription_url)
248+
await copy(user.subscription_url)
249249
toast.success(t('userSettings.subscriptionUrlCopied'))
250250
}
251251
refreshAllUserData()

0 commit comments

Comments
 (0)