Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/mobile-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ on:

jobs:
lint:
runs-on: macos-14
runs-on: macos-latest
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this fixes the failing 16.4 error. missed it from earlier

steps:
- uses: actions/checkout@v4
- name: Read and sanitize Node.js version
Expand Down Expand Up @@ -62,7 +62,7 @@ jobs:
working-directory: ./app

test:
runs-on: macos-14
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- name: Read and sanitize Node.js version
Expand Down Expand Up @@ -102,7 +102,7 @@ jobs:
run: yarn test
working-directory: ./app
build:
runs-on: macos-14
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- name: Read and sanitize Node.js version
Expand Down
8 changes: 5 additions & 3 deletions app/.eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -93,24 +93,26 @@ module.exports = {
],

// Prevent empty lines at the beginning and end of files, and limit consecutive empty lines
// Exception: allow one empty line after license header at file start

'no-multiple-empty-lines': [
'error',
{
max: 1,
maxEOF: 0,
maxBOF: 0,
maxBOF: 1, // Allow one empty line at beginning (for license header)
},
],
// Enforce empty line after header comments (but not at file start)
// Keep lines-around-comment rule disabled for normal comments
// License header newlines will be enforced by the check-license-headers.mjs script

'lines-around-comment': [
'error',
{
beforeBlockComment: false,
afterBlockComment: false,
beforeLineComment: false,
afterLineComment: false,
afterLineComment: false, // Keep disabled - license script handles this
allowBlockStart: true,
allowBlockEnd: false,
allowObjectStart: false,
Expand Down
21 changes: 12 additions & 9 deletions app/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { LoggerProvider } from './src/providers/loggerProvider';
import { NotificationTrackingProvider } from './src/providers/notificationTrackingProvider';
import { PassportProvider } from './src/providers/passportDataProvider';
import { RemoteConfigProvider } from './src/providers/remoteConfigProvider';
import { SelfClientProvider } from './src/providers/selfClientProvider';
import { initSentry, wrapWithSentry } from './src/Sentry';

initSentry();
Expand All @@ -25,15 +26,17 @@ function App(): React.JSX.Element {
<YStack flex={1} height="100%" width="100%">
<RemoteConfigProvider>
<LoggerProvider>
<AuthProvider>
<PassportProvider>
<DatabaseProvider>
<NotificationTrackingProvider>
<AppNavigation />
</NotificationTrackingProvider>
</DatabaseProvider>
</PassportProvider>
</AuthProvider>
<SelfClientProvider>
<AuthProvider>
<PassportProvider>
<DatabaseProvider>
<NotificationTrackingProvider>
<AppNavigation />
</NotificationTrackingProvider>
</DatabaseProvider>
</PassportProvider>
</AuthProvider>
</SelfClientProvider>
</LoggerProvider>
</RemoteConfigProvider>
</YStack>
Expand Down
7 changes: 4 additions & 3 deletions app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,16 @@
"ios": "yarn build:deps && react-native run-ios --scheme OpenPassport",
"ios:fastlane-debug": "yarn reinstall && bundle exec fastlane --verbose ios internal_test",
"lint": "yarn lint:headers && eslint .",
"lint:fix": "yarn lint:headers && eslint --fix .",
"lint:headers": "node scripts/check-duplicate-headers.cjs",
"lint:fix": "yarn lint:headers:fix && eslint --fix .",
"lint:headers": "node scripts/check-duplicate-headers.cjs && node scripts/check-license-headers.mjs --check",
"lint:headers:fix": "node scripts/check-duplicate-headers.cjs && node scripts/check-license-headers.mjs --fix",
"mobile-deploy": "node scripts/mobile-deploy-confirm.cjs both",
"mobile-deploy:android": "node scripts/mobile-deploy-confirm.cjs android",
"mobile-deploy:ios": "node scripts/mobile-deploy-confirm.cjs ios",
"mobile-local-deploy": "FORCE_UPLOAD_LOCAL_DEV=true node scripts/mobile-deploy-confirm.cjs both",
"mobile-local-deploy:android": "FORCE_UPLOAD_LOCAL_DEV=true node scripts/mobile-deploy-confirm.cjs android",
"mobile-local-deploy:ios": "FORCE_UPLOAD_LOCAL_DEV=true node scripts/mobile-deploy-confirm.cjs ios",
"nice": "yarn imports:fix && yarn lint:fix && yarn fmt:fix",
"nice": "yarn imports:fix && yarn lint:headers:fix && yarn lint:fix && yarn fmt:fix",
"reinstall": "yarn clean && yarn install && yarn install-app",
"release": "./scripts/release.sh",
"release:major": "./scripts/release.sh major",
Expand Down
1 change: 1 addition & 0 deletions app/scripts/check-duplicate-headers.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ function main() {
// Get all relevant files
const patterns = EXTENSIONS.map(ext => path.join('src', ext));
patterns.push(...EXTENSIONS.map(ext => path.join('tests', ext)));
patterns.push(...EXTENSIONS.map(ext => path.join('scripts', ext)));
patterns.push('*.ts', '*.tsx', '*.js', '*.jsx');

for (const pattern of patterns) {
Expand Down
127 changes: 127 additions & 0 deletions app/scripts/check-license-headers.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
#!/usr/bin/env node

/**
* Script to check and fix license header formatting
* Ensures there's a newline after license headers
*/

// SPDX-License-Identifier: BUSL-1.1; Copyright (c) 2025 Social Connect Labs, Inc.; Licensed under BUSL-1.1 (see LICENSE); Apache-2.0 from 2029-06-11
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixes issue with header license eslint fix not working


import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

const LICENSE_HEADER =
'// SPDX-License-Identifier: BUSL-1.1; Copyright (c) 2025 Social Connect Labs, Inc.; Licensed under BUSL-1.1 (see LICENSE); Apache-2.0 from 2029-06-11';

function findFiles(dir, extensions = ['.ts', '.tsx', '.js', '.jsx']) {
const files = [];

function traverse(currentDir) {
const items = fs.readdirSync(currentDir);

for (const item of items) {
const fullPath = path.join(currentDir, item);
const stat = fs.statSync(fullPath);

if (stat.isDirectory()) {
// Skip node_modules, .git, and other common directories
if (
!['node_modules', '.git', 'dist', 'build', 'coverage'].includes(item)
) {
traverse(fullPath);
}
} else if (extensions.some(ext => item.endsWith(ext))) {
files.push(fullPath);
}
}
}

traverse(dir);
return files;
}

function checkLicenseHeader(filePath) {
const content = fs.readFileSync(filePath, 'utf8');
const lines = content.split('\n');

// Check if first line is license header
if (lines[0] === LICENSE_HEADER) {
// Check if second line is empty (has newline after license header)
if (lines[1] !== '') {
return {
file: filePath,
issue: 'Missing newline after license header',
fixed: false,
};
}
}

return null;
}

function fixLicenseHeader(filePath) {
const content = fs.readFileSync(filePath, 'utf8');
const lines = content.split('\n');

if (lines[0] === LICENSE_HEADER && lines[1] !== '') {
// Insert empty line after license header
lines.splice(1, 0, '');
const fixedContent = lines.join('\n');
fs.writeFileSync(filePath, fixedContent, 'utf8');
return true;
}

return false;
}

function main() {
const args = process.argv.slice(2);
const isFix = args.includes('--fix');
const isCheck = args.includes('--check') || !isFix;

const projectRoot = path.resolve(__dirname, '..');
const files = findFiles(projectRoot);

const issues = [];

for (const file of files) {
const issue = checkLicenseHeader(file);
if (issue) {
issues.push(issue);

if (isFix) {
const fixed = fixLicenseHeader(file);
if (fixed) {
issue.fixed = true;
console.log(`✅ Fixed: ${file}`);
}
}
}
}

if (isCheck) {
if (issues.length === 0) {
console.log('✅ All license headers are properly formatted');
} else {
console.log(
`❌ Found ${issues.length} files with license header issues:`,
);
for (const issue of issues) {
console.log(` - ${issue.file}: ${issue.issue}`);
}
console.log('\nRun with --fix to automatically fix these issues');
process.exit(1);
}
} else if (isFix) {
const fixedCount = issues.filter(issue => issue.fixed).length;
console.log(`\n✅ Fixed ${fixedCount} files`);
}
}

if (import.meta.url === `file://${process.argv[1]}`) {
main();
}
9 changes: 5 additions & 4 deletions app/src/components/native/PassportCamera.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import React, { useCallback } from 'react';
import type { NativeSyntheticEvent, StyleProp, ViewStyle } from 'react-native';
import { PixelRatio, Platform, requireNativeComponent } from 'react-native';

import { extractMRZInfo } from '@selfxyz/mobile-sdk-alpha';
import { type SelfClient, useSelfClient } from '@selfxyz/mobile-sdk-alpha';

import { RCTFragment } from '@/components/native/RCTFragment';

Expand Down Expand Up @@ -47,14 +47,15 @@ export interface PassportCameraProps {
isMounted: boolean;
onPassportRead: (
error: Error | null,
mrzData?: ReturnType<typeof extractMRZInfo>,
mrzData?: ReturnType<SelfClient['extractMRZInfo']>,
) => void;
}

export const PassportCamera: React.FC<PassportCameraProps> = ({
onPassportRead,
isMounted,
}) => {
const selfClient = useSelfClient();
const _onError = useCallback(
(
event: NativeSyntheticEvent<{
Expand Down Expand Up @@ -93,7 +94,7 @@ export const PassportCamera: React.FC<PassportCameraProps> = ({
return;
}
if (typeof event.nativeEvent.data === 'string') {
onPassportRead(null, extractMRZInfo(event.nativeEvent.data));
onPassportRead(null, selfClient.extractMRZInfo(event.nativeEvent.data));
} else {
onPassportRead(null, {
passportNumber: event.nativeEvent.data.documentNumber,
Expand All @@ -117,7 +118,7 @@ export const PassportCamera: React.FC<PassportCameraProps> = ({
});
}
},
[onPassportRead, isMounted],
[onPassportRead, isMounted, selfClient],
);

if (Platform.OS === 'ios') {
Expand Down
17 changes: 14 additions & 3 deletions app/src/components/native/PassportCamera.web.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,32 @@

import React, { useCallback, useEffect } from 'react';

import type { extractMRZInfo } from '@selfxyz/mobile-sdk-alpha';
import { type SelfClient, useSelfClient } from '@selfxyz/mobile-sdk-alpha';

// TODO: Web find a lightweight ocr or mrz scanner.

export interface PassportCameraProps {
isMounted: boolean;
onPassportRead: (
error: Error | null,
mrzData?: ReturnType<typeof extractMRZInfo>,
mrzData?: ReturnType<SelfClient['extractMRZInfo']>,
) => void;
}

export const PassportCamera: React.FC<PassportCameraProps> = ({
onPassportRead,
isMounted,
}) => {
const selfClient = useSelfClient();
const _onPassportRead = useCallback(
(mrz: string) => {
if (!isMounted) {
return;
}
onPassportRead(null, selfClient.extractMRZInfo(mrz));
},
[onPassportRead, isMounted, selfClient],
);
const handleError = useCallback(() => {
if (!isMounted) {
return;
Expand All @@ -28,6 +38,7 @@ export const PassportCamera: React.FC<PassportCameraProps> = ({

// Web stub - no functionality yet
useEffect(() => {
void _onPassportRead; // noop until web implementation exists
// Simulate that the component is not ready for web
if (isMounted) {
console.warn('PassportCamera: Web implementation not yet available');
Expand All @@ -37,7 +48,7 @@ export const PassportCamera: React.FC<PassportCameraProps> = ({
}, 100);
return () => clearTimeout(timer);
}
}, [isMounted, handleError]);
}, [isMounted, handleError, _onPassportRead]);

return (
<div
Expand Down
Loading
Loading