fix: update version codes and enhance document scanning functionality

This commit is contained in:
Mo Tarbin
2026-07-07 21:16:30 -04:00
parent 20e5c0e292
commit fd3f7f4a37
10 changed files with 86 additions and 33 deletions

View File

@@ -1,5 +1,11 @@
apply plugin: 'com.android.application'
def keystorePropertiesFile = rootProject.file("keystore.properties")
def keystoreProperties = new Properties()
if (keystorePropertiesFile.exists()) {
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
}
android {
namespace "com.donetick.app"
compileSdk rootProject.ext.compileSdkVersion
@@ -7,8 +13,8 @@ android {
applicationId "com.donetick.app"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 25
versionName "1.2.3"
versionCode 26
versionName "1.2.4"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
aaptOptions {
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
@@ -16,8 +22,19 @@ android {
ignoreAssetsPattern '!.svn:!.git:!.ds_store:!*.scc:.*:!CVS:!thumbs.db:!picasa.ini:!*~'
}
}
signingConfigs {
release {
if (keystorePropertiesFile.exists()) {
storeFile file(keystoreProperties['storeFile'])
storePassword keystoreProperties['storePassword']
keyAlias keystoreProperties['keyAlias']
keyPassword keystoreProperties['keyPassword']
}
}
}
buildTypes {
release {
signingConfig keystorePropertiesFile.exists() ? signingConfigs.release : signingConfigs.debug
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}

View File

@@ -11,8 +11,8 @@
"type": "SINGLE",
"filters": [],
"attributes": [],
"versionCode": 20,
"versionName": "1.0.20",
"versionCode": 25,
"versionName": "1.2.3",
"outputFile": "app-release.apk"
}
],
@@ -33,5 +33,5 @@
]
}
],
"minSdkVersionForDexing": 24
"minSdkVersionForDexing": 28
}

View File

@@ -357,15 +357,16 @@
baseConfigurationReference = FC68EB0AF532CFC21C3344DD /* Pods-App.debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ALLOW_ENTITLEMENTS_MODIFICATION = YES;
CODE_SIGN_ENTITLEMENTS = App/App.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 25;
CURRENT_PROJECT_VERSION = 26;
DEVELOPMENT_TEAM = 6UJJ78R3BS;
INFOPLIST_FILE = App/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.donetick.app;
MARKETING_VERSION = 1.2.3;
MARKETING_VERSION = 1.2.4;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
@@ -377,14 +378,15 @@
baseConfigurationReference = AF51FD2D460BCFE21FA515B2 /* Pods-App.release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ALLOW_ENTITLEMENTS_MODIFICATION = YES;
CODE_SIGN_ENTITLEMENTS = App/App.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 25;
CURRENT_PROJECT_VERSION = 26;
DEVELOPMENT_TEAM = 6UJJ78R3BS;
INFOPLIST_FILE = App/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
MARKETING_VERSION = 1.2.3;
MARKETING_VERSION = 1.2.4;
PRODUCT_BUNDLE_IDENTIFIER = com.donetick.app;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "";

View File

@@ -15,13 +15,16 @@ def capacitor_pods
pod 'CapacitorApp', :path => '../../node_modules/@capacitor/app'
pod 'CapacitorBrowser', :path => '../../node_modules/@capacitor/browser'
pod 'CapacitorDevice', :path => '../../node_modules/@capacitor/device'
pod 'CapacitorLocalLlm', :path => '../../node_modules/@capacitor/local-llm'
pod 'CapacitorLocalNotifications', :path => '../../node_modules/@capacitor/local-notifications'
pod 'CapacitorNetwork', :path => '../../node_modules/@capacitor/network'
pod 'CapacitorPreferences', :path => '../../node_modules/@capacitor/preferences'
pod 'CapacitorPushNotifications', :path => '../../node_modules/@capacitor/push-notifications'
pod 'CapacitorStatusBar', :path => '../../node_modules/@capacitor/status-bar'
pod 'CapgoCapacitorDocumentScanner', :path => '../../node_modules/@capgo/capacitor-document-scanner'
pod 'CapgoCapacitorNfc', :path => '../../node_modules/@capgo/capacitor-nfc'
pod 'CapgoCapacitorSocialLogin', :path => '../../node_modules/@capgo/capacitor-social-login'
pod 'JcesarmobileCapacitorOcr', :path => '../../node_modules/@jcesarmobile/capacitor-ocr'
pod 'RevenuecatPurchasesCapacitor', :path => '../../node_modules/@revenuecat/purchases-capacitor'
pod 'RevenuecatPurchasesCapacitorUi', :path => '../../node_modules/@revenuecat/purchases-capacitor-ui'
pod 'CapacitorPluginSafeArea', :path => '../../node_modules/capacitor-plugin-safe-area'

View File

@@ -1,7 +1,7 @@
{
"name": "donetick",
"private": true,
"version": "1.2.3",
"version": "1.2.4",
"type": "module",
"engines": {
"node": ">=20.0.0",
@@ -30,8 +30,9 @@
"ionic:build": "npm run build",
"ionic:serve": "npm run start",
"android": "npm run build && npx cap sync android",
"android:dev": "npx cap run android --live-reload",
"cap": "npm run build && npx cap sync",
"bump": "node bump-version.js",
"bump": "node bump-version.js patch",
"bump:minor": "node bump-version.js minor",
"bump:major": "node bump-version.js major",
"bump:patch": "node bump-version.js patch"

View File

@@ -25,7 +25,7 @@ function normalizeScannedImage(raw) {
export function useDocumentScanner() {
const isNativeScanner = Capacitor.isNativePlatform()
const scanDocument = async ({ maxDocuments = 1, quality = 90, letUserAdjustCrop = true } = {}) => {
const scanDocument = async ({ maxDocuments = 1, quality = 90, letUserAdjustCrop = false } = {}) => {
if (!isNativeScanner) return { image: null, cancelled: false }
try {

View File

@@ -30,6 +30,7 @@ import LearnMoreButton from './LearnMore'
import NotificationPickerField from './NotificationPickerField'
import ScanPanel from './ScanToTask/ScanPanel'
import { useDocumentScanner } from '../../hooks/useDocumentScanner'
import { localAIService } from '../../service/LocalAIService'
import PriorityPickerField from './PriorityPickerField'
import RepeatPickerField from './RepeatPickerField'
import RichTextEditor from './RichTextEditor'
@@ -107,9 +108,15 @@ const TaskInput = ({ onChoreUpdate, isModalOpen, onClose }) => {
const [attachments, setAttachments] = useState([])
const [draftId, setDraftId] = useState(() => crypto.randomUUID())
const [showScan, setShowScan] = useState(false)
const [scanAutoCapture, setScanAutoCapture] = useState(false)
const [pendingPhotoUrl, setPendingPhotoUrl] = useState(null)
const [llmAvailable, setLlmAvailable] = useState(false)
const { isNativeScanner } = useDocumentScanner()
useEffect(() => {
localAIService.isAvailable().then(setLlmAvailable)
}, [])
// Priority colors
const priorityColors = {
0: TASK_COLOR.NO_PRIORITY,
@@ -793,11 +800,15 @@ const TaskInput = ({ onChoreUpdate, isModalOpen, onClose }) => {
autoFocus
value={taskText}
isNativeScanner={isNativeScanner}
onScanClick={() => setShowScan(true)}
onPhotoSelected={dataUrl => {
onScanClick={llmAvailable ? () => {
setScanAutoCapture(true)
setShowScan(true)
} : undefined}
onPhotoSelected={llmAvailable ? dataUrl => {
setScanAutoCapture(false)
setPendingPhotoUrl(dataUrl)
setShowScan(true)
}}
} : undefined}
placeholder='Type your task...'
onChange={text => {
setTaskText(text)
@@ -984,10 +995,12 @@ const TaskInput = ({ onChoreUpdate, isModalOpen, onClose }) => {
{showScan && (
<ScanPanel
open
autoCapture={scanAutoCapture}
onTaskExtracted={handleTaskExtracted}
initialImageUrl={pendingPhotoUrl}
onClose={() => {
setShowScan(false)
setScanAutoCapture(false)
setPendingPhotoUrl(null)
}}
/>

View File

@@ -70,10 +70,16 @@ Output:
async function runNativeOCR(imageSource) {
const { Ocr } = await import('@jcesarmobile/capacitor-ocr')
let image = imageSource
if (imageSource.includes('/_capacitor_file_/')) {
// Convert Capacitor WebView file URL → native file:// URL the plugin can read
const image = imageSource.includes('/_capacitor_file_/')
? 'file://' + imageSource.replace(/^https?:\/\/localhost\/_capacitor_file_/, '')
: imageSource
image =
'file://' +
imageSource.replace(/^https?:\/\/localhost\/_capacitor_file_/, '')
} else if (imageSource.startsWith('data:')) {
// iOS document scanner returns base64; strip the data URI prefix for the native plugin
image = imageSource.split(',')[1] || imageSource
}
const result = await Ocr.process({ image })
return result.results.map(r => r.text).join('\n').trim()
@@ -512,7 +518,9 @@ const PhotoTaskModal = ({ open, onClose, onTaskExtracted }) => {
>
Retake
</Button>
{isNativeScanner && (
{isNativeScanner &&
capturedImage &&
!capturedImage.startsWith('data:') && (
<Button
variant='outlined'
color='primary'

View File

@@ -21,7 +21,7 @@ import { useScanToTask } from './useScanToTask'
* Flow: capture → (auto) processing → done [calls onTaskExtracted + onClose]
* → error [retake or cancel]
*/
const ScanPanel = ({ open, onTaskExtracted, onClose, initialImageUrl }) => {
const ScanPanel = ({ open, onTaskExtracted, onClose, initialImageUrl, autoCapture }) => {
const {
isNativeScanner,
phase,
@@ -47,6 +47,9 @@ const ScanPanel = ({ open, onTaskExtracted, onClose, initialImageUrl }) => {
useEffect(() => {
if (open) {
activate(initialImageUrl)
if (autoCapture && isNativeScanner && !initialImageUrl) {
handleNativeScan()
}
} else {
reset()
}

View File

@@ -50,9 +50,13 @@ Output:
async function runNativeOCR(imageSource) {
const { Ocr } = await import('@jcesarmobile/capacitor-ocr')
const image = imageSource.includes('/_capacitor_file_/')
? 'file://' + imageSource.replace(/^https?:\/\/localhost\/_capacitor_file_/, '')
: imageSource
let image = imageSource
if (imageSource.includes('/_capacitor_file_/')) {
image = 'file://' + imageSource.replace(/^https?:\/\/localhost\/_capacitor_file_/, '')
} else if (imageSource.startsWith('data:')) {
// iOS document scanner returns base64; strip the data URI prefix for the native plugin
image = imageSource.split(',')[1] || imageSource
}
const result = await Ocr.process({ image })
return result.results.map(r => r.text).join('\n').trim()
}
@@ -196,7 +200,9 @@ export function useScanToTask() {
return
}
setCapturedImage(image)
processImage(image, 'native')
// Native scanner already applied edge detection + crop; use browser OCR
// since the native OCR plugin requires a file path, not a base64 data URI
processImage(image, 'browser')
}, [scanDocument, processImage])
const retake = useCallback(() => {