From 5fb32ab6f44abd3ff7994b42a69e6899435fa099 Mon Sep 17 00:00:00 2001 From: paytondev <81662879+paytontech@users.noreply.github.com> Date: Tue, 6 May 2025 09:37:51 -0400 Subject: [PATCH 01/11] basic sharing --- src/api/devices.ts | 18 +++++++ src/components/material/Icon.tsx | 2 +- .../dashboard/activities/SettingsActivity.tsx | 48 ++++++++++++++++++- 3 files changed, 65 insertions(+), 3 deletions(-) diff --git a/src/api/devices.ts b/src/api/devices.ts index 95be84fa5..da8913526 100644 --- a/src/api/devices.ts +++ b/src/api/devices.ts @@ -74,6 +74,24 @@ export const unpairDevice = async (dongleId: string) => method: 'POST', }) +export const shareDevice = async (dongleId: string, email: string) => + fetcher<{ success: number }>(`/v1/devices/${dongleId}/add_user`, { + method: 'POST', + body: JSON.stringify({ email: email }), + headers: { + 'Content-Type': 'application/json', + }, + }) + +export const unshareDevice = async (dongleId: string, email: string) => + fetcher<{ success: number }>(`/v1/devices/${dongleId}/del_user`, { + method: 'POST', + body: JSON.stringify({ email: email }), + }) + +export const getDeviceUsers = async (dongleId: string): Promise<{ email: string; permission: string }[]> => + fetcher<{ email: string; permission: string }[]>(`/v1/devices/${dongleId}/users`) + const validatePairToken = ( input: string, ): { diff --git a/src/components/material/Icon.tsx b/src/components/material/Icon.tsx index dd0968bb7..42c35c3b7 100644 --- a/src/components/material/Icon.tsx +++ b/src/components/material/Icon.tsx @@ -8,7 +8,7 @@ export const Icons = [ 'add', 'arrow_back', 'camera', 'check', 'chevron_right', 'clear', 'close', 'delete', 'description', 'directions_car', 'download', 'error', 'file_copy', 'flag', 'info', 'keyboard_arrow_down', 'keyboard_arrow_up', 'local_fire_department', 'logout', 'menu', 'my_location', 'open_in_new', 'payments', 'person', 'progress_activity', 'satellite_alt', 'search', 'settings', 'upload', 'videocam', 'refresh', - 'login', 'person_off', 'autorenew', 'close_small', 'pause', 'play_arrow', 'clear_all', + 'login', 'person_off', 'autorenew', 'close_small', 'pause', 'play_arrow', 'clear_all', 'share' ] as const export type IconName = (typeof Icons)[number] diff --git a/src/pages/dashboard/activities/SettingsActivity.tsx b/src/pages/dashboard/activities/SettingsActivity.tsx index ec046fc86..577001dad 100644 --- a/src/pages/dashboard/activities/SettingsActivity.tsx +++ b/src/pages/dashboard/activities/SettingsActivity.tsx @@ -3,7 +3,7 @@ import type { Accessor, VoidComponent, Setter, ParentComponent, Resource, JSXEle import { useLocation } from '@solidjs/router' import clsx from 'clsx' -import { getDevice, unpairDevice } from '~/api/devices' +import { getDevice, getDeviceUsers, shareDevice, unpairDevice } from '~/api/devices' import { cancelSubscription, getStripeCheckout, @@ -22,6 +22,7 @@ import IconButton from '~/components/material/IconButton' import TopAppBar from '~/components/material/TopAppBar' import { createQuery } from '~/utils/createQuery' import { getDeviceName } from '~/utils/device' +import TextField from '~/components/material/TextField' const useAction = (action: () => Promise): [() => void, Resource] => { const [source, setSource] = createSignal(false) @@ -401,15 +402,58 @@ const PrimeManage: VoidComponent<{ dongleId: string }> = (props) => { const DeviceSettingsForm: VoidComponent<{ dongleId: string; device: Resource }> = (props) => { const [deviceName] = createResource(props.device, getDeviceName) - + const [deviceUsers, { refetch: refetchDeviceUsers }] = createResource(props.dongleId, getDeviceUsers) + const [shareEmail, setShareEmail] = createSignal('') const [unpair, unpairData] = useAction(async () => { const { success } = await unpairDevice(props.dongleId) if (success) window.location.href = window.location.origin }) + const share = async () => { + console.log(shareEmail()) + const { success } = await shareDevice(props.dongleId, shareEmail()) + //update deviceUsers + if (success) refetchDeviceUsers() + clearShareEmail() + } + + const updateShareEmail = () => { + const emailBox = document.getElementById('email-box') as HTMLInputElement + setShareEmail(emailBox.value) + } + + const clearShareEmail = () => { + const emailBox = document.getElementById('email-box') as HTMLInputElement + emailBox.value = '' + setShareEmail('') + } return (

{deviceName()}

+
+ shared with: + loading
}> + {(user, _index) => ( + +
{user.email}
+
+ )} + +
+ { + updateShareEmail() + }} + /> + +
+
+
From 2120eb2b3d02db08a969890e79f4b66265b9e5ee Mon Sep 17 00:00:00 2001 From: paytondev <81662879+paytontech@users.noreply.github.com> Date: Tue, 6 May 2025 15:57:53 -0400 Subject: [PATCH 02/11] add unsharing && ui fixes --- src/api/devices.ts | 3 +++ .../dashboard/activities/SettingsActivity.tsx | 21 ++++++++++++++----- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/api/devices.ts b/src/api/devices.ts index da8913526..7fc0f9312 100644 --- a/src/api/devices.ts +++ b/src/api/devices.ts @@ -87,6 +87,9 @@ export const unshareDevice = async (dongleId: string, email: string) => fetcher<{ success: number }>(`/v1/devices/${dongleId}/del_user`, { method: 'POST', body: JSON.stringify({ email: email }), + headers: { + 'Content-Type': 'application/json', + }, }) export const getDeviceUsers = async (dongleId: string): Promise<{ email: string; permission: string }[]> => diff --git a/src/pages/dashboard/activities/SettingsActivity.tsx b/src/pages/dashboard/activities/SettingsActivity.tsx index 577001dad..e6aa43f6c 100644 --- a/src/pages/dashboard/activities/SettingsActivity.tsx +++ b/src/pages/dashboard/activities/SettingsActivity.tsx @@ -3,7 +3,7 @@ import type { Accessor, VoidComponent, Setter, ParentComponent, Resource, JSXEle import { useLocation } from '@solidjs/router' import clsx from 'clsx' -import { getDevice, getDeviceUsers, shareDevice, unpairDevice } from '~/api/devices' +import { getDevice, getDeviceUsers, shareDevice, unpairDevice, unshareDevice } from '~/api/devices' import { cancelSubscription, getStripeCheckout, @@ -409,6 +409,7 @@ const DeviceSettingsForm: VoidComponent<{ dongleId: string; device: Resource { + updateShareEmail() console.log(shareEmail()) const { success } = await shareDevice(props.dongleId, shareEmail()) //update deviceUsers @@ -427,19 +428,29 @@ const DeviceSettingsForm: VoidComponent<{ dongleId: string; device: Resource { + const { success } = await unshareDevice(props.dongleId, email) + if (success) refetchDeviceUsers() + } + return (

{deviceName()}

-
- shared with: +
+

{((deviceUsers() || []).length - 1) > 0 ? "shared with:" : "share device"}

loading
}> {(user, _index) => ( -
{user.email}
+
+
{user.email}
+ +
)} -
+
Date: Tue, 6 May 2025 16:03:00 -0400 Subject: [PATCH 03/11] only show if owner --- src/pages/dashboard/activities/SettingsActivity.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/pages/dashboard/activities/SettingsActivity.tsx b/src/pages/dashboard/activities/SettingsActivity.tsx index e6aa43f6c..b0b1643d2 100644 --- a/src/pages/dashboard/activities/SettingsActivity.tsx +++ b/src/pages/dashboard/activities/SettingsActivity.tsx @@ -436,6 +436,7 @@ const DeviceSettingsForm: VoidComponent<{ dongleId: string; device: Resource

{deviceName()}

+

{((deviceUsers() || []).length - 1) > 0 ? "shared with:" : "share device"}

loading
}> @@ -464,6 +465,7 @@ const DeviceSettingsForm: VoidComponent<{ dongleId: string; device: Resource
+
From a30d59cb07b154f8b7c71db8082ff50a32bde7e7 Mon Sep 17 00:00:00 2001 From: paytondev <81662879+paytontech@users.noreply.github.com> Date: Tue, 6 May 2025 16:08:30 -0400 Subject: [PATCH 04/11] fix indentation --- .../dashboard/activities/SettingsActivity.tsx | 54 +++++++++---------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/src/pages/dashboard/activities/SettingsActivity.tsx b/src/pages/dashboard/activities/SettingsActivity.tsx index b0b1643d2..105533a98 100644 --- a/src/pages/dashboard/activities/SettingsActivity.tsx +++ b/src/pages/dashboard/activities/SettingsActivity.tsx @@ -437,34 +437,34 @@ const DeviceSettingsForm: VoidComponent<{ dongleId: string; device: Resource

{deviceName()}

-
-

{((deviceUsers() || []).length - 1) > 0 ? "shared with:" : "share device"}

- loading
}> - {(user, _index) => ( - -
-
{user.email}
- -
-
- )} - -
- { - updateShareEmail() - }} - /> - +
+

{(deviceUsers() || []).length - 1 > 0 ? 'shared with:' : 'share device'}

+ loading
}> + {(user, _index) => ( + +
+
{user.email}
+ +
+
+ )} + +
+ { + updateShareEmail() + }} + /> + +
-
From ebc78a300c85fe21c3db476879953b9dc5816f2e Mon Sep 17 00:00:00 2001 From: paytondev <81662879+paytontech@users.noreply.github.com> Date: Tue, 6 May 2025 19:15:57 -0400 Subject: [PATCH 05/11] loading states --- .../dashboard/activities/SettingsActivity.tsx | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/pages/dashboard/activities/SettingsActivity.tsx b/src/pages/dashboard/activities/SettingsActivity.tsx index 105533a98..9dbe1ff4f 100644 --- a/src/pages/dashboard/activities/SettingsActivity.tsx +++ b/src/pages/dashboard/activities/SettingsActivity.tsx @@ -408,14 +408,12 @@ const DeviceSettingsForm: VoidComponent<{ dongleId: string; device: Resource { + const [share, shareData] = useAction(async () => { updateShareEmail() - console.log(shareEmail()) const { success } = await shareDevice(props.dongleId, shareEmail()) - //update deviceUsers if (success) refetchDeviceUsers() clearShareEmail() - } + }) const updateShareEmail = () => { const emailBox = document.getElementById('email-box') as HTMLInputElement @@ -428,9 +426,13 @@ const DeviceSettingsForm: VoidComponent<{ dongleId: string; device: Resource { + setUnshareLoading(true) const { success } = await unshareDevice(props.dongleId, email) if (success) refetchDeviceUsers() + setUnshareLoading(false) } return ( @@ -444,7 +446,7 @@ const DeviceSettingsForm: VoidComponent<{ dongleId: string; device: Resource
{user.email}
-
@@ -456,11 +458,8 @@ const DeviceSettingsForm: VoidComponent<{ dongleId: string; device: Resource { - updateShareEmail() - }} /> -
From 54bdeb53d24d50cbb239e672339cc0b39a440365 Mon Sep 17 00:00:00 2001 From: paytondev <81662879+paytontech@users.noreply.github.com> Date: Tue, 6 May 2025 19:17:58 -0400 Subject: [PATCH 06/11] reduce line count --- src/pages/dashboard/activities/SettingsActivity.tsx | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/pages/dashboard/activities/SettingsActivity.tsx b/src/pages/dashboard/activities/SettingsActivity.tsx index 9dbe1ff4f..232482554 100644 --- a/src/pages/dashboard/activities/SettingsActivity.tsx +++ b/src/pages/dashboard/activities/SettingsActivity.tsx @@ -454,11 +454,7 @@ const DeviceSettingsForm: VoidComponent<{ dongleId: string; device: Resource
- + From 9f4e25b6e4e598e16e37b3b832f86ee01367964a Mon Sep 17 00:00:00 2001 From: paytondev <81662879+paytontech@users.noreply.github.com> Date: Wed, 7 May 2025 10:27:26 -0400 Subject: [PATCH 07/11] func names match API, textfield onInput fixes, design updates --- src/api/devices.ts | 4 +- src/components/material/TextField.tsx | 19 ++++----- .../dashboard/activities/SettingsActivity.tsx | 41 ++++++++----------- 3 files changed, 28 insertions(+), 36 deletions(-) diff --git a/src/api/devices.ts b/src/api/devices.ts index 7fc0f9312..8729c7c27 100644 --- a/src/api/devices.ts +++ b/src/api/devices.ts @@ -74,7 +74,7 @@ export const unpairDevice = async (dongleId: string) => method: 'POST', }) -export const shareDevice = async (dongleId: string, email: string) => +export const grantDeviceReadPermission = async (dongleId: string, email: string) => fetcher<{ success: number }>(`/v1/devices/${dongleId}/add_user`, { method: 'POST', body: JSON.stringify({ email: email }), @@ -83,7 +83,7 @@ export const shareDevice = async (dongleId: string, email: string) => }, }) -export const unshareDevice = async (dongleId: string, email: string) => +export const removeDeviceReadPermission = async (dongleId: string, email: string) => fetcher<{ success: number }>(`/v1/devices/${dongleId}/del_user`, { method: 'POST', body: JSON.stringify({ email: email }), diff --git a/src/components/material/TextField.tsx b/src/components/material/TextField.tsx index aac5c4b6b..71361bee2 100644 --- a/src/components/material/TextField.tsx +++ b/src/components/material/TextField.tsx @@ -1,4 +1,4 @@ -import { Show, createEffect, createSignal, splitProps, type Component, type JSX } from 'solid-js' +import { Show, createSignal, splitProps, type Component, type JSX } from 'solid-js' import clsx from 'clsx' type TextFieldProps = { @@ -7,7 +7,8 @@ type TextFieldProps = { helperText?: string error?: string value?: string -} & Omit, 'class'> + onInput?: (value: string) => void +} & Omit, 'class' | 'onInput'> const stateColors = { base: { @@ -40,14 +41,8 @@ const TextField: Component = (props) => { const [focused, setFocused] = createSignal(false) const [hovered, setHovered] = createSignal(false) - const [inputValue, setInputValue] = createSignal(props.value || '') - // Keep local value in sync with prop value - createEffect(() => { - if (props.value) setInputValue(props.value) - }) - - const labelFloating = () => focused() || inputValue()?.length > 0 + const labelFloating = () => focused() || (props.value?.length || 0) > 0 const getStateStyle = () => { const state = { ...stateColors.base } @@ -100,8 +95,10 @@ const TextField: Component = (props) => { getStateStyle().input, props.label && labelFloating() && 'pt-6 pb-2', )} - value={inputValue()} - onInput={(e) => setInputValue(e.target.value)} + value={props.value} + onInput={(e) => { + if (props.onInput) props.onInput(e.target.value) + }} onFocus={() => setFocused(true)} onBlur={() => setFocused(false)} /> diff --git a/src/pages/dashboard/activities/SettingsActivity.tsx b/src/pages/dashboard/activities/SettingsActivity.tsx index 232482554..d7b38c60c 100644 --- a/src/pages/dashboard/activities/SettingsActivity.tsx +++ b/src/pages/dashboard/activities/SettingsActivity.tsx @@ -3,7 +3,7 @@ import type { Accessor, VoidComponent, Setter, ParentComponent, Resource, JSXEle import { useLocation } from '@solidjs/router' import clsx from 'clsx' -import { getDevice, getDeviceUsers, shareDevice, unpairDevice, unshareDevice } from '~/api/devices' +import { getDevice, getDeviceUsers, grantDeviceReadPermission, unpairDevice, removeDeviceReadPermission } from '~/api/devices' import { cancelSubscription, getStripeCheckout, @@ -409,28 +409,16 @@ const DeviceSettingsForm: VoidComponent<{ dongleId: string; device: Resource { - updateShareEmail() - const { success } = await shareDevice(props.dongleId, shareEmail()) + const { success } = await grantDeviceReadPermission(props.dongleId, shareEmail()) if (success) refetchDeviceUsers() - clearShareEmail() - }) - - const updateShareEmail = () => { - const emailBox = document.getElementById('email-box') as HTMLInputElement - setShareEmail(emailBox.value) - } - - const clearShareEmail = () => { - const emailBox = document.getElementById('email-box') as HTMLInputElement - emailBox.value = '' setShareEmail('') - } + }) const [unshareLoading, setUnshareLoading] = createSignal(false) const unshare = async (email: string) => { setUnshareLoading(true) - const { success } = await unshareDevice(props.dongleId, email) + const { success } = await removeDeviceReadPermission(props.dongleId, email) if (success) refetchDeviceUsers() setUnshareLoading(false) } @@ -444,19 +432,26 @@ const DeviceSettingsForm: VoidComponent<{ dongleId: string; device: Resourceloading
}> {(user, _index) => ( -
+
{user.email}
-
)} -
- -
From 575993bf670c275a493a64a8d854a0de15ed98f3 Mon Sep 17 00:00:00 2001 From: paytondev <81662879+paytontech@users.noreply.github.com> Date: Thu, 8 May 2025 16:47:53 -0400 Subject: [PATCH 08/11] use native textfield onInput + design changes --- src/components/material/TextField.tsx | 6 +----- src/pages/dashboard/activities/SettingsActivity.tsx | 5 ++--- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/components/material/TextField.tsx b/src/components/material/TextField.tsx index 71361bee2..611b95364 100644 --- a/src/components/material/TextField.tsx +++ b/src/components/material/TextField.tsx @@ -7,8 +7,7 @@ type TextFieldProps = { helperText?: string error?: string value?: string - onInput?: (value: string) => void -} & Omit, 'class' | 'onInput'> +} & Omit, 'class'> const stateColors = { base: { @@ -96,9 +95,6 @@ const TextField: Component = (props) => { props.label && labelFloating() && 'pt-6 pb-2', )} value={props.value} - onInput={(e) => { - if (props.onInput) props.onInput(e.target.value) - }} onFocus={() => setFocused(true)} onBlur={() => setFocused(false)} /> diff --git a/src/pages/dashboard/activities/SettingsActivity.tsx b/src/pages/dashboard/activities/SettingsActivity.tsx index d7b38c60c..3ec2c8b20 100644 --- a/src/pages/dashboard/activities/SettingsActivity.tsx +++ b/src/pages/dashboard/activities/SettingsActivity.tsx @@ -446,9 +446,8 @@ const DeviceSettingsForm: VoidComponent<{ dongleId: string; device: Resource { - setShareEmail(val) - }} + class="w-full" + onInput={(e) => setShareEmail(e.currentTarget.value)} /> -
+
From 842e19adf861791e4cd18b888603a4dfe97a4ab4 Mon Sep 17 00:00:00 2001 From: paytondev <81662879+paytontech@users.noreply.github.com> Date: Thu, 8 May 2025 19:39:28 -0400 Subject: [PATCH 10/11] fix formatting --- src/pages/dashboard/activities/SettingsActivity.tsx | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/pages/dashboard/activities/SettingsActivity.tsx b/src/pages/dashboard/activities/SettingsActivity.tsx index a2015f4d2..d8aef2d2b 100644 --- a/src/pages/dashboard/activities/SettingsActivity.tsx +++ b/src/pages/dashboard/activities/SettingsActivity.tsx @@ -448,12 +448,7 @@ const DeviceSettingsForm: VoidComponent<{ dongleId: string; device: Resource )} -
+