Skip to content

Commit

Permalink
Improved clips (#171)
Browse files Browse the repository at this point in the history
  • Loading branch information
lemonmade authored Aug 29, 2022
1 parent c43c521 commit bf2f0b9
Show file tree
Hide file tree
Showing 59 changed files with 937 additions and 517 deletions.
7 changes: 7 additions & 0 deletions .changeset/tough-coins-complain.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@watching/cli': patch
'@watching/clips': patch
'@watching/clips-react': patch
---

Improved thread state mechanism
48 changes: 18 additions & 30 deletions app/components/Clip/Clip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,21 +29,23 @@ import type {
JSON as GraphQlJSON,
} from '~/graphql/types';
import {useQuery, useMutation} from '~/shared/graphql';
import {useRenderSandbox, useLocalDevelopmentServer} from '~/shared/clips';
import {useRenderSandbox, useLocalDevelopmentServerQuery} from '~/shared/clips';
import type {
RenderController,
ExtensionSandbox,
LocalExtension,
} from '~/shared/clips';
import type {ArrayElement, ThenType} from '~/shared/types';

import clipsExtensionSettingsQuery from './graphql/ClipExtensionSettingsQuery.graphql';
import type {ClipExtensionSettingsQueryData} from './graphql/ClipExtensionSettingsQuery.graphql';
import clipsExtensionSettingsQuery, {
type ClipExtensionSettingsQueryData,
} from './graphql/ClipExtensionSettingsQuery.graphql';
import updateClipsExtensionSettingsMutation from './graphql/UpdateClipsExtensionSettingsMutation.graphql';
import type {InstalledClipExtensionFragmentData} from './graphql/InstalledClipExtensionFragment.graphql';
import {type InstalledClipExtensionFragmentData} from './graphql/InstalledClipExtensionFragment.graphql';
import uninstallClipsExtensionFromClipMutation from './graphql/UninstallClipsExtensionFromClipMutation.graphql';
import localClipQuery from './graphql/LocalClipQuery.graphql';
import type {LocalClipQueryData} from './graphql/LocalClipQuery.graphql';
import localClipQuery, {
type LocalClipQueryData,
} from './graphql/LocalClipQuery.graphql';

type ReactComponentsForRuntimeExtension<T extends ExtensionPoint> = {
[Identifier in IdentifierForRemoteComponent<
Expand Down Expand Up @@ -97,36 +99,22 @@ export function InstalledClip<T extends ExtensionPoint>({
);
}

const DEFAULT_BUILD_STATE = {
__typename: 'ExtensionBuildInProgress',
id: '0',
startedAt: new Date().toISOString(),
} as const;

export function LocalClip<T extends ExtensionPoint>({
id,
api,
name,
extensionPoint,
}: LocalExtension & Pick<Props<T>, 'api' | 'extensionPoint'>) {
const {query} = useLocalDevelopmentServer();
const [buildState, setBuildState] =
useState<LocalClipQueryData.App.ClipsExtension.Build>({
__typename: 'ExtensionBuildInProgress',
id: '0',
startedAt: new Date().toISOString(),
});

useEffect(() => {
const abort = new AbortController();

(async () => {
for await (const result of query(localClipQuery, {
signal: abort.signal,
variables: {id},
})) {
const extension = result.data?.app?.clipsExtension;
if (extension == null) continue;
setBuildState(extension.build);
}
})();

return () => abort.abort();
}, [query, id]);
const buildState =
useLocalDevelopmentServerQuery(localClipQuery, {
variables: {id},
})?.data?.app?.clipsExtension?.build ?? DEFAULT_BUILD_STATE;

const assetRef = useRef<string>();

Expand Down
126 changes: 99 additions & 27 deletions app/features/Developer/Console/Console.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,29 @@
import {useEffect, useState} from 'react';
// import type {ReactNode} from 'react';
import {useState} from 'react';

import {useCurrentUrl} from '@quilted/quilt';
import {BlockStack, TextField, Button, Form, TextBlock} from '@lemon/zest';
import {
BlockStack,
TextField,
Button,
Form,
TextBlock,
Heading,
Section,
Link,
Text,
Icon,
} from '@lemon/zest';

import {Page} from '~/components';
import {useLocalDevelopmentServer} from '~/shared/clips';
import type {LocalDevelopmentServer} from '~/shared/clips';
import {
useLocalDevelopmentServer,
useLocalDevelopmentServerQuery,
type LocalDevelopmentServer,
} from '~/shared/clips';

import developerConsoleQuery from './graphql/DeveloperConsoleQuery.graphql';
import developerConsoleQuery, {
type DeveloperConsoleQueryData,
} from './graphql/DeveloperConsoleQuery.graphql';

export function Console() {
const developmentServer = useLocalDevelopmentServer({required: false});
Expand All @@ -25,32 +40,89 @@ export function Console() {
}

function ConnectedConsole({server}: {server: LocalDevelopmentServer}) {
const [data, setData] = useState<any>();

useEffect(() => {
const abort = new AbortController();

(async () => {
for await (const result of server.query(developerConsoleQuery, {
signal: abort.signal,
})) {
setData(result);
}
})();

return () => {
abort.abort();
};
}, [server]);
const {data} = useLocalDevelopmentServerQuery(developerConsoleQuery);

return (
<BlockStack>
<TextBlock>Connected to: {server.url.href}</TextBlock>
<pre>{JSON.stringify(data, null, 2)}</pre>
<BlockStack spacing="large">
<TextBlock>
<Text accessibilityRole="code">{server.url.href}</Text>
</TextBlock>
{data?.app.extensions.map((extension) => {
if (extension.__typename !== 'ClipsExtension') return null;

return (
<Section key={extension.id}>
<BlockStack>
<BlockStack spacing="small">
<Heading>{extension.name}</Heading>
<ExtensionBuildResult build={extension.build} />
</BlockStack>
<BlockStack spacing="small">
{extension.extends.map(({target, conditions, preview}) => {
return (
<Link
to={preview.url}
key={target}
padding="small"
border="base"
cornerRadius={4}
background="#222"
accessory={<Icon source="arrowEnd" />}
>
<BlockStack spacing="tiny">
<Text accessibilityRole="code">
{target
.split('.')
.join('.' + String.fromCharCode(8203))}
</Text>
{conditions.length > 0 &&
conditions.map(({series}) => {
if (series == null) return null;
return (
<TextBlock key={`series:${series.handle}`}>
<Text emphasis="subdued">Series: </Text>
<Text emphasis="strong">{series.handle}</Text>
</TextBlock>
);
})}
</BlockStack>
</Link>
);
})}
</BlockStack>
</BlockStack>
</Section>
);
})}
</BlockStack>
);
}

function ExtensionBuildResult({
build,
}: {
build: DeveloperConsoleQueryData.App.Extensions_ClipsExtension.Build;
}) {
switch (build.__typename) {
case 'ExtensionBuildSuccess': {
return <TextBlock>Build succeeded in {build.duration}ms</TextBlock>;
}
case 'ExtensionBuildInProgress': {
return <TextBlock>Build in progress</TextBlock>;
}
case 'ExtensionBuildError': {
return (
<TextBlock>
Build failed in {build.duration}ms: {build.error}
</TextBlock>
);
}
default: {
return null;
}
}
}

function ConnectToConsole() {
const currentUrl = useCurrentUrl();
const [localUrl, setLocalUrl] = useState<string>('');
Expand All @@ -59,7 +131,7 @@ function ConnectToConsole() {
if (!localUrl) return;

const normalizedUrl = new URL('/connect', localUrl);
normalizedUrl.protocol = 'ws:';
normalizedUrl.protocol = normalizedUrl.protocol.replace(/^http/, 'ws');

const targetUrl = new URL(currentUrl);
targetUrl.searchParams.set('connect', normalizedUrl.href);
Expand Down
60 changes: 36 additions & 24 deletions app/features/Series/Series.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,11 @@ import {
Text,
Section,
Heading,
TextBlock,
Link,
Icon,
} from '@lemon/zest';

import {
Link,
LocalClip,
InstalledClip,
Page,
SpoilerAvoidance,
} from '~/components';
import {LocalClip, InstalledClip, Page, SpoilerAvoidance} from '~/components';
import type {ClipProps} from '~/components';

import {parseGid, useQuery, useMutation} from '~/shared/graphql';
Expand Down Expand Up @@ -182,23 +177,40 @@ function SeriesWithData({
</View>
{watchThroughs.length > 0 && (
<Section>
<Heading>Watchthroughs</Heading>
<BlockStack>
<Heading>Watchthroughs</Heading>
{watchThroughs.map((watchThrough) => (
<BlockStack key={watchThrough.id}>
<TextBlock>
From <EpisodeSliceText {...watchThrough.from} />, to{' '}
<EpisodeSliceText {...watchThrough.to} />
{watchThrough.status === 'ONGOING'
? ' (still watching)'
: ''}
</TextBlock>
<Link
to={`/app/watchthrough/${parseGid(watchThrough.id).id}`}
>
See watch through
</Link>
</BlockStack>
<Link
key={watchThrough.id}
to={`/app/watchthrough/${parseGid(watchThrough.id).id}`}
border="base"
padding="base"
cornerRadius={4}
background="#222"
accessory={<Icon source="arrowEnd" />}
>
<BlockStack spacing="tiny">
<Text>
{watchThrough.from.season === watchThrough.to.season ? (
`Season ${watchThrough.from.season}`
) : (
<>
From <EpisodeSliceText {...watchThrough.from} /> to{' '}
<EpisodeSliceText {...watchThrough.to} />
</>
)}
</Text>
<Text emphasis="subdued">
{watchThrough.finishedAt
? `Finished on ${new Intl.DateTimeFormat().format(
new Date(watchThrough.finishedAt),
)}`
: watchThrough.unfinishedEpisodeCount > 0
? `${watchThrough.unfinishedEpisodeCount} episodes left`
: 'Still watching'}
</Text>
</BlockStack>
</Link>
))}
</BlockStack>
</Section>
Expand Down Expand Up @@ -288,7 +300,7 @@ function EpisodeSliceText({
return (
<>
season {season}
{episode == null ? '' : `, episode ${episode}`}
{episode == null || episode === 1 ? '' : `, episode ${episode}`}
</>
);
}
2 changes: 2 additions & 0 deletions app/features/Series/graphql/SeriesQuery.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ query Series($id: ID, $handle: String) {
watchThroughs {
id
status
unfinishedEpisodeCount
finishedAt
from {
season
episode
Expand Down
Loading

0 comments on commit bf2f0b9

Please sign in to comment.