From a107f6ce22f33719ed8c5fbeb49eb854d3238e9f Mon Sep 17 00:00:00 2001 From: Alec Aivazis Date: Sat, 31 Dec 2022 17:27:45 -0500 Subject: [PATCH] Rename isFetching to fetching (#800) * rename isFetching to fetching * fix e2e test routes * changeset * base64 encode better emojis * use async emoji alternatives * try emoji with just a static image * unused import * rename isFetching in getting started guide --- .changeset/popular-lobsters-collect.md | 6 + e2e/sveltekit/src/lib/utils/routes.ts | 6 +- .../route_1/+page.svelte | 4 +- .../src/routes/fetching/route_2/+page.svelte | 11 ++ .../src/routes/fetching/route_2/+page.ts | 7 + .../routes/{isFetching => fetching}/spec.ts | 36 ++--- .../with_load/+page.svelte | 6 +- .../without_load/+page.svelte | 6 +- .../routes/isFetching/route_2/+page.svelte | 11 -- .../src/routes/isFetching/route_2/+page.ts | 7 - .../src/routes/stores/mutation/spec.ts | 2 +- .../routes/stores/prefetch-[userId]/spec.ts | 2 +- .../routes/stores/ssr-[userId]/+page.svelte | 2 +- .../src/runtime/stores/mutation.ts | 16 +-- .../src/runtime/stores/pagination/cursor.ts | 2 +- .../src/runtime/stores/pagination/fragment.ts | 12 +- .../src/runtime/stores/pagination/offset.ts | 2 +- .../src/runtime/stores/query.ts | 14 +- packages/houdini/src/runtime/lib/types.ts | 2 +- site/src/components/Emoji.svelte | 7 + site/src/components/index.js | 1 + site/src/routes/+page.svelte | 5 +- .../src/routes/guides/release-notes/+page.svx | 131 ++++++++++++------ site/src/routes/intro/fetching-data/+page.svx | 38 ++--- site/static/images/emojis/exploding-head.png | Bin 0 -> 10971 bytes site/static/images/emojis/partying-face.png | Bin 0 -> 12662 bytes 26 files changed, 199 insertions(+), 137 deletions(-) create mode 100644 .changeset/popular-lobsters-collect.md rename e2e/sveltekit/src/routes/{isFetching => fetching}/route_1/+page.svelte (69%) create mode 100644 e2e/sveltekit/src/routes/fetching/route_2/+page.svelte create mode 100644 e2e/sveltekit/src/routes/fetching/route_2/+page.ts rename e2e/sveltekit/src/routes/{isFetching => fetching}/spec.ts (60%) rename e2e/sveltekit/src/routes/{isFetching => fetching}/with_load/+page.svelte (55%) rename e2e/sveltekit/src/routes/{isFetching => fetching}/without_load/+page.svelte (62%) delete mode 100644 e2e/sveltekit/src/routes/isFetching/route_2/+page.svelte delete mode 100644 e2e/sveltekit/src/routes/isFetching/route_2/+page.ts create mode 100644 site/src/components/Emoji.svelte create mode 100644 site/static/images/emojis/exploding-head.png create mode 100644 site/static/images/emojis/partying-face.png diff --git a/.changeset/popular-lobsters-collect.md b/.changeset/popular-lobsters-collect.md new file mode 100644 index 0000000000..afcefe65e1 --- /dev/null +++ b/.changeset/popular-lobsters-collect.md @@ -0,0 +1,6 @@ +--- +'houdini-svelte': major +'houdini': major +--- + +Rename `isFetching` to `fetching` diff --git a/e2e/sveltekit/src/lib/utils/routes.ts b/e2e/sveltekit/src/lib/utils/routes.ts index 7d7ce6eabc..36774c8b22 100644 --- a/e2e/sveltekit/src/lib/utils/routes.ts +++ b/e2e/sveltekit/src/lib/utils/routes.ts @@ -4,9 +4,9 @@ export const routes = { // features nested_routes: '/nested-routes', - isFetching_with_load: '/isFetching/with_load', - isFetching_without_load: '/isFetching/without_load', - isFetching_route_1: '/isFetching/route_1', + fetching_with_load: '/fetching/with_load', + fetching_without_load: '/fetching/without_load', + fetching_route_1: '/fetching/route_1', lists_all: '/lists-all?limit=15', union_result: '/union-result', diff --git a/e2e/sveltekit/src/routes/isFetching/route_1/+page.svelte b/e2e/sveltekit/src/routes/fetching/route_1/+page.svelte similarity index 69% rename from e2e/sveltekit/src/routes/isFetching/route_1/+page.svelte rename to e2e/sveltekit/src/routes/fetching/route_1/+page.svelte index 55e3e6a4d6..35209062ec 100644 --- a/e2e/sveltekit/src/routes/isFetching/route_1/+page.svelte +++ b/e2e/sveltekit/src/routes/fetching/route_1/+page.svelte @@ -2,8 +2,8 @@ import { graphql } from '$houdini'; const store = graphql(` - query isFetching_route_1 { - user(id: 1, snapshot: "isFetching_route_1", delay: 200) { + query fetching_route_1 { + user(id: 1, snapshot: "fetching_route_1", delay: 200) { id name } diff --git a/e2e/sveltekit/src/routes/fetching/route_2/+page.svelte b/e2e/sveltekit/src/routes/fetching/route_2/+page.svelte new file mode 100644 index 0000000000..92adcc4dcc --- /dev/null +++ b/e2e/sveltekit/src/routes/fetching/route_2/+page.svelte @@ -0,0 +1,11 @@ + + +
{JSON.stringify($fetching_route_1, null, 2)}
diff --git a/e2e/sveltekit/src/routes/fetching/route_2/+page.ts b/e2e/sveltekit/src/routes/fetching/route_2/+page.ts new file mode 100644 index 0000000000..6eb3439f33 --- /dev/null +++ b/e2e/sveltekit/src/routes/fetching/route_2/+page.ts @@ -0,0 +1,7 @@ +import { load_fetching_route_1 } from '$houdini'; +import type { PageLoad } from './$types'; + +export const load: PageLoad = async (event) => { + // Here we have a new but no initial state with fetching to true! + return await load_fetching_route_1({ event }); +}; diff --git a/e2e/sveltekit/src/routes/isFetching/spec.ts b/e2e/sveltekit/src/routes/fetching/spec.ts similarity index 60% rename from e2e/sveltekit/src/routes/isFetching/spec.ts rename to e2e/sveltekit/src/routes/fetching/spec.ts index 2903822b93..b3538cf335 100644 --- a/e2e/sveltekit/src/routes/isFetching/spec.ts +++ b/e2e/sveltekit/src/routes/fetching/spec.ts @@ -7,14 +7,14 @@ import { waitForConsoleInfo } from '../../lib/utils/testsHelper.js'; -test.describe('isFetching', () => { +test.describe('fetching', () => { test('with_load SSR', async ({ page }) => { const [msg] = await Promise.all([ waitForConsoleInfo(page), - goto(page, routes.isFetching_with_load) + goto(page, routes.fetching_with_load) ]); - expect(msg.text()).toBe('with_load - isFetching: false'); + expect(msg.text()).toBe('with_load - fetching: false'); }); test('with_load CSR', async ({ page }) => { @@ -23,13 +23,13 @@ test.describe('isFetching', () => { // Switch page and check directly the first console log const [msg] = await Promise.all([ waitForConsoleInfo(page), - clientSideNavigation(page, routes.isFetching_with_load) + clientSideNavigation(page, routes.fetching_with_load) ]); - expect(msg.text()).toBe('with_load - isFetching: true'); + expect(msg.text()).toBe('with_load - fetching: true'); - // wait for the isFetching false + // wait for the fetching false const msg2 = await waitForConsoleInfo(page); - expect(msg2.text()).toBe('with_load - isFetching: false'); + expect(msg2.text()).toBe('with_load - fetching: false'); }); test('without_load CSR', async ({ page }) => { @@ -39,32 +39,32 @@ test.describe('isFetching', () => { // It's expected to stay true until the first fetch! const [msg] = await Promise.all([ waitForConsoleInfo(page), - clientSideNavigation(page, routes.isFetching_without_load) + clientSideNavigation(page, routes.fetching_without_load) ]); - expect(msg.text()).toBe('without_load - isFetching: true'); + expect(msg.text()).toBe('without_load - fetching: true'); const [msg2] = await Promise.all([ waitForConsoleInfo(page), // manual fetch locator_click(page, 'button') ]); - expect(msg2.text()).toBe('without_load - isFetching: true'); + expect(msg2.text()).toBe('without_load - fetching: true'); - // wait for the isFetching false + // wait for the fetching false const msg3 = await waitForConsoleInfo(page); - expect(msg3.text()).toBe('without_load - isFetching: false'); + expect(msg3.text()).toBe('without_load - fetching: false'); - // second click should not refetch... so isFetching should be false + // second click should not refetch... so fetching should be false const [msg4] = await Promise.all([ waitForConsoleInfo(page), // manual fetch locator_click(page, 'button') ]); - expect(msg4.text()).toBe('without_load - isFetching: false'); + expect(msg4.text()).toBe('without_load - fetching: false'); }); test('loading the same store somewhere else', async ({ page }) => { - await goto(page, routes.isFetching_route_1); + await goto(page, routes.fetching_route_1); // Switch page and check the first console log const [msg] = await Promise.all([ @@ -72,9 +72,9 @@ test.describe('isFetching', () => { clientSideNavigation(page, './route_2') ]); - // Here we load the same query with load_isFetching_route_1, but + // Here we load the same query with load_fetching_route_1, but // 1/ we get the values from the cache directly! - // 2/ we never go to isFetching! no more flickering. - expect(msg.text()).toBe('isFetching_route_2 - isFetching: false'); + // 2/ we never go to fetching! no more flickering. + expect(msg.text()).toBe('fetching_route_2 - fetching: false'); }); }); diff --git a/e2e/sveltekit/src/routes/isFetching/with_load/+page.svelte b/e2e/sveltekit/src/routes/fetching/with_load/+page.svelte similarity index 55% rename from e2e/sveltekit/src/routes/isFetching/with_load/+page.svelte rename to e2e/sveltekit/src/routes/fetching/with_load/+page.svelte index 5f5dabd329..3645774230 100644 --- a/e2e/sveltekit/src/routes/isFetching/with_load/+page.svelte +++ b/e2e/sveltekit/src/routes/fetching/with_load/+page.svelte @@ -2,15 +2,15 @@ import { graphql } from '$houdini'; const store = graphql(` - query isFetching_w { - user(id: 1, snapshot: "isFetching_w", delay: 200) { + query fetching_w { + user(id: 1, snapshot: "fetching_w", delay: 200) { id name } } `); - $: console.info(`with_load - isFetching: ${$store.isFetching}`); + $: console.info(`with_load - fetching: ${$store.fetching}`);
{JSON.stringify($store, null, 2)}
diff --git a/e2e/sveltekit/src/routes/isFetching/without_load/+page.svelte b/e2e/sveltekit/src/routes/fetching/without_load/+page.svelte similarity index 62% rename from e2e/sveltekit/src/routes/isFetching/without_load/+page.svelte rename to e2e/sveltekit/src/routes/fetching/without_load/+page.svelte index 1ac96e4dac..88b4fbd9ac 100644 --- a/e2e/sveltekit/src/routes/isFetching/without_load/+page.svelte +++ b/e2e/sveltekit/src/routes/fetching/without_load/+page.svelte @@ -2,8 +2,8 @@ import { graphql } from '$houdini'; const store = graphql` - query isFetching_wo @manual_load { - user(id: 1, snapshot: "isFetching_wo", delay: 200) { + query fetching_wo @manual_load { + user(id: 1, snapshot: "fetching_wo", delay: 200) { id name } @@ -14,7 +14,7 @@ store.fetch(); }; - $: console.info(`without_load - isFetching: ${$store.isFetching}`); + $: console.info(`without_load - fetching: ${$store.fetching}`); diff --git a/e2e/sveltekit/src/routes/isFetching/route_2/+page.svelte b/e2e/sveltekit/src/routes/isFetching/route_2/+page.svelte deleted file mode 100644 index 6f4d2db82f..0000000000 --- a/e2e/sveltekit/src/routes/isFetching/route_2/+page.svelte +++ /dev/null @@ -1,11 +0,0 @@ - - -
{JSON.stringify($isFetching_route_1, null, 2)}
diff --git a/e2e/sveltekit/src/routes/isFetching/route_2/+page.ts b/e2e/sveltekit/src/routes/isFetching/route_2/+page.ts deleted file mode 100644 index e2875c7a21..0000000000 --- a/e2e/sveltekit/src/routes/isFetching/route_2/+page.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { load_isFetching_route_1 } from '$houdini'; -import type { PageLoad } from './$types'; - -export const load: PageLoad = async (event) => { - // Here we have a new but no initial state with isFetching to true! - return await load_isFetching_route_1({ event }); -}; diff --git a/e2e/sveltekit/src/routes/stores/mutation/spec.ts b/e2e/sveltekit/src/routes/stores/mutation/spec.ts index a87650ed23..466f88333c 100644 --- a/e2e/sveltekit/src/routes/stores/mutation/spec.ts +++ b/e2e/sveltekit/src/routes/stores/mutation/spec.ts @@ -10,7 +10,7 @@ test.describe('Mutation Page', () => { const defaultStoreValues = { data: null, errors: null, - isFetching: false, + fetching: false, isOptimisticResponse: false, variables: null }; diff --git a/e2e/sveltekit/src/routes/stores/prefetch-[userId]/spec.ts b/e2e/sveltekit/src/routes/stores/prefetch-[userId]/spec.ts index f18f5bac6f..edf662b98f 100644 --- a/e2e/sveltekit/src/routes/stores/prefetch-[userId]/spec.ts +++ b/e2e/sveltekit/src/routes/stores/prefetch-[userId]/spec.ts @@ -7,7 +7,7 @@ test.describe('prefetch-[userId] Page', () => { await goto(page, routes.Stores_Prefetch_UserId_2); const dataDisplayedSSR = - '{"data":{"user":{"id":"store-user-query:2","name":"Samuel Jackson"}},"errors":null,"isFetching":false,"partial":false,"source":"ssr","variables":{"id":"2"}}'; + '{"data":{"user":{"id":"store-user-query:2","name":"Samuel Jackson"}},"errors":null,"fetching":false,"partial":false,"source":"ssr","variables":{"id":"2"}}'; // The page should have the right data directly await expectToBe(page, dataDisplayedSSR); diff --git a/e2e/sveltekit/src/routes/stores/ssr-[userId]/+page.svelte b/e2e/sveltekit/src/routes/stores/ssr-[userId]/+page.svelte index 47ca78e720..36e8ea9b92 100644 --- a/e2e/sveltekit/src/routes/stores/ssr-[userId]/+page.svelte +++ b/e2e/sveltekit/src/routes/stores/ssr-[userId]/+page.svelte @@ -30,7 +30,7 @@ -{#if $User.isFetching} +{#if $User.fetching}

Loading...

{:else if $User.errors}
diff --git a/packages/houdini-svelte/src/runtime/stores/mutation.ts b/packages/houdini-svelte/src/runtime/stores/mutation.ts
index e8beda067b..a600e0e571 100644
--- a/packages/houdini-svelte/src/runtime/stores/mutation.ts
+++ b/packages/houdini-svelte/src/runtime/stores/mutation.ts
@@ -20,8 +20,8 @@ export class MutationStore<
 
 	private store: Writable>
 
-	protected setFetching(isFetching: boolean) {
-		this.store?.update((s) => ({ ...s, isFetching }))
+	protected setFetching(fetching: boolean) {
+		this.store?.update((s) => ({ ...s, fetching }))
 	}
 
 	constructor({ artifact }: { artifact: MutationArtifact }) {
@@ -46,7 +46,7 @@ export class MutationStore<
 		const config = await this.getConfig()
 
 		this.store.update((c) => {
-			return { ...c, isFetching: true }
+			return { ...c, fetching: true }
 		})
 
 		// treat a mutation like it has an optimistic layer regardless of
@@ -99,7 +99,7 @@ export class MutationStore<
 				this.store.update((s) => ({
 					...s,
 					errors: result.errors,
-					isFetching: false,
+					fetching: false,
 					isOptimisticResponse: false,
 					data: result.data,
 					variables: (newVariables || {}) as _Input,
@@ -132,7 +132,7 @@ export class MutationStore<
 			const storeData: MutationResult<_Data, _Input> = {
 				data: unmarshalSelection(config, this.artifact.selection, result.data) as _Data,
 				errors: result.errors ?? null,
-				isFetching: false,
+				fetching: false,
 				isOptimisticResponse: false,
 				variables: newVariables,
 			}
@@ -146,7 +146,7 @@ export class MutationStore<
 			this.store.update((s) => ({
 				...s,
 				errors: error as { message: string }[],
-				isFetching: false,
+				fetching: false,
 				isOptimisticResponse: false,
 				data: null,
 				variables: newVariables,
@@ -170,7 +170,7 @@ export class MutationStore<
 		return {
 			data: null as _Data | null,
 			errors: null,
-			isFetching: false,
+			fetching: false,
 			isOptimisticResponse: false,
 			variables: null,
 		}
@@ -184,7 +184,7 @@ export type MutationConfig<_Result, _Input, _Optimistic> = {
 export type MutationResult<_Data, _Input> = {
 	data: _Data | null
 	errors: { message: string }[] | null
-	isFetching: boolean
+	fetching: boolean
 	isOptimisticResponse: boolean
 	variables: _Input | null
 }
diff --git a/packages/houdini-svelte/src/runtime/stores/pagination/cursor.ts b/packages/houdini-svelte/src/runtime/stores/pagination/cursor.ts
index 2fa22c9398..a953534850 100644
--- a/packages/houdini-svelte/src/runtime/stores/pagination/cursor.ts
+++ b/packages/houdini-svelte/src/runtime/stores/pagination/cursor.ts
@@ -231,7 +231,7 @@ export function cursorHandlers<_Data extends GraphQLObject, _Input>({
 			return {
 				data: result.data,
 				variables: queryVariables as _Input,
-				isFetching: false,
+				fetching: false,
 				partial: result.partial,
 				errors: null,
 				source: result.source,
diff --git a/packages/houdini-svelte/src/runtime/stores/pagination/fragment.ts b/packages/houdini-svelte/src/runtime/stores/pagination/fragment.ts
index 8f619c4f87..1415bee395 100644
--- a/packages/houdini-svelte/src/runtime/stores/pagination/fragment.ts
+++ b/packages/houdini-svelte/src/runtime/stores/pagination/fragment.ts
@@ -77,7 +77,7 @@ class FragmentStoreCursor<_Data extends GraphQLObject, _Input> extends BasePagin
 	get(initialValue: _Data | null) {
 		const store = writable>({
 			data: initialValue,
-			isFetching: false,
+			fetching: false,
 			pageInfo: nullPageInfo(),
 		})
 
@@ -171,7 +171,7 @@ export class FragmentStoreBackwardCursor<
 		const handlers = this.storeHandlers(
 			parent,
 			// it really is a writable under the hood :(
-			(isFetching: boolean) => parent.data.update((p) => ({ ...p, isFetching }))
+			(fetching: boolean) => parent.data.update((p) => ({ ...p, fetching }))
 		)
 
 		return {
@@ -189,7 +189,7 @@ export class FragmentStoreOffset<
 	get(initialValue: _Data | null) {
 		const parent = writable>({
 			data: initialValue,
-			isFetching: false,
+			fetching: false,
 		})
 
 		// create the offset handlers we'll add to the store
@@ -197,7 +197,7 @@ export class FragmentStoreOffset<
 			artifact: this.paginationArtifact,
 			fetch: async () => ({} as any),
 			getValue: () => get(parent).data,
-			setFetching: (isFetching: boolean) => parent.update((p) => ({ ...p, isFetching })),
+			setFetching: (fetching: boolean) => parent.update((p) => ({ ...p, fetching })),
 			queryVariables: () => this.queryVariables({ subscribe: parent.subscribe }),
 			storeName: this.name,
 			getConfig: () => this.getConfig(),
@@ -215,7 +215,7 @@ export class FragmentStoreOffset<
 
 export type FragmentStorePaginated<_Data extends GraphQLObject, _Input> = Readable<{
 	data: _Data
-	isFetching: boolean
+	fetching: boolean
 	pageInfo: PageInfo
 }> & {
 	loadNextPage(
@@ -232,5 +232,5 @@ export type FragmentStorePaginated<_Data extends GraphQLObject, _Input> = Readab
 
 export type FragmentPaginatedResult<_Data, _ExtraFields = {}> = {
 	data: _Data | null
-	isFetching: boolean
+	fetching: boolean
 } & _ExtraFields
diff --git a/packages/houdini-svelte/src/runtime/stores/pagination/offset.ts b/packages/houdini-svelte/src/runtime/stores/pagination/offset.ts
index 64edecc58a..7d9598528f 100644
--- a/packages/houdini-svelte/src/runtime/stores/pagination/offset.ts
+++ b/packages/houdini-svelte/src/runtime/stores/pagination/offset.ts
@@ -137,7 +137,7 @@ export function offsetHandlers<_Data extends GraphQLObject, _Input>({
 			return {
 				data: result.data,
 				variables: queryVariables as _Input,
-				isFetching: false,
+				fetching: false,
 				partial: result.partial,
 				errors: null,
 				source: result.source,
diff --git a/packages/houdini-svelte/src/runtime/stores/query.ts b/packages/houdini-svelte/src/runtime/stores/query.ts
index d1ac95d687..d0edf1a594 100644
--- a/packages/houdini-svelte/src/runtime/stores/query.ts
+++ b/packages/houdini-svelte/src/runtime/stores/query.ts
@@ -56,8 +56,8 @@ export class QueryStore<
 	// the string identifying the store
 	protected storeName: string
 
-	protected setFetching(isFetching: boolean) {
-		this.store?.update((s) => ({ ...s, isFetching }))
+	protected setFetching(fetching: boolean) {
+		this.store?.update((s) => ({ ...s, fetching }))
 	}
 
 	protected async currentVariables() {
@@ -163,12 +163,12 @@ This will result in duplicate queries. If you are trying to ensure there is alwa
 				rawCacheOnlyResult: true,
 			})
 			if (cachedStore && cachedStore?.result.data) {
-				// update only what matters at this stage (data & isFetching),
+				// update only what matters at this stage (data & fetching),
 				// not all the store. The complete store will be filled later.
 				this.store.update((s) => ({
 					...s,
 					data: cachedStore?.result.data,
-					isFetching: false,
+					fetching: false,
 				}))
 			}
 		}
@@ -292,7 +292,7 @@ This will result in duplicate queries. If you are trying to ensure there is alwa
 			store.update((s) => ({
 				...s,
 				errors: result.errors,
-				isFetching: false,
+				fetching: false,
 				partial: false,
 				data: unmarshaled as _Data,
 				source,
@@ -311,7 +311,7 @@ This will result in duplicate queries. If you are trying to ensure there is alwa
 				data: (unmarshaled || {}) as _Data,
 				variables: variables || ({} as _Input),
 				errors: null,
-				isFetching: false,
+				fetching: false,
 				partial: request.partial,
 				source: request.source,
 			})
@@ -381,7 +381,7 @@ This will result in duplicate queries. If you are trying to ensure there is alwa
 		return {
 			data: null,
 			errors: null,
-			isFetching: true,
+			fetching: true,
 			partial: false,
 			source: null,
 			variables: {} as _Input,
diff --git a/packages/houdini/src/runtime/lib/types.ts b/packages/houdini/src/runtime/lib/types.ts
index 7f958108c4..fa45bf7046 100644
--- a/packages/houdini/src/runtime/lib/types.ts
+++ b/packages/houdini/src/runtime/lib/types.ts
@@ -187,7 +187,7 @@ export type FetchQueryResult<_Data> = {
 export type QueryResult<_Data, _Input, _Extra = {}> = {
 	data: _Data | null
 	errors: { message: string }[] | null
-	isFetching: boolean
+	fetching: boolean
 	partial: boolean
 	source: DataSource | null
 	variables: _Input
diff --git a/site/src/components/Emoji.svelte b/site/src/components/Emoji.svelte
new file mode 100644
index 0000000000..c5a44a1639
--- /dev/null
+++ b/site/src/components/Emoji.svelte
@@ -0,0 +1,7 @@
+
+
+{name}
diff --git a/site/src/components/index.js b/site/src/components/index.js
index e84c22dd69..4f7fa96bb4 100644
--- a/site/src/components/index.js
+++ b/site/src/components/index.js
@@ -6,5 +6,6 @@ export { default as DeepDive } from './DeepDive.svelte'
 export { default as Transformation } from './Transformation.svelte'
 export { default as Warning } from './Warning.svelte'
 export { default as Highlight } from './Highlight.svelte'
+export { default as Emoji } from './Emoji.svelte'
 
 export * from './search'
diff --git a/site/src/routes/+page.svelte b/site/src/routes/+page.svelte
index e873092310..79eb8ef5d5 100644
--- a/site/src/routes/+page.svelte
+++ b/site/src/routes/+page.svelte
@@ -1,5 +1,5 @@
 
 
 # Release Notes
 
+## 1.0.0
+
+It's finally here! 
+
+There are just a few breaking changes that have been introduced with this and one _very_ cool feature.
+
+### `isFetching` has been renamed to `fetching`
+
+This sounds big but we suspect that the fix should be relatively safe and quick. Just replace all instances of `isFetching` in your
+application with `fetching`.
+
+```diff
+- {#if $MyQuery.isFetching}
++ {#if $MyQuery.fetching}
+```
+
+### `error` and `redirect` are no longer attached to `this` in hooks
+
+We felt that these methods didn't offer a lot on top of SvelteKit's exports and were just complicating the situation. Now, you should just
+use the exported values from SvelteKit:
+
+```diff
++ import { error } from '@sveltejs/kit'
+
+  export function _houdini_beforeLoad({ params }) {
+    if (valueIsValid(params)) {
+-         throw this.error(400, "message")
++         throw error(400, "message")
+    }
+  }
+```
+
+### Query Inputs can now be inferred from route parameters
+
+For example, if a route is defined at `src/routes/users/[id]` and a query looks like this:
+
+```graphql
+query UserInfo($id: Int!) {
+    user(id: $id) {
+        ...
+    }
+}
+```
+
+Then the `Variable` function is not required. That being said, you can still define a `Variable` function for custom logic if you want.
+
 ## 0.19.0
 
 `v0.19.0` brings changes quickly after `0.18`. We're trying to get out as much as possible as quickly as possible
@@ -45,14 +91,14 @@ and add it to the `plugins` section of your config file (order doesn't matter).
 
  export default {
 
- 	plugins: {
- 		'houdini-svelte': {
-- 			globalStorePrefix: "G_"
- 		},
-+		'houdini-plugin-svelte-global-stores': {
-+ 			prefix: "G_"
-+		},
- 	}
+     plugins: {
+         'houdini-svelte': {
+-             globalStorePrefix: "G_"
+         },
++        'houdini-plugin-svelte-global-stores': {
++             prefix: "G_"
++        },
+     }
  }
 ```
 
@@ -83,11 +129,11 @@ will allow TypeScript to infer types for your store values:
 
 - const store: MyQueryStore = graphql`
 + const store = graphql(`
-  	query MyQuery {
-  		user {
-  			id
-  		}
-  	}
+      query MyQuery {
+          user {
+              id
+          }
+      }
 - `
 + `)
 ```
@@ -235,17 +281,17 @@ with the underlying query store directly
 
 ```svelte
 
 
 
- {$MyQuery.data.viewer.id} + {$MyQuery.data.viewer.id}
``` @@ -255,10 +301,10 @@ The core of this change is that instead of using stores that you import, your co ```svelte {$UserInfo.data.firstName} @@ -325,9 +371,9 @@ You can now define a `+page.gql` file inside of a route directory to automatical ```graphql:title=src/routes/myProfile/+page.gql query MyQuery { - viewer { - id - } + viewer { + id + } } ``` @@ -340,13 +386,13 @@ The `graphql` template tag can now be used to define your stores in your javascr ```svelte id: {$store.data?.viewer.id} @@ -363,6 +409,7 @@ import { GQL_MyQuery } from '$houdini' export const houdini_load = GQL_MyQuery ``` + ```typescript:title=src/routes/myProfile/+page.ts import { GQL_Query1, GQL_Query2 } from '$houdini' @@ -376,11 +423,11 @@ This can be mixed with the new `graphql` tag api to define your queries inside o import { GQL_MyQuery, graphql } from '$houdini' const otherQuery = graphql` - query ViewerInfo { - viewer { - id - } - } + query ViewerInfo { + viewer { + id + } + } ` export const houdini_load = [ GQL_MyQuery, otherQuery ] @@ -433,8 +480,8 @@ previous behavior, you should use the `quiet` log level: ```javascript:title=houdini.config.js export default { - // ... - logLevel: 'quiet' + // ... + logLevel: 'quiet' } ``` diff --git a/site/src/routes/intro/fetching-data/+page.svx b/site/src/routes/intro/fetching-data/+page.svx index 858886934e..0e3493d508 100644 --- a/site/src/routes/intro/fetching-data/+page.svx +++ b/site/src/routes/intro/fetching-data/+page.svx @@ -67,9 +67,9 @@ A GraphQL query is a string that describes what information you want from the AP ```graphql query CurrentUserInfo { - current_user { - firstName - } + current_user { + firstName + } } ``` @@ -77,11 +77,11 @@ The result of the above query might look something like: ```json { - "data": { - "current_user": { - "firstName": "Bill" - } - } + "data": { + "current_user": { + "firstName": "Bill" + } + } } ``` @@ -91,10 +91,10 @@ Fields in a query can take arguments to customize their behavior, for example: ```graphql query CurrentUserInfo { - current_user { - # specify the format for the date time stamp - lastLogin(format: "YYYY-MM-DD") - } + current_user { + # specify the format for the date time stamp + lastLogin(format: "YYYY-MM-DD") + } } ``` @@ -102,11 +102,11 @@ might return something like this: ```json { - "data": { - "current_user": { - "lastLogin": "2022-12-25" - } - } + "data": { + "current_user": { + "lastLogin": "2022-12-25" + } + } } ``` @@ -175,7 +175,7 @@ Defining variables for your document looks like the follow: ```graphql query MyQuery($variable1: Boolean) { - myField(argument: $variable1) + myField(argument: $variable1) } ``` @@ -236,7 +236,7 @@ If you've already clicked on those links you probably saw a scary message about to handle the loading state for our view. Let's just do something quick and dirty: ```svelte:title=src/routes/[[id]]/+page.svelte -{#if $Info.isFetching} +{#if $Info.fetching} {:else} diff --git a/site/static/images/emojis/exploding-head.png b/site/static/images/emojis/exploding-head.png new file mode 100644 index 0000000000000000000000000000000000000000..6411f9e537c5a15e391db07ee17d20f40faf73c9 GIT binary patch literal 10971 zcmV<1DkRm3P)T`5^9bGqSh;@NMB8%CMOX{adLdFwkwT2XFaiL3RtN7;W|ID6`@K_OEkJQ{ZckYD4gH)A@;+SB7mX?;f9=qxsw45z1G1h2ZZwa|aVcN9T z&n{?gTrGsq0LB=(`qeErTF9?UOHQz)L<)iQ{t78vCIEz2eS^W^F@ZK{W6)ZojX`OR zR=Qmq^Q8m3w%@gA(IN-nL1V0@ja%mnAx^gthhHY7MElXlaiIo1|2on^M072(# zuk*D=X^m1ErPL}?++i|YC#AUFlCq%?P1=yrN6YJX%RAlKnnM--)Vm3gVApcT6-$MV*sTz zN-G?f0ni34DFWsSREjT6j3+^Sx|Bv~{j@N0$=v3tuMJL%7RHccfYlo}FSdkrk7dbv z+p@5v#Iht(NLPI0j^1#jg(KF50AUE9jh2xu1@K31?&vqh?>LlFD5Y_<0wZwQ1-Bd- zGG#y$cxoi*d>pro4o90M3!0}tI`FMp7(n81h08U2p;yrUEuNA()T z<@07u`=0@7(vlc546t^?wrNP#rEFQ3vMg-dB4yid2TBWRxgy*b-L4978Nk!I7)($h zfot&DmLyA(_@X=K{RX_w&mOss(irWViSB&@f%NQ%K&2o8RiceXD}$q5>5*|997mCH znc$Fe%t;GoPyg$HHEt2ugH3=BCDb?5(yp^3ou9HTZxnmF+?N1CfXTMjqi!4mq{jf3 zl=#xbvSh*c3vii4N~F*TZ`2DRT#+7({?LewsuZ6lV+_g|l+;KiaD)op;WC*SuB7@p zaBXE*?fjI>04dAHw!;w| zNB~1%tZA)LMq_ly0DcEqQU=eL1En>Nqg>)3z+Bv?X5RDg8*ANT!%1aTA@<&bd{ znT$imap=uB^g51FO5ZP~6kx`paSZG+!fcUH>ExVl@MSZgoYq#IkH`OOmoImkCmK!OoAuQ7UlZj0yF# zFV_14<-3=jo#8T-JL;3t)SGcTKjRsKO8E?+fo%8DHy}gzQ+I2vKRds<@w2|T%8grk>jYM%Bt*NBKg(NCy|5x92aYu~YL zxil)ia~U9z0LBDjRd_~~B}LGANxM!8ffgW)aJ_-xLDz2Y8$xyBnie8ZElJ=TmRcGt z%k>|n5Evr^bro%gEC!GoM+&??U)r>BYd7%WK31eCjs1!jH*CGxw#1KpD?Vl05r5vw zQUehB*1q8;2t{|f#+z?^g?2!wtNQ|fBxaT8NvU$m2#iV3o%9V#Vj_rs@X3!KWE{_k zbR4wOH_vNs{Ar&mQvm(S02?-J7~Uh&uclJ=gmlD`_gy>N3(ZO?B8eCR_sRe5Th_Mc zwb zQ3W1Euj@hVacnbVVPoULvMN&q{i*`GMC$L-mYm@G?tTy^+o#XA1ETTwz>fzIfhtc7 ze{{JaS%WZcRM0VQMUyf?wA0)m5P|Dy%A_g`Pzx*>Mj)ek35e)_FMg0E8MKA%ULR{5 zbqDapvWO^xeq{iOxXHJ=eUTrGe$S`RHOO2sEs#cd#u0&WWOA*fB5>mYIe{Tun?QJs zVOb{D2o1`}3E}z-<>w#JZii`M5*wC5Tq0sW$Qbug3W2mNq>usg`)a|~2Cdz8b(DTT z{m1})Q;soM)v~$Cl5%Pg9Pt>zkHx1ft7xP72B7rAt6s)lCu9PpRp7Sy9jH8CTzM{- zZ;fXh6()PU`!h~>J#g2Yh}#yuUneuhwebVTBFGuUb$%353R00ANMJaJZ8~-J`YkgO z5s(1=!T@VGZav4cy?+CQzHuQKW3oCoNWM4@jxyPd5oG54 zP7aej!8x7X(T*}+S-wjaGD$HB6$Yh3k3@*#o$tf#=&=0)W7@W`q`*k=tu-6BoL#b@ z1UM=qaLp@Q-;U(VmXxPjQj$)ms7gh`xe;bas(<|s4=it2eg+~m>NffJ`W=(;I!Z^L zl^CQSO7=#d%rn3UatlFZQERlL(5?ajTrut2G_HFa6WH=|5}tSCKO1#*lR#@7SpWXt z-i$*}#-X=2j*zrCd;(OTzp zbT~gSR0H8lwJ{iBK7Iujq3Y2 zgd@|PXyMP@bN{-on{8*)<>w@dxq(?R_=xx5W-R^4Vi@BHqMJoLoTPQirNu-J-^I`|Tj)E3*O`rQrMS zBxopKd@u87tWtAm_FW@6;#iOGbe0AbWmL%=q)y7Re z`IA~~w~bSQksV6mAm)Pnb~&mb@r|-}>ZGtN>FITYF;-TDG6>;D(#=Vb8%ZqI4*vZjU@Z5q1t=*H zN`=}hkPs5BCCZYs8YUih3Ghe~x)QgFgsXgO--`OFz8bLYf{hVO4Ra8!nA;J(fWhz4M?r+|w**DooZc(#i#&g0s`ySZT5ZjvlN&Jyh#kXI$8E3pI+ zmN;8PjHw`F=S{sXrQ>}II+!}`jdp*ex%ZUUsNiQ1vkkCl%%I$k$|^TF+>kO^WhU3> zQDk`y!uJShZt;&QfJ0VgPB=5Sk;=z()CgKAsL# z#zb=M-X$;drnN{EIyQuJ`TbE91cMAnblw26&lw;NkA~5 zwI5{BK}OAu*cvQp;m^GXH;_|kigUSSvJK-wzFk7c^933pY zh`N$O7^E}^MPRiV4BCWIyIe*H=Hz893-eRN%5&SiNgdLkr(nXewF%b=cuv1xUJ$kO z4##nPC%~Nmf?KB20;x(%6t^wiwhvTbR!+ohSu}0>ktBVa*bP)#KV@m%6og>?jG0t8 z!f%+uOc0%ElRFtBY6pY?4%b$&bhsJ_LMu9)Jei%Jcg3N=wxYHL?(#0npb{(&b?4>f zmap*_p1RRSPxl)<#xhw8MSWG5#ZZph`57l%c4k@O&tZtRWe{Ib z<_}5xHF+EW?$g?QAV`%;jpKURd1*W9#X z?Z&Ntbd)+*N`a%oQRH*Gt))A{A{`c$7$F0M)2@_uRYA(OgY>yJ2FucJheh`F@k6O5 zM+${Wn;r}nwz0#B8OETcMCow7LDBgDIuxzhP~L3isO0n2lsBX4>G{pm{@Cvf0AQrLLn*P+5n2kN zB8eVP#70Je5F+RV-%1asRXJJG03hQyL5|CTzM)AI|s< zY4yufIh#c|o(gc3Mhcb9jDG!s)OB`S1`}XZ4ih*UrLU9c_V?4P){bQ!_<%>3r2ZqUORpczYwA`XmiD*F$Q`&sok}Lahrca&HlCSLl;hiv0?n9+f1k3 zxrl?Ab1nBVFSb z(A|6{y&Q+44oxiplEYYZlHUC)!*;(&&4IPlo!5a##nAZJ7C4aYVVRwhF6PoRu8~aL z7@WFMw4ZqcI#rdpj{b_{=+0(QTO|m0N9_|=J$0+2SV7$9g&_-OHZ5DZY17jtBR{7s zd7~6E9j6CF_sXD+?$%1*qYdMP;L4=$QZ*xynJ&h!__y$66)-u6e_!taXCm0yE|kDw z_@2Koe9vEC?5|*Afjr4`8zAE`l`|7kInC3R$?JRo(Wa>Fev#V0z6ijscYU&~&Iiz% zM|!l=nzH2kgpk$I4XAu%x25SZ%2_U?{^$A4CvGiLbWt4D%AkcaW^4g&Ub%kT-`gT{ zSw@Hpg%tBF#CRj92M%as_84PU8YBK-+4@)Wnw#3zY}`2682e)IhF}7;o%cb;{N+CE z&b{u59^fE&o4vYlG_-cYarM~`ry>l|))mhH+Jv1Cpkvh2>xc5sbpqWH5^6`X=dJH6 zUYUG!D`jq9IHz&@OD}ExLME*)6Uw{|Ax@D(j0GAn9MqULqgjpMiHswDb=sV!MA5!t z=$EBo-k!Su;Td7?C3dwjau806TKe zo&q8ABTn{jCr145?an{9Kl|stQ?Ej&6Xjt72xIP8IHzelz)2@f-wpiOJ1UNTTN+j_ z=Fgn|BZ2vca-P|D>Q$(khVaDxn45OTWI$7q&}w^ke2%y$hXLXwK#cgOGrO$)3l_%^ zRTxI_g?Y1@5)Xv_#-K3(z`W+BFB@UrM}oXHfKFAjZ~o=si42fCppQ7Nu8yKA!T{R9 zp_u)bdUHBFH)*5|bi}N?h-ih(i1Bn!Y%WB8F1mp`7R+q?r=$;50)xr`;2M*{a0>7X zps-));owOZ=bVVSX>V8d@0wKh@6H$oa7r-1pqBw%fgnqHpPmduc4_d%O5;4D$ zd;cWGkk}`1-mJz~fvZ=wY;KaaxWO3SCg5}+opqyNbJq#;pF+x|XzgA*>`wuS;9yP% z=Q2P?4@?@KeJ+#B069eV#;gd=5eT}f_IU)OFf`@dnKAIJFg%IjXY*z^Zptrga4;ko zz{e?bnl|IEeg7iB%8eVx*xDY2bPlH+XOF*kp1F*9z(OF)0uPX@&xhF{ML2@HV>&Z8 zC}rhvK&CVUjDGI^uaEis{SUXaw4^h(Jr-@(YFqDLFk{A^7%GPZh7<$jV&Tl0dx5<% zm$u_GKqlsDu0<~dRNJ9#kb50UtAIEL$X8hV0X#;1J%+L&hatlN#vpdzJL_QsV+7(c zsd;Sd()DW!%#;c>f81UTapJnZDrA7(I0ndhCY>`WBiBfTdw}3Q!vL-K&ib@~#a?>mY%Tf9?t5powk|i186zL7R_Vt_-n8a`cY7E>ikm!> ziaA+d&S|TqcCP63hY(mrq*q%8Pv|YuB>o^8|c3=F$WZHwj^G>alyAUCU=Z zBaC^>ys~kg=rXXiKn<$m7$9O##^oWRet+EasTc;xxvk^0NndNdw!R6Y$C+H_5KK`$ z=8M250L%tXM$jl`^2?YSl#NZ_YpNPPDt-3&8`eKr&ivA{7+}x+GbcGpE*6F>f%Dww z-FHd&!|r>U-!=Yy8=fhr%(lDdOwdB#pMVxSHLT~H+DUuQ5vLjG0=`$^&Z?NAMnq!s zu?!ITcdqy^crN>=(E6-m`89#L{m_cB&(uxab2)GWhKyOySz1LuwVPHdiZ{i_=a z78HZw4XGP3=Eu8#Hmha#a`V03s?Oz8uGtx9B4?pw9N>+g%{rlV`K*t%E}#8p<;Wev za2N14@;j`-Nb&gYduCmjlxXuHmQLyAz{FzrSyjD^XndW~bGA}Fs>3~51Kb3RjCr6g zm49wTbqQi3Op$~Es(}gFr|g4;H|VfykI>Mx)2^Gi=jXr@+_tYma=&RINKUjapL4lX z)~a30o6j#!aD?tK_#6;?U}!f*rAXRgO z+DUt2i+|Iu<+CmU^O?8;gr%sRu#cK?`w-HT&4skuWT4ByD;~MF%og`ijH&S$AV%;- z)k1FI$S=1p4c)zPhS&K3rvfJdPlElI9^?@RU2T)9>2^DQL}zjO-D^t@tr$K0@J#E-=uZ{7oI>#4;gj0lV^%OsEXp!>V^ly5Qww!IK<&gmSk)!J zWso*P{8#I;S=VCoZwTIl;6>BIou-BKuDhG3N?1;gC=t?7JHDMUCvBm2!rrj+OJN3Z zocHgsHHBA%j`1Wzb<7Bls(_jT=E%ruIL+j%ooZZ3^l=!T-c3W(4o1z|7IuCD)Q;W1 zIJaGm&|fU|+^%KK=V3Ur*gZAl_E9sT-R`YR=OrrQY=9Z~YaU__Aq@4$C;Fmls(SRn zpJ8~f_iP&Y0?;ySRM+pano&UY$PPx&+CtsrHg~Fd5gKBTlMOTglXF76QTX!!M#e~p zD6dt=NB|7X=z%dsXc!6A3p=Q)?<|{sAv6usb|Pff8i}(@*;G=9SSjkGYD6c)C+`LW zmXUMwp3i$i6=whpq-(nvHYP3?8^TIu7=GMt8S{M8h{o+R>!<8OSekVGVMaG^WyI9i zT|1-Pj`t442F$9K=r7Dh+N}N*idsW41Xt7zXhyuU^Rd`0ICg+yab0HgRVb;$%*Jy z0p@jJIt2~Yw@z{OxiQeQ3#!Js?~E?ZtCtDsRGM-os`czrodLCDGGS z$AQr!c7)n-dyu?>LM9|8jePbiqu@KTQs_8ZC>V!L%rY6D(4@1ovNnckOUC+R_E5V7(ldL@fQZYv1 z;q8#=$UpxEaAY(rnCM>b+Ckp*u}afi1`h6+#F70Yvd%S}u#>70F_TvT3~R+a=0#7% z?CC~rIfjdris2Jn5m`5(4ZEr**|X_pXnMYX8WN!9OqlTkq-TcjIJ^^74l`ytptmEQ zris7_9niBMI(OiGj&xvYF5{@5)S4yc>nF8RRez}1GkFpqjsb?3OGtIYLF&f0Q8Q{^ zvS;KdnD!{7O6ciwkva~h{}!w(uc zVggwWb2dV{Xo8B>FRtb5|NO5!{pV-1R?|u;Is2S5`OIhUVBY+bi@&RP8@${MrtB^B zkA0d8qgyN_D+(5;0($$R0umh~Z!Fo6Klb?B@uxrjC2uLU)|x;5@6%j<$+i6G zKkg}B)$~*tb;FyJ=E6r_5+{xUx+=#2#n*%0f8Q^-^B@1&-SD9Zlv3RJ4`1bh`yVQL z9}Vw*bJAS+$jevftAOWN4|%!eqlZ-o^Y^$SH3ob4w)580FQT(E(e`!#)YjJU=Vu>h z+_*9EH<%uHaV)3;esL)ri!>L<%i{Fx7*Cu8*jo|qt39J&LjwHt$M=@ikN}4dcd_h0 z?<;(RsDheP-&`~oKk~w(ap~X!f$yV4RqkS$Y&`M!iar(l#N$sDzoEMLdyC>&q`4Rb zc?p_0H}KVpyxfkPycgoH+qU0G5kQ{xoD|9_o?Zp3?~QpZ(p(H8zi;Cvglw#c{!uZ>mk1_Lp46v0O_@?6 zH0vCAbJ1M1K1mqBsSpEn7vIL_{6&5H66*OEoKyUU?k#UFnu{HFzIxAN0I`Sz74Np1-Z+)pZvRl>hKa!)e|Ia>rk_ywhJ$~COwuIP|0gsTK3{71}l@(+)4a8&vO`o967}u|lsd@?8@Co8SX^{d{z9 zgO{!bRbC>cBvdE7bR~3Ez>hu_X?71ohJu!C(O9SX1AoTw)}yLmG1Q$5b02}~sYhQ{ z&rVpo7!IvE`m&1Q*rWL=aUqKeT`X>y7?xFtE8XMJI(YtcXnV*z(H9ul{d0JBK6I?A z9IeML&C82XQd|N&&Ik;}JZC zxSc1DQ3&2HF6Qy#wPYHE_r%!8d<7nx8)2Kvz$B8>81$Y5wM-1AOw` z?WC-}8he8X!Ik2jrS~9A#sGIBIgPbI1w&!|i`VzreB=j{ShYEv)G37Ej5AN=l1ndU z{(_SkH-0PtZEbs5y=pDL`St(s>@zQ9eaiOasYm$tTX%87xMNB+tmk=75_gu~en0V7 z!_0430xUZ!vIYp-_ole~OUJXPBis(|l!f#7(pUe1xpRy2@Y=O6^N*kZGAmzL9h@6g zr+D=5cQ9>yMZ&cG55slhQWAYWHo07^;eHI8EAD3><*~oh;>K@J&g%S|Z@GyFA70K$ zC(kKa&67@^!vha3=JTx2W;^!qR>Y?;^$~}DX9`v0z zunEJBNuL=EeE0{GShXb#TYTuFxA2Kie~fg>j{R(WE?nU+IQ`7i$vBEtFSt>wvewS!*IBBTEo;!=K7P94ktW{}`^42K83vN6Z^ejD!9Q&U^Z z$3FeZk|VxoRfXW=pZzqob>Xu5@Benxpx3j&(S^;xd^*-V!e=pjwJ*TgefwTO58ooi z^$a1mFe>^1dU}rV#_N>{yFRh5mJePygc!iEjy?2gDp8+48z-)z7sa(yaw&P4#|xV) zpRe-q{N`bUUf*jysS+n?CdnjZA}Mvjk) z#jNjZU5@I*k>V?M~iED0nK6zJ%iCO)&PD6 z+me;9Pk|$Y;yvOz{?cD{>DOix*YO85H(}5N-8jS2ecP;|FQ}=~gVFg0_<*>+e`ja) z`?wL;b3caL2AT=R4B+gYF?G&BcfLmRQE_emr{gOkIK}n+0JsLjk%4NY>Ei}2GBIVl9@C3w86&peZ>8ym|i>w(Q%`1G^Spd&Mc=N)&1NB?$vrD6_; zFx(<;N;a9Zzc7H|6Bptp9>(ERU}b-cP6>SXnpQ^Egf8Kj>MEwym%q_TjR?LCB>1F8a;(`|%~d~~LU+cko)m(s z8XNi0>{*;RqPXDMc<=x}Y+27kTek+Izsfc|@PVD2I<>n$$pkon;7)4!zv3DOWf{_- zF@O(qHxtl&1Guq2>3L@BFh2Ug#H=*2KrnaoD9)NVffGkGFv_zB_Vx7e>cIm%y{naV z?fbHH|M+@`yWYEtGpF}+=L>$0;x_TY3T@7vn2aF;^Ha`4@qI+{Zx)uq-oqB3dU!m) zeo8gm+3Y*)u8SE}tC(2taO&~hoH4DN+Wt)}J&ok++{~{_t<)V13|R*7 zVg7@&Fnj=b4^&{^!ph(v@DPR{iw{>|!_9ugkY@lN=5DI#;UZuOu$Y(+Cl4|dh7|~w zQ^P}IY5t~+LlMUe14Lnd#CS4X0bGFKd|><_k+K_D0XzvjB0iG;NtU6GW0C=)Fb2V0 z%tmqnhDAtD#n8_m_zkcF!%8GiqF5pRmX->=XTWgGGe8{74;YE&B%le&Gz?RLi3rAE z7!B0o&WQFurhEu+fKH$tXh-k{hHV(OA=w1HEN-O&yEqRn{vUqWr`Ur}PImwR002ov JPDHLkV1kmnSqT6D literal 0 HcmV?d00001 diff --git a/site/static/images/emojis/partying-face.png b/site/static/images/emojis/partying-face.png new file mode 100644 index 0000000000000000000000000000000000000000..f38a09520534391748ace632b17d04e6094c3964 GIT binary patch literal 12662 zcmV-+F^SHJP)Ph*3O2Ne~6RV~huhU;P=A7?YR;jWPO- z(HL)y@fHtM#CRbp-XNkVBA48-CoC*`?{ruF{+RBW?m2d6cV`jv^|PsuBV=- zp698js;dPF!V}j$f9CK_RgYGc6}`}&nSRX~zdxlaQDuLRzJn)G1!*1aWjWQgsRg@s zpC^6Z^FF%#(d`Y{+4m=VGjBd_&T&Sf?(;no3oTIv$qrd;8YZS;p!*FfHt#4K{pIRE zm#kaWy71b0AKdrQ(jyXeukVpKBuoIbj*cSJG%!sQ(=?G%BBdm&vB@)Fd-a5`hYeo# zvt^B+{%l$Er9WS#(i3&x@6kIXOaN$Y?`Ik&hNT6hlqjW;!2Bu4((dt~q&gfW@291? z_VQ1gpS|L0feDjLW+qPW2yyceXZd*JtQFZw)LngP}3;d>L?8d@!+CG~=;--F!ccz9!Vj*Q_|B5|R{ivW#zlWx@c2s)F4`x=?{C;KX45Pb z=L0@Ni3|6|JrkyWpE~Ie+Xrr~DosjG#+Q`D^5e#__>>CZ28 zm1THzs!NNv)Ry$S=aY_;Z=Ewo$VAn1e?`sV>klwB{WBobI<&EP*}x%!zNI~JCZMG? z)$sdi5rUVepU9$-aw%1~2|y`DK~*iMzVtRqDOXt@!=!A>_5p`|yyBL#H{8?y z>R%VW@vAS?z(mz`e}g*wQ?IG(PZDB2z@W+_HXYyB_fA+GfX1fejl%}<(FN0K?3ars z6-ooC6dDqx+_IL|(#FhZUd7YV0V1{{%4}%#Y#Ll~lGMy^u3l+;EHv?$FtjIcJw!Ao ztnS}3s`R{DP-<-WaUr-2c(X6>oiGz{`E84;>xK_06q<&n2{aM-6xOG01~g40*>7;c zfZjY83r?+eud&4H9*K2w7)x1Tb$23lYihk zrj!Cj7K-zglyCm(Yqk2HFKxVY-d!I@^q~D60Z(5&Oze*6@#w~deOZTuXn+~duJRcv z$?ck>_Y14nJ2invz>F6cF>=iYSC5YNy)7k~M`oYHp1ka+8p8gb*4WGicRx~F zi|Th4=g;f#q+d7XcgNHvg0{YbLTOcFHoXCy&_&j*#T5ewiRXmb7xhSp1~7Gz7j*8V zqvy+jMvzJ&q{-Bm7BPI)I#;g;TpBHeE~C%m?USwR!W2eN~Ty2!P+~%~dMI zY!#;ZHNVLj^IqrR6>Fl72EfhSH&2?tH$y6-=5ykt`3ou88fhMr+S^6{4O@qdT>9nh z>wbKH+oMmceCRi;RR2WKnh4a4-B^g=Ou!%41wzg4tGXnlcbqzZ?L-~%o+bnVCs4!w zo@|<&_54DHZrmDl2!+ET3|IdjO*o9VPdwUn-kI#YN51z7$G-kvmv!4!(2pms{4s{t zBZ0Sp$Bhi0yS-erCW7|fKpp#zr?z=ee*<$QLLDxY7y)V|hy!tVPQ&zgE6P7E-;wA# zBxD$<{6?)!4+j&%Q=kbsb7*ped9BqyjC}>xB>~rTA*pP~JZNs8G34 zdc6>WpnDo8^nt|O2no%@c zqm0cc)}dH~&RUY#ECdO)!(#Dq2C&geFWbj=ja{GFCdDYB^n;X82MHLZnf~d(or%0Z zBuE3C^6aV$HC=mHcl7?^x_Zuk_EoYQTLKQDJ)KCaP z+15(Ves~_b--tSFbmGI4k73EA@tx+mviJzfe9Pk@vjg}F#k&aJrj7SR9(&_%r2P~L z3Iv(CN`f7;9P`=NOnLJ|d`h7-4J0T{fz|t!CP8T^0>`RK3wid;Q!q7wh|=qAV*CykH_3dP4a$0=OC2u9|p7HN`c=`__>V z4RG50MH9z-@im&JA$1+3WekKsNEy)lLIk8uDU^h7hLrQ%tSOjY55k%^h{n{^=2m7s z@&Z})dm`ox?A{;z6tM90lc_2%arJzsmKc=u9vAdGom8#f(ntx14om^2s76)*H<8Xf zA^PZUy+lGZKw(vNaj>^Ld%m?SOxnk-9=&>K1#@Rkro*EX3?GMuj+IjQTHBfR*bC&> z)dO_e`;}6BeC&8ujvnDU=KMZ3?Ex;yKOw;U9%4=NVBW4js_Tpa911*36Dw3BGnGo* z+)JX65CQPEwI@rZg6jZf6hW}RFFSNNPoH@*eoeFTIZZ@JNWASGoc;KV6j#>9j2b#W zS08*JAD%G5b4pWk5SQ!i7ptqLUsg6PhkeQe|43SOBp8HqxKb+U5br<~L8 zeDXc(dad#er8BSQR63-Wi&y_I)$IjbUiN5rikJv|t{ShA@)Dl8cn1EYB=>nFM`zx{)a+}}MMpv!YhnE{ z%>3#e*RiY0A7akH+q++wVjJ)?k;N+sshown0mF;)x2%r)p&XY9cxwAmRbxx|@+Ae}>-ZBBUIjHTI&HX<;leI$zL>&o86;k^tO)Z@N@C%e} ztpue44r7eD0HqXFWraNR3y)=5_EqR~1bTh~(la$vBO34@+kVO~ z*Z%=2weFP%<-p6TfjgAK7YDC8g*yCjZ*|$Q|7V(VeO1}exb2hM2hi1hK(I+L2boS=i}oGEmEPit)CyvJUku(HNtct8dm4Hh8OV)V8F zrOdtX3|g%rWFXEU8K3?yt}YtGz@(4jZG*VbXr}Y(uH&Mfd#Gv_=dZb)Hks7jVg+y| zd-+i9VNg%&R#P%;LQPHM=fFRJGy(ryKX`Cll?e*KQ&kg2qBuB?YmRt)+xWru55q6} zI|V>WNrxtQ>9pf`ZrX{oc|EQXLA2Sx%sqR#@ZT>`P`xY2_~3c_&!Gi2SCsP1dDCd~ z`GUs_ylc3k@K|Q#{tO?@3G4NCda>?gT4i#~MtNoD@tpVd9X&1o2;ft-n;*u}{z2@n zsK~A^8+M1%cn`$L&>1l}vt7G9o-%?0Ae0;$?~OekF)VnU(NW=KepSC*?*HiwJ{dhC zI2w=<%nwZ)_p9B_MGrquZv7q{+=np6+iBglBpD+RC^Pwq)aEu0oL4dvxCCkG~sfMKbNnbq$X6#CW&ujvbHZ7*fX-ov`gcUJy_-qt|?e^>Psh+M9X zmZ5II@M2S{$55)PNMoinj6VaHbe1V70J<_(OQFYAONCe+ysoXixT+wRSEip#)qv7~ z=PxCKK=co%AP}1R9kF^NGZ(yt*ECU@#p)P~ca#t;J^Bb1pEvG{_)E^}^N z_3bU%q>p~y-PHMWW9G^7G;&Jr;>6rg@Ef(8LbAC;2wYEAs}`-1Bx6LZOT%zcRr$~d zinpy>7Ar$gb(~f9$vOn_=+MRImVC6u{;}+^;g$E!KAUZ&g&|e%7zwzdf!48cOII*! z-fQT7D<~7Rg&`AQt9(P#STOAb79Bqxgu!t+_i=s25sb^2n{YcHD-BKF#G415!=RL{ zF&kxi-fJX__C((ILU!|{Qn-3$LEWk*rNr;z;1hr*l=w#!Wg5!h&Mhtl_Hr5F%ow(aS=1i>B|m?o#a@gAox{1BWYhA569O3{>&$|FCS&WeMFF*s!je;9Zy zXBAvavepzQ{fR?G@^)SubpZ#Zt%-X6tbzr7aod^f=AQ6_MLX6!24ZQPWgJyge&AWL zPPzo(tfG&;GL<$CSQM5|DR@oO#gXTiez?|o!bIjcL%>dl&nXDrwsy{&_XgvZeHE4A zu7qM+Ss@QxG=tWP4lXO5&9wukQs(=5pY{4^snD~N|BUzv<1^h0NCqWu<;e6e`s%)y zs$E>~yq^Gt5?2w2#~~2!NyCh}=piB$Y%%!6xmzcc2#*>im8m>lxpHQ~vZ!;X(v*2Z z56t=rJ8UaN(BTt6PD3LX&0j?E&OIo5Z2$yRz4Zx}e;@=)#vIHCCy!=w*?s)5=x)5C zy(>ER8L~W$%pZOUm#n{q1@(c&x@YAtppPf}{-}0QFY>swt9UAQ)yhi>5n{Cdj_A6o=ip3_P)NQL@`a|ht>|( z3>;Oc{Qh;gpD9ELo>Lvh{}k11sO_rIUatp`QsxQ~@cNx`d#M!1F8z`peYiX@>0_-9 za`k#C1ILwO&Cm+Aoa^O^gD#>d+43>_v{UsCI+A}3yq=>omr>!{zAx&k0gtL`4i`mi z0w~<{RZVr-(7%HCtCeSi5aQ>B+tw_MU5pzE5C9b+0Lg~I*^8EN#JbI(X@MES7;3*c zIf>=d1~BQ2N0^ZrG0(JLL!g+Em(aPKu8@Py(@HT>2s!|EWqEJQYcS6jsFzzj?Q;fh z-rUxm0()%&`rj~bm#*u%wh1WP)5yiIy-)G(y`gpen&$p41n4>&4lQKh&);Y4pm3k_ zdvprHtsJbNbqGoIDUM$)0{x~6Lyx_Dw#QA+{S@#9W{=WRDUjK({o2+DIWf+ezdOqu(QuFp!jsf${H8&^<57}8J1Iqm5W%tRpeqF%>oJHUPk+{%34X}w7 zK*VEXeFZ6D-m96+U694HFH`IpWB`0##iXN}xp2lFCXQ*@S5CH$D>sIXvFXaSMR@hEZtyZXcYl)+TYy&4qliI`-2z zHcp+?$c?|OqabhJyql_~&cpBjUchUMyMFV{=#g#w@3l3I7_twSwJ8o32gJv#_yu4S zH=+1#&ubMw-uxhqpIui%Yg_yZsxaT+$=i1@d~n}3^tw$x&bz9N9X0V=!qgPWeK%Hd z{Dj1BaK5^I2v6?$5v$sUVW4xMZv%&=u6*>I(F-n&=lb{sU;|5mBYRrWc=6Jr46dA0 zgkg3+M3J3o^4wk9`>eOET${wKE6Zru8{Y#oPIz?1-Pc!h+VP3o65y7dKjW62KSQc$ zHby+u(6Z_>X3p%?8HuZv)dsT69o}93b;-KiqoZ0hv7PVy<9zh@hxM|5k|R#ZIJa0XXaUEg^o z#mx;pkwczXpX1*FyI@LsZo6@^I3H1E{O-eSu>RZsqyA zwxK7eZiXrN(PibVh{GT03I%xvZ$7$(emOlCP^mOVZd^o_QP5S^cH#FI?KypP%!hrt z^iMU-cqD@PfR=3}jQIf9-Byqg<^wGIGKGK7%SlLC0QWtWlMv=ZK+xyh(Iw+L#k!6` z@h~4CNqD@SzKt!O0ZP>EHho95B<$&zw?0hcl{h@=?%!J8kX^|(&8X8iS$(CH)MT^lrwbw~~cK!@}Tcj7sq4c}oc37PLV48e7`$ChS~ zt~mABCVq56Bcq46k&$Lnx7)+g6)8OZY8FdYMBmV)wN3EX`}%R;^}TzC^^N!R>(nPX zYIF;;PTkAcLt5#VZLqgV=d-VqnZGE5*WSy(jG^goxGSGYW1D+qrm{shwkX!*E88#<&Ao7%t5XE?V1{f3~;Y_?qB4m|0#ZE zoU)g9m!)#;t%Xs`cx7=0Yd7s;SVhk^iCVQji8nrou9*gu`uX2q)o}FT?jTf3lELJO zO-!EH#M;ff`1LJCEMFCAJ~nRiF@I4eXPngi{icWe)|0L6Wsl74DyuXQA9j|ZOB0aw z>m5r)YAEE|wlI^nH@m!XTrdp5osUO<=Kj2?4b1!J_6X*O;|F6}c=O>coP2cT;#eu+ zjz@d0zHfUZ&lP>NF=>1=Z#=jqg8AVXR?)$WcWveJbE9{{+aJk|sUbRp7lX@ly562{ zuy@O%=2J&@Ixoc|CSdQKg_zrYy|%Bfy_?RKN_Wo{0&e<6Ew}ut29LeN zco!)>Me^Vcm7H~ILqz>wekX(N)!p|6HgESv?fvIXZQ#+Hb_C`j;wVSg;CGkqYB?_DWA4rWJuenr zLIFFweA#A~aR9Z7*MXCbZ9eKRAAo8P$xPa;deSfI*~_`A>O71+dyq>7&YrcKKg{mk z%f1XlaQ<&gdGFKI;IWc?gC%n}#_e#KCX73K5S4Wy7is(fv2p4%wJWzM5cLO4#tM09!c=LH!@w!Kjr8q zet$__uQUoO(4hDKv67(<-=VV3!_qHP<1XWa&r)5?A3C6ehi|OJtE*nu?~k+VIC(n1E!$y{(>G= zF3A__Nrw(=4*=ktVirZ31f0yaN2KAYcF(gfV~ zyJ`*{777P@J-rvK7%3wbe{DoH0afKgFBeMO3Pg-wgioP5+?VCuw+T=G?hUo1gx5aE zU{{0A^yBxEo|^c@9hJ2nUU@%*Lx;67W>oiH61L>a6jrWJ=CorPDb7#4dTwmic=7E_ z`ehqTKDw#1*C+P|o5aDL??4)HTLq$~Y*-VJ8YO>Jq+hU;v{^ma%|nINApG}#6XKyQ z@$~N$hmhY{hKN9r0974$Lsr}ZprxCE%6A5VwNs67e0Tg-1pW!!zcsopdw+(4AXk;+ znBC=-itGfaLfJCZY;}Ks=~WK>;u; zy<}Zkh39}y7FtB(k1hSm>ejB`IF5yW1`i0!lMz;H1TXn62!aS{yCCf`XtoYT((Mq2 zp3~gr>XVrARb4jhMPQo!ThI5Cd+QdocvrhR33q;%wvKo74qM`dax{hlO}QH+ym9tN zNMZGSM{?0DVweDnUMH$eta2o>HKj%%GnOS@5I;I}5-!m>X(jW#Vdok|dR6`J*lLl9zV0AYZ6<~ z;!9l$K&R8eg$3!i>=e7NF{`}+Z&E`0(sVrKF?<1ML)b}L35yWy-wtAI*c!mJdb=q> zmjS{xD+x;r1V6uJa}ciWaL^py4t8E!cx^aUza|4300+&Q^W(K6SVs_!}Y*-Q+EqQu*W!@Kx{j2H-;JbtqTyQ#cWG!X!Zz! z?(~SnA}H{}A@sT1j*g;ZZKr3k1fa5HWPeRJ zABy!B3Xv*FJFTA7)9VAvz{A@*Xa{Y3afbCfmV!l#= zMXA!x-xa9|gzd!#gbP$>oqMvcSMNLAuNcl1z!;T z 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 literal 0 HcmV?d00001