@maccesar/titools 4.3.0 → 4.4.0
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/README.md +8 -2
- package/package.json +1 -1
- package/skills/ti-expert/SKILL.md +38 -5
- package/skills/ti-expert/references/alloy-builtins.md +11 -22
- package/skills/ti-expert/references/alloy-structure.md +42 -18
- package/skills/ti-expert/references/anti-patterns.md +13 -5
- package/skills/ti-expert/references/code-conventions.md +9 -3
- package/skills/ti-expert/references/controller-patterns.md +33 -26
- package/skills/ti-expert/references/examples.md +48 -21
- package/skills/ti-expert/references/feedback-migration.md +165 -0
- package/skills/ti-expert/references/feedback-surfaces.md +140 -0
- package/skills/ti-expert/references/feedback-widget-contracts.md +213 -0
- package/skills/ti-expert/references/migration-patterns.md +47 -25
- package/skills/ti-expert/references/security-device.md +58 -56
- package/skills/ti-expert/references/state-management.md +5 -5
- package/skills/ti-expert/references/theming.md +134 -392
|
@@ -105,6 +105,9 @@ const { TokenStorage } = require('services/tokenStorage')
|
|
|
105
105
|
const { AuthService } = require('services/authService')
|
|
106
106
|
|
|
107
107
|
function init() {
|
|
108
|
+
// The login view declares the app-owned Dialog Widget as its last child.
|
|
109
|
+
$.dialog.attach($.getView())
|
|
110
|
+
|
|
108
111
|
// Check if biometric login is available and enabled
|
|
109
112
|
const { available, type } = BiometricService.checkAvailability()
|
|
110
113
|
const biometricEnabled = Ti.App.Properties.getBool('biometricEnabled', false)
|
|
@@ -143,22 +146,25 @@ async function onLoginSuccess(user) {
|
|
|
143
146
|
if (available && !Ti.App.Properties.getBool('askedBiometric', false)) {
|
|
144
147
|
Ti.App.Properties.setBool('askedBiometric', true)
|
|
145
148
|
|
|
146
|
-
//
|
|
147
|
-
|
|
149
|
+
// The choice is app-owned; the later biometric authentication prompt remains native.
|
|
150
|
+
$.dialog.show({
|
|
148
151
|
title: String.format(L('enable_biometric_title'), type),
|
|
149
152
|
message: String.format(L('enable_biometric_msg'), type),
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
dialog.addEventListener('click', (e) => {
|
|
154
|
-
if (e.index === 1) {
|
|
153
|
+
cancelTitle: L('not_now'),
|
|
154
|
+
confirmTitle: L('enable'),
|
|
155
|
+
onConfirm: () => {
|
|
155
156
|
Ti.App.Properties.setBool('biometricEnabled', true)
|
|
156
157
|
}
|
|
157
158
|
})
|
|
158
|
-
|
|
159
|
-
dialog.show()
|
|
160
159
|
}
|
|
161
160
|
}
|
|
161
|
+
|
|
162
|
+
function cleanup() {
|
|
163
|
+
$.dialog.destroy()
|
|
164
|
+
$.destroy()
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
$.cleanup = cleanup
|
|
162
168
|
```
|
|
163
169
|
|
|
164
170
|
## Deep link security
|
|
@@ -482,7 +488,9 @@ exports.SecurityService = {
|
|
|
482
488
|
enforceSecurityPolicy() {
|
|
483
489
|
const { compromised, reasons } = this.checkDeviceIntegrity()
|
|
484
490
|
|
|
485
|
-
if (!compromised)
|
|
491
|
+
if (!compromised) {
|
|
492
|
+
return { outcome: 'allow', reasons: [], restricted: false }
|
|
493
|
+
}
|
|
486
494
|
|
|
487
495
|
// Log security event
|
|
488
496
|
logger.error('Security', 'Compromised device detected', {
|
|
@@ -496,50 +504,16 @@ exports.SecurityService = {
|
|
|
496
504
|
|
|
497
505
|
switch (policy) {
|
|
498
506
|
case 'block':
|
|
499
|
-
|
|
500
|
-
this._showBlockedScreen()
|
|
501
|
-
return false
|
|
507
|
+
return { outcome: 'block', reasons, restricted: true }
|
|
502
508
|
|
|
503
509
|
case 'restrict':
|
|
504
|
-
// Disable sensitive features
|
|
505
510
|
Ti.App.Properties.setBool('restrictedMode', true)
|
|
506
|
-
|
|
507
|
-
return true
|
|
511
|
+
return { outcome: 'warn', reasons, restricted: true }
|
|
508
512
|
|
|
509
513
|
case 'warn':
|
|
510
514
|
default:
|
|
511
|
-
|
|
512
|
-
this._showWarning()
|
|
513
|
-
return true
|
|
515
|
+
return { outcome: 'warn', reasons, restricted: false }
|
|
514
516
|
}
|
|
515
|
-
},
|
|
516
|
-
|
|
517
|
-
_showBlockedScreen() {
|
|
518
|
-
// Note: iOS guidelines prohibit programmatically terminating an app.
|
|
519
|
-
// The best practice is to show a blocking dialog and prevent further interaction.
|
|
520
|
-
// On Android you can call Ti.Android.currentActivity.finish() to close the activity.
|
|
521
|
-
const dialog = Ti.UI.createAlertDialog({
|
|
522
|
-
title: L('security_blocked_title'),
|
|
523
|
-
message: L('security_blocked_msg'),
|
|
524
|
-
buttonNames: [L('close')]
|
|
525
|
-
})
|
|
526
|
-
|
|
527
|
-
dialog.addEventListener('click', () => {
|
|
528
|
-
if (OS_ANDROID) {
|
|
529
|
-
Ti.Android.currentActivity.finish()
|
|
530
|
-
}
|
|
531
|
-
// On iOS: leave the dialog visible — Apple guidelines prohibit force-quitting apps.
|
|
532
|
-
})
|
|
533
|
-
|
|
534
|
-
dialog.show()
|
|
535
|
-
},
|
|
536
|
-
|
|
537
|
-
_showWarning() {
|
|
538
|
-
Ti.UI.createAlertDialog({
|
|
539
|
-
title: L('security_warning_title'),
|
|
540
|
-
message: L('security_warning_msg'),
|
|
541
|
-
buttonNames: [L('i_understand')]
|
|
542
|
-
}).show()
|
|
543
517
|
}
|
|
544
518
|
}
|
|
545
519
|
```
|
|
@@ -550,18 +524,45 @@ exports.SecurityService = {
|
|
|
550
524
|
// alloy.js
|
|
551
525
|
const { SecurityService } = require('services/securityService')
|
|
552
526
|
|
|
553
|
-
//
|
|
554
|
-
const
|
|
527
|
+
// Services return policy; they do not create UI before the root exists.
|
|
528
|
+
const securityDecision = SecurityService.enforceSecurityPolicy()
|
|
529
|
+
const root = Alloy.createController('index', { securityDecision })
|
|
530
|
+
root.getView().open()
|
|
531
|
+
```
|
|
532
|
+
|
|
533
|
+
```javascript
|
|
534
|
+
// controllers/index.js
|
|
535
|
+
const decision = $.args.securityDecision
|
|
536
|
+
|
|
537
|
+
function init() {
|
|
538
|
+
$.dialog.attach($.indexWindow)
|
|
539
|
+
|
|
540
|
+
if (decision.outcome === 'block') {
|
|
541
|
+
// Show a dedicated blocking state in the root XML and do not initialize
|
|
542
|
+
// protected application content. Do not force-quit the app.
|
|
543
|
+
$.blockingState.visible = true
|
|
544
|
+
$.appContent.visible = false
|
|
545
|
+
return
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
initializeAppContent({ restricted: decision.restricted })
|
|
549
|
+
|
|
550
|
+
if (decision.outcome === 'warn') {
|
|
551
|
+
$.dialog.show({
|
|
552
|
+
title: L('security_warning_title'),
|
|
553
|
+
message: L('security_warning_msg'),
|
|
554
|
+
tone: 'warning',
|
|
555
|
+
confirmTitle: L('i_understand')
|
|
556
|
+
})
|
|
557
|
+
}
|
|
558
|
+
}
|
|
555
559
|
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
560
|
+
function cleanup() {
|
|
561
|
+
$.dialog.destroy()
|
|
562
|
+
$.destroy()
|
|
559
563
|
}
|
|
560
564
|
|
|
561
|
-
|
|
562
|
-
setInterval(() => {
|
|
563
|
-
SecurityService.checkDeviceIntegrity()
|
|
564
|
-
}, 5 * 60 * 1000) // Every 5 minutes
|
|
565
|
+
$.cleanup = cleanup
|
|
565
566
|
```
|
|
566
567
|
|
|
567
568
|
## Security checklist
|
|
@@ -578,3 +579,4 @@ setInterval(() => {
|
|
|
578
579
|
| **Integrity** | Check for jailbreak/root | checkDeviceIntegrity() |
|
|
579
580
|
| **Integrity** | Define security policy | block/restrict/warn |
|
|
580
581
|
| **Integrity** | Log security events | Always log compromises |
|
|
582
|
+
| **Integrity UI** | Keep policy out of services | Root controller presents blocking state or Dialog |
|
|
@@ -746,6 +746,7 @@ appStore.use(debugMiddleware)
|
|
|
746
746
|
// controllers/debug/stateViewer.js (Development only)
|
|
747
747
|
const { StateDebug } = require('services/stateDebug')
|
|
748
748
|
const { appStore } = require('services/stateStore')
|
|
749
|
+
const { Navigation } = require('services/navigation')
|
|
749
750
|
|
|
750
751
|
function init() {
|
|
751
752
|
if (!Alloy.CFG.debug) {
|
|
@@ -779,12 +780,11 @@ function onHistoryItemClick(e) {
|
|
|
779
780
|
const index = e.itemIndex
|
|
780
781
|
const entry = StateDebug.getHistory()[index]
|
|
781
782
|
|
|
782
|
-
//
|
|
783
|
-
|
|
783
|
+
// Structured, potentially long content belongs in a scrollable debug screen.
|
|
784
|
+
Navigation.open('debug/stateDiff', {
|
|
784
785
|
title: entry.action,
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
}).show()
|
|
786
|
+
content: JSON.stringify(entry.diff, null, 2)
|
|
787
|
+
})
|
|
788
788
|
}
|
|
789
789
|
|
|
790
790
|
function cleanup() {
|