fix(front): try to correct crash on ios
This commit is contained in:
@@ -200,11 +200,7 @@ async function submitTicket() {
|
||||
console.log(`DEBUG - FormData entry: ${key} = ${value}`)
|
||||
}
|
||||
|
||||
await axios.post('/api/tickets/', formData, {
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data',
|
||||
}
|
||||
})
|
||||
await axios.post('/api/tickets/', formData)
|
||||
toast.success('Ticket envoyé avec succès !')
|
||||
closeModal()
|
||||
|
||||
|
||||
@@ -442,10 +442,20 @@ onMounted(async () => {
|
||||
// Demander la permission pour les notifications push
|
||||
// Sur iOS, attendre un peu pour s'assurer que la PWA est bien détectée
|
||||
if (typeof notificationService.isIOS === 'function' && notificationService.isIOS()) {
|
||||
// Attendre que la page soit complètement chargée
|
||||
console.log('🍎 iOS détecté, vérification de la PWA...')
|
||||
// Attendre que la page soit complètement chargée et que la PWA soit détectée
|
||||
setTimeout(async () => {
|
||||
await notificationService.requestNotificationPermission()
|
||||
}, 1000)
|
||||
const isInstalled = notificationService.isPWAInstalled()
|
||||
console.log('🍎 PWA installée:', isInstalled)
|
||||
|
||||
if (isInstalled) {
|
||||
console.log('🍎 Demande de permission pour les notifications...')
|
||||
await notificationService.requestNotificationPermission()
|
||||
} else {
|
||||
console.warn('🍎 PWA non installée - Les notifications ne fonctionneront pas sur iOS')
|
||||
console.warn('🍎 Instructions: Ajouter l\'app à l\'écran d\'accueil, puis l\'ouvrir depuis l\'écran d\'accueil')
|
||||
}
|
||||
}, 2000) // Attendre 2 secondes pour iOS
|
||||
} else {
|
||||
await notificationService.requestNotificationPermission()
|
||||
}
|
||||
|
||||
@@ -130,55 +130,101 @@ class NotificationService {
|
||||
|
||||
// Gestion des notifications push PWA
|
||||
async requestNotificationPermission() {
|
||||
console.log('🔔 requestNotificationPermission appelée')
|
||||
console.log('🔔 isIOS:', this.isIOS())
|
||||
console.log('🔔 isPWAInstalled:', this.isPWAInstalled())
|
||||
console.log('🔔 Notification API disponible:', 'Notification' in window)
|
||||
console.log('🔔 Permission actuelle:', Notification.permission)
|
||||
|
||||
if (!this.isNotificationSupported()) {
|
||||
console.log('Notifications not supported on this platform')
|
||||
console.warn('⚠️ Notifications not supported on this platform')
|
||||
if (this.isIOS()) {
|
||||
if (!this.isPWAInstalled()) {
|
||||
console.warn('⚠️ iOS: PWA must be installed (added to home screen)')
|
||||
}
|
||||
const iosVersion = navigator.userAgent.match(/OS (\d+)_(\d+)/)
|
||||
if (iosVersion) {
|
||||
const major = parseInt(iosVersion[1], 10)
|
||||
const minor = parseInt(iosVersion[2], 10)
|
||||
if (major < 16 || (major === 16 && minor < 4)) {
|
||||
console.warn(`⚠️ iOS: Version ${major}.${minor} - Push notifications require iOS 16.4+`)
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
if (Notification.permission === 'granted') {
|
||||
console.log('Notification permission already granted')
|
||||
console.log('✅ Notification permission already granted')
|
||||
return true
|
||||
}
|
||||
|
||||
if (Notification.permission === 'denied') {
|
||||
console.warn('Notification permission denied by user')
|
||||
console.warn('❌ Notification permission denied by user')
|
||||
return false
|
||||
}
|
||||
|
||||
// Sur iOS, s'assurer que la PWA est installée avant de demander
|
||||
if (this.isIOS() && !this.isPWAInstalled()) {
|
||||
console.warn('iOS: Cannot request notification permission - PWA must be installed first')
|
||||
console.warn('⚠️ iOS: Cannot request notification permission - PWA must be installed first')
|
||||
console.warn('⚠️ Instructions: Add the app to home screen, then open it from home screen')
|
||||
return false
|
||||
}
|
||||
|
||||
try {
|
||||
console.log('🔔 Demande de permission...')
|
||||
const permission = await Notification.requestPermission()
|
||||
const granted = permission === 'granted'
|
||||
|
||||
console.log('🔔 Résultat de la demande:', permission)
|
||||
|
||||
if (granted) {
|
||||
console.log('Notification permission granted')
|
||||
console.log('✅ Notification permission granted')
|
||||
|
||||
// Sur iOS, vérifier que le service worker est prêt
|
||||
if (this.isIOS() && 'serviceWorker' in navigator) {
|
||||
try {
|
||||
const registration = await navigator.serviceWorker.ready
|
||||
console.log('✅ Service worker ready on iOS')
|
||||
|
||||
// Tester une notification pour vérifier que ça fonctionne
|
||||
await registration.showNotification('Test LeDiscord', {
|
||||
body: 'Les notifications sont activées !',
|
||||
icon: '/icon-192x192.png',
|
||||
badge: '/icon-96x96.png',
|
||||
tag: 'test-notification'
|
||||
})
|
||||
console.log('✅ Test notification sent successfully')
|
||||
} catch (error) {
|
||||
console.error('❌ Error testing notification on iOS:', error)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.warn('Notification permission denied:', permission)
|
||||
console.warn('❌ Notification permission denied:', permission)
|
||||
}
|
||||
|
||||
return granted
|
||||
} catch (error) {
|
||||
console.error('Error requesting notification permission:', error)
|
||||
console.error('❌ Error requesting notification permission:', error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
async showPushNotification(title, options = {}) {
|
||||
console.log('🔔 showPushNotification appelée:', { title, options })
|
||||
|
||||
if (!this.isNotificationSupported()) {
|
||||
console.warn('Cannot show notification - not supported on this platform')
|
||||
console.warn('⚠️ Cannot show notification - not supported on this platform')
|
||||
return null
|
||||
}
|
||||
|
||||
const hasPermission = await this.requestNotificationPermission()
|
||||
if (!hasPermission) {
|
||||
console.warn('Cannot show notification - permission not granted')
|
||||
console.warn('⚠️ Cannot show notification - permission not granted')
|
||||
return null
|
||||
}
|
||||
|
||||
console.log('✅ Permission granted, affichage de la notification...')
|
||||
|
||||
// Préparer les options de notification (iOS ne supporte pas vibrate)
|
||||
const notificationOptions = {
|
||||
@@ -203,29 +249,46 @@ class NotificationService {
|
||||
// Cela permet aux notifications de fonctionner même quand l'app est fermée
|
||||
if ('serviceWorker' in navigator) {
|
||||
try {
|
||||
console.log('🔔 Tentative d\'affichage via service worker...')
|
||||
const registration = await navigator.serviceWorker.ready
|
||||
console.log('🔔 Service worker ready, active:', !!registration.active)
|
||||
|
||||
if (!registration.active) {
|
||||
console.warn('Service worker not active, using fallback')
|
||||
console.warn('⚠️ Service worker not active, using fallback')
|
||||
throw new Error('Service worker not active')
|
||||
}
|
||||
|
||||
// Envoyer un message au service worker pour afficher la notification
|
||||
registration.active.postMessage({
|
||||
type: 'SHOW_NOTIFICATION',
|
||||
title,
|
||||
options: notificationOptions
|
||||
})
|
||||
// Sur iOS, utiliser directement l'API du service worker
|
||||
// (les messages peuvent ne pas fonctionner correctement)
|
||||
if (this.isIOS()) {
|
||||
console.log('🔔 iOS: Utilisation directe de showNotification')
|
||||
await registration.showNotification(title, notificationOptions)
|
||||
console.log('✅ Notification affichée via service worker (iOS)')
|
||||
} else {
|
||||
// Envoyer un message au service worker pour afficher la notification
|
||||
registration.active.postMessage({
|
||||
type: 'SHOW_NOTIFICATION',
|
||||
title,
|
||||
options: notificationOptions
|
||||
})
|
||||
|
||||
// Aussi utiliser l'API directe du service worker
|
||||
await registration.showNotification(title, notificationOptions)
|
||||
console.log('✅ Notification affichée via service worker')
|
||||
}
|
||||
|
||||
// Aussi utiliser l'API directe du service worker
|
||||
await registration.showNotification(title, notificationOptions)
|
||||
|
||||
console.log('Notification shown via service worker')
|
||||
return null
|
||||
} catch (error) {
|
||||
console.error('Error showing notification via service worker:', error)
|
||||
console.error('❌ Error showing notification via service worker:', error)
|
||||
console.error('❌ Error details:', {
|
||||
message: error.message,
|
||||
stack: error.stack,
|
||||
name: error.name
|
||||
})
|
||||
// Continuer avec le fallback
|
||||
}
|
||||
} else {
|
||||
console.warn('⚠️ Service worker not available')
|
||||
}
|
||||
|
||||
// Fallback: notification native du navigateur (seulement si le SW n'est pas disponible)
|
||||
@@ -270,6 +333,23 @@ class NotificationService {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Fonction de test pour vérifier que les notifications fonctionnent
|
||||
async testNotification() {
|
||||
console.log('🧪 Test de notification...')
|
||||
const result = await this.showPushNotification('Test LeDiscord', {
|
||||
body: 'Si vous voyez cette notification, les notifications push fonctionnent !',
|
||||
link: '/'
|
||||
})
|
||||
|
||||
if (result) {
|
||||
console.log('✅ Test réussi - notification affichée')
|
||||
return true
|
||||
} else {
|
||||
console.warn('⚠️ Test échoué - notification non affichée')
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default new NotificationService()
|
||||
|
||||
@@ -112,11 +112,7 @@ export const useAuthStore = defineStore('auth', () => {
|
||||
const formData = new FormData()
|
||||
formData.append('file', file)
|
||||
|
||||
const response = await axios.post('/api/users/me/avatar', formData, {
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data'
|
||||
}
|
||||
})
|
||||
const response = await axios.post('/api/users/me/avatar', formData)
|
||||
|
||||
user.value = response.data
|
||||
toast.success('Avatar mis à jour')
|
||||
|
||||
@@ -68,6 +68,16 @@ instance.interceptors.request.use(
|
||||
}
|
||||
}
|
||||
|
||||
// Augmenter le timeout pour les requêtes POST/PUT avec FormData (uploads)
|
||||
if ((config.method === 'POST' || config.method === 'PUT') && config.data instanceof FormData) {
|
||||
config.timeout = 120000 // 2 minutes pour les uploads
|
||||
// Ne pas définir Content-Type pour FormData - laisser le navigateur l'ajouter avec la boundary
|
||||
// C'est crucial sur mobile où définir explicitement le Content-Type peut causer des erreurs
|
||||
if (config.headers && config.headers['Content-Type'] === 'multipart/form-data') {
|
||||
delete config.headers['Content-Type']
|
||||
}
|
||||
}
|
||||
|
||||
return config
|
||||
},
|
||||
error => {
|
||||
@@ -112,12 +122,24 @@ instance.interceptors.response.use(
|
||||
}
|
||||
|
||||
if (error.response?.status === 401) {
|
||||
// Ne pas déconnecter automatiquement sur les requêtes POST/PUT avec FormData
|
||||
// car les erreurs peuvent être dues à des problèmes réseau temporaires sur mobile
|
||||
const isFormDataUpload = error.config?.data instanceof FormData ||
|
||||
(error.config?.method === 'POST' || error.config?.method === 'PUT')
|
||||
|
||||
// Ne pas rediriger si on est déjà sur une page d'auth
|
||||
const currentRoute = router.currentRoute.value
|
||||
if (!currentRoute.path.includes('/login') && !currentRoute.path.includes('/register')) {
|
||||
localStorage.removeItem('token')
|
||||
router.push('/login')
|
||||
toast.error('Session expirée, veuillez vous reconnecter')
|
||||
// Pour les uploads, ne déconnecter que si c'est vraiment une erreur d'authentification
|
||||
// (pas juste une erreur réseau qui se manifeste comme 401)
|
||||
if (!isFormDataUpload || error.response?.data?.detail?.includes('credentials') || error.response?.data?.detail?.includes('token')) {
|
||||
localStorage.removeItem('token')
|
||||
router.push('/login')
|
||||
toast.error('Session expirée, veuillez vous reconnecter')
|
||||
} else {
|
||||
// Pour les uploads, juste afficher une erreur sans déconnecter
|
||||
toast.error('Erreur lors de l\'upload. Veuillez réessayer.')
|
||||
}
|
||||
}
|
||||
} else if (error.response?.status === 403) {
|
||||
toast.error('Accès non autorisé')
|
||||
|
||||
@@ -716,9 +716,7 @@ async function uploadMedia() {
|
||||
formData.append('files', media.file)
|
||||
})
|
||||
|
||||
await axios.post(`/api/albums/${album.value.id}/media`, formData, {
|
||||
headers: { 'Content-Type': 'multipart/form-data' }
|
||||
})
|
||||
await axios.post(`/api/albums/${album.value.id}/media`, formData)
|
||||
|
||||
// Refresh album data
|
||||
await fetchAlbum()
|
||||
|
||||
@@ -654,7 +654,6 @@ async function createAlbum() {
|
||||
})
|
||||
|
||||
await axios.post(`/api/albums/${album.id}/media`, formData, {
|
||||
headers: { 'Content-Type': 'multipart/form-data' },
|
||||
onUploadProgress: (progressEvent) => {
|
||||
// Update progress for this batch
|
||||
const batchProgress = (progressEvent.loaded / progressEvent.total) * 100
|
||||
|
||||
@@ -465,9 +465,7 @@ async function handleImageChange(event) {
|
||||
const formData = new FormData()
|
||||
formData.append('file', file)
|
||||
|
||||
const response = await axios.post('/api/posts/upload-image', formData, {
|
||||
headers: { 'Content-Type': 'multipart/form-data' }
|
||||
})
|
||||
const response = await axios.post('/api/posts/upload-image', formData)
|
||||
|
||||
newPost.value.image_url = response.data.image_url
|
||||
} catch (error) {
|
||||
|
||||
@@ -435,9 +435,7 @@ async function createVlog() {
|
||||
formData.append('thumbnail', newVlog.value.thumbnailFile)
|
||||
}
|
||||
|
||||
const response = await axios.post('/api/vlogs/upload', formData, {
|
||||
headers: { 'Content-Type': 'multipart/form-data' }
|
||||
})
|
||||
const response = await axios.post('/api/vlogs/upload', formData)
|
||||
|
||||
vlogs.value.unshift(response.data)
|
||||
showCreateModal.value = false
|
||||
|
||||
@@ -211,7 +211,11 @@ module.exports = defineConfig(({ command, mode }) => {
|
||||
}
|
||||
},
|
||||
{
|
||||
urlPattern: /^https?:\/\/.*\/api\/.*/i,
|
||||
// Intercepter uniquement les requêtes GET pour l'API
|
||||
urlPattern: ({ url, request }) => {
|
||||
const urlString = url.href || url.toString()
|
||||
return /^https?:\/\/.*\/api\/.*/i.test(urlString) && request.method === 'GET'
|
||||
},
|
||||
handler: 'NetworkFirst',
|
||||
options: {
|
||||
cacheName: 'api-cache',
|
||||
|
||||
Reference in New Issue
Block a user