@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.
@@ -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
- // Ask user if they want to enable biometric login
147
- const dialog = Ti.UI.createAlertDialog({
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
- buttonNames: [L('not_now'), L('enable')]
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) return true
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
- // Prevent app usage
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
- this._showWarning()
507
- return true
511
+ return { outcome: 'warn', reasons, restricted: true }
508
512
 
509
513
  case 'warn':
510
514
  default:
511
- // Just warn the user
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
- // Check device integrity at app start
554
- const securityCheck = SecurityService.enforceSecurityPolicy()
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
- if (!securityCheck) {
557
- // Don't initialize the app
558
- return
560
+ function cleanup() {
561
+ $.dialog.destroy()
562
+ $.destroy()
559
563
  }
560
564
 
561
- // Check again periodically (in case of runtime jailbreak tools)
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
- // Show diff
783
- Ti.UI.createAlertDialog({
783
+ // Structured, potentially long content belongs in a scrollable debug screen.
784
+ Navigation.open('debug/stateDiff', {
784
785
  title: entry.action,
785
- message: JSON.stringify(entry.diff, null, 2),
786
- buttonNames: ['OK']
787
- }).show()
786
+ content: JSON.stringify(entry.diff, null, 2)
787
+ })
788
788
  }
789
789
 
790
790
  function cleanup() {