Every connection now has distince SubmitFunction for adding/removing connections

This commit is contained in:
Eclypsed
2024-02-12 00:39:15 -05:00
parent cb03d2661b
commit a8241c6e19
5 changed files with 83 additions and 64 deletions

View File

@@ -44,7 +44,7 @@
if (data.redirectLocation) formData.append('redirectLocation', data.redirectLocation)
return async ({ result }) => {
return ({ result }) => {
if (result.type === 'failure') return ($newestAlert = ['warning', result.data?.message])
if (result.type === 'redirect') return goto(result.location)
}

View File

@@ -58,6 +58,7 @@ export const actions: Actions = {
const tokenData: Jellyfin.JFTokens = {
accessToken: authData.AccessToken,
}
const newConnectionResponse = await fetch(`/api/users/${locals.user.id}/connections`, {
method: 'POST',
headers: { apikey: SECRET_INTERNAL_API_KEY },
@@ -69,7 +70,7 @@ export const actions: Actions = {
const newConnection: Jellyfin.JFConnection = await newConnectionResponse.json()
return { newConnection }
},
youtubeMusicLogin: async ({ request }) => {
youtubeMusicLogin: async ({ request, fetch, locals }) => {
const formData = await request.formData()
const { code } = Object.fromEntries(formData)
const client = new google.auth.OAuth2({ clientId: PUBLIC_YOUTUBE_API_CLIENT_ID, clientSecret: YOUTUBE_API_CLIENT_SECRET, redirectUri: 'http://localhost:5173' }) // DO NOT SHIP THIS. THE CLIENT SECRET SHOULD NOT BE MADE AVAILABLE TO USERS. MAKE A REQUEST TO THE LAZULI WEBSITE INSTEAD.
@@ -92,6 +93,17 @@ export const actions: Actions = {
username: userChannel.snippet?.title as string,
profilePicture: userChannel.snippet?.thumbnails?.default?.url as string | undefined,
}
const newConnectionResponse = await fetch(`/api/users/${locals.user.id}/connections`, {
method: 'POST',
headers: { apikey: SECRET_INTERNAL_API_KEY },
body: JSON.stringify({ service: serviceData, tokens: tokenData }),
})
if (!newConnectionResponse.ok) return fail(500, { message: 'Internal Server Error' })
const newConnection: YouTubeMusic.YTConnection = await newConnectionResponse.json()
return { newConnection }
},
deleteConnection: async ({ request, fetch, locals }) => {
const formData = await request.formData()

View File

@@ -13,78 +13,85 @@
export let data: PageServerData
let connections = data.userConnections
const submitCredentials: SubmitFunction = async ({ formData, action, cancel }) => {
switch (action.search) {
case '?/authenticateJellyfin':
const { serverUrl, username, password } = Object.fromEntries(formData)
const authenticateJellyfin: SubmitFunction = ({ formData, cancel }) => {
const { serverUrl, username, password } = Object.fromEntries(formData)
if (!(serverUrl && username && password)) {
$newestAlert = ['caution', 'All fields must be filled out']
return cancel()
}
try {
formData.set('serverUrl', new URL(serverUrl.toString()).origin)
} catch {
$newestAlert = ['caution', 'Server URL is invalid']
return cancel()
}
const deviceId = getDeviceUUID()
formData.append('deviceId', deviceId)
break
case '?/youtubeMusicLogin':
const code = await youtubeAuthenication()
formData.append('code', code)
break
case '?/deleteConnection':
break
default:
console.log(action.search)
cancel()
if (!(serverUrl && username && password)) {
$newestAlert = ['caution', 'All fields must be filled out']
return cancel()
}
try {
formData.set('serverUrl', new URL(serverUrl.toString()).origin)
} catch {
$newestAlert = ['caution', 'Server URL is invalid']
return cancel()
}
return async ({ result }) => {
const deviceId = getDeviceUUID()
formData.append('deviceId', deviceId)
return ({ result }) => {
if (result.type === 'failure') {
return ($newestAlert = ['warning', result.data?.message])
} else if (result.type === 'success') {
if (result.data?.newConnection) {
const newConnection: Connection = result.data.newConnection
connections = [newConnection, ...connections]
const newConnection: Connection = result.data!.newConnection
connections = [...connections, newConnection]
newConnectionModal = null
return ($newestAlert = ['success', `Added ${Services[newConnection.service.type].displayName}`])
} else if (result.data?.deletedConnectionId) {
const id = result.data.deletedConnectionId
const indexToDelete = connections.findIndex((connection) => connection.id === id)
const serviceType = connections[indexToDelete].service.type
newConnectionModal = null
return ($newestAlert = ['success', `Added ${Services[newConnection.service.type].displayName}`])
}
}
}
connections.splice(indexToDelete, 1)
connections = connections
const authenticateYouTube: SubmitFunction = async ({ formData, cancel }) => {
const googleLoginProcess = (): Promise<string> => {
return new Promise((resolve) => {
// @ts-ignore (google variable is a global variable imported by html script tag)
const client = google.accounts.oauth2.initCodeClient({
client_id: PUBLIC_YOUTUBE_API_CLIENT_ID,
scope: 'https://www.googleapis.com/auth/youtube',
ux_mode: 'popup',
callback: (response: any) => {
resolve(response.code)
},
})
client.requestCode()
})
}
return ($newestAlert = ['success', `Deleted ${Services[serviceType].displayName}`])
}
} else if (result.type === 'redirect') {
window.open(result.location, '_blank')
const code = await googleLoginProcess()
if (!code) cancel()
formData.append('code', code)
return ({ result }) => {
if (result.type === 'failure') {
return ($newestAlert = ['warning', result.data?.message])
} else if (result.type === 'success') {
const newConnection: Connection = result.data!.newConnection
connections = [...connections, newConnection]
return ($newestAlert = ['success', 'Added Youtube Music'])
}
}
}
const deleteConnection: SubmitFunction = () => {
return ({ result }) => {
if (result.type === 'failure') {
return ($newestAlert = ['warning', result.data?.message])
} else if (result.type === 'success') {
const id = result.data!.deletedConnectionId
const indexToDelete = connections.findIndex((connection) => connection.id === id)
const serviceType = connections[indexToDelete].service.type
connections.splice(indexToDelete, 1)
connections = connections
return ($newestAlert = ['success', `Deleted ${Services[serviceType].displayName}`])
}
}
}
let newConnectionModal: ComponentType<SvelteComponent<{ submitFunction: SubmitFunction }>> | null = null
const youtubeAuthenication = async (): Promise<string> => {
return new Promise((resolve) => {
// @ts-ignore (google variable is a global variable imported by html script tag)
const client = google.accounts.oauth2.initCodeClient({
client_id: PUBLIC_YOUTUBE_API_CLIENT_ID,
scope: 'https://www.googleapis.com/auth/youtube',
ux_mode: 'popup',
callback: (response: any) => {
resolve(response.code)
},
})
client.requestCode()
})
}
</script>
<main>
@@ -94,7 +101,7 @@
<button class="add-connection-button h-14 rounded-md" on:click={() => (newConnectionModal = JellyfinAuthBox)}>
<img src={Services.jellyfin.icon} alt="{Services.jellyfin.displayName} icon" class="aspect-square h-full p-2" />
</button>
<form method="post" action="?/youtubeMusicLogin" use:enhance={submitCredentials}>
<form method="post" action="?/youtubeMusicLogin" use:enhance={authenticateYouTube}>
<button class="add-connection-button h-14 rounded-md">
<img src={Services['youtube-music'].icon} alt="{Services['youtube-music'].displayName} icon" class="aspect-square h-full p-2" />
</button>
@@ -103,11 +110,11 @@
</section>
<div class="grid gap-8">
{#each connections as connection}
<ConnectionProfile {connection} submitFunction={submitCredentials} />
<ConnectionProfile {connection} submitFunction={deleteConnection} />
{/each}
</div>
{#if newConnectionModal !== null}
<svelte:component this={newConnectionModal} submitFunction={submitCredentials} on:close={() => (newConnectionModal = null)} />
<svelte:component this={newConnectionModal} submitFunction={authenticateJellyfin} on:close={() => (newConnectionModal = null)} />
{/if}
</main>