From fbba6327fe0c51f2fc78cbb7a11ea98b43333460 Mon Sep 17 00:00:00 2001 From: Vagner Leite da Silva Date: Thu, 18 Sep 2025 14:26:40 -0300 Subject: [PATCH 01/12] =?UTF-8?q?style(theme):=20refina=20tokens=20do=20de?= =?UTF-8?q?sign=20Pink=E2=80=91Code\n\n-=20Ajusta=20contraste=20do=20prim?= =?UTF-8?q?=C3=A1rio=20no=20dark=20para=20branco\n-=20Define=20conjuntos?= =?UTF-8?q?=20completos=20para=20secondary/tertiary/success/warning/danger?= =?UTF-8?q?\n-=20Adiciona=20tokens=20de=20superf=C3=ADcie=20e=20eleva?= =?UTF-8?q?=C3=A7=C3=A3o=20(--surface,=20--surface-2,=20--elevation-1)\n-?= =?UTF-8?q?=20Cria=20token=20--scan-outline=20para=20overlay=20do=20scanne?= =?UTF-8?q?r\n-=20Define=20cor=20selecionada=20das=20tabs=20para=20o=20pri?= =?UTF-8?q?m=C3=A1rio?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/theme/variables.scss | 171 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 171 insertions(+) diff --git a/src/theme/variables.scss b/src/theme/variables.scss index 6146c39..0004b83 100644 --- a/src/theme/variables.scss +++ b/src/theme/variables.scss @@ -1,2 +1,173 @@ // For information on how to create your own theme, please see: // http://ionicframework.com/docs/theming/ +/* ========================================================================== + Pink-Code · Ionic 8 Design Tokens + Place this complete file at: src/theme/variables.scss + ========================================================================== */ + +/* 1 ▪ Base palette (Light mode) */ +:root { + /* --- Brand ------------------------------------------------------------- */ + --ion-color-primary: #ff5eae; + --ion-color-primary-rgb: 255, 94, 174; + --ion-color-primary-contrast: #ffffff; + --ion-color-primary-contrast-rgb: 255, 255, 255; + --ion-color-primary-shade: #e0559c; + --ion-color-primary-tint: #ff7ab9; + + /* Optional secondary / tertiary accents */ + --ion-color-secondary: #ffb347; /* laranja suave */ + --ion-color-tertiary: #5ec6ff; /* azul para estados neutros */ + /* Secondary full token set */ + --ion-color-secondary-rgb: 255, 179, 71; + --ion-color-secondary-contrast: #ffffff; + --ion-color-secondary-contrast-rgb: 255, 255, 255; + --ion-color-secondary-shade: #e09e3f; + --ion-color-secondary-tint: #ffc46a; + /* Tertiary full token set */ + --ion-color-tertiary-rgb: 94, 198, 255; + --ion-color-tertiary-contrast: #ffffff; + --ion-color-tertiary-contrast-rgb: 255, 255, 255; + --ion-color-tertiary-shade: #52addf; + --ion-color-tertiary-tint: #7bd1ff; + + /* Neutral greys */ + --ion-color-step-50: #f9f9fa; + --ion-color-step-100: #f4f5f8; + --ion-color-step-150: #e9eaee; + --ion-color-step-200: #dcdde3; + --ion-color-step-250: #c3c5ce; + --ion-color-step-300: #a9acb8; + --ion-color-step-350: #9194a1; + --ion-color-step-400: #7a7d8b; + --ion-color-step-450: #636673; + --ion-color-step-500: #4c4f5b; + --ion-color-step-550: #373a44; + --ion-color-step-600: #272a33; + + /* Text & surface */ + --ion-text-color: var(--ion-color-step-500); + --ion-background-color: var(--ion-color-step-50); + --ion-card-background: #ffffff; + --ion-item-background: #ffffff; + + /* Status colors (optional) */ + --ion-color-success: #4cd964; + --ion-color-success-rgb: 76, 217, 100; + --ion-color-success-contrast: #ffffff; + --ion-color-success-contrast-rgb: 255, 255, 255; + --ion-color-success-shade: #42bf58; + --ion-color-success-tint: #69e07d; + + --ion-color-warning: #ffcc00; + --ion-color-warning-rgb: 255, 204, 0; + --ion-color-warning-contrast: #1f222a; + --ion-color-warning-contrast-rgb: 31, 34, 42; + --ion-color-warning-shade: #e0b400; + --ion-color-warning-tint: #ffd52b; + + --ion-color-danger: #ff3b30; + --ion-color-danger-rgb: 255, 59, 48; + --ion-color-danger-contrast: #ffffff; + --ion-color-danger-contrast-rgb: 255, 255, 255; + --ion-color-danger-shade: #e0352b; + --ion-color-danger-tint: #ff5c52; + + /* Surfaces & elevation */ + --surface: #ffffff; + --surface-2: var(--ion-color-step-100); + --elevation-1: 0 8px 24px rgba(0, 0, 0, 0.12); + + /* Scanner outline */ + --scan-outline: var(--ion-color-primary); +} + +/* 2 ▪ Dark mode overrides */ +@media (prefers-color-scheme: dark) { + :root { + --ion-color-primary: #ff7ab9; /* tom mais claro p/ contraste */ + --ion-color-primary-rgb: 255, 122, 185; + --ion-color-primary-contrast: #ffffff; + --ion-color-primary-contrast-rgb: 255, 255, 255; + --ion-color-primary-shade: #e467a3; + --ion-color-primary-tint: #ff93c5; + + /* Neutrals flipped */ + --ion-color-step-50: #1f222a; + --ion-color-step-100: #242731; + --ion-color-step-150: #2a2e38; + --ion-color-step-200: #30333e; + --ion-color-step-250: #363a45; + --ion-color-step-300: #3d404c; + --ion-color-step-350: #454854; + --ion-color-step-400: #4d505d; + --ion-color-step-450: #5a5d6a; + --ion-color-step-500: #dadde5; /* texto principal #E4E7EC like */ + --ion-color-step-550: #e4e7ec; + --ion-color-step-600: #f0f1f5; + + --ion-text-color: var(--ion-color-step-550); + --ion-background-color: var(--ion-color-step-50); + --ion-card-background: #2a2e38; + --ion-item-background: #2a2e38; + + /* Secondary/Tertiary full sets in dark */ + --ion-color-secondary-rgb: 255, 179, 71; + --ion-color-secondary-contrast: #ffffff; + --ion-color-secondary-contrast-rgb: 255, 255, 255; + --ion-color-secondary-shade: #e09e3f; + --ion-color-secondary-tint: #ffc46a; + + --ion-color-tertiary-rgb: 94, 198, 255; + --ion-color-tertiary-contrast: #ffffff; + --ion-color-tertiary-contrast-rgb: 255, 255, 255; + --ion-color-tertiary-shade: #52addf; + --ion-color-tertiary-tint: #7bd1ff; + + --ion-color-success-rgb: 76, 217, 100; + --ion-color-success-contrast: #1f222a; + --ion-color-success-contrast-rgb: 31, 34, 42; + --ion-color-success-shade: #42bf58; + --ion-color-success-tint: #69e07d; + + --ion-color-warning-rgb: 255, 204, 0; + --ion-color-warning-contrast: #1f222a; + --ion-color-warning-contrast-rgb: 31, 34, 42; + --ion-color-warning-shade: #e0b400; + --ion-color-warning-tint: #ffd52b; + + --ion-color-danger-rgb: 255, 59, 48; + --ion-color-danger-contrast: #ffffff; + --ion-color-danger-contrast-rgb: 255, 255, 255; + --ion-color-danger-shade: #e0352b; + --ion-color-danger-tint: #ff5c52; + + /* Surfaces & elevation */ + --surface: #2a2e38; + --surface-2: #242731; + --elevation-1: 0 12px 36px rgba(0, 0, 0, 0.45); + + /* Scanner outline */ + --scan-outline: var(--ion-color-primary); + } +} + +/* 3 ▪ Typography helpers */ +ion-title { + font-weight: 600; +} + +ion-card-title, ion-label { + line-height: 1.3; +} + +/* 4 ▪ Button tweaks (optional) */ +ion-button { + --border-radius: 24px; + font-weight: 600; +} + +/* Tabs active color aligned to brand */ +ion-tab-bar { + --color-selected: var(--ion-color-primary); +} From 80a7faa9a91a9d152f3d90fac61bd3ae4140ec61 Mon Sep 17 00:00:00 2001 From: Vagner Leite da Silva Date: Thu, 18 Sep 2025 14:36:25 -0300 Subject: [PATCH 02/12] =?UTF-8?q?feat(scanner-ui):=20estrutura=20visual=20?= =?UTF-8?q?da=20tela=20Scanner=20(sem=20l=C3=B3gica)\n\n-=20Header=20com?= =?UTF-8?q?=20t=C3=ADtulo=20e=20=C3=ADcone=20de=20settings\n-=20Overlay=20?= =?UTF-8?q?central=20com=20moldura=20tracejada=20usando=20--scan-outline\n?= =?UTF-8?q?-=20FAB=20central=20rosa=20com=20=C3=ADcone=20de=20scan\n-=20Ba?= =?UTF-8?q?rra=20de=20a=C3=A7=C3=B5es=20inferior=20(Gallery,=20Flash,=20Cr?= =?UTF-8?q?eate,=20History)=20usando=20--surface/--surface-2=20e=20--eleva?= =?UTF-8?q?tion-1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/scan/scan.page.html | 70 +++++++++++++++++------------------ src/app/scan/scan.page.scss | 73 +++++++++++++++++++++++++++++++++++++ src/app/scan/scan.page.ts | 63 +++++--------------------------- 3 files changed, 115 insertions(+), 91 deletions(-) diff --git a/src/app/scan/scan.page.html b/src/app/scan/scan.page.html index 8c38c95..af10ec6 100644 --- a/src/app/scan/scan.page.html +++ b/src/app/scan/scan.page.html @@ -1,46 +1,42 @@ - Scan + Scanner + + + - - - - - - Scan - -
- - - {{ isScanning ? 'Scanning…' : 'Start Scan' }} - + + +
+
+
- - - Cancel - + + + + + + - - - Last result - - -
{{ lastResult }}
-
- - - Copy - - - - - Open Link - - -
-
-
+ +
+
+
+
Gallery
+
+
+
+
Flash
+
+
+
+
Create
+
+
+
+
History
+
diff --git a/src/app/scan/scan.page.scss b/src/app/scan/scan.page.scss index e69de29..a32125e 100644 --- a/src/app/scan/scan.page.scss +++ b/src/app/scan/scan.page.scss @@ -0,0 +1,73 @@ +:host { + --overlay-padding: 32px; +} + +.scanner-content { + position: relative; + --background: transparent; /* permite ver a câmera por trás quando ativarmos */ + padding-bottom: 140px; /* espaço para action bar + fab */ +} + +.scan-overlay { + display: grid; + place-items: center; + height: calc(100% - 160px); + padding: var(--overlay-padding); +} + +.scan-window { + width: 75vw; + height: 55vw; + max-width: 480px; + max-height: 360px; + border-radius: 24px; + border: 3px dashed var(--scan-outline); +} + +.scan-fab { + --box-shadow: 0 12px 40px rgba(0,0,0,.35); + ion-fab-button { + width: 68px; + height: 68px; + --border-radius: 50%; + ion-icon { font-size: 28px; } + } +} + +.action-bar { + position: absolute; + left: 0; + right: 0; + bottom: 0; + padding: 16px 20px calc(16px + env(safe-area-inset-bottom)); + background: var(--surface); + box-shadow: var(--elevation-1); + border-top-left-radius: 20px; + border-top-right-radius: 20px; + display: flex; + align-items: center; + justify-content: space-around; +} + +.action-item { + display: flex; + flex-direction: column; + align-items: center; + gap: 6px; + color: var(--ion-text-color); +} + +.icon-circle { + width: 56px; + height: 56px; + border-radius: 50%; + background: var(--surface-2); + display: grid; + place-items: center; + ion-icon { font-size: 24px; opacity: .9; } +} + +.label { + font-size: 12px; + opacity: .8; +} diff --git a/src/app/scan/scan.page.ts b/src/app/scan/scan.page.ts index bf4320b..b7b8734 100644 --- a/src/app/scan/scan.page.ts +++ b/src/app/scan/scan.page.ts @@ -1,22 +1,18 @@ import { CommonModule } from '@angular/common'; -import { Component, inject } from '@angular/core'; +import { Component } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { IonContent, IonHeader, IonTitle, IonToolbar, - IonButton, IonIcon, - IonCard, - IonCardHeader, - IonCardTitle, - IonCardContent, + IonButtons, + IonFab, + IonFabButton, } from '@ionic/angular/standalone'; import { addIcons } from 'ionicons'; -import { camera, close, checkmark, copy, time, trash } from 'ionicons/icons'; -import { Qr } from '../services/qr'; -import { Storage, ScanEntry } from '../services/storage'; +import { settings, scan, images, flash, create, time } from 'ionicons/icons'; @Component({ selector: 'app-scan', @@ -28,57 +24,16 @@ import { Storage, ScanEntry } from '../services/storage'; IonHeader, IonTitle, IonToolbar, - IonButton, IonIcon, - IonCard, - IonCardHeader, - IonCardTitle, - IonCardContent, + IonButtons, + IonFab, + IonFabButton, CommonModule, FormsModule, ], }) export class ScanPage { - isScanning = false; - lastResult: string | null = null; - saving = false; - - private qr = inject(Qr); - private store = inject(Storage); - constructor() { - addIcons({ camera, close, checkmark, copy, time, trash }); - } - - async start() { - this.isScanning = true; - this.lastResult = null; - try { - const content = await this.qr.startScan(); - if (content) { - this.lastResult = content; - await this.save(content); - } - } finally { - this.isScanning = false; - } - } - - async cancel() { - await this.qr.stopScan(); - this.isScanning = false; - } - - async save(content: string) { - this.saving = true; - try { - await this.store.addEntry(content); - } finally { - this.saving = false; - } - } - - copyToClipboard(text: string) { - if (navigator?.clipboard) navigator.clipboard.writeText(text).catch(() => {}); + addIcons({ settings, scan, images, flash, create, time }); } } From d830c1185fc01c0546e375fed7e3831b5c7d27a8 Mon Sep 17 00:00:00 2001 From: Vagner Leite da Silva Date: Thu, 18 Sep 2025 14:48:29 -0300 Subject: [PATCH 03/12] chore(logo): adiciona watermark do logo no Scanner e atualiza favicon para usar assets/logo.png\n\nObs.: certifique-se de colocar o arquivo do logo em src/assets/logo.png (1024x1024 recomendado para melhor qualidade). --- src/app/scan/scan.page.html | 2 ++ src/app/scan/scan.page.scss | 10 ++++++++++ src/index.html | 3 ++- 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/app/scan/scan.page.html b/src/app/scan/scan.page.html index af10ec6..a33a7f1 100644 --- a/src/app/scan/scan.page.html +++ b/src/app/scan/scan.page.html @@ -10,6 +10,8 @@
+ + logo
diff --git a/src/app/scan/scan.page.scss b/src/app/scan/scan.page.scss index a32125e..99a7ccd 100644 --- a/src/app/scan/scan.page.scss +++ b/src/app/scan/scan.page.scss @@ -13,6 +13,7 @@ place-items: center; height: calc(100% - 160px); padding: var(--overlay-padding); + position: relative; } .scan-window { @@ -24,6 +25,15 @@ border: 3px dashed var(--scan-outline); } +.scan-watermark { + position: absolute; + width: 220px; + height: 220px; + object-fit: contain; + opacity: .12; /* sutil, como no mock */ + filter: drop-shadow(0 20px 60px rgba(0,0,0,.35)); +} + .scan-fab { --box-shadow: 0 12px 40px rgba(0,0,0,.35); ion-fab-button { diff --git a/src/index.html b/src/index.html index 29902c2..52f303f 100644 --- a/src/index.html +++ b/src/index.html @@ -12,7 +12,8 @@ - + + From 63419c61b00dca2dc88b8988980b468e06625949 Mon Sep 17 00:00:00 2001 From: Vagner Leite da Silva Date: Thu, 18 Sep 2025 14:53:05 -0300 Subject: [PATCH 04/12] =?UTF-8?q?chore(splash):=20script=20para=20aplicar?= =?UTF-8?q?=20logo=20na=20splash=20(Android/iOS)=20a=20partir=20de=20src/a?= =?UTF-8?q?ssets/logo.png=20e=20instru=C3=A7=C3=B5es=20de=20sync?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 4 +++- scripts/apply-splash.mjs | 51 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 scripts/apply-splash.mjs diff --git a/package.json b/package.json index cdd0860..7607caf 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,9 @@ "build": "ng build", "watch": "ng build --watch --configuration development", "test": "ng test", - "lint": "ng lint" + "lint": "ng lint", + "splash:apply": "node scripts/apply-splash.mjs", + "native:sync": "npx cap sync" }, "private": true, "dependencies": { diff --git a/scripts/apply-splash.mjs b/scripts/apply-splash.mjs new file mode 100644 index 0000000..cdfa7df --- /dev/null +++ b/scripts/apply-splash.mjs @@ -0,0 +1,51 @@ +#!/usr/bin/env node +import { cpSync, existsSync, mkdirSync } from 'node:fs'; +import { join } from 'node:path'; + +const root = process.cwd(); +const srcLogo = join(root, 'src', 'assets', 'logo.png'); + +if (!existsSync(srcLogo)) { + console.error('Logo não encontrado em src/assets/logo.png'); + process.exit(1); +} + +// Android: substituir todos os splash.png existentes e garantir nodpi +const androidDirs = [ + 'android/app/src/main/res/drawable', + 'android/app/src/main/res/drawable-land-hdpi', + 'android/app/src/main/res/drawable-land-mdpi', + 'android/app/src/main/res/drawable-land-xhdpi', + 'android/app/src/main/res/drawable-land-xxhdpi', + 'android/app/src/main/res/drawable-land-xxxhdpi', + 'android/app/src/main/res/drawable-port-hdpi', + 'android/app/src/main/res/drawable-port-mdpi', + 'android/app/src/main/res/drawable-port-xhdpi', + 'android/app/src/main/res/drawable-port-xxhdpi', + 'android/app/src/main/res/drawable-port-xxxhdpi', + 'android/app/src/main/res/drawable-nodpi', +]; + +for (const d of androidDirs) { + const dir = join(root, d); + mkdirSync(dir, { recursive: true }); + cpSync(srcLogo, join(dir, 'splash.png')); +} + +// iOS: substituir imagens do Splash.imageset +const iosDir = join(root, 'ios', 'App', 'App', 'Assets.xcassets', 'Splash.imageset'); +const iosTargets = [ + 'splash-2732x2732.png', + 'splash-2732x2732-1.png', + 'splash-2732x2732-2.png', +]; +try { + for (const f of iosTargets) { + cpSync(srcLogo, join(iosDir, f)); + } +} catch (e) { + // ignore if iOS folder not present +} + +console.log('Logo aplicado às pastas de splash (Android/iOS).'); +console.log('Execute: npx cap sync para propagar para os projetos nativos.'); From 59d68dd4b556c5d4b3490986faee853f9b85f02c Mon Sep 17 00:00:00 2001 From: Vagner Leite da Silva Date: Thu, 18 Sep 2025 14:57:09 -0300 Subject: [PATCH 05/12] =?UTF-8?q?feat(scanner):=20integra=20FAB=20para=20i?= =?UTF-8?q?niciar/cancelar=20leitura=20via=20Qr=20service=20e=20salva=20no?= =?UTF-8?q?=20hist=C3=B3rico;=20atalho=20para=20History?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/scan/scan.page.html | 12 +++++----- src/app/scan/scan.page.ts | 44 ++++++++++++++++++++++++++++++++++--- 2 files changed, 47 insertions(+), 9 deletions(-) diff --git a/src/app/scan/scan.page.html b/src/app/scan/scan.page.html index a33a7f1..afe27e7 100644 --- a/src/app/scan/scan.page.html +++ b/src/app/scan/scan.page.html @@ -17,26 +17,26 @@ - - + +
-
+
Gallery
-
+
Flash
-
+
Create
-
+
History
diff --git a/src/app/scan/scan.page.ts b/src/app/scan/scan.page.ts index b7b8734..5a4ede5 100644 --- a/src/app/scan/scan.page.ts +++ b/src/app/scan/scan.page.ts @@ -1,5 +1,5 @@ import { CommonModule } from '@angular/common'; -import { Component } from '@angular/core'; +import { Component, inject } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { IonContent, @@ -12,7 +12,10 @@ import { IonFabButton, } from '@ionic/angular/standalone'; import { addIcons } from 'ionicons'; -import { settings, scan, images, flash, create, time } from 'ionicons/icons'; +import { settings, scan, images, flash, create, time, close } from 'ionicons/icons'; +import { Qr } from '../services/qr'; +import { Storage } from '../services/storage'; +import { Router } from '@angular/router'; @Component({ selector: 'app-scan', @@ -33,7 +36,42 @@ import { settings, scan, images, flash, create, time } from 'ionicons/icons'; ], }) export class ScanPage { + private qr = inject(Qr); + private store = inject(Storage); + private router = inject(Router); + + isScanning = false; + lastResult: string | null = null; + constructor() { - addIcons({ settings, scan, images, flash, create, time }); + addIcons({ settings, scan, images, flash, create, time, close }); + } + + async onFabClick() { + if (this.isScanning) return this.cancel(); + await this.start(); + } + + async start() { + this.isScanning = true; + this.lastResult = null; + try { + const content = await this.qr.startScan(); + if (content) { + this.lastResult = content; + await this.store.addEntry(content); + } + } finally { + this.isScanning = false; + } + } + + async cancel() { + await this.qr.stopScan(); + this.isScanning = false; + } + + goHistory() { + this.router.navigateByUrl('/history'); } } From 2114f1b8a426f18d6c5f4338653655bb52715a63 Mon Sep 17 00:00:00 2001 From: Vagner Leite da Silva Date: Thu, 18 Sep 2025 15:01:52 -0300 Subject: [PATCH 06/12] =?UTF-8?q?fix(scanner):=20ajustar=20servi=C3=A7o=20?= =?UTF-8?q?para=20usar=20CapacitorBarcodeScanner=20(plugin=20@capacitor/ba?= =?UTF-8?q?rcode-scanner=20v2)=20em=20vez=20da=20API=20antiga=20da=20comun?= =?UTF-8?q?idade?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/services/qr.ts | 44 +++++++++++------------------------------- 1 file changed, 11 insertions(+), 33 deletions(-) diff --git a/src/app/services/qr.ts b/src/app/services/qr.ts index 4b21aa5..a4f75f6 100644 --- a/src/app/services/qr.ts +++ b/src/app/services/qr.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { BarcodeScanner } from '@capacitor/barcode-scanner'; +import { CapacitorBarcodeScanner, CapacitorBarcodeScannerTypeHint } from '@capacitor/barcode-scanner'; @Injectable({ providedIn: 'root' @@ -7,47 +7,25 @@ import { BarcodeScanner } from '@capacitor/barcode-scanner'; export class Qr { private active = false; - async ensurePermission(): Promise { - const status = await BarcodeScanner.checkPermission({ force: true }); - return status.granted === true; - } - - private setBackground(active: boolean) { - const body = document.querySelector('body'); - if (!body) return; - if (active) { - body.classList.add('scanner-active'); - } else { - body.classList.remove('scanner-active'); - } - } - async startScan(): Promise { - const granted = await this.ensurePermission(); - if (!granted) return null; - this.active = true; - this.setBackground(true); - await BarcodeScanner.hideBackground(); - try { - const result = await BarcodeScanner.startScan(); - const content = (result as any)?.content; + const result = await CapacitorBarcodeScanner.scanBarcode({ + hint: (17 as CapacitorBarcodeScannerTypeHint), // ALL + scanInstructions: '', + scanButton: false, + }); + const content = (result as any)?.ScanResult ?? null; return content ?? null; + } catch (e) { + return null; } finally { - await this.stopScan(); + this.active = false; } } async stopScan(): Promise { - if (!this.active) return; + // Plugin atual não expõe stop; manter no-op para compatibilidade this.active = false; - try { - await BarcodeScanner.showBackground(); - } catch {} - try { - await BarcodeScanner.stopScan(); - } catch {} - this.setBackground(false); } } From 0867963b5e5aeec024c76a4519bbd78398e1dbd1 Mon Sep 17 00:00:00 2001 From: Vagner Leite da Silva Date: Thu, 18 Sep 2025 15:43:41 -0300 Subject: [PATCH 07/12] =?UTF-8?q?fix(scanner-template):=20remove=20handler?= =?UTF-8?q?s=20com=20coment=C3=A1rios=20inline=20e=20adiciona=20stubs=20(o?= =?UTF-8?q?penGallery,=20toggleFlash,=20createCode,=20goSettings)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/scan/scan.page.html | 8 ++++---- src/app/scan/scan.page.ts | 19 +++++++++++++++++++ 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/app/scan/scan.page.html b/src/app/scan/scan.page.html index afe27e7..683622f 100644 --- a/src/app/scan/scan.page.html +++ b/src/app/scan/scan.page.html @@ -2,7 +2,7 @@ Scanner - + @@ -24,15 +24,15 @@
-
+
Gallery
-
+
Flash
-
+
Create
diff --git a/src/app/scan/scan.page.ts b/src/app/scan/scan.page.ts index 5a4ede5..a1de747 100644 --- a/src/app/scan/scan.page.ts +++ b/src/app/scan/scan.page.ts @@ -74,4 +74,23 @@ export class ScanPage { goHistory() { this.router.navigateByUrl('/history'); } + + goSettings() { + this.router.navigateByUrl('/settings'); + } + + openGallery() { + // Placeholder da POC: futura leitura por imagem/galeria + console.log('openGallery: not implemented in POC'); + } + + toggleFlash() { + // Placeholder da POC: avaliar suporte do plugin/alternativas + console.log('toggleFlash: not implemented in POC'); + } + + createCode() { + // Placeholder da POC: futura tela de criação de QR + console.log('createCode: not implemented in POC'); + } } From 6c058e2a3c8223cb73f663138bd44cf9dfda7a51 Mon Sep 17 00:00:00 2001 From: Vagner Leite da Silva Date: Thu, 18 Sep 2025 15:54:46 -0300 Subject: [PATCH 08/12] =?UTF-8?q?chore(permissions):=20adiciona=20CAMERA?= =?UTF-8?q?=20no=20AndroidManifest=20e=20NSCameraUsageDescription=20no=20i?= =?UTF-8?q?OS=20Info.plist=20para=20uso=20da=20c=C3=A2mera=20no=20scanner?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- android/app/src/main/AndroidManifest.xml | 6 +++++- ios/App/App/Info.plist | 5 ++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 340e7df..cb760b2 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -36,6 +36,10 @@ - + + + + + diff --git a/ios/App/App/Info.plist b/ios/App/App/Info.plist index d8dfae4..a9cdb34 100644 --- a/ios/App/App/Info.plist +++ b/ios/App/App/Info.plist @@ -42,8 +42,11 @@ UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight - + UIViewControllerBasedStatusBarAppearance + + NSCameraUsageDescription + We need camera access to scan barcodes and QR codes. From a8b3ede3882c6dcbc7cead297c8b3cad725370a4 Mon Sep 17 00:00:00 2001 From: Vagner Leite da Silva Date: Thu, 18 Sep 2025 16:34:23 -0300 Subject: [PATCH 09/12] fix(tabs): adiciona componentes IonTabs/TabBar/TabButton/Icon/Label aos imports do standalone para resolver NG8001 --- ios/App/App/Info.plist | 11 +++-------- src/app/app.routes.ts | 2 +- src/app/tabs/tabs.page.html | 29 +++++++++++++++++------------ src/app/tabs/tabs.page.ts | 12 ++++++++++-- 4 files changed, 31 insertions(+), 23 deletions(-) diff --git a/ios/App/App/Info.plist b/ios/App/App/Info.plist index a9cdb34..add3bcf 100644 --- a/ios/App/App/Info.plist +++ b/ios/App/App/Info.plist @@ -22,6 +22,8 @@ $(CURRENT_PROJECT_VERSION) LSRequiresIPhoneOS + NSCameraUsageDescription + We need camera access to scan barcodes and QR codes. UILaunchStoryboardName LaunchScreen UIMainStoryboardFile @@ -33,20 +35,13 @@ UISupportedInterfaceOrientations UIInterfaceOrientationPortrait - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight UISupportedInterfaceOrientations~ipad UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - + UIViewControllerBasedStatusBarAppearance - - NSCameraUsageDescription - We need camera access to scan barcodes and QR codes. diff --git a/src/app/app.routes.ts b/src/app/app.routes.ts index e3f3f8e..e2e4c16 100644 --- a/src/app/app.routes.ts +++ b/src/app/app.routes.ts @@ -7,7 +7,7 @@ export const routes: Routes = [ }, { path: '', - redirectTo: 'home', + redirectTo: 'history', pathMatch: 'full', }, { diff --git a/src/app/tabs/tabs.page.html b/src/app/tabs/tabs.page.html index 3f4e0c7..b158e2c 100644 --- a/src/app/tabs/tabs.page.html +++ b/src/app/tabs/tabs.page.html @@ -1,13 +1,18 @@ - - - tabs - - + + + + + Scan + - - - - tabs - - - + + + History + + + + + Settings + + + diff --git a/src/app/tabs/tabs.page.ts b/src/app/tabs/tabs.page.ts index 7558e6d..dbf189c 100644 --- a/src/app/tabs/tabs.page.ts +++ b/src/app/tabs/tabs.page.ts @@ -6,6 +6,11 @@ import { IonHeader, IonTitle, IonToolbar, + IonTabs, + IonTabBar, + IonTabButton, + IonIcon, + IonLabel, } from '@ionic/angular/standalone'; @Component({ @@ -16,8 +21,11 @@ import { imports: [ IonContent, IonHeader, - IonTitle, - IonToolbar, + IonTabs, + IonTabBar, + IonTabButton, + IonIcon, + IonLabel, CommonModule, FormsModule, ], From 7cff433bee0d164396091818ee29baaa5572b350 Mon Sep 17 00:00:00 2001 From: Vagner Leite da Silva Date: Thu, 18 Sep 2025 22:33:43 -0300 Subject: [PATCH 10/12] wip --- angular.json | 4 + src/app/app.component.html | 1 + src/app/app.component.ts | 3 +- src/app/app.routes.ts | 9 +- src/app/error/not-found/not-found.page.html | 13 +++ src/app/error/not-found/not-found.page.scss | 0 .../error/not-found/not-found.page.spec.ts | 17 ++++ src/app/error/not-found/not-found.page.ts | 20 +++++ src/app/history/history.page.html | 16 ++-- src/app/history/history.page.ts | 82 +++++++++---------- src/app/home/home.page.html | 15 ++-- src/app/home/home.page.ts | 11 ++- src/app/services/storage.service.ts | 25 ++++++ src/app/settings/settings.page.html | 48 +++++++++-- src/app/settings/settings.page.ts | 10 +++ src/app/tabs/tabs.page.html | 6 +- src/app/tabs/tabs.page.ts | 24 +++--- src/global.scss | 1 + src/theme/variables.scss | 1 + 19 files changed, 227 insertions(+), 79 deletions(-) create mode 100644 src/app/error/not-found/not-found.page.html create mode 100644 src/app/error/not-found/not-found.page.scss create mode 100644 src/app/error/not-found/not-found.page.spec.ts create mode 100644 src/app/error/not-found/not-found.page.ts create mode 100644 src/app/services/storage.service.ts diff --git a/angular.json b/angular.json index 9b72354..9bee49f 100644 --- a/angular.json +++ b/angular.json @@ -75,6 +75,10 @@ }, "serve": { "builder": "@angular-devkit/build-angular:dev-server", + "options": { + "host": "0.0.0.0", + "port": 4200 + }, "configurations": { "production": { "buildTarget": "app:build:production" diff --git a/src/app/app.component.html b/src/app/app.component.html index 13b9677..3e2fba1 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -1,3 +1,4 @@ + diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 1da531b..be61a15 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,10 +1,11 @@ import { Component } from '@angular/core'; import { IonApp, IonRouterOutlet } from '@ionic/angular/standalone'; +import { TabsPage } from './tabs/tabs.page'; @Component({ selector: 'app-root', templateUrl: 'app.component.html', - imports: [IonApp, IonRouterOutlet], + imports: [IonApp, IonRouterOutlet, TabsPage], }) export class AppComponent { constructor() {} diff --git a/src/app/app.routes.ts b/src/app/app.routes.ts index e2e4c16..c828330 100644 --- a/src/app/app.routes.ts +++ b/src/app/app.routes.ts @@ -1,10 +1,6 @@ import { Routes } from '@angular/router'; export const routes: Routes = [ - { - path: 'home', - loadComponent: () => import('./home/home.page').then((m) => m.HomePage), - }, { path: '', redirectTo: 'history', @@ -28,4 +24,9 @@ export const routes: Routes = [ loadComponent: () => import('./settings/settings.page').then((m) => m.SettingsPage), }, + { + path: '*', + loadComponent: () => + import('./error/not-found/not-found.page').then((m) => m.NotFoundPage), + }, ]; diff --git a/src/app/error/not-found/not-found.page.html b/src/app/error/not-found/not-found.page.html new file mode 100644 index 0000000..1a01b0c --- /dev/null +++ b/src/app/error/not-found/not-found.page.html @@ -0,0 +1,13 @@ + + + not-found + + + + + + + not-found + + + diff --git a/src/app/error/not-found/not-found.page.scss b/src/app/error/not-found/not-found.page.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/app/error/not-found/not-found.page.spec.ts b/src/app/error/not-found/not-found.page.spec.ts new file mode 100644 index 0000000..e703d14 --- /dev/null +++ b/src/app/error/not-found/not-found.page.spec.ts @@ -0,0 +1,17 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { NotFoundPage } from './not-found.page'; + +describe('NotFoundPage', () => { + let component: NotFoundPage; + let fixture: ComponentFixture; + + beforeEach(() => { + fixture = TestBed.createComponent(NotFoundPage); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/error/not-found/not-found.page.ts b/src/app/error/not-found/not-found.page.ts new file mode 100644 index 0000000..ff99281 --- /dev/null +++ b/src/app/error/not-found/not-found.page.ts @@ -0,0 +1,20 @@ +import { Component, OnInit } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { FormsModule } from '@angular/forms'; +import { IonContent, IonHeader, IonTitle, IonToolbar } from '@ionic/angular/standalone'; + +@Component({ + selector: 'app-not-found', + templateUrl: './not-found.page.html', + styleUrls: ['./not-found.page.scss'], + standalone: true, + imports: [IonContent, IonHeader, IonTitle, IonToolbar, CommonModule, FormsModule] +}) +export class NotFoundPage implements OnInit { + + constructor() { } + + ngOnInit() { + } + +} diff --git a/src/app/history/history.page.html b/src/app/history/history.page.html index 00213e2..ed1e98a 100644 --- a/src/app/history/history.page.html +++ b/src/app/history/history.page.html @@ -11,8 +11,13 @@ -
- +
+ Clear history @@ -20,8 +25,9 @@ - -

{{ i.content }}

+ ` +

{{ i.content }}

{{ i.timestamp | date:'short' }} @@ -34,6 +40,6 @@

{{ i.content }}

-
No items yet.
+
No items yet.
diff --git a/src/app/history/history.page.ts b/src/app/history/history.page.ts index badf1c5..63bc101 100644 --- a/src/app/history/history.page.ts +++ b/src/app/history/history.page.ts @@ -1,69 +1,69 @@ import { CommonModule } from '@angular/common'; -import { Component, OnInit, inject } from '@angular/core'; -import { FormsModule } from '@angular/forms'; +import { Component, signal } from '@angular/core'; import { - IonContent, + IonButton, IonHeader, - IonTitle, - IonToolbar, - IonList, + IonIcon, IonItem, IonLabel, - IonButton, - IonIcon, + IonList, + IonToolbar, + IonContent, + IonTitle, + ViewWillEnter, } from '@ionic/angular/standalone'; -import { addIcons } from 'ionicons'; -import { trash, copy, time } from 'ionicons/icons'; -import { Storage, ScanEntry } from '../services/storage'; +import { StorageService } from '../services/storage.service'; @Component({ + standalone: true, selector: 'app-history', templateUrl: './history.page.html', - styleUrls: ['./history.page.scss'], - standalone: true, + imports: [ - IonContent, IonHeader, - IonTitle, - IonToolbar, + IonIcon, + IonButton, IonList, + IonToolbar, IonItem, IonLabel, - IonButton, - IonIcon, + IonTitle, + IonContent, CommonModule, - FormsModule, ], }) -export class HistoryPage implements OnInit { - items: ScanEntry[] = []; - loading = false; +export class HistoryPage implements ViewWillEnter { + list = signal>({}); // group by section + items: any = []; - private store = inject(Storage); - - constructor() { - addIcons({ trash, copy, time }); - } - - ngOnInit(): void { + constructor(private store: StorageService) {} + ionViewWillEnter(): void { this.load(); } - async load() { - this.loading = true; - try { - this.items = await this.store.getHistory(); - } finally { - this.loading = false; - } + this.items = (await this.store.all()) || []; + const sections: Record = {}; + const today = new Date().toDateString(); + const yesterday = new Date(Date.now() - 864e5).toDateString(); + + this.items.forEach((code: string | number | Date) => { + const key = + new Date(code).toDateString() === today + ? 'Today' + : new Date(code).toDateString() === yesterday + ? 'Yesterday' + : 'Last 7 Days'; + sections[key] ??= []; + sections[key].push(code as unknown as string); + }); + this.list.set(sections); } - async clear() { - await this.store.clearHistory(); - await this.load(); + clear() { + console.log(`clear`); } - copy(text: string) { - if (navigator?.clipboard) navigator.clipboard.writeText(text).catch(() => {}); + copy(content: string) { + console.log(`copied`); } } diff --git a/src/app/home/home.page.html b/src/app/home/home.page.html index 2356666..ed8b39e 100644 --- a/src/app/home/home.page.html +++ b/src/app/home/home.page.html @@ -1,8 +1,6 @@ - - Blank - + Blank @@ -14,12 +12,19 @@
-
+
Scan Barcode/QR - + View History diff --git a/src/app/home/home.page.ts b/src/app/home/home.page.ts index 9de844d..7c0b43c 100644 --- a/src/app/home/home.page.ts +++ b/src/app/home/home.page.ts @@ -1,7 +1,14 @@ import { Component } from '@angular/core'; -import { IonHeader, IonToolbar, IonTitle, IonContent, IonButton, IonIcon } from '@ionic/angular/standalone'; +import { + IonButton, + IonContent, + IonHeader, + IonIcon, + IonTitle, + IonToolbar, +} from '@ionic/angular/standalone'; import { addIcons } from 'ionicons'; -import { camera, time, list } from 'ionicons/icons'; +import { camera, list, time } from 'ionicons/icons'; @Component({ selector: 'app-home', diff --git a/src/app/services/storage.service.ts b/src/app/services/storage.service.ts new file mode 100644 index 0000000..5622ec4 --- /dev/null +++ b/src/app/services/storage.service.ts @@ -0,0 +1,25 @@ +import { Injectable } from '@angular/core'; +import { Preferences } from '@capacitor/preferences'; + +const KEY = 'history'; + +@Injectable({ providedIn: 'root' }) +export class StorageService { + async all(): Promise { + const { value } = await Preferences.get({ key: KEY }); + return value ? JSON.parse(value) : []; + } + + async save(item: string) { + const list = await this.all(); + list.unshift(item); + await Preferences.set({ + key: KEY, + value: JSON.stringify(list.slice(0, 50)), + }); + } + + async clear() { + await Preferences.remove({ key: KEY }); + } +} diff --git a/src/app/settings/settings.page.html b/src/app/settings/settings.page.html index 003abd6..7e9c9f1 100644 --- a/src/app/settings/settings.page.html +++ b/src/app/settings/settings.page.html @@ -1,13 +1,45 @@ - + - settings + Settings - - - - settings - - + + + Preferences + + App Theme + System + + + Language + English + + + + Notifications + + App Updates + + + + Scan Notifications + + + + + Privacy + + Data Management + + + Privacy Policy + + + + About + + App Version + 1.2.3 + diff --git a/src/app/settings/settings.page.ts b/src/app/settings/settings.page.ts index 83d889f..a0229f3 100644 --- a/src/app/settings/settings.page.ts +++ b/src/app/settings/settings.page.ts @@ -6,6 +6,11 @@ import { IonHeader, IonTitle, IonToolbar, + IonNote, + IonToggle, + IonItem, + IonListHeader, + IonLabel, } from '@ionic/angular/standalone'; @Component({ @@ -18,6 +23,11 @@ import { IonHeader, IonTitle, IonToolbar, + IonNote, + IonToggle, + IonLabel, + IonItem, + IonListHeader, CommonModule, FormsModule, ], diff --git a/src/app/tabs/tabs.page.html b/src/app/tabs/tabs.page.html index b158e2c..0f51e5f 100644 --- a/src/app/tabs/tabs.page.html +++ b/src/app/tabs/tabs.page.html @@ -1,12 +1,12 @@ - - Scan + + Scan - + History diff --git a/src/app/tabs/tabs.page.ts b/src/app/tabs/tabs.page.ts index dbf189c..5e9f42e 100644 --- a/src/app/tabs/tabs.page.ts +++ b/src/app/tabs/tabs.page.ts @@ -2,25 +2,23 @@ import { CommonModule } from '@angular/common'; import { Component } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { - IonContent, - IonHeader, - IonTitle, - IonToolbar, - IonTabs, - IonTabBar, - IonTabButton, IonIcon, IonLabel, + IonTabBar, + IonTabButton, + IonTabs, } from '@ionic/angular/standalone'; +import { addIcons } from 'ionicons'; + +import { settingsOutline, refreshOutline, qrCodeOutline } from 'ionicons/icons'; + @Component({ selector: 'app-tabs', templateUrl: './tabs.page.html', styleUrls: ['./tabs.page.scss'], standalone: true, imports: [ - IonContent, - IonHeader, IonTabs, IonTabBar, IonTabButton, @@ -31,5 +29,11 @@ import { ], }) export class TabsPage { - constructor() {} + constructor() { + addIcons({ + settingsOutline, + refreshOutline, + qrCodeOutline, + }); + } } diff --git a/src/global.scss b/src/global.scss index d43a041..121d308 100644 --- a/src/global.scss +++ b/src/global.scss @@ -40,3 +40,4 @@ body.scanner-active { background: transparent !important; } + diff --git a/src/theme/variables.scss b/src/theme/variables.scss index 0004b83..169d8d5 100644 --- a/src/theme/variables.scss +++ b/src/theme/variables.scss @@ -171,3 +171,4 @@ ion-button { ion-tab-bar { --color-selected: var(--ion-color-primary); } + \ No newline at end of file From 40df96c46485cf63784faebc624d3f46f719ce64 Mon Sep 17 00:00:00 2001 From: Vagner Leite da Silva Date: Fri, 19 Sep 2025 01:12:51 -0300 Subject: [PATCH 11/12] =?UTF-8?q?chore(build):=20arquiva=20com=20destino?= =?UTF-8?q?=20gen=C3=A9rico=20do=20iOS=20(-destination=20'generic/platform?= =?UTF-8?q?=3DiOS')=20para=20evitar=20depend=C3=AAncia=20do=20device=20e?= =?UTF-8?q?=20incompatibilidade=20de=20device=20support?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ExportOptions.plist | 13 ++++++++++++ build.sh | 43 +++++++++++++++++++++++++++++++++++++++ package.json | 3 ++- scripts/patch-cap-ios.mjs | 27 ++++++++++++++++++++++++ 4 files changed, 85 insertions(+), 1 deletion(-) create mode 100644 ExportOptions.plist create mode 100755 build.sh create mode 100644 scripts/patch-cap-ios.mjs diff --git a/ExportOptions.plist b/ExportOptions.plist new file mode 100644 index 0000000..8b14c2f --- /dev/null +++ b/ExportOptions.plist @@ -0,0 +1,13 @@ + + + + + method + development + compileBitcode + + signingStyle + automatic + + diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..a8e6194 --- /dev/null +++ b/build.sh @@ -0,0 +1,43 @@ +#!/bin/bash +set -e # fail fast + +### 0) Caminhos/nomes +PROJECT_ROOT="$(pwd)" # raiz do app Ionic +IOS_DIR="${PROJECT_ROOT}/ios/App" +WORKSPACE_PATH="${IOS_DIR}/App.xcworkspace" +SCHEME_NAME="App" +ARCHIVE_PATH="${PROJECT_ROOT}/build/${SCHEME_NAME}.xcarchive" +IPA_EXPORT_PATH="${PROJECT_ROOT}/build/ipa" +EXPORT_OPTIONS_PLIST="${IOS_DIR}/ExportOptions.plist" # já existe no projeto iOS + +### 1) Build web + sync +echo "📦 Building web assets…" +# Patch conhecido do Capacitor iOS (milissegundos) +node scripts/patch-cap-ios.mjs || true +npm run build +npx cap sync ios # copia www e plugins + +### 2) Limpar build anterior +rm -rf "${PROJECT_ROOT}/build" +mkdir -p "$IPA_EXPORT_PATH" + +### 3) Arquivar (Release) +echo "🛠️ Archiving (${SCHEME_NAME})…" +xcodebuild \ + -workspace "$WORKSPACE_PATH" \ + -scheme "$SCHEME_NAME" \ + -configuration Release \ + -sdk iphoneos \ + -destination 'generic/platform=iOS' \ + -archivePath "$ARCHIVE_PATH" \ + archive + +### 4) Exportar IPA +echo "📤 Exporting IPA…" +xcodebuild \ + -exportArchive \ + -archivePath "$ARCHIVE_PATH" \ + -exportOptionsPlist "$EXPORT_OPTIONS_PLIST" \ + -exportPath "$IPA_EXPORT_PATH" + +echo "✅ IPA gerada em: $IPA_EXPORT_PATH" diff --git a/package.json b/package.json index 7607caf..1c9fa78 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,8 @@ "test": "ng test", "lint": "ng lint", "splash:apply": "node scripts/apply-splash.mjs", - "native:sync": "npx cap sync" + "native:sync": "npx cap sync", + "postinstall": "node scripts/patch-cap-ios.mjs" }, "private": true, "dependencies": { diff --git a/scripts/patch-cap-ios.mjs b/scripts/patch-cap-ios.mjs new file mode 100644 index 0000000..6e79b94 --- /dev/null +++ b/scripts/patch-cap-ios.mjs @@ -0,0 +1,27 @@ +#!/usr/bin/env node +import { readFileSync, writeFileSync, existsSync } from 'node:fs'; +import { join } from 'node:path'; + +const root = process.cwd(); +const files = [ + join(root, 'node_modules', '@capacitor', 'ios', 'Capacitor', 'Capacitor', 'Codable', 'JSValueDecoder.swift'), + join(root, 'node_modules', '@capacitor', 'ios', 'Capacitor', 'Capacitor', 'Codable', 'JSValueEncoder.swift'), +]; + +let changed = 0; +for (const file of files) { + if (!existsSync(file)) continue; + const src = readFileSync(file, 'utf8'); + if (src.includes('MSEC_PER_SEC')) { + const out = src.replaceAll('MSEC_PER_SEC', '1000.0'); + writeFileSync(file, out); + changed++; + console.log(`Patched: ${file}`); + } else { + // already patched or not needed + } +} + +if (changed === 0) { + console.log('No iOS Capacitor patches needed.'); +} From 9fb511e43f285322cd9df89a85e9c176e250408c Mon Sep 17 00:00:00 2001 From: Vagner Leite da Silva Date: Fri, 19 Sep 2025 04:30:00 -0300 Subject: [PATCH 12/12] =?UTF-8?q?chore(ios/build):=20ajustes=20no=20Xcode?= =?UTF-8?q?=20project=20e=20ExportOptions;=20patch=20autom=C3=A1tico=20do?= =?UTF-8?q?=20Capacitor=20iOS=20(postinstall)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ios/App/App.xcodeproj/project.pbxproj | 41 ++++++++++++++++++++------- ios/App/ExportOptions.plist | 14 ++++----- scripts/patch-cap-ios.mjs | 34 ++++++++++++++++------ 3 files changed, 64 insertions(+), 25 deletions(-) diff --git a/ios/App/App.xcodeproj/project.pbxproj b/ios/App/App.xcodeproj/project.pbxproj index 64375f6..e17cc0c 100644 --- a/ios/App/App.xcodeproj/project.pbxproj +++ b/ios/App/App.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 48; + objectVersion = 53; objects = { /* Begin PBXBuildFile section */ @@ -122,13 +122,13 @@ 504EC2FC1FED79650016851F /* Project object */ = { isa = PBXProject; attributes = { + BuildIndependentTargetsInParallel = YES; LastSwiftUpdateCheck = 0920; - LastUpgradeCheck = 1420; + LastUpgradeCheck = 1430; TargetAttributes = { 504EC3031FED79650016851F = { CreatedOnToolsVersion = 9.2; LastSwiftMigration = 1100; - ProvisioningStyle = Automatic; }; }; }; @@ -267,7 +267,6 @@ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - CODE_SIGN_IDENTITY = "iPhone Developer"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; @@ -327,7 +326,6 @@ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - CODE_SIGN_IDENTITY = "iPhone Developer"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; @@ -343,7 +341,8 @@ IPHONEOS_DEPLOYMENT_TARGET = 16.2; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; VALIDATE_PRODUCT = YES; }; name = Release; @@ -353,19 +352,30 @@ baseConfigurationReference = FC68EB0AF532CFC21C3344DD /* Pods-App.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = 5H4TCPBPYP; INFOPLIST_FILE = App/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = PinkCOdeScanner; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities"; IPHONEOS_DEPLOYMENT_TARGET = 16.2; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); MARKETING_VERSION = 1.0; OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\""; PRODUCT_BUNDLE_IDENTIFIER = com.vagner.pinkcode; + "PRODUCT_BUNDLE_IDENTIFIER[sdk=*]" = com.vagner.pinkcode; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; }; name = Debug; }; @@ -374,18 +384,29 @@ baseConfigurationReference = AF51FD2D460BCFE21FA515B2 /* Pods-App.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = 5H4TCPBPYP; INFOPLIST_FILE = App/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = PinkCOdeScanner; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities"; IPHONEOS_DEPLOYMENT_TARGET = 16.2; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.vagner.pinkcode; + "PRODUCT_BUNDLE_IDENTIFIER[sdk=*]" = com.vagner.pinkcode; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_ACTIVE_COMPILATION_CONDITIONS = ""; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; }; name = Release; }; diff --git a/ios/App/ExportOptions.plist b/ios/App/ExportOptions.plist index 4e3db35..a3db099 100644 --- a/ios/App/ExportOptions.plist +++ b/ios/App/ExportOptions.plist @@ -3,12 +3,12 @@ "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> - methodad-hoc - signingStylemanual - provisioningProfiles - - com.vagner.pinkcodePINKCODE_AdHoc - - teamIDABCDE12345 + method + development + compileBitcode + + signingStyle + automatic + diff --git a/scripts/patch-cap-ios.mjs b/scripts/patch-cap-ios.mjs index 6e79b94..70e38a3 100644 --- a/scripts/patch-cap-ios.mjs +++ b/scripts/patch-cap-ios.mjs @@ -3,22 +3,40 @@ import { readFileSync, writeFileSync, existsSync } from 'node:fs'; import { join } from 'node:path'; const root = process.cwd(); -const files = [ - join(root, 'node_modules', '@capacitor', 'ios', 'Capacitor', 'Capacitor', 'Codable', 'JSValueDecoder.swift'), - join(root, 'node_modules', '@capacitor', 'ios', 'Capacitor', 'Capacitor', 'Codable', 'JSValueEncoder.swift'), -]; +const decoder = join(root, 'node_modules', '@capacitor', 'ios', 'Capacitor', 'Capacitor', 'Codable', 'JSValueDecoder.swift'); +const encoder = join(root, 'node_modules', '@capacitor', 'ios', 'Capacitor', 'Capacitor', 'Codable', 'JSValueEncoder.swift'); +const files = [decoder, encoder]; let changed = 0; for (const file of files) { if (!existsSync(file)) continue; const src = readFileSync(file, 'utf8'); - if (src.includes('MSEC_PER_SEC')) { - const out = src.replaceAll('MSEC_PER_SEC', '1000.0'); + let out = src; + let fileChanged = false; + if (out.includes('MSEC_PER_SEC')) { + out = out.replaceAll('MSEC_PER_SEC', '1000.0'); + fileChanged = true; + } + if (file === encoder) { + // Ensure switch returns String in EncodingContainer.type + out = out.replace( + /case \.singleValue:\s*"SingleValueContainer"/m, + 'case .singleValue:\n return "SingleValueContainer"' + ); + out = out.replace( + /case \.unkeyed:\s*"UnkeyedContainer"/m, + 'case .unkeyed:\n return "UnkeyedContainer"' + ); + out = out.replace( + /case \.keyed:\s*"KeyedContainer"/m, + 'case .keyed:\n return "KeyedContainer"' + ); + if (out !== src) fileChanged = true; + } + if (fileChanged) { writeFileSync(file, out); changed++; console.log(`Patched: ${file}`); - } else { - // already patched or not needed } }