Every connection now has distince SubmitFunction for adding/removing connections
This commit is contained in:
2
src/app.d.ts
vendored
2
src/app.d.ts
vendored
@@ -13,7 +13,7 @@ declare global {
|
|||||||
|
|
||||||
// General Interface Desing tips:
|
// General Interface Desing tips:
|
||||||
// Use possibly undefined `?:` for when a property is optional, meaning it could be there, or it could be not applicable
|
// Use possibly undefined `?:` for when a property is optional, meaning it could be there, or it could be not applicable
|
||||||
// Use possibly null `| nulll` for when the property is expected to be there but could possbily be explicitly empty
|
// Use possibly null `| null` for when the property is expected to be there but could possbily be explicitly empty
|
||||||
|
|
||||||
interface User {
|
interface User {
|
||||||
id: string
|
id: string
|
||||||
|
|||||||
Binary file not shown.
@@ -44,7 +44,7 @@
|
|||||||
|
|
||||||
if (data.redirectLocation) formData.append('redirectLocation', data.redirectLocation)
|
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 === 'failure') return ($newestAlert = ['warning', result.data?.message])
|
||||||
if (result.type === 'redirect') return goto(result.location)
|
if (result.type === 'redirect') return goto(result.location)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ export const actions: Actions = {
|
|||||||
const tokenData: Jellyfin.JFTokens = {
|
const tokenData: Jellyfin.JFTokens = {
|
||||||
accessToken: authData.AccessToken,
|
accessToken: authData.AccessToken,
|
||||||
}
|
}
|
||||||
|
|
||||||
const newConnectionResponse = await fetch(`/api/users/${locals.user.id}/connections`, {
|
const newConnectionResponse = await fetch(`/api/users/${locals.user.id}/connections`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { apikey: SECRET_INTERNAL_API_KEY },
|
headers: { apikey: SECRET_INTERNAL_API_KEY },
|
||||||
@@ -69,7 +70,7 @@ export const actions: Actions = {
|
|||||||
const newConnection: Jellyfin.JFConnection = await newConnectionResponse.json()
|
const newConnection: Jellyfin.JFConnection = await newConnectionResponse.json()
|
||||||
return { newConnection }
|
return { newConnection }
|
||||||
},
|
},
|
||||||
youtubeMusicLogin: async ({ request }) => {
|
youtubeMusicLogin: async ({ request, fetch, locals }) => {
|
||||||
const formData = await request.formData()
|
const formData = await request.formData()
|
||||||
const { code } = Object.fromEntries(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.
|
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,
|
username: userChannel.snippet?.title as string,
|
||||||
profilePicture: userChannel.snippet?.thumbnails?.default?.url as string | undefined,
|
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 }) => {
|
deleteConnection: async ({ request, fetch, locals }) => {
|
||||||
const formData = await request.formData()
|
const formData = await request.formData()
|
||||||
|
|||||||
@@ -13,9 +13,7 @@
|
|||||||
export let data: PageServerData
|
export let data: PageServerData
|
||||||
let connections = data.userConnections
|
let connections = data.userConnections
|
||||||
|
|
||||||
const submitCredentials: SubmitFunction = async ({ formData, action, cancel }) => {
|
const authenticateJellyfin: SubmitFunction = ({ formData, cancel }) => {
|
||||||
switch (action.search) {
|
|
||||||
case '?/authenticateJellyfin':
|
|
||||||
const { serverUrl, username, password } = Object.fromEntries(formData)
|
const { serverUrl, username, password } = Object.fromEntries(formData)
|
||||||
|
|
||||||
if (!(serverUrl && username && password)) {
|
if (!(serverUrl && username && password)) {
|
||||||
@@ -31,47 +29,22 @@
|
|||||||
|
|
||||||
const deviceId = getDeviceUUID()
|
const deviceId = getDeviceUUID()
|
||||||
formData.append('deviceId', deviceId)
|
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()
|
|
||||||
}
|
|
||||||
|
|
||||||
return async ({ result }) => {
|
return ({ result }) => {
|
||||||
if (result.type === 'failure') {
|
if (result.type === 'failure') {
|
||||||
return ($newestAlert = ['warning', result.data?.message])
|
return ($newestAlert = ['warning', result.data?.message])
|
||||||
} else if (result.type === 'success') {
|
} else if (result.type === 'success') {
|
||||||
if (result.data?.newConnection) {
|
const newConnection: Connection = result.data!.newConnection
|
||||||
const newConnection: Connection = result.data.newConnection
|
connections = [...connections, newConnection]
|
||||||
connections = [newConnection, ...connections]
|
|
||||||
|
|
||||||
newConnectionModal = null
|
newConnectionModal = null
|
||||||
return ($newestAlert = ['success', `Added ${Services[newConnection.service.type].displayName}`])
|
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
|
|
||||||
|
|
||||||
connections.splice(indexToDelete, 1)
|
|
||||||
connections = connections
|
|
||||||
|
|
||||||
return ($newestAlert = ['success', `Deleted ${Services[serviceType].displayName}`])
|
|
||||||
}
|
|
||||||
} else if (result.type === 'redirect') {
|
|
||||||
window.open(result.location, '_blank')
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let newConnectionModal: ComponentType<SvelteComponent<{ submitFunction: SubmitFunction }>> | null = null
|
const authenticateYouTube: SubmitFunction = async ({ formData, cancel }) => {
|
||||||
|
const googleLoginProcess = (): Promise<string> => {
|
||||||
const youtubeAuthenication = async (): Promise<string> => {
|
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
// @ts-ignore (google variable is a global variable imported by html script tag)
|
// @ts-ignore (google variable is a global variable imported by html script tag)
|
||||||
const client = google.accounts.oauth2.initCodeClient({
|
const client = google.accounts.oauth2.initCodeClient({
|
||||||
@@ -85,6 +58,40 @@
|
|||||||
client.requestCode()
|
client.requestCode()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<main>
|
<main>
|
||||||
@@ -94,7 +101,7 @@
|
|||||||
<button class="add-connection-button h-14 rounded-md" on:click={() => (newConnectionModal = JellyfinAuthBox)}>
|
<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" />
|
<img src={Services.jellyfin.icon} alt="{Services.jellyfin.displayName} icon" class="aspect-square h-full p-2" />
|
||||||
</button>
|
</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">
|
<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" />
|
<img src={Services['youtube-music'].icon} alt="{Services['youtube-music'].displayName} icon" class="aspect-square h-full p-2" />
|
||||||
</button>
|
</button>
|
||||||
@@ -103,11 +110,11 @@
|
|||||||
</section>
|
</section>
|
||||||
<div class="grid gap-8">
|
<div class="grid gap-8">
|
||||||
{#each connections as connection}
|
{#each connections as connection}
|
||||||
<ConnectionProfile {connection} submitFunction={submitCredentials} />
|
<ConnectionProfile {connection} submitFunction={deleteConnection} />
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
{#if newConnectionModal !== null}
|
{#if newConnectionModal !== null}
|
||||||
<svelte:component this={newConnectionModal} submitFunction={submitCredentials} on:close={() => (newConnectionModal = null)} />
|
<svelte:component this={newConnectionModal} submitFunction={authenticateJellyfin} on:close={() => (newConnectionModal = null)} />
|
||||||
{/if}
|
{/if}
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user