Skip to content

Commit

Permalink
feat: add UI to stop device sharing
Browse files Browse the repository at this point in the history
Closes #351
  • Loading branch information
coderbyheart committed Dec 5, 2024
1 parent 1fad340 commit 6210eb2
Show file tree
Hide file tree
Showing 5 changed files with 146 additions and 11 deletions.
3 changes: 1 addition & 2 deletions src/base.css
Original file line number Diff line number Diff line change
Expand Up @@ -319,12 +319,11 @@ input:-webkit-autofill:active {
border-radius: calc(var(--border-radius) / 2);
border-color: var(--color-form-border-default);
background-color: var(--color-form-bg);
height: 32px;
min-height: 32px;
padding: 0 var(--gap-medium);
color: var(--color-form-text);
font-weight: 300;
display: inline-block;
height: 32px;
font-family: var(--condensed-font-family);
}

Expand Down
18 changes: 10 additions & 8 deletions src/dashboard/Card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,18 @@ import type { ParentProps } from 'solid-js'

import './Card.css'

export const Card = (props: ParentProps) => (
<section class="card boxed bg-lighter">{props.children}</section>
export const Card = (props: ParentProps<{ class?: string }>) => (
<section class={`card boxed bg-lighter ${props.class ?? ''}`}>
{props.children}
</section>
)
export const CardHeader = (props: ParentProps) => (
<header class="pad bg-light">{props.children}</header>
export const CardHeader = (props: ParentProps<{ class?: string }>) => (
<header class={`pad bg-light ${props.class ?? ''}`}>{props.children}</header>
)
export const CardBody = (props: ParentProps) => (
<div class="pad">{props.children}</div>
export const CardBody = (props: ParentProps<{ class?: string }>) => (
<div class={`pad ${props.class ?? ''}`}>{props.children}</div>
)

export const CardFooter = (props: ParentProps) => (
<footer class="pad bg-light">{props.children}</footer>
export const CardFooter = (props: ParentProps<{ class?: string }>) => (
<footer class={`pad bg-light ${props.class ?? ''}`}>{props.children}</footer>
)
10 changes: 10 additions & 0 deletions src/dashboard/ShowDevice.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
.stop-sharing {
display: flex;
flex-direction: column;
}

.stop-sharing .confirm {
display: flex;
align-items: center;
justify-content: space-between;
}
107 changes: 106 additions & 1 deletion src/dashboard/ShowDevice.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { useUser } from '#context/User.tsx'
import { useViteEnv } from '#context/ViteEnv.tsx'
import { extendDeviceSharing } from '#resources/extendDeviceSharing.ts'
import { listUserDevices } from '#resources/listUserDevices.ts'
import { stopDeviceSharing } from '#resources/stopDeviceSharing.ts'
import { type ModelID, models } from '@hello.nrfcloud.com/proto-map/models'
import {
createEffect,
Expand All @@ -15,7 +16,7 @@ import {
Show,
Switch,
} from 'solid-js'
import { Card, CardBody, CardHeader } from './Card.tsx'
import { Card, CardBody, CardFooter, CardHeader } from './Card.tsx'
import { CopyableProp } from './CopyableProp.tsx'

const f = new Intl.DateTimeFormat(undefined, {
Expand All @@ -25,6 +26,10 @@ const f = new Intl.DateTimeFormat(undefined, {

const formatAsDate = (d: Date) => f.format(d)

import { Success } from '#component/notifications/Success.tsx'
import { Checked, Unchecked } from '#icons/LucideIcon.tsx'
import './ShowDevice.css'

export const ShowDevice = () => {
const { protoVersion } = useViteEnv()
const deviceId = new URLSearchParams(
Expand Down Expand Up @@ -98,7 +103,26 @@ export const ShowDevice = () => {
>
<Problem class="gap-t" problem={devicesRequest.error} />
</Show>
<Show
when={
!devicesRequest.loading &&
devicesRequest.error === undefined &&
deviceInfo() === undefined
}
>
<Problem
problem={{
status: 404,
title: `Device ${deviceId} not found!`,
}}
/>
</Show>
</CardBody>
<Show when={deviceInfo() !== undefined}>
<CardFooter class="stop-sharing">
<StopSharing id={deviceId!} />
</CardFooter>
</Show>
</Card>
)
}
Expand Down Expand Up @@ -157,3 +181,84 @@ const ExtendSharing = (props: { id: string; expires: Date }) => {
</>
)
}

const StopSharing = (props: { id: string }) => {
const [stop, setStopped] = createSignal(false)
const [unlocked, setUnlocked] = createSignal(false)
const { jwt } = useUser()
const { apiURL } = useParameters()
const [stopRequest] = createResource(() => {
if (!stop()) return undefined
return {
id: props.id,
jwt: jwt()!,
}
}, stopDeviceSharing(apiURL))

createEffect(() => {
if (stopRequest.loading) return
if (stopRequest.state !== 'ready') return
setStopped(true)
})

return (
<>
<p>
You can stop the publication of this device at any time. If you stop the
publication, the device will no longer be visible on the map. You can
start the publication again at any time. It may take a few minutes for
the device to disappear from the map.
</p>
<p class="confirm">
<button
class="pad-e"
type="button"
onClick={() => setUnlocked((l) => !l)}
>
<Show
when={unlocked()}
fallback={<Unchecked strokeWidth={1} size={20} />}
>
<Checked strokeWidth={1} size={20} />
</Show>
Yes, I want to stop the publication of this device.
</button>
<Show
when={unlocked()}
fallback={
<Show
when={!stopRequest.loading}
fallback={
<button type="button" class="btn" disabled>
stopping ...
</button>
}
>
<button type="button" class="btn" disabled>
stop publication
</button>
</Show>
}
>
<button
type="button"
class="btn"
onClick={() => {
setStopped(true)
}}
>
stop publication
</button>
</Show>
</p>
<Switch>
<Match when={!stopRequest.loading && stopRequest.error !== undefined}>
<Problem class="gap-t" problem={stopRequest.error} />
</Match>
<Match when={!stopRequest.loading && stopRequest.state === 'ready'}>
<Success>The publication of this device has been stopped.</Success>
</Match>
</Switch>
</>
)
}
19 changes: 19 additions & 0 deletions src/resources/stopDeviceSharing.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { ProblemDetailError } from '#component/notifications/Problem.tsx'
import { typedFetch } from '@hello.nrfcloud.com/proto/hello'
import { Type } from '@sinclair/typebox'

export const stopDeviceSharing =
(apiURL: URL) =>
async ({ id, jwt }: { id: string; jwt: string }): Promise<void> => {
const res = await typedFetch({
responseBodySchema: Type.Undefined(),
})(new URL(`./user/device/${id}`, apiURL), undefined, {
method: 'DELETE',
headers: { Authorization: `Bearer ${jwt}` },
})
if ('error' in res) {
console.error(res.error)
throw new ProblemDetailError(res.error)
}
return res.result
}

0 comments on commit 6210eb2

Please sign in to comment.