@shopify/shop-minis-cli 0.0.37 → 0.0.38
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/commands/check/index.d.ts +1 -0
- package/build/commands/check/index.js +11 -1
- package/build/commands/check/index.js.map +1 -1
- package/build/commands/check/utils/manifest.d.ts +4 -0
- package/build/commands/check/utils/manifest.js +26 -0
- package/build/commands/check/utils/manifest.js.map +1 -0
- package/build/commands/check/utils/schemas/contextual-image.schema.json +21 -0
- package/build/commands/check/utils/schemas/contextual-object.schema.json +17 -0
- package/build/commands/check/utils/schemas/manifest.schema.json +226 -0
- package/build/commands/check/utils/schemas/visibility.schema.json +17 -0
- package/package.json +8 -1
- package/templates/__template_common/package.json +1 -0
- package/templates/__template_hello_world/src/components/ComponentLink.ts +6 -2
- package/templates/__template_hello_world/src/components/quiz/ImageCarouselSlide.tsx +54 -0
- package/templates/__template_hello_world/src/components/quiz/MultipleChoiceSlide.tsx +77 -0
- package/templates/__template_hello_world/src/components/quiz/QuizProvider.tsx +64 -0
- package/templates/__template_hello_world/src/components/quiz/QuizSlide.tsx +76 -0
- package/templates/__template_hello_world/src/components/quiz/QuizSlideCommander.tsx +35 -0
- package/templates/__template_hello_world/src/components/quiz/TextFieldSlide.tsx +47 -0
- package/templates/__template_hello_world/src/components/quiz/TextSlide.tsx +6 -0
- package/templates/__template_hello_world/src/components/quiz/types.ts +72 -0
- package/templates/__template_hello_world/src/hooks/useQuizData.ts +26 -0
- package/templates/__template_hello_world/src/hooks/useQuizState.ts +27 -0
- package/templates/__template_hello_world/src/routes.tsx +9 -3
- package/templates/__template_hello_world/src/screens/{ImageCarouselScreen.tsx → ImageMultipleChoiceScreen.tsx} +4 -6
- package/templates/__template_hello_world/src/screens/InputScreen.tsx +42 -58
- package/templates/__template_hello_world/src/screens/QuizResultScreen.tsx +82 -0
- package/templates/__template_hello_world/src/screens/QuizScreen.tsx +40 -0
- package/templates/__template_hello_world/src/screens/QuizSlideScreen.tsx +150 -0
- package/templates/__template_hello_world/src/types.ts +2 -1
- package/templates/__template_hello_world/src/utils/mockQuizData.ts +313 -0
- package/templates/__template_hello_world/src/utils/quizUtils.ts +75 -0
|
@@ -2,4 +2,5 @@ import { Command } from 'commander';
|
|
|
2
2
|
export declare function assertDependenciesUpToDate(): Promise<void>;
|
|
3
3
|
export declare function assertApiKeyIsSet(): void;
|
|
4
4
|
export declare function assertNoActiveSubmissions(): Promise<void>;
|
|
5
|
+
export declare function assertManifestValid(): void;
|
|
5
6
|
export declare function loadCommand(parentProgram: Command): Promise<void>;
|
|
@@ -26,13 +26,14 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
26
26
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
27
|
};
|
|
28
28
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
-
exports.loadCommand = exports.assertNoActiveSubmissions = exports.assertApiKeyIsSet = exports.assertDependenciesUpToDate = void 0;
|
|
29
|
+
exports.loadCommand = exports.assertManifestValid = exports.assertNoActiveSubmissions = exports.assertApiKeyIsSet = exports.assertDependenciesUpToDate = void 0;
|
|
30
30
|
const commander_1 = require("commander");
|
|
31
31
|
const semver_1 = __importDefault(require("semver"));
|
|
32
32
|
const chalk_1 = __importDefault(require("chalk"));
|
|
33
33
|
const dotenv = __importStar(require("dotenv"));
|
|
34
34
|
const exec_async_child_process_1 = require("../utils/exec-async-child-process");
|
|
35
35
|
const versions_1 = require("./utils/versions");
|
|
36
|
+
const manifest_1 = require("./utils/manifest");
|
|
36
37
|
async function assertDependenciesUpToDate() {
|
|
37
38
|
const { stdout: latestCliVersion } = await (0, exec_async_child_process_1.execAsync)({
|
|
38
39
|
cmd: 'npm show @shopify/shop-minis-cli version',
|
|
@@ -61,12 +62,21 @@ async function assertNoActiveSubmissions() {
|
|
|
61
62
|
// TODO: check if there are any active submissions
|
|
62
63
|
}
|
|
63
64
|
exports.assertNoActiveSubmissions = assertNoActiveSubmissions;
|
|
65
|
+
function assertManifestValid() {
|
|
66
|
+
const { valid, errors } = (0, manifest_1.validateManifest)();
|
|
67
|
+
if (!valid) {
|
|
68
|
+
console.warn(chalk_1.default.red(`Manifest invalid: ${errors}`));
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
exports.assertManifestValid = assertManifestValid;
|
|
64
73
|
async function loadCommand(parentProgram) {
|
|
65
74
|
const command = new commander_1.Command()
|
|
66
75
|
.name('check')
|
|
67
76
|
.version('2.0.0')
|
|
68
77
|
.description('Check your Shop Mini configuration')
|
|
69
78
|
.action(async () => {
|
|
79
|
+
assertManifestValid();
|
|
70
80
|
assertApiKeyIsSet();
|
|
71
81
|
await assertDependenciesUpToDate();
|
|
72
82
|
await assertNoActiveSubmissions();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/commands/check/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,yCAAiC;AACjC,oDAA2B;AAC3B,kDAAyB;AACzB,+CAAgC;AAEhC,gFAA2D;AAE3D,+CAA2D;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/commands/check/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,yCAAiC;AACjC,oDAA2B;AAC3B,kDAAyB;AACzB,+CAAgC;AAEhC,gFAA2D;AAE3D,+CAA2D;AAC3D,+CAAiD;AAE1C,KAAK,UAAU,0BAA0B;IAC9C,MAAM,EAAC,MAAM,EAAE,gBAAgB,EAAC,GAAG,MAAM,IAAA,oCAAS,EAAC;QACjD,GAAG,EAAE,0CAA0C;KAChD,CAAC,CAAA;IACF,MAAM,mBAAmB,GACvB,IAAA,qCAA0B,EAAC,yBAAyB,CAAC,IAAI,OAAO,CAAA;IAElE,MAAM,UAAU,GAAG,gBAAM,CAAC,EAAE,CAAC,mBAAmB,EAAE,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAA;IAEtE,IAAI,CAAC,UAAU,EAAE;QACf,OAAO,CAAC,IAAI,CAAC,eAAK,CAAC,GAAG,CAAC,iDAAiD,CAAC,CAAC,CAAA;QAC1E,OAAO,CAAC,GAAG,CACT,sBAAsB,mBAAmB,aAAa,gBAAgB,GAAG,CAC1E,CAAA;QACD,OAAO,CAAC,GAAG,CACT,OAAO,eAAK,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAC9B,2CAA2C,CAC5C,aAAa,CACf,CAAA;QACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;KAChB;AACH,CAAC;AArBD,gEAqBC;AAED,SAAgB,iBAAiB;IAC/B,MAAM,CAAC,MAAM,EAAE,CAAA;IACf,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAA;IAE7C,IAAI,CAAC,MAAM,EAAE;QACX,OAAO,CAAC,IAAI,CAAC,eAAK,CAAC,GAAG,CAAC,8CAA8C,CAAC,CAAC,CAAA;QACvE,OAAO,CAAC,GAAG,CACT,qDAAqD,eAAK,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAC5E,8CAA8C,CAC/C,EAAE,CACJ,CAAA;QACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;KAChB;AACH,CAAC;AAbD,8CAaC;AAEM,KAAK,UAAU,yBAAyB;IAC7C,kDAAkD;AACpD,CAAC;AAFD,8DAEC;AAED,SAAgB,mBAAmB;IACjC,MAAM,EAAC,KAAK,EAAE,MAAM,EAAC,GAAG,IAAA,2BAAgB,GAAE,CAAA;IAE1C,IAAI,CAAC,KAAK,EAAE;QACV,OAAO,CAAC,IAAI,CAAC,eAAK,CAAC,GAAG,CAAC,qBAAqB,MAAM,EAAE,CAAC,CAAC,CAAA;QACtD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;KAChB;AACH,CAAC;AAPD,kDAOC;AAEM,KAAK,UAAU,WAAW,CAAC,aAAsB;IACtD,MAAM,OAAO,GAAG,IAAI,mBAAO,EAAE;SAC1B,IAAI,CAAC,OAAO,CAAC;SACb,OAAO,CAAC,OAAO,CAAC;SAChB,WAAW,CAAC,oCAAoC,CAAC;SACjD,MAAM,CAAC,KAAK,IAAI,EAAE;QACjB,mBAAmB,EAAE,CAAA;QACrB,iBAAiB,EAAE,CAAA;QACnB,MAAM,0BAA0B,EAAE,CAAA;QAClC,MAAM,yBAAyB,EAAE,CAAA;IACnC,CAAC,CAAC,CAAA;IAEJ,aAAa,CAAC,UAAU,CAAC,OAAO,CAAC,CAAA;AACnC,CAAC;AAbD,kCAaC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.validateManifest = void 0;
|
|
7
|
+
const jsonschema_1 = require("jsonschema");
|
|
8
|
+
const minis_manifest_1 = require("../../dev/utils/minis-manifest");
|
|
9
|
+
const contextual_image_schema_json_1 = __importDefault(require("./schemas/contextual-image.schema.json"));
|
|
10
|
+
const contextual_object_schema_json_1 = __importDefault(require("./schemas/contextual-object.schema.json"));
|
|
11
|
+
const manifest_schema_json_1 = __importDefault(require("./schemas/manifest.schema.json"));
|
|
12
|
+
const visibility_schema_json_1 = __importDefault(require("./schemas/visibility.schema.json"));
|
|
13
|
+
function validateManifest() {
|
|
14
|
+
const manifest = (0, minis_manifest_1.getMiniManifest)();
|
|
15
|
+
const validator = new jsonschema_1.Validator();
|
|
16
|
+
validator.addSchema(contextual_image_schema_json_1.default, '/contextual-image.schema.json');
|
|
17
|
+
validator.addSchema(contextual_object_schema_json_1.default, '/contextual-object.schema.json');
|
|
18
|
+
validator.addSchema(visibility_schema_json_1.default, '/visibility.schema.json');
|
|
19
|
+
const result = validator.validate(manifest, manifest_schema_json_1.default);
|
|
20
|
+
return {
|
|
21
|
+
valid: result.valid,
|
|
22
|
+
errors: result.errors.map(validationError => validationError.stack),
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
exports.validateManifest = validateManifest;
|
|
26
|
+
//# sourceMappingURL=manifest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.js","sourceRoot":"","sources":["../../../../src/commands/check/utils/manifest.ts"],"names":[],"mappings":";;;;;;AAAA,2CAAoC;AAEpC,mEAA8D;AAE9D,0GAA0E;AAC1E,4GAA4E;AAC5E,0FAA2D;AAC3D,8FAA+D;AAE/D,SAAgB,gBAAgB;IAC9B,MAAM,QAAQ,GAAG,IAAA,gCAAe,GAAE,CAAA;IAClC,MAAM,SAAS,GAAG,IAAI,sBAAS,EAAE,CAAA;IAEjC,SAAS,CAAC,SAAS,CAAC,sCAAqB,EAAE,+BAA+B,CAAC,CAAA;IAC3E,SAAS,CAAC,SAAS,CAAC,uCAAsB,EAAE,gCAAgC,CAAC,CAAA;IAC7E,SAAS,CAAC,SAAS,CAAC,gCAAgB,EAAE,yBAAyB,CAAC,CAAA;IAEhE,MAAM,MAAM,GAAG,SAAS,CAAC,QAAQ,CAAC,QAAQ,EAAE,8BAAc,CAAC,CAAA;IAE3D,OAAO;QACL,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,eAAe,CAAC,EAAE,CAAC,eAAe,CAAC,KAAK,CAAC;KACpE,CAAA;AACH,CAAC;AAdD,4CAcC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$id": "/contextual-image.schema.json",
|
|
3
|
+
"$schema": "http://json-schema.org/draft-07/schema",
|
|
4
|
+
"type": "object",
|
|
5
|
+
"title": "Contextual image entry-point",
|
|
6
|
+
"required": ["default_value"],
|
|
7
|
+
"properties": {
|
|
8
|
+
"namespace": {
|
|
9
|
+
"type": "string"
|
|
10
|
+
},
|
|
11
|
+
"key": {
|
|
12
|
+
"type": "string"
|
|
13
|
+
},
|
|
14
|
+
"default_value": {
|
|
15
|
+
"type": "array",
|
|
16
|
+
"items": {
|
|
17
|
+
"type": "string"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$id": "/contextual-object.schema.json",
|
|
3
|
+
"$schema": "http://json-schema.org/draft-07/schema",
|
|
4
|
+
"type": "object",
|
|
5
|
+
"title": "Contextual entry-point",
|
|
6
|
+
"properties": {
|
|
7
|
+
"namespace": {
|
|
8
|
+
"type": "string"
|
|
9
|
+
},
|
|
10
|
+
"key": {
|
|
11
|
+
"type": "string"
|
|
12
|
+
},
|
|
13
|
+
"default_value": {
|
|
14
|
+
"type": "string"
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema",
|
|
3
|
+
"type": "object",
|
|
4
|
+
"title": "Shop Mini Manifest",
|
|
5
|
+
"additionalProperties": false,
|
|
6
|
+
"properties": {
|
|
7
|
+
"name": {
|
|
8
|
+
"type": "string",
|
|
9
|
+
"description": "The name of the Mini",
|
|
10
|
+
"maxLength": 20
|
|
11
|
+
},
|
|
12
|
+
"handle": {
|
|
13
|
+
"type": "string",
|
|
14
|
+
"description": "A unique identifier for the Mini",
|
|
15
|
+
"pattern": "^(?:@[a-z0-9-*~][a-z0-9-*._~]*/)?[a-z0-9-~][a-z0-9-._~]*$"
|
|
16
|
+
},
|
|
17
|
+
"description": {
|
|
18
|
+
"type": "string",
|
|
19
|
+
"description": "The description of the Mini",
|
|
20
|
+
"maxLength": 100
|
|
21
|
+
},
|
|
22
|
+
"icon_url": {
|
|
23
|
+
"type": "string",
|
|
24
|
+
"title": "The icon of the Mini",
|
|
25
|
+
"description": "An absolute path to the image (e.g. https://example.com/icon.png)"
|
|
26
|
+
},
|
|
27
|
+
"partner_name": {
|
|
28
|
+
"type": "string",
|
|
29
|
+
"description": "The name of the Shopify partner who built the Mini",
|
|
30
|
+
"maxLength": 20
|
|
31
|
+
},
|
|
32
|
+
"contact_email": {
|
|
33
|
+
"type": "string",
|
|
34
|
+
"description": "An email address for receiving notifications.",
|
|
35
|
+
"format": "email"
|
|
36
|
+
},
|
|
37
|
+
"shopify_app_ids": {
|
|
38
|
+
"type": "array",
|
|
39
|
+
"description": "The list of Shopify app IDs associated with the Mini",
|
|
40
|
+
"items": {
|
|
41
|
+
"type": "integer"
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
"notification_topics": {
|
|
45
|
+
"type": "array",
|
|
46
|
+
"description": "Specifies the reason for communication and the scope for messages. An example of a topic could be live streaming related notifications related to a merchant",
|
|
47
|
+
"items": {
|
|
48
|
+
"type": "object",
|
|
49
|
+
"required": ["handle", "description", "scope"],
|
|
50
|
+
"properties": {
|
|
51
|
+
"handle": {
|
|
52
|
+
"type": "string",
|
|
53
|
+
"description": "Text identifier of the topic"
|
|
54
|
+
},
|
|
55
|
+
"description": {
|
|
56
|
+
"type": "string",
|
|
57
|
+
"description": "Reason for the communication using the topic"
|
|
58
|
+
},
|
|
59
|
+
"scope": {
|
|
60
|
+
"type": "string",
|
|
61
|
+
"description": "The scope in which messages are processed for the topic. SHOP means each message will be processed in the scope of a specific merchant, PRODUCT means each message will be processed in the scope of a specific product",
|
|
62
|
+
"enum": ["SHOP", "PRODUCT"]
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
"notification_templates": {
|
|
68
|
+
"type": "array",
|
|
69
|
+
"description": "Specify the message which will be sent to subscribers when notification is triggered by the mutation",
|
|
70
|
+
"items": {
|
|
71
|
+
"type": "object",
|
|
72
|
+
"required": [
|
|
73
|
+
"handle",
|
|
74
|
+
"topic_handle",
|
|
75
|
+
"title",
|
|
76
|
+
"body",
|
|
77
|
+
"cooldown_minutes"
|
|
78
|
+
],
|
|
79
|
+
"properties": {
|
|
80
|
+
"handle": {
|
|
81
|
+
"type": "string",
|
|
82
|
+
"description": "Text identifier of the template"
|
|
83
|
+
},
|
|
84
|
+
"topic_handle": {
|
|
85
|
+
"type": "string",
|
|
86
|
+
"description": "Reference to a notification topic"
|
|
87
|
+
},
|
|
88
|
+
"title": {
|
|
89
|
+
"type": "string",
|
|
90
|
+
"description": "Template of notification title, supports interpolation using the Liquid template language"
|
|
91
|
+
},
|
|
92
|
+
"body": {
|
|
93
|
+
"type": "string",
|
|
94
|
+
"description": "Template of notification body, supports interpolation using the Liquid template language"
|
|
95
|
+
},
|
|
96
|
+
"cooldown_minutes": {
|
|
97
|
+
"type": "number",
|
|
98
|
+
"description": "How often the template allowes to send the exact same message to the same user, in minutes"
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
"entry_points": {
|
|
104
|
+
"type": "array",
|
|
105
|
+
"description": "A list of entry points that the Mini supports",
|
|
106
|
+
"items": {
|
|
107
|
+
"type": "object",
|
|
108
|
+
"properties": {
|
|
109
|
+
"location": {
|
|
110
|
+
"type": "string",
|
|
111
|
+
"enum": ["PRODUCT_PAGE", "STORE_PAGE"]
|
|
112
|
+
},
|
|
113
|
+
"type": {
|
|
114
|
+
"type": "string",
|
|
115
|
+
"enum": ["CONTENT_CARD", "ACTION_BUTTON", "EVENT_CARD"]
|
|
116
|
+
},
|
|
117
|
+
"primary_text": {
|
|
118
|
+
"$ref": "/contextual-object.schema.json"
|
|
119
|
+
},
|
|
120
|
+
"call_to_action_text": {
|
|
121
|
+
"$ref": "/contextual-object.schema.json"
|
|
122
|
+
},
|
|
123
|
+
"images": {
|
|
124
|
+
"$ref": "/contextual-image.schema.json"
|
|
125
|
+
},
|
|
126
|
+
"visibility": {
|
|
127
|
+
"$ref": "/visibility.schema.json"
|
|
128
|
+
},
|
|
129
|
+
"visibility_rule": {
|
|
130
|
+
"$ref": "/visibility.schema.json"
|
|
131
|
+
},
|
|
132
|
+
"pre_active_images": {
|
|
133
|
+
"$ref": "/contextual-image.schema.json",
|
|
134
|
+
"description": "The images list used to render entry point in PRE_ACTIVE state"
|
|
135
|
+
},
|
|
136
|
+
"post_active_images": {
|
|
137
|
+
"$ref": "/contextual-image.schema.json",
|
|
138
|
+
"description": "(Optional) The images list used to render entry point in POST_ACTIVE state"
|
|
139
|
+
},
|
|
140
|
+
"notification_topic": {
|
|
141
|
+
"type": "string",
|
|
142
|
+
"description": "The notification topic handle which Shop users could subscribe to, must match one from the notification_topics manifest section"
|
|
143
|
+
},
|
|
144
|
+
"event_status": {
|
|
145
|
+
"type": "object",
|
|
146
|
+
"description": "The status of related event, affects how the user can interact with entry point",
|
|
147
|
+
"required": ["default_value"],
|
|
148
|
+
"properties": {
|
|
149
|
+
"namespace": {
|
|
150
|
+
"type": "string"
|
|
151
|
+
},
|
|
152
|
+
"key": {
|
|
153
|
+
"type": "string"
|
|
154
|
+
},
|
|
155
|
+
"default_value": {
|
|
156
|
+
"type": "string",
|
|
157
|
+
"enum": ["PRE_ACTIVE", "ACTIVE", "POST_ACTIVE"]
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
"allOf": [
|
|
163
|
+
{
|
|
164
|
+
"if": {
|
|
165
|
+
"properties": {
|
|
166
|
+
"type": {
|
|
167
|
+
"const": "CONTENT_CARD"
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
},
|
|
171
|
+
"then": {
|
|
172
|
+
"required": ["location", "type", "primary_text", "images"]
|
|
173
|
+
}
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
"if": {
|
|
177
|
+
"properties": {
|
|
178
|
+
"type": {
|
|
179
|
+
"const": "ACTION_BUTTON"
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
},
|
|
183
|
+
"then": {
|
|
184
|
+
"required": ["location", "type", "call_to_action_text"]
|
|
185
|
+
}
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
"if": {
|
|
189
|
+
"properties": {
|
|
190
|
+
"type": {
|
|
191
|
+
"const": "EVENT_CARD"
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
},
|
|
195
|
+
"then": {
|
|
196
|
+
"required": [
|
|
197
|
+
"location",
|
|
198
|
+
"type",
|
|
199
|
+
"primary_text",
|
|
200
|
+
"images",
|
|
201
|
+
"notification_topic",
|
|
202
|
+
"pre_active_images"
|
|
203
|
+
]
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
]
|
|
207
|
+
}
|
|
208
|
+
},
|
|
209
|
+
"ios_app_store_url": {
|
|
210
|
+
"type": "string",
|
|
211
|
+
"description": "The URL to the Mini's publisher's app on the iOS App Store"
|
|
212
|
+
},
|
|
213
|
+
"android_play_store_url": {
|
|
214
|
+
"type": "string",
|
|
215
|
+
"description": "The URL to the Mini's publisher's app on the Android Play Store"
|
|
216
|
+
}
|
|
217
|
+
},
|
|
218
|
+
"required": [
|
|
219
|
+
"name",
|
|
220
|
+
"handle",
|
|
221
|
+
"icon_url",
|
|
222
|
+
"partner_name",
|
|
223
|
+
"shopify_app_ids",
|
|
224
|
+
"entry_points"
|
|
225
|
+
]
|
|
226
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$id": "/visibility.schema.json",
|
|
3
|
+
"$schema": "http://json-schema.org/draft-07/schema",
|
|
4
|
+
"type": "object",
|
|
5
|
+
"title": "Entry-point visibility schema",
|
|
6
|
+
"properties": {
|
|
7
|
+
"owner_type": {
|
|
8
|
+
"type": "string"
|
|
9
|
+
},
|
|
10
|
+
"namespace": {
|
|
11
|
+
"type": "string"
|
|
12
|
+
},
|
|
13
|
+
"key": {
|
|
14
|
+
"type": "string"
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"access": "public",
|
|
5
5
|
"@shopify:registry": "https://registry.npmjs.org/"
|
|
6
6
|
},
|
|
7
|
-
"version": "0.0.
|
|
7
|
+
"version": "0.0.38",
|
|
8
8
|
"description": "Shop Minis CLI",
|
|
9
9
|
"main": "build/index.js",
|
|
10
10
|
"bin": {
|
|
@@ -32,6 +32,8 @@
|
|
|
32
32
|
"@babel/core": "^7.20.12",
|
|
33
33
|
"@babel/preset-env": "^7.20.2",
|
|
34
34
|
"@babel/preset-typescript": "^7.18.6",
|
|
35
|
+
"@graphql-codegen/cli": "^3.2.2",
|
|
36
|
+
"@graphql-codegen/client-preset": "^2.1.1",
|
|
35
37
|
"@react-native-community/eslint-config": "^3.1.0",
|
|
36
38
|
"@shopify/eslint-plugin": "^42.0.1",
|
|
37
39
|
"@types/connect": "^3.4.35",
|
|
@@ -54,6 +56,7 @@
|
|
|
54
56
|
"eslint-plugin-reanimated": "^2.0.0",
|
|
55
57
|
"jest": "^29.3.1",
|
|
56
58
|
"prettier": "^2.7.1",
|
|
59
|
+
"ts-jest": "^29.0.5",
|
|
57
60
|
"ts-node": "^10.9.1",
|
|
58
61
|
"typescript": "^4.9.4"
|
|
59
62
|
},
|
|
@@ -61,7 +64,9 @@
|
|
|
61
64
|
"@google-cloud/storage": "^6.9.0",
|
|
62
65
|
"@react-native-community/cli": "^10.0.0",
|
|
63
66
|
"@react-native-community/cli-plugin-metro": "^10.0.0",
|
|
67
|
+
"@shopify/cli-kit": "3.44",
|
|
64
68
|
"@types/qrcode-terminal": "^0.12.0",
|
|
69
|
+
"archiver": "^5.3.1",
|
|
65
70
|
"chalk": "^4.1.2",
|
|
66
71
|
"commander": "^9.4.1",
|
|
67
72
|
"connect": "^3.7.0",
|
|
@@ -69,9 +74,11 @@
|
|
|
69
74
|
"fs-extra": "^11.1.0",
|
|
70
75
|
"graphql": "^15.0.0",
|
|
71
76
|
"graphql-config": "^4.3.5",
|
|
77
|
+
"graphql-request": "^5.2.0",
|
|
72
78
|
"graphql-typescript-definitions": "^3.2.2",
|
|
73
79
|
"inquirer": "^8.2.3",
|
|
74
80
|
"internal-ip": "^6.2.0",
|
|
81
|
+
"jsonschema": "^1.4.1",
|
|
75
82
|
"lodash": "^4.17.21",
|
|
76
83
|
"metro": "^0.73.6",
|
|
77
84
|
"metro-config": "^0.73.6",
|
|
@@ -28,8 +28,8 @@ export const componentLinks: ComponentLink[] = [
|
|
|
28
28
|
screen: '__MINI_APP_HANDLE_PASCAL_CASE__.Icons',
|
|
29
29
|
},
|
|
30
30
|
{
|
|
31
|
-
name: '
|
|
32
|
-
screen: '__MINI_APP_HANDLE_PASCAL_CASE__.
|
|
31
|
+
name: 'ImageMultipleChoice',
|
|
32
|
+
screen: '__MINI_APP_HANDLE_PASCAL_CASE__.ImageMultipleChoice',
|
|
33
33
|
},
|
|
34
34
|
{
|
|
35
35
|
name: 'Inputs',
|
|
@@ -75,6 +75,10 @@ export const componentLinks: ComponentLink[] = [
|
|
|
75
75
|
name: 'QuantityPicker',
|
|
76
76
|
screen: '__MINI_APP_HANDLE_PASCAL_CASE__.QuantityPicker',
|
|
77
77
|
},
|
|
78
|
+
{
|
|
79
|
+
name: 'Quiz Example',
|
|
80
|
+
screen: '__MINI_APP_HANDLE_PASCAL_CASE__.Quiz',
|
|
81
|
+
},
|
|
78
82
|
{
|
|
79
83
|
name: 'WebView',
|
|
80
84
|
screen: '__MINI_APP_HANDLE_PASCAL_CASE__.WebView',
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ImageCarousel,
|
|
3
|
+
ImageCarouselProps,
|
|
4
|
+
} from '@shopify/shop-minis-platform-sdk'
|
|
5
|
+
import {useCallback, useEffect, useState} from 'react'
|
|
6
|
+
|
|
7
|
+
import {QuizSlide, QuizSlideProps} from './QuizSlide'
|
|
8
|
+
import {SlideLogic} from './types'
|
|
9
|
+
|
|
10
|
+
export interface ImageCarouselSlideProps extends SlideLogic<number[]> {
|
|
11
|
+
quizSlideProps: QuizSlideProps
|
|
12
|
+
imageCarouselProps: ImageCarouselProps
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const ImageCarouselSlide = ({
|
|
16
|
+
quizSlideProps,
|
|
17
|
+
imageCarouselProps,
|
|
18
|
+
determineCtaDisabled,
|
|
19
|
+
handleAnswerSelection,
|
|
20
|
+
}: ImageCarouselSlideProps) => {
|
|
21
|
+
const [selectedIndex, setSelectedIndex] = useState<number>()
|
|
22
|
+
|
|
23
|
+
const [isCtaDisabled, setCtaDisabled] = useState(
|
|
24
|
+
quizSlideProps.ctaDisabled ?? false
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
const handleOnChoiceSelected = useCallback((index: number) => {
|
|
28
|
+
setSelectedIndex(index)
|
|
29
|
+
}, [])
|
|
30
|
+
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
if (handleAnswerSelection === undefined) return
|
|
33
|
+
|
|
34
|
+
handleAnswerSelection(selectedIndex === undefined ? [] : [selectedIndex])
|
|
35
|
+
}, [handleAnswerSelection, selectedIndex])
|
|
36
|
+
|
|
37
|
+
useEffect(() => {
|
|
38
|
+
if (determineCtaDisabled === undefined) return
|
|
39
|
+
|
|
40
|
+
setCtaDisabled(
|
|
41
|
+
determineCtaDisabled(selectedIndex === undefined ? [] : [selectedIndex])
|
|
42
|
+
)
|
|
43
|
+
}, [determineCtaDisabled, selectedIndex])
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<QuizSlide {...quizSlideProps} ctaDisabled={isCtaDisabled}>
|
|
47
|
+
<ImageCarousel
|
|
48
|
+
{...imageCarouselProps}
|
|
49
|
+
selectedIndexes={selectedIndex === undefined ? [] : [selectedIndex]}
|
|
50
|
+
onChoiceSelected={handleOnChoiceSelected}
|
|
51
|
+
/>
|
|
52
|
+
</QuizSlide>
|
|
53
|
+
)
|
|
54
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Box,
|
|
3
|
+
MultipleChoice,
|
|
4
|
+
MultipleChoiceProps,
|
|
5
|
+
useMultiSelect,
|
|
6
|
+
} from '@shopify/shop-minis-platform-sdk'
|
|
7
|
+
import {useCallback, useEffect, useMemo, useState} from 'react'
|
|
8
|
+
|
|
9
|
+
import {QuizSlide, QuizSlideProps} from './QuizSlide'
|
|
10
|
+
import {SlideLogic} from './types'
|
|
11
|
+
|
|
12
|
+
export interface MultipleChoiceSlideProps extends SlideLogic<number[]> {
|
|
13
|
+
quizSlideProps: QuizSlideProps
|
|
14
|
+
multipleChoiceProps: MultipleChoiceProps
|
|
15
|
+
isMultiSelect?: boolean
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const MultipleChoiceSlide = ({
|
|
19
|
+
quizSlideProps,
|
|
20
|
+
multipleChoiceProps,
|
|
21
|
+
isMultiSelect,
|
|
22
|
+
determineCtaDisabled,
|
|
23
|
+
handleAnswerSelection,
|
|
24
|
+
}: MultipleChoiceSlideProps) => {
|
|
25
|
+
const [multiSelectedIndexes, toggleIndex] = useMultiSelect()
|
|
26
|
+
const [selectedIndex, setSelectedIndex] = useState<number>()
|
|
27
|
+
|
|
28
|
+
const [isCtaDisabled, setCtaDisabled] = useState(
|
|
29
|
+
quizSlideProps.ctaDisabled ?? false
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
const singleSelectIndex = useMemo(
|
|
33
|
+
() => (selectedIndex === undefined ? [] : [selectedIndex]),
|
|
34
|
+
[selectedIndex]
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
const selectedIndexes = useMemo(
|
|
38
|
+
() => (isMultiSelect ? multiSelectedIndexes : singleSelectIndex),
|
|
39
|
+
[isMultiSelect, multiSelectedIndexes, singleSelectIndex]
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
const handleOnChoiceSelected = useCallback(
|
|
43
|
+
(index: number) => {
|
|
44
|
+
if (isMultiSelect) {
|
|
45
|
+
toggleIndex(index)
|
|
46
|
+
} else {
|
|
47
|
+
setSelectedIndex(index)
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
[isMultiSelect, toggleIndex]
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
useEffect(() => {
|
|
54
|
+
if (selectedIndexes.length < 1 || handleAnswerSelection === undefined)
|
|
55
|
+
return
|
|
56
|
+
|
|
57
|
+
handleAnswerSelection(selectedIndexes)
|
|
58
|
+
}, [handleAnswerSelection, selectedIndexes])
|
|
59
|
+
|
|
60
|
+
useEffect(() => {
|
|
61
|
+
if (determineCtaDisabled === undefined) return
|
|
62
|
+
|
|
63
|
+
setCtaDisabled(determineCtaDisabled(selectedIndexes))
|
|
64
|
+
}, [determineCtaDisabled, selectedIndexes])
|
|
65
|
+
|
|
66
|
+
return (
|
|
67
|
+
<QuizSlide {...quizSlideProps} ctaDisabled={isCtaDisabled}>
|
|
68
|
+
<Box marginBottom="xl">
|
|
69
|
+
<MultipleChoice
|
|
70
|
+
{...multipleChoiceProps}
|
|
71
|
+
selectedIndexes={selectedIndexes}
|
|
72
|
+
onChoiceSelected={handleOnChoiceSelected}
|
|
73
|
+
/>
|
|
74
|
+
</Box>
|
|
75
|
+
</QuizSlide>
|
|
76
|
+
)
|
|
77
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import {createContext, useContext, useMemo} from 'react'
|
|
2
|
+
|
|
3
|
+
import {useQuizData} from '../../hooks/useQuizData'
|
|
4
|
+
import {useQuizState} from '../../hooks/useQuizState'
|
|
5
|
+
|
|
6
|
+
import {QuizData, QuizDataState, QuizResult, QuizResultEntry} from './types'
|
|
7
|
+
|
|
8
|
+
interface QuizContext {
|
|
9
|
+
recordQuestionResult: (id: string, answer: QuizResultEntry) => void
|
|
10
|
+
quizResult: QuizResult | undefined
|
|
11
|
+
quizDataState: QuizDataState | 'Loading'
|
|
12
|
+
quizData: QuizData | undefined
|
|
13
|
+
setUserName: (name: string) => void
|
|
14
|
+
userName: string | undefined
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const QuizContext = createContext<QuizContext>({
|
|
18
|
+
recordQuestionResult: () => undefined,
|
|
19
|
+
quizResult: undefined,
|
|
20
|
+
quizDataState: 'Loading',
|
|
21
|
+
quizData: undefined,
|
|
22
|
+
setUserName: (_name: string) => undefined,
|
|
23
|
+
userName: undefined,
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
export interface QuizProviderProps {
|
|
27
|
+
children: React.ReactElement
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export const QuizContextProvider = ({children}: QuizProviderProps) => {
|
|
31
|
+
const {quizDataState, quizData} = useQuizData()
|
|
32
|
+
const {recordQuestionResult, quizResult, setUserName, userName} =
|
|
33
|
+
useQuizState()
|
|
34
|
+
|
|
35
|
+
const value = useMemo(
|
|
36
|
+
() => ({
|
|
37
|
+
recordQuestionResult,
|
|
38
|
+
quizResult,
|
|
39
|
+
quizDataState,
|
|
40
|
+
quizData,
|
|
41
|
+
setUserName,
|
|
42
|
+
userName,
|
|
43
|
+
}),
|
|
44
|
+
[
|
|
45
|
+
quizData,
|
|
46
|
+
quizDataState,
|
|
47
|
+
quizResult,
|
|
48
|
+
recordQuestionResult,
|
|
49
|
+
setUserName,
|
|
50
|
+
userName,
|
|
51
|
+
]
|
|
52
|
+
)
|
|
53
|
+
return <QuizContext.Provider value={value}>{children}</QuizContext.Provider>
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export const useQuizContext = () => {
|
|
57
|
+
const context = useContext(QuizContext)
|
|
58
|
+
|
|
59
|
+
if (!context) {
|
|
60
|
+
throw new Error('Make sure to wrap your component with QuizProvider')
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return context
|
|
64
|
+
}
|