diff --git a/packages/@ionic/cli/src/commands/start.ts b/packages/@ionic/cli/src/commands/start.ts index 710b38d026..2dbe8e3390 100644 --- a/packages/@ionic/cli/src/commands/start.ts +++ b/packages/@ionic/cli/src/commands/start.ts @@ -31,6 +31,8 @@ interface StartWizardApp { email: string; theme: string; ip: string; + appIcon: string; + appSplash: string; utm: { [key: string]: string }; } @@ -197,6 +199,14 @@ Use the ${input('--type')} option to start projects using older versions of Ioni await this.startIdConvert(startId as string); + const appIconBuffer = data.appIcon ? + Buffer.from(data.appIcon.replace(/^data:image\/\w+;base64,/, ''), 'base64') : + undefined; + + const splashBuffer = data.appSplash ? + Buffer.from(data.appSplash.replace(/^data:image\/\w+;base64,/, ''), 'base64') : + undefined; + this.schema = { cloned: false, name: data.name, @@ -206,6 +216,8 @@ Use the ${input('--type')} option to start projects using older versions of Ioni projectDir, packageId: data['package-id'], appflowId: undefined, + appIcon: appIconBuffer, + splash: splashBuffer, themeColor: data.theme, }; } @@ -538,24 +550,18 @@ Use the ${input('--type')} option to start projects using older versions of Ioni } } - if (options['capacitor'] === null && !options['cordova']) { - const confirm = await this.env.prompt({ - type: 'confirm', - name: 'confirm', - message: 'Integrate your new app with Capacitor to target native iOS and Android?', - default: false, - }); - - if (confirm) { - options['capacitor'] = true; - } - } - - if (options['capacitor']) { + if (!options['cordova']) { await runCommand(runinfo, ['integrations', 'enable', 'capacitor', '--quiet', '--', this.schema.name, packageId ? packageId : 'io.ionic.starter']); } - await this.project.personalize({ name: this.schema.name, projectId, packageId, themeColor: this.schema.themeColor }); + await this.project.personalize({ + name: this.schema.name, + projectId, + packageId, + appIcon: this.schema.appIcon, + splash: this.schema.splash, + themeColor: this.schema.themeColor, + }); this.env.log.nl(); } @@ -622,7 +628,7 @@ Use the ${input('--type')} option to start projects using older versions of Ioni this.env.log.nl(); - await this.showNextSteps(projectDir, this.schema.cloned, linkConfirmed); + await this.showNextSteps(projectDir, this.schema.cloned, linkConfirmed, !options['cordova']); } async checkForExisting(projectDir: string) { @@ -728,18 +734,23 @@ Use the ${input('--type')} option to start projects using older versions of Ioni tasks.end(); } - async showNextSteps(projectDir: string, cloned: boolean, linkConfirmed: boolean) { + async showNextSteps(projectDir: string, cloned: boolean, linkConfirmed: boolean, isCapacitor: boolean) { + const cordovaResCommand = isCapacitor ? 'cordova-res --skip-config --copy' : 'cordova-res'; const steps = [ - `Go to your ${cloned ? 'cloned' : 'newly created'} project: ${input(`cd ${prettyPath(projectDir)}`)}`, - `Run ${input('ionic serve')} within the app directory to see your app`, - `Build features and components: ${strong('https://ion.link/scaffolding-docs')}`, - `Run your app on a hardware or virtual device: ${strong('https://ion.link/running-docs')}`, + `Go to your ${cloned ? 'cloned' : 'new'} project: ${input(`cd ${prettyPath(projectDir)}`)}`, + `Run ${input('ionic serve')} within the app directory to see your app in the browser`, + isCapacitor ? + `Run ${input('ionic capacitor add')} to add a native iOS or Android project using Capacitor` : + `Run ${input('ionic cordova platform add')} to add a native iOS or Android project using Cordova`, + `Generate your app icon and splash screens using ${input(cordovaResCommand)}`, + `Explore the Ionic docs for components, tutorials, and more: ${strong('https://ion.link/docs')}`, + `Building an enterprise app? Ionic has Enterprise Support and Features: ${strong('https://ion.link/enterprise-edition')}`, ]; if (linkConfirmed) { steps.push(`Push your code to Ionic Appflow to perform real-time updates, and more: ${input('git push ionic master')}`); } - this.env.log.info(`${strong('Next Steps')}:\n${steps.map(s => `- ${s}`).join('\n')}`); + this.env.log.msg(`${strong('Your Ionic app is ready! Follow these next steps')}:\n${steps.map(s => ` - ${s}`).join('\n')}`); } } diff --git a/packages/@ionic/cli/src/definitions.ts b/packages/@ionic/cli/src/definitions.ts index 049078b840..08df99a4df 100644 --- a/packages/@ionic/cli/src/definitions.ts +++ b/packages/@ionic/cli/src/definitions.ts @@ -260,6 +260,8 @@ export interface ProjectPersonalizationDetails { version?: string; description?: string; themeColor?: string; + appIcon?: Buffer; + splash?: Buffer; } export interface IProjectConfig { diff --git a/packages/@ionic/cli/src/lib/project/index.ts b/packages/@ionic/cli/src/lib/project/index.ts index 00e573c472..ada74a3dd7 100644 --- a/packages/@ionic/cli/src/lib/project/index.ts +++ b/packages/@ionic/cli/src/lib/project/index.ts @@ -3,7 +3,7 @@ import { PromptModule } from '@ionic/cli-framework-prompts'; import { resolveValue } from '@ionic/cli-framework/utils/fn'; import { TTY_WIDTH, prettyPath, wordWrap } from '@ionic/cli-framework/utils/format'; import { ERROR_INVALID_PACKAGE_JSON, compileNodeModulesPaths, isValidPackageName, readPackageJsonFile } from '@ionic/cli-framework/utils/node'; -import { findBaseDirectory, readFile, writeFile, writeJson } from '@ionic/utils-fs'; +import { ensureDir, findBaseDirectory, readFile, writeFile, writeJson } from '@ionic/utils-fs'; import * as Debug from 'debug'; import * as lodash from 'lodash'; import * as path from 'path'; @@ -89,7 +89,7 @@ export class ProjectDetails { async getIdFromPathMatch(config: IMultiProjectConfig): Promise { const { ctx } = this.e; - for (const [ key, value ] of lodash.entries(config.projects)) { + for (const [key, value] of lodash.entries(config.projects)) { const id = key; if (value && value.root) { @@ -569,7 +569,7 @@ export abstract class Project implements IProject { } async personalize(details: ProjectPersonalizationDetails): Promise { - const { name, projectId, description, version, themeColor } = details; + const { name, projectId, description, version, themeColor, appIcon, splash } = details; this.config.set('name', name); @@ -585,6 +585,10 @@ export abstract class Project implements IProject { await this.setPrimaryTheme(themeColor); } + if (appIcon && splash) { + await this.setAppResources(appIcon, splash); + } + const integrations = await this.getIntegrations(); await Promise.all(integrations.map(async i => i.personalize(details))); @@ -592,7 +596,7 @@ export abstract class Project implements IProject { // Empty to avoid sub-classes having to implement // tslint:disable-next-line:no-empty - async setPrimaryTheme(_themeColor: string): Promise {} + async setPrimaryTheme(_themeColor: string): Promise { } async writeThemeColor(variablesPath: string, themeColor: string): Promise { const light = new Color(themeColor); @@ -683,6 +687,22 @@ export abstract class Project implements IProject { } } + async setAppResources(appIcon: Buffer, splash: Buffer) { + const resourcesDir = path.join(this.directory, 'resources'); + const iconPath = path.join(resourcesDir, 'icon.png'); + const splashPath = path.join(resourcesDir, 'splash.png'); + + try { + await ensureDir(resourcesDir); + + await writeFile(iconPath, appIcon); + await writeFile(splashPath, splash); + } catch (e) { + const { log } = this.e; + log.error(`Unable to find or create the resources directory. Skipping icon generation: ${e}`); + } + } + async registerAilments(registry: IAilmentRegistry): Promise { const ailments = await import('../doctor/ailments'); const deps = { ...this.e, project: this }; diff --git a/packages/@ionic/cli/src/lib/start.ts b/packages/@ionic/cli/src/lib/start.ts index b7c4ae4801..752bc5f4d2 100644 --- a/packages/@ionic/cli/src/lib/start.ts +++ b/packages/@ionic/cli/src/lib/start.ts @@ -27,6 +27,8 @@ export interface NewAppSchema extends BaseAppSchema { type: ProjectType; template: string; themeColor?: string; + appIcon?: Buffer; + splash?: Buffer; } export interface ClonedAppSchema extends BaseAppSchema {