mirror of
https://github.com/appwrite/console.git
synced 2026-06-06 19:27:48 +00:00
add: android starter onboarding [WIP].
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
import { trackEvent } from '$lib/actions/analytics';
|
||||
import WizardExitModal from './wizardExitModal.svelte';
|
||||
import { goto } from '$app/navigation';
|
||||
import { wizard } from '$lib/stores/wizard';
|
||||
|
||||
type $$Props =
|
||||
| {
|
||||
@@ -13,6 +14,7 @@
|
||||
invertColumns?: boolean;
|
||||
hideAside?: boolean;
|
||||
hideFooter?: boolean;
|
||||
onExit?: () => void;
|
||||
}
|
||||
| {
|
||||
title: string;
|
||||
@@ -22,6 +24,7 @@
|
||||
invertColumns?: boolean;
|
||||
hideAside?: boolean;
|
||||
hideFooter?: boolean;
|
||||
onExit?: () => void;
|
||||
};
|
||||
|
||||
export let title: $$Props['title'] = '';
|
||||
@@ -31,6 +34,7 @@
|
||||
export let invertColumns: $$Props['invertColumns'] = false;
|
||||
export let hideAside = false;
|
||||
export let hideFooter = false;
|
||||
export let onExit: $$Props['onExit'] = undefined;
|
||||
|
||||
function handleKeydown(event: KeyboardEvent) {
|
||||
if (event.key === 'Escape') {
|
||||
@@ -83,6 +87,11 @@
|
||||
trackEvent('wizard_exit', {
|
||||
from: 'prompt'
|
||||
});
|
||||
|
||||
wizard.hide();
|
||||
if (onExit) {
|
||||
onExit();
|
||||
}
|
||||
}}>
|
||||
<slot name="exit">
|
||||
Are you sure you want to exit from this process? All data will be deleted. This action
|
||||
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
<script lang="ts">
|
||||
export let status: boolean = false;
|
||||
</script>
|
||||
|
||||
<div class="connection-line u-flex u-cross-center">
|
||||
<div
|
||||
class="line-left"
|
||||
style={`opacity: ${status ? 1 : 0.15}; transition: opacity 5s;`}
|
||||
data-status={status}>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="tick-container"
|
||||
style={`opacity: ${status ? 1 : 0.15}; transition: opacity 2.5s;`}
|
||||
data-status={status}>
|
||||
{#if status}
|
||||
<span class="icon-check" style="color: #fd366e;"></span>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="line-right"
|
||||
style={`opacity: ${status ? 1 : 0.15}; transition: opacity 5s;`}
|
||||
data-status={status}>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.connection-line {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.line-left[data-status='false'],
|
||||
.line-right[data-status='false'] {
|
||||
flex: 1;
|
||||
height: 1px;
|
||||
background: none;
|
||||
border-top: 1px dashed #d8d8db;
|
||||
}
|
||||
|
||||
.line-left[data-status='true'] {
|
||||
flex: 1;
|
||||
height: 1px;
|
||||
background: linear-gradient(to left, #f02e65 0%, rgba(254, 149, 103, 0.15) 100%);
|
||||
}
|
||||
|
||||
.line-right[data-status='true'] {
|
||||
flex: 1;
|
||||
height: 1px;
|
||||
background: linear-gradient(to right, #f02e65 6.32%, rgba(254, 149, 103, 0.15) 116.81%);
|
||||
}
|
||||
|
||||
.tick-container[data-status='false'] {
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
display: flex;
|
||||
border-radius: 50%;
|
||||
margin-inline: 0.25rem;
|
||||
background-color: #ccc;
|
||||
}
|
||||
|
||||
.tick-container[data-status='true'] {
|
||||
display: flex;
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
color: #fd366e;
|
||||
border-radius: 50%;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 1.8px solid hsla(343, 98%, 60%, 0.32);
|
||||
background-color: hsla(343, 98%, 60%, 0.08);
|
||||
}
|
||||
|
||||
.icon-check {
|
||||
color: #fd366e;
|
||||
}
|
||||
</style>
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
<script lang="ts">
|
||||
import type { ComponentType } from 'svelte';
|
||||
|
||||
export let icon: ComponentType;
|
||||
export let iconSize: number | undefined = undefined;
|
||||
export let iconColor: string | undefined = undefined;
|
||||
</script>
|
||||
|
||||
<div class="outer-card">
|
||||
<div
|
||||
class="inner-card"
|
||||
style="
|
||||
--icon-color: {iconColor || 'inherit'};
|
||||
--icon-size: {iconSize ? `${iconSize}rem` : 'inherit'};
|
||||
">
|
||||
<svelte:component this={icon} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.outer-card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 94.304px;
|
||||
height: 94.303px;
|
||||
flex-shrink: 0;
|
||||
border-radius: 26.944px;
|
||||
border: 1.684px solid rgba(255, 255, 255, 0.08);
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
box-shadow: 0px 8.757px 8.757px 0px rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
|
||||
.inner-card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 80.832px;
|
||||
height: 80.831px;
|
||||
flex-shrink: 0;
|
||||
border-radius: 20.208px;
|
||||
background: #fff;
|
||||
border: 0.54px solid #ededf0;
|
||||
box-shadow: 0px 6.736px 6.736px 0px rgba(0, 0, 0, 0.05);
|
||||
|
||||
& :global(svg) {
|
||||
width: var(--icon-size);
|
||||
height: var(--icon-size);
|
||||
color: var(--icon-color);
|
||||
}
|
||||
}
|
||||
|
||||
:global(.theme-dark) {
|
||||
& .outer-card {
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
}
|
||||
|
||||
& .inner-card {
|
||||
border: 0.54px solid rgba(237, 237, 240, 0.08);
|
||||
background: var(--color-bgColor-neutral-primary, #1d1d21);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,46 +1,227 @@
|
||||
<script lang="ts">
|
||||
import { WizardWithSteps } from '$lib/layout';
|
||||
import { Wizard } from '$lib/layout';
|
||||
import { invalidate } from '$app/navigation';
|
||||
import { wizard } from '$lib/stores/wizard';
|
||||
import { createPlatform } from './wizard/store';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import type { WizardStepsType } from '$lib/layout/wizardWithSteps.svelte';
|
||||
import Step1 from './wizard/android/step1.svelte';
|
||||
import Step2 from './wizard/android/step2.svelte';
|
||||
import Step3 from './wizard/android/step3.svelte';
|
||||
import Step4 from './wizard/step4.svelte';
|
||||
import {
|
||||
Code,
|
||||
Layout,
|
||||
Icon,
|
||||
Typography,
|
||||
Fieldset,
|
||||
InlineCode,
|
||||
Spinner
|
||||
} from '@appwrite.io/pink-svelte';
|
||||
import { Button, Form, InputText } from '$lib/elements/forms';
|
||||
import { IconAndroid, IconAppwrite } from '@appwrite.io/pink-icons-svelte';
|
||||
import { Card } from '$lib/components';
|
||||
import { base } from '$app/paths';
|
||||
import { page } from '$app/stores';
|
||||
import { onMount } from 'svelte';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { fade } from 'svelte/transition';
|
||||
import OnboardingPlatformCard from '$routes/(console)/project-[project]/overview/platforms/components/OnboardingPlatformCard.svelte';
|
||||
import ConnectionLine from '$routes/(console)/project-[project]/overview/platforms/components/ConnectionLine.svelte';
|
||||
import { PlatformType } from '@appwrite.io/console';
|
||||
import { sleep } from '$lib/helpers/promises';
|
||||
|
||||
async function onFinish() {
|
||||
await Promise.all([invalidate(Dependencies.PROJECT), invalidate(Dependencies.PLATFORMS)]);
|
||||
createPlatform.reset();
|
||||
wizard.hide();
|
||||
let showExitModal = false;
|
||||
let isPlatformCreated = false;
|
||||
let isCreatingPlatform = false;
|
||||
let connectionSuccessful = false;
|
||||
const projectId = $page.params.project;
|
||||
|
||||
const gitCloneCode =
|
||||
'\ngit clone https://github.com/appwrite/starter-for-android\ncd starter-for-android\n';
|
||||
|
||||
async function createAndroidPlatform() {
|
||||
try {
|
||||
isCreatingPlatform = true;
|
||||
await sleep(1500);
|
||||
// await sdk.forConsole.projects.createPlatform(
|
||||
// projectId,
|
||||
// PlatformType.Android,
|
||||
// $createPlatform.name,
|
||||
// $createPlatform.key || undefined,
|
||||
// undefined,
|
||||
// undefined
|
||||
// );
|
||||
|
||||
isPlatformCreated = true;
|
||||
trackEvent(Submit.PlatformCreate, {
|
||||
type: 'android'
|
||||
});
|
||||
addNotification({
|
||||
type: 'success',
|
||||
message: `Android platform has been created.`
|
||||
});
|
||||
} catch (error) {
|
||||
trackError(error, Submit.PlatformCreate);
|
||||
} finally {
|
||||
isCreatingPlatform = false;
|
||||
}
|
||||
}
|
||||
|
||||
const stepsComponents: WizardStepsType = new Map();
|
||||
stepsComponents.set(1, {
|
||||
label: 'Settings',
|
||||
component: Step1
|
||||
});
|
||||
stepsComponents.set(2, {
|
||||
label: 'Install',
|
||||
component: Step2,
|
||||
optional: true
|
||||
});
|
||||
stepsComponents.set(3, {
|
||||
label: 'Import',
|
||||
component: Step3,
|
||||
optional: true
|
||||
});
|
||||
stepsComponents.set(4, {
|
||||
label: 'Build',
|
||||
component: Step4,
|
||||
optional: true
|
||||
async function resetPlatformStore() {
|
||||
await Promise.all([invalidate(Dependencies.PROJECT), invalidate(Dependencies.PLATFORMS)]);
|
||||
createPlatform.reset();
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
const unsubscribe = sdk.forConsole.client.subscribe('console', (response) => {
|
||||
if (response.events.includes(`projects.${projectId}.ping`) && isPlatformCreated) {
|
||||
connectionSuccessful = true;
|
||||
unsubscribe();
|
||||
}
|
||||
});
|
||||
|
||||
return () => {
|
||||
unsubscribe();
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
<WizardWithSteps
|
||||
title="Add an Android platform"
|
||||
steps={stepsComponents}
|
||||
on:finish={onFinish}
|
||||
on:exit={onFinish}
|
||||
finalAction="Go to dashboard" />
|
||||
<Wizard title="Add an Android platform" bind:showExitModal confirmExit onExit={resetPlatformStore}>
|
||||
<Form onSubmit={createAndroidPlatform}>
|
||||
<Layout.Stack gap="xxl">
|
||||
<!-- Step One -->
|
||||
{#if !isPlatformCreated}
|
||||
<Fieldset legend="Details">
|
||||
<Layout.Stack gap="l" alignItems="flex-end">
|
||||
<Layout.Stack gap="s">
|
||||
<InputText
|
||||
id="name"
|
||||
label="Name"
|
||||
placeholder="My Android App"
|
||||
required
|
||||
bind:value={$createPlatform.name} />
|
||||
|
||||
<!-- Tooltips on InputText don't work as of now -->
|
||||
<InputText
|
||||
id="key"
|
||||
label="Package Name"
|
||||
placeholder="com.company.appname"
|
||||
tooltip="Your package name is generally the applicationId in your app-level build.gradle file."
|
||||
required
|
||||
bind:value={$createPlatform.key} />
|
||||
</Layout.Stack>
|
||||
|
||||
<Button
|
||||
fullWidthMobile
|
||||
size="s"
|
||||
submit
|
||||
forceShowLoader
|
||||
submissionLoader={isCreatingPlatform}
|
||||
disabled={!$createPlatform.name ||
|
||||
!$createPlatform.key ||
|
||||
isCreatingPlatform}
|
||||
on:click={async () => await createAndroidPlatform()}>
|
||||
Create platform
|
||||
</Button>
|
||||
</Layout.Stack>
|
||||
</Fieldset>
|
||||
{:else}
|
||||
<Layout.Stack gap="xxl">
|
||||
<Card isTile padding="s" radius="s">
|
||||
<Layout.Stack
|
||||
direction="row"
|
||||
justifyContent="space-between"
|
||||
alignItems="center"
|
||||
gap="xs">
|
||||
<Layout.Stack direction="row" alignItems="center" gap="s">
|
||||
<Icon size="m" icon={IconAndroid} />
|
||||
<Typography.Text
|
||||
variant="m-400"
|
||||
color="--color-fgcolor-neutral-primary">
|
||||
{$createPlatform.name} ({$createPlatform.key})
|
||||
</Typography.Text>
|
||||
</Layout.Stack>
|
||||
</Layout.Stack>
|
||||
</Card>
|
||||
</Layout.Stack>
|
||||
{/if}
|
||||
|
||||
<!-- Step Two -->
|
||||
{#if isPlatformCreated}
|
||||
<Fieldset legend="Clone starter">
|
||||
<Layout.Stack gap="l">
|
||||
<Typography.Text variant="m-500">
|
||||
1. Clone the Android starter kit from GitHub using your terminal or
|
||||
Android Studio.
|
||||
</Typography.Text>
|
||||
|
||||
<!-- Temporary fix: Remove this div once Code splitting issue with stack spacing is resolved -->
|
||||
<div class="pink2-code-margin-fix">
|
||||
<Code lang="bash" lineNumbers code={gitCloneCode} />
|
||||
</div>
|
||||
|
||||
<Typography.Text variant="m-500">2. Configure the project</Typography.Text>
|
||||
|
||||
<Typography.Text variant="m-400">
|
||||
Update the configuration in the file
|
||||
<InlineCode size="s" code="data/repository/AppwriteRepository.kt" />
|
||||
.
|
||||
</Typography.Text>
|
||||
|
||||
<Typography.Text variant="m-500">3. Run the starter kit</Typography.Text>
|
||||
<Typography.Text variant="m-400">
|
||||
Launch the starter kit and click the
|
||||
<InlineCode size="s" code="Send a ping" />
|
||||
button to test the setup.
|
||||
</Typography.Text>
|
||||
</Layout.Stack>
|
||||
</Fieldset>
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
</Form>
|
||||
<svelte:fragment slot="aside">
|
||||
<Card padding="l">
|
||||
<Layout.Stack gap="xxl">
|
||||
<Layout.Stack direction="row" justifyContent="center" gap="none">
|
||||
<OnboardingPlatformCard
|
||||
iconSize={2.526}
|
||||
iconColor="#3ddc84"
|
||||
icon={IconAndroid} />
|
||||
|
||||
<ConnectionLine status={connectionSuccessful} />
|
||||
|
||||
<OnboardingPlatformCard
|
||||
iconSize={2.526}
|
||||
iconColor="#FD366E"
|
||||
icon={IconAppwrite} />
|
||||
</Layout.Stack>
|
||||
|
||||
<Layout.Stack direction="row" justifyContent="center" alignItems="center" gap="l">
|
||||
{#if !connectionSuccessful}
|
||||
<Spinner />
|
||||
<Typography.Text variant="m-400">Waiting for connection...</Typography.Text>
|
||||
{:else}
|
||||
<!-- cannot apply fade on components -->
|
||||
<div in:fade={{ duration: 2500 }}>
|
||||
<Typography.Title size="m">Congratulations!</Typography.Title>
|
||||
</div>
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
</Layout.Stack>
|
||||
</Card>
|
||||
</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="footer">
|
||||
<Button
|
||||
size="s"
|
||||
fullWidthMobile
|
||||
secondary
|
||||
disabled={isCreatingPlatform}
|
||||
href={`${base}/project-${projectId}/overview`}>
|
||||
Go to dashboard
|
||||
</Button>
|
||||
</svelte:fragment>
|
||||
</Wizard>
|
||||
|
||||
<style lang="scss">
|
||||
:global(.pink2-code-margin-fix pre) {
|
||||
margin: revert;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { page } from '$app/stores';
|
||||
import { FormList, InputText } from '$lib/elements/forms';
|
||||
import { WizardStep } from '$lib/layout';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { createPlatform } from '../store';
|
||||
import { Submit, trackEvent } from '$lib/actions/analytics';
|
||||
import { PlatformType } from '@appwrite.io/console';
|
||||
|
||||
const projectId = $page.params.project;
|
||||
|
||||
async function beforeSubmit() {
|
||||
if ($createPlatform.$id) {
|
||||
await sdk.forConsole.projects.updatePlatform(
|
||||
projectId,
|
||||
$createPlatform.$id,
|
||||
$createPlatform.name,
|
||||
$createPlatform.key || undefined,
|
||||
$createPlatform.store || undefined,
|
||||
$createPlatform.hostname || undefined
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const platform = await sdk.forConsole.projects.createPlatform(
|
||||
projectId,
|
||||
PlatformType.Android,
|
||||
$createPlatform.name,
|
||||
$createPlatform.key || undefined,
|
||||
undefined,
|
||||
undefined
|
||||
);
|
||||
|
||||
trackEvent(Submit.PlatformCreate, {
|
||||
type: 'android'
|
||||
});
|
||||
|
||||
$createPlatform.$id = platform.$id;
|
||||
}
|
||||
</script>
|
||||
|
||||
<WizardStep {beforeSubmit}>
|
||||
<svelte:fragment slot="title">Android registration</svelte:fragment>
|
||||
<FormList>
|
||||
<InputText
|
||||
id="name"
|
||||
label="Name"
|
||||
placeholder="My Android App"
|
||||
required
|
||||
bind:value={$createPlatform.name} />
|
||||
<InputText
|
||||
id="key"
|
||||
label="Package Name"
|
||||
placeholder="com.company.appname"
|
||||
tooltip="Your package name is generally the applicationId in your app-level build.gradle file."
|
||||
required
|
||||
bind:value={$createPlatform.key} />
|
||||
</FormList>
|
||||
</WizardStep>
|
||||
@@ -1,18 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { Code } from '$lib/components';
|
||||
import { WizardStep } from '$lib/layout';
|
||||
import { versions } from '../store';
|
||||
|
||||
const example1 = `repositories {
|
||||
mavenCentral()
|
||||
}`;
|
||||
const example2 = `implementation("io.appwrite:sdk-for-android:${$versions['client-android']}")`;
|
||||
</script>
|
||||
|
||||
<WizardStep>
|
||||
<svelte:fragment slot="title">Install</svelte:fragment>
|
||||
<p>First, add this to your root level <code class="inline-code">build.gradle</code> file.</p>
|
||||
<Code label="Groovy" labelIcon="android" language="kotlin" code={example1} withCopy />
|
||||
<p class="common-section">And add this to your project's build.gradle file.</p>
|
||||
<Code label="Groovy" labelIcon="android" language="kotlin" code={example2} withCopy />
|
||||
</WizardStep>
|
||||
@@ -1,41 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { Code } from '$lib/components';
|
||||
import Id from '$lib/components/id.svelte';
|
||||
import { WizardStep } from '$lib/layout';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { isCloud } from '$lib/system';
|
||||
|
||||
const { endpoint, project } = sdk.forProject.client.config;
|
||||
const code = `import io.appwrite.Client
|
||||
import io.appwrite.services.Account
|
||||
|
||||
${
|
||||
isCloud
|
||||
? `val client = Client(context).setProject("${project}")`
|
||||
: `val client = Client(context)
|
||||
.setEndpoint("${endpoint}")
|
||||
.setProject("${project}")
|
||||
.setSelfSigned(status: true) // For self signed certificates, only use for development`
|
||||
}`;
|
||||
</script>
|
||||
|
||||
<WizardStep>
|
||||
<svelte:fragment slot="title">Initialize</svelte:fragment>
|
||||
|
||||
<h2 class="heading-level-7">Initialize your SDK</h2>
|
||||
<p>
|
||||
Initialize your SDK by pointing the client to your Appwrite project using your <Id
|
||||
value={project}>Project ID</Id
|
||||
>.
|
||||
</p>
|
||||
<div class="u-margin-block-start-16">
|
||||
<Code
|
||||
label="Android SDK"
|
||||
labelIcon="android"
|
||||
language="kotlin"
|
||||
{code}
|
||||
withCopy
|
||||
withLineNumbers
|
||||
noMargin />
|
||||
</div>
|
||||
</WizardStep>
|
||||
Reference in New Issue
Block a user